chore(foundation): Wave 1 scaffolding — CI gate / Dockerfile / .env.example
CI (SIAX Cloud) / quality (push) Failing after 34s
CI (SIAX Cloud) / security (push) Failing after 36s

This commit is contained in:
SIAX Foundation Bot
2026-07-20 00:01:37 +02:00
parent 4a164c5494
commit bd5e309851
4 changed files with 235 additions and 5 deletions
+35
View File
@@ -0,0 +1,35 @@
# Keep the build context small + reproducible. Do NOT exclude app source dirs
# (app/, components/, hooks/, lib/, packages/, src/, styles/, public/) -- next
# build needs them.
node_modules
.next
out
build
.git
.gitea
.github
# local env / secrets -- never ship into an image
.env
.env.*
!.env.example
.vercel
*.tsbuildinfo
next-env.d.ts
# python services + their large sample dumps are not part of the Next build
**/__pycache__
*.pyc
services/scrapers/output
# editor / OS noise
.DS_Store
*.log
npm-debug.log*
.pnpm-debug.log*
# the Docker files themselves
Dockerfile
.dockerignore
+61
View File
@@ -0,0 +1,61 @@
# =============================================================================
# 0pt web console -- example environment
# =============================================================================
# Copy to `.env` (gitignored) and fill in real values. Every key below is
# actually referenced by the code (derived from `process.env.*` usage); nothing
# here is invented. All secrets are placeholders -- do NOT commit real values.
#
# The app is designed to build and boot with these UNSET: lib/db.ts and
# lib/stripe.ts are lazy + env-guarded, and the storage/payment/doc routes
# return HTTP 501 ("not configured") until the relevant keys are provided.
# ---- Core -------------------------------------------------------------------
# Public base URL of this app (used for absolute links / callbacks).
NEXT_PUBLIC_APP_URL=http://localhost:3000
# ---- Database (Postgres) ----------------------------------------------------
# First one set wins: DATABASE_URL -> POSTGRES_URL -> POSTGRES_PRISMA_URL.
DATABASE_URL=postgres://user:password@localhost:5432/opt
POSTGRES_URL=
POSTGRES_PRISMA_URL=
# ---- Queue / cache (Redis) --------------------------------------------------
REDIS_URL=redis://localhost:6379
# ---- Auth -------------------------------------------------------------------
# Session-signing secret (generate a long random string).
AUTH_SECRET=change-me-long-random-string
# Optional dev-only login shortcut key (leave unset in production).
DEV_LOGIN_KEY=
# Encryption key for stored DB-connection grants (/api/connections/db-grant).
GRANT_ENC_KEY=change-me-32-byte-key
# Shared token for internal/service-to-service calls.
INTERNAL_TOKEN=change-me-internal-token
# ---- Payments (Stripe) ------------------------------------------------------
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
# ---- Object storage (Cloudflare R2 / S3-compatible) -------------------------
# All three required to enable /api/uploads; otherwise it returns 501.
R2_ENDPOINT=https://<accountid>.r2.cloudflarestorage.com
R2_BUCKET=opt-uploads
R2_ACCESS_KEY_ID=change-me
# Optional local blob dir fallback used when R2 is not configured.
OPT_LOCAL_BLOB_DIR=./.opt-blobs
# ---- Document intelligence (Azure) ------------------------------------------
# Powers document extraction routes; unset => extraction "not configured".
DOC_INTELLIGENCE_ENDPOINT=https://<resource>.cognitiveservices.azure.com
DOC_INTELLIGENCE_KEY=change-me
DOC_INTELLIGENCE_API_VERSION=2024-11-30
# ---- Engine / integrations --------------------------------------------------
# URL of the 0pt data engine (services/api orchestrator).
ENGINE_URL=http://localhost:8000
# Odoo base URL for the bills/write-back adapter.
ODOO_BASE_URL=https://<your-odoo-host>
# ---- Test-only (tests/p0 real-infra suite; not needed to run the app) --------
# ST0RE_URL=
# ST0RE_TEST_TENANT_ID=
+68 -5
View File
@@ -1,10 +1,73 @@
name: Scraper CI
on: [push]
name: CI (SIAX Cloud)
# Self-hosted CI on SIAX Cloud (Gitea Actions, runners siax-runner-1/2),
# following the fleet-wide pattern (quality gate -> advisory security scan).
#
# WHAT THIS REPO ACTUALLY SHIPS (0pt web console): a Next.js 16 app. Its
# package.json exposes exactly four scripts -- build / dev / lint / start --
# so the BLOCKING quality gate below runs the one real, installed, functional
# build script (`pnpm run build` == `next build`), which type-checks and
# compiles the whole app.
#
# Deliberately NOT run here (honest gaps, additive Wave-1 scope only):
# * lint -> the "lint" script is `eslint .` but eslint is NOT in the
# dependency tree (zero eslint entries in pnpm-lock.yaml), so a
# blocking lint step would fail on "eslint: not found" for a
# missing-devDependency reason, not a code reason. Adding eslint
# is a source/package.json change (Wave 2/3), not Wave-1
# scaffolding. Re-enable once eslint is a real devDependency.
# * typecheck-> no `typecheck` script exists, and next.config.mjs sets
# `typescript.ignoreBuildErrors: true` on purpose; `next build`
# already runs the TS compiler.
# * test -> tests/p0/*.test.ts exist but there is NO test runner installed
# (no vitest/jest in pnpm-lock.yaml) and no `test` script, so
# there is nothing to invoke. Wiring a runner is Wave 2/3.
#
# The security job is intentionally advisory (separate job, `--exit-code 0`) so
# it never blocks merges, matching the fleet convention.
on:
push:
branches: ["**"]
pull_request:
workflow_dispatch:
schedule:
- cron: "0 4 * * *" # daily safety re-run so CI runs even without a push
jobs:
lint:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check Python files
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: Enable corepack + pin pnpm@9 (pnpm-lock.yaml is lockfileVersion 9.0)
run: |
echo "Scraper code verified"
corepack enable
corepack prepare pnpm@9 --activate
# --ignore-workspace: the repo's pnpm-workspace.yaml is a v0-export artifact
# that carries only an `allowBuilds:` block and NO `packages:` field, which
# makes pnpm abort with "packages field missing or empty". There are no real
# workspace members (only the root package.json), so ignoring the workspace
# file is a semantic no-op and lets the frozen install resolve cleanly.
- name: Install (frozen lockfile)
run: pnpm install --frozen-lockfile --ignore-workspace
- name: Build (next build -- blocking quality gate, no `|| true`)
# --ignore-workspace: `pnpm run` also re-parses pnpm-workspace.yaml and aborts
# on the missing `packages:` field unless the workspace is ignored (see above).
run: pnpm --ignore-workspace run build
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Install Trivy natively and scan the checked-out workspace directly.
# (The `docker run -v "$PWD:/repo" aquasec/trivy` pattern is a silent no-op
# on this Docker-outside-of-Docker runner -- $PWD inside the job container
# is not a real path on the Docker daemon host, so it scans nothing.)
- name: Install Trivy
run: |
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
- name: Trivy scan (vuln + secrets, advisory)
run: |
trivy fs --scanners vuln,secret --severity HIGH,CRITICAL --exit-code 0 .
+71
View File
@@ -0,0 +1,71 @@
# =============================================================================
# 0pt web console -- Dockerfile for Coolify (SIAX Cloud)
# =============================================================================
# This repo's deployable service is the Next.js 16 app at the repo root
# (package.json `start` == `next start`, liveness at GET /api/health). Unlike
# the tsx backend services elsewhere in the fleet (aud0/n0d/api0), a Next app
# needs a real build step, so this is a multi-stage build: install+build, then
# a slim runtime that runs the built output as the non-root `node` user.
#
# node:24-alpine matches the tag already proven in ../aud0 and ../api0. `wget`
# is added to the runtime stage so Coolify's container healthcheck has a binary
# to hit /api/health (bare node:*-alpine ships neither curl nor wget).
#
# next.config.mjs does NOT set `output: "standalone"` (that would be a source
# change, out of Wave-1 scope), so the runtime stage carries node_modules + the
# .next build output and runs `next start` directly.
# =============================================================================
# ---- build stage ------------------------------------------------------------
FROM node:24-alpine AS builder
WORKDIR /app
ENV NEXT_TELEMETRY_DISABLED=1
# Enable corepack (pnpm@9 pinned below to match pnpm-lock.yaml lockfileVersion 9.0).
RUN corepack enable
# Install deps against the frozen lockfile first for layer caching.
# --ignore-workspace: the repo's pnpm-workspace.yaml is a v0-export artifact that
# carries only an `allowBuilds:` block and NO `packages:` field, which makes pnpm
# abort with "packages field missing or empty". There are no real workspace
# members (only the root package.json), so ignoring the workspace file is
# semantically a no-op and lets the frozen install resolve the root deps cleanly.
COPY package.json pnpm-lock.yaml .npmrc ./
RUN corepack prepare pnpm@9 --activate && pnpm install --frozen-lockfile --ignore-workspace
# Build the app. The lib/* clients (db, stripe, ...) are lazy + env-guarded, so
# `next build` succeeds without any runtime secrets present.
# --ignore-workspace again here: `COPY . .` reintroduces the v0 pnpm-workspace.yaml,
# and `pnpm run` re-parses it (same "packages field missing" abort) unless ignored.
COPY . .
RUN pnpm --ignore-workspace run build
# ---- runtime stage ----------------------------------------------------------
FROM node:24-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV HOSTNAME=0.0.0.0
ENV PORT=3000
RUN apk add --no-cache wget
# Built artifacts + resolved dependencies from the build stage. .next is chowned
# so `next start` can write its runtime cache as the non-root user.
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder --chown=node:node /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/next.config.mjs ./next.config.mjs
USER node
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD wget -qO- http://127.0.0.1:3000/api/health >/dev/null 2>&1 || exit 1
# Invoke the next binary directly (pnpm/corepack are not set up in this stage).
CMD ["node_modules/.bin/next", "start"]