- Critical 1 (CSRF): core wrap refuses every POST/PUT/PATCH/DELETE whose Origin (Referer as fallback) is not publicUrl's origin or STC_ALLOWED_ORIGINS, and any body that is not application/json (multipart only on declared upload routes). Better Auth routes go through the same guard; only declared identity-provider callbacks (SAML ACS/SLO, OAuth callbacks) are exempt. Better Auth trustedOrigins use the same list. Session cookie stays Lax (Strict breaks OAuth/OIDC/SAML returns); documented. - Important 4 / learner minor 4: the first-owner claim and the role row are one transaction guarded by "no owner yet"; only a unique violation means learner; any other error fails the sign-up with 500 and removes the half-created user. Serialised per DB handle (Bun's SQLite connection). - Important 2 / learner minor 12: code zips stream entry by entry (large files stored in 256 KB chunks with a data descriptor and incremental CRC-32), so memory stays at one chunk; the cap is now 1 GiB (ZIP32). - Important 3 / learner minor 10: the e2e runs the production wiring (productFeatures order, AI drafts through the real sink) against a restricted course and asserts 404/401 for learner and anonymous on course, lesson, media, cover, code, zip, notes, progress, attempts, AI chat, library, search and AI status; a real Range request (206, 416); forged writes (foreign Origin, text/plain, no Origin) are 403 and the MCP test command never runs. - Minors 7-9: compose passes every documented variable; the health check uses STC_PORT; productFeatures checks the studio-before-AI order. Co-Authored-By: Claude Opus 5.5 <[email protected]>
53 lines
2.2 KiB
Docker
53 lines
2.2 KiB
Docker
# STCourses: the server and the SPA in one image. AGPL-3.0-only.
|
|
# Build: docker build -t stc-app .
|
|
# Run: see docker-compose.yml and docs/operations.md
|
|
FROM oven/bun:1.3
|
|
|
|
# Tools the importer and the studio call: ffmpeg/ffprobe (frames, durations), git (studio repos, code imports),
|
|
# unzip (Packt and code zips), python3 with PyMuPDF (PDF page images and text; STC_PYTHON points at it).
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends ffmpeg git unzip ca-certificates python3 python3-pymupdf \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& python3 -c "import pymupdf"
|
|
|
|
WORKDIR /app
|
|
|
|
# Dependencies first (cached unless a manifest or the lockfile changes).
|
|
COPY package.json bun.lock ./
|
|
COPY apps/server/package.json apps/server/
|
|
COPY apps/web/package.json apps/web/
|
|
COPY packages/contracts/package.json packages/contracts/
|
|
COPY packages/core/package.json packages/core/
|
|
COPY packages/course-format/package.json packages/course-format/
|
|
COPY packages/db/package.json packages/db/
|
|
COPY packages/ui/package.json packages/ui/
|
|
COPY packages/feature-learner/package.json packages/feature-learner/
|
|
COPY packages/feature-import/package.json packages/feature-import/
|
|
COPY packages/feature-studio/package.json packages/feature-studio/
|
|
COPY packages/feature-ai/package.json packages/feature-ai/
|
|
RUN bun install --production --frozen-lockfile
|
|
|
|
# Sources (Bun runs TypeScript directly; tsconfig.json carries the @stc/* path mapping).
|
|
COPY tsconfig.json ./
|
|
COPY apps apps
|
|
COPY packages packages
|
|
COPY overlays overlays
|
|
|
|
# /data holds the database (SQLite), imported courses, studio repos and caches. /courses is for course repos you mount.
|
|
RUN mkdir -p /data /courses && chown bun:bun /data
|
|
VOLUME /data
|
|
|
|
ENV NODE_ENV=production \
|
|
STC_PORT=3000 \
|
|
STC_DATA_DIR=/data \
|
|
STC_DATABASE_URL=sqlite:///data/stcourses.db \
|
|
STC_COURSE_DIRS=/app/overlays:/courses \
|
|
STC_IMPORT_ROOTS=/import \
|
|
STC_PYTHON=python3
|
|
|
|
USER bun
|
|
EXPOSE 3000
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
|
CMD bun -e "fetch('http://127.0.0.1:' + (process.env.STC_PORT || 3000) + '/api/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
|
|
CMD ["bun", "apps/server/src/main.ts"]
|