Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
**/node_modules
**/.next
.git
.github
coverage
*.log
.env
.env.*
49 changes: 17 additions & 32 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,52 +1,37 @@
FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS builder

WORKDIR /app

# Copy the prisma directory up front so that (a) any future postinstall
# hook from @prisma/client sees the schema, and (b) the prebuild script
# `prisma generate` fired by `npm run build` below can find it. Doing
# the layout this way means the only required write is the generated
# client under src/generated/prisma, which is already covered by the
# later `COPY src ./src` step below + the runner-stage copy.
COPY package*.json ./
COPY prisma ./prisma

RUN npm install

COPY tsconfig.json ./
COPY prisma.config.ts ./
COPY src ./src

RUN npm run build

FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS runner

WORKDIR /app

ENV NODE_ENV=production

# Order matters here for layer caching: COPY package*.json + npm install
# stay pinned together so that pure source-only changes do NOT invalidate
# the dependency install. --ignore-scripts skips the @prisma/client
# postinstall (we already COPY the generated client from the builder
# below), so the runner never needs the schema to install dependencies.
# The schema is then copied AFTER install so it is on disk for the
# `npx prisma db push` step the workflow runs against this container,
# while keeping the install layer cached whenever prisma/ is unchanged.
COPY package*.json ./
RUN npm install --omit=dev --ignore-scripts
# /app is created by root via WORKDIR. Give node ownership of the
# directory itself (cheap metadata change, empty dir) BEFORE switching
# users, so npm can create node_modules without a permission error.
RUN chown node:node /app

# Copy the prisma schema into the runner. The boot-and-check-health step
# in .github/workflows/ci.yml runs `npx prisma db push` against this
# container; without prisma/schema.prisma on disk, that command errors
# out and the 60-iteration /health poll times the job out.
COPY prisma ./prisma
COPY --chown=node:node package*.json ./

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/src/generated ./dist/generated
COPY --from=builder /app/prisma ./prisma
COPY prisma.config.ts ./
USER node

EXPOSE 3001
RUN npm install --omit=dev --ignore-scripts && npm cache clean --force

CMD ["npm", "start"]
COPY --chown=node:node prisma ./prisma
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/src/generated ./dist/generated
COPY --chown=node:node prisma.config.ts ./

HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD node -e "const http=require('http');const req=http.request({host:'127.0.0.1',port:3001,path:'/health',method:'GET',timeout:2000},res=>{process.exit(res.statusCode===200?0:1)});req.on('error',()=>process.exit(1));req.end();"

EXPOSE 3001
CMD ["npm", "start"]
8 changes: 8 additions & 0 deletions frontend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
.next
.git
.github
coverage
*.log
.env
.env.*
25 changes: 25 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS runner
WORKDIR /app
ENV NODE_ENV=production

RUN chown node:node /app
USER node

# Standalone output includes a minimal server.js + only the node_modules
# actually required at runtime — no need for a separate npm install here.
COPY --from=builder --chown=node:node /app/public ./public
COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static

HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD node -e "const http=require('http');const req=http.request({host:'127.0.0.1',port:3000,path:'/health',method:'GET',timeout:2000},res=>{process.exit(res.statusCode===200?0:1)});req.on('error',()=>process.exit(1));req.end();"

EXPOSE 3000
CMD ["node", "server.js"]
9 changes: 6 additions & 3 deletions frontend/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import path from "node:path";
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
// The workspace root lives one level above this directory. Pinning it here
// prevents Turbopack from inferring a wrong root when stray package-lock
// files exist outside the repo (e.g. ~/package-lock.json).
output: "standalone",

// The workspace root lives one level above this directory.
// Pin it to prevent Turbopack from inferring the wrong root.
turbopack: {
root: path.join(path.dirname(new URL(import.meta.url).pathname), ".."),
},

// Enable tree-shaking for icon/utility libraries to reduce per-route
// bundle sizes (Issue #1254).
experimental: {
Expand All @@ -17,6 +19,7 @@ const nextConfig: NextConfig = {
"@tanstack/react-virtual",
],
},

async redirects() {
return [
{
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/app/health/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { NextResponse } from "next/server";

export async function GET() {
return NextResponse.json({ status: "ok" }, { status: 200 });
}
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"verify-security": "node scripts/verify-security-setup.js"
},
"devDependencies": {
"@playwright/test": "^1.62.1",
"@vitest/coverage-v8": "^3.2.7",
"husky": "^9.1.7",
"lint-staged": "^17.0.7",
Expand Down
Loading