course-routes.js — student-facing: the full course tree through an enrollment (GET /api/student/programs/:id/courses) and the lesson completion marker (POST /api/student/activities/:id/complete, refused for anything but a manual_marker activity — a lab/assessment can never be faked complete through this route). admin-course-routes.js — instructor-facing, read-only (no WYSIWYG CMS this phase): program structure with instructor notes (GET /api/admin/programs/:id/courses) and per-student progress (GET /api/admin/students/:id/courses), scoped via requireStudentRelationshipAccess — the same self-paced-no-cohort rule Phase 5C's notification routes already established, not requireStudentCohortAccess (which would 403 a plain instructor out of every self-paced student). Dockerfile now also COPYs content/ alongside the existing scenarios/ — same 'read-only, never executed' reasoning as that line. Co-Authored-By: Claude Sonnet 5 <[email protected]>
47 lines
2.1 KiB
Docker
47 lines
2.1 KiB
Docker
# RootNode Platform API — lightweight, no k3s, no --privileged, no Lab terminal runtime.
|
|
# This is the fix for the Phase 2A "identity" instance problem: that was a full,
|
|
# --privileged Lab image booting a k3s cluster nobody used, just to serve roster/curriculum/
|
|
# progress APIs. This image never runs k3s and needs no elevated privileges at all.
|
|
#
|
|
# Built from the REPO ROOT as build context (see platform/docker-compose.dev.yml's
|
|
# `context: .. / dockerfile: platform/api/Dockerfile`), because it needs read-only access to
|
|
# scenarios/ and the frontend source — both live outside platform/api/'s own directory.
|
|
#
|
|
# Multi-stage: the root .dockerignore excludes frontend/dist/ from every build context (it's a
|
|
# build artifact, and the main Lab image always builds it fresh — see that Dockerfile's
|
|
# node-pty fix commit for why leaking host-built artifacts into an image is actively
|
|
# dangerous). Rather than special-case that, this image builds the frontend fresh too, the
|
|
# same way the Lab image does, then discards the Node/npm frontend toolchain from the final
|
|
# stage — the running image only ever needs the static output.
|
|
|
|
FROM node:20-slim AS frontend-build
|
|
WORKDIR /fe
|
|
COPY frontend/package.json frontend/vite.config.js ./
|
|
RUN npm install
|
|
COPY frontend/ ./
|
|
RUN npm run build
|
|
|
|
FROM node:20-slim
|
|
WORKDIR /app
|
|
COPY platform/api/package.json ./
|
|
RUN npm install --production
|
|
COPY platform/api/src/ ./src/
|
|
COPY platform/api/migrations/ ./migrations/
|
|
COPY platform/api/migrate-sqlite.js ./
|
|
|
|
# Read-only curriculum content — same source the Lab image ships, needed here purely for
|
|
# listing/progress-shaping (never executed; setup/validate/teardown stay Lab-only).
|
|
COPY scenarios/ ./scenarios/
|
|
|
|
# Phase 6A — course/module lesson prose (content.js). Git-tracked Markdown, read-only, same
|
|
# reasoning as scenarios/ above: this image only ever reads it, never executes anything from it.
|
|
COPY content/ ./content/
|
|
|
|
# Platform-shell + admin frontend, built in the stage above — served statically, no nginx
|
|
# needed for a lightweight Node API+static server.
|
|
COPY --from=frontend-build /fe/dist/ /frontend-dist/
|
|
ENV FRONTEND_DIST_DIR=/frontend-dist
|
|
|
|
EXPOSE 4300
|
|
CMD ["node", "src/server.js"]
|