From 8d3fcae99c5023479324d0e815c18bba1f94ea12 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 28 Jun 2026 11:07:23 -0400 Subject: [PATCH 01/52] initial docker setup --- backend/Dockerfile | 20 +++++++++++++++++++ docker-compose.yml | 43 +++++++++++++++++++++++++++++++++++++++++ frontend/Dockerfile | 26 +++++++++++++++++++++++++ frontend/Dockerfile.dev | 10 ++++++++++ 4 files changed, 99 insertions(+) create mode 100644 backend/Dockerfile create mode 100644 docker-compose.yml create mode 100644 frontend/Dockerfile create mode 100644 frontend/Dockerfile.dev diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 00000000..c54804fa --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.11-slim + +# Prevent Python from writing pyc files and buffering stdout +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +# Install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy project files +COPY . . + +# Expose the API port +EXPOSE 8000 + +# Use Uvicorn for ASGI to support the SSE live updates endpoint +CMD ["uvicorn", "api.asgi:application", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..0bc4cf75 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,43 @@ +version: "3.8" + +services: + redis: + image: redis:7-alpine + ports: + - "6379:6379" + + backend: + build: + context: ./backend + command: uvicorn api.asgi:application --host 0.0.0.0 --port 8000 --reload + volumes: + - ./backend:/app + ports: + - "8000:8000" + env_file: + - ./backend/.env + environment: + - LIVE_UPDATES_URL=redis://redis:6379/0 + depends_on: + - redis + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.dev + ports: + - "3000:3000" + volumes: + - ./frontend:/app + - /app/node_modules + - /app/.next + # env_file: + # - ./frontend/.env + environment: + - NEXT_PUBLIC_DEBUG=true + - NEXT_PUBLIC_API_URL=/server + - NEXT_PUBLIC_TEST_ENVIRONMENT=Local + - INTERNAL_API_URL=http://backend:8000 + - WATCHPACK_POLLING=true + depends_on: + - backend diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 00000000..c59558ad --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,26 @@ +FROM node:18-alpine AS builder + +WORKDIR /app + +# Install dependencies +COPY package.json package-lock.json* ./ +RUN npm ci + +# Copy project files and build +COPY . . +RUN npm run build + +# Production image +FROM node:18-alpine AS runner +WORKDIR /app + +ENV NODE_ENV=production + +# Copy built assets from builder +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static + +EXPOSE 3000 + +CMD ["node", "server.js"] \ No newline at end of file diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev new file mode 100644 index 00000000..ab7e00c4 --- /dev/null +++ b/frontend/Dockerfile.dev @@ -0,0 +1,10 @@ +FROM node:18-alpine + +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm install + +EXPOSE 3000 + +CMD ["npm", "run", "dev"] \ No newline at end of file From 5cd14f8ae62545f166c057053d4dfbe406bcb71c Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 28 Jun 2026 11:07:39 -0400 Subject: [PATCH 02/52] adjust configs --- backend/api/settings.py | 4 ++-- frontend/next.config.ts | 2 +- frontend/src/lib/utils/api/server-fetch.ts | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/api/settings.py b/backend/api/settings.py index e8f59a56..41c294cc 100644 --- a/backend/api/settings.py +++ b/backend/api/settings.py @@ -166,10 +166,10 @@ class ThrottleScopes: "schedule": crontab(hour=0, minute=0), # Every day at midnight }, } -CELERY_BROKER_URL = "redis://localhost:6379/0" +CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="redis://localhost:6379/1") # Live updates -LIVE_UPDATES_URL = "redis://localhost:6379/1" +LIVE_UPDATES_URL = env("LIVE_UPDATES_URL", default="redis://localhost:6379/0") LIVE_UPDATES_HEARTBEAT_SECONDS = 1 MAX_LIVE_CONNECTIONS_EVENT = 25 MAX_LIVE_CONNECTIONS_GLOBAL = 500 diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 6d6d98fa..6f063547 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -7,7 +7,7 @@ const nextConfig: NextConfig = { return [ { source: "/api/:path*", - destination: "http://127.0.0.1:8000/:path*/", + destination: `${process.env.INTERNAL_API_URL || "http://localhost:8000"}/:path*`, }, ]; } diff --git a/frontend/src/lib/utils/api/server-fetch.ts b/frontend/src/lib/utils/api/server-fetch.ts index 6eb777a7..0739dd24 100644 --- a/frontend/src/lib/utils/api/server-fetch.ts +++ b/frontend/src/lib/utils/api/server-fetch.ts @@ -32,7 +32,8 @@ export async function serverGet( params?: InferReq, options?: RequestInit, ): Promise> { - const baseUrl = process.env.NEXT_PUBLIC_API_URL; + const baseUrl = + process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL; let queryString = ""; if (params && Object.keys(params).length > 0) { From cb9fb35663c1f52fe55d97f0f2f461d58749fe1d Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:38:17 -0400 Subject: [PATCH 03/52] add named volumes --- docker-compose.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 0bc4cf75..e36efe3d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,8 @@ version: "3.8" services: redis: image: redis:7-alpine + volumes: + - redis_data:/data ports: - "6379:6379" @@ -29,10 +31,12 @@ services: - "3000:3000" volumes: - ./frontend:/app - - /app/node_modules - - /app/.next # env_file: # - ./frontend/.env + - node_modules:/app/node_modules + - next_cache:/app/.next + env_file: + - ./frontend/.env environment: - NEXT_PUBLIC_DEBUG=true - NEXT_PUBLIC_API_URL=/server @@ -41,3 +45,8 @@ services: - WATCHPACK_POLLING=true depends_on: - backend + +volumes: + redis_data: + node_modules: + next_cache: From 68fa8c6e08a74421bd4f52e616472831b4e22cd7 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:38:24 -0400 Subject: [PATCH 04/52] remove version from compose --- docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e36efe3d..4250506f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: redis: image: redis:7-alpine From ae695e8f3625eb4d220bc1b298139d50c29ab10a Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:38:58 -0400 Subject: [PATCH 05/52] update frontend env configs --- docker-compose.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4250506f..7602c706 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,17 +29,14 @@ services: - "3000:3000" volumes: - ./frontend:/app - # env_file: - # - ./frontend/.env - node_modules:/app/node_modules - next_cache:/app/.next env_file: - ./frontend/.env environment: - - NEXT_PUBLIC_DEBUG=true - - NEXT_PUBLIC_API_URL=/server - - NEXT_PUBLIC_TEST_ENVIRONMENT=Local + # The API URL for both the client and the Next.js server to communicate with the backend - INTERNAL_API_URL=http://backend:8000 + # For Next.js to detect file changes in Docker - WATCHPACK_POLLING=true depends_on: - backend From cff933c233885b60b954ebf927f7a225ad4f558c Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:46:36 -0400 Subject: [PATCH 06/52] update dockerfiles --- backend/Dockerfile | 10 +++------- frontend/Dockerfile | 9 ++++++--- frontend/Dockerfile.dev | 6 ++++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index c54804fa..ddfa63bd 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim +FROM python:3.13-slim # Prevent Python from writing pyc files and buffering stdout ENV PYTHONDONTWRITEBYTECODE=1 @@ -6,15 +6,11 @@ ENV PYTHONUNBUFFERED=1 WORKDIR /app -# Install dependencies COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt +RUN pip install --upgrade pip \ + && pip install --no-cache-dir -r requirements.txt -# Copy project files COPY . . - -# Expose the API port EXPOSE 8000 -# Use Uvicorn for ASGI to support the SSE live updates endpoint CMD ["uvicorn", "api.asgi:application", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index c59558ad..310c3a66 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,7 @@ -FROM node:18-alpine AS builder +# This Dockerfile is used to build the production image for the frontend. + +# BUILD IMAGE +FROM node:22-slim AS builder WORKDIR /app @@ -10,8 +13,8 @@ RUN npm ci COPY . . RUN npm run build -# Production image -FROM node:18-alpine AS runner +# PRODUCTION IMAGE +FROM node:22-slim AS runner WORKDIR /app ENV NODE_ENV=production diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index ab7e00c4..b0c3dbf7 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -1,9 +1,11 @@ -FROM node:18-alpine +# This Dockerfile is used to build the development image for the frontend. + +FROM node:22-slim WORKDIR /app COPY package.json package-lock.json* ./ -RUN npm install +RUN npm ci EXPOSE 3000 From 8b9d3baae11cc9b935561fe91f0e30776cb0e321 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:26:31 -0400 Subject: [PATCH 07/52] add trailing slash to destination --- frontend/next.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 6f063547..e8dccbde 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -7,7 +7,7 @@ const nextConfig: NextConfig = { return [ { source: "/api/:path*", - destination: `${process.env.INTERNAL_API_URL || "http://localhost:8000"}/:path*`, + destination: `${process.env.INTERNAL_API_URL || "http://localhost:8000"}/:path*/`, }, ]; } From 8e101dafe69339cf5915356c7cd7d9cc8a0f13dd Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:26:48 -0400 Subject: [PATCH 08/52] start scripts --- docker-compose.yml | 1 + frontend/Dockerfile.dev | 2 +- frontend/start.sh | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 frontend/start.sh diff --git a/docker-compose.yml b/docker-compose.yml index 7602c706..5a1b8980 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,6 +38,7 @@ services: - INTERNAL_API_URL=http://backend:8000 # For Next.js to detect file changes in Docker - WATCHPACK_POLLING=true + command: ["sh", "./start.sh"] depends_on: - backend diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index b0c3dbf7..a094bb6e 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -5,7 +5,7 @@ FROM node:22-slim WORKDIR /app COPY package.json package-lock.json* ./ -RUN npm ci +RUN npm install EXPOSE 3000 diff --git a/frontend/start.sh b/frontend/start.sh new file mode 100644 index 00000000..59a835f9 --- /dev/null +++ b/frontend/start.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +echo "Checking and syncing node_modules..." +npm install + +echo "Starting Next.js..." +exec npm run dev From ea139bd7e69ce7136548385c7f8e30915a560b47 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:26:59 -0400 Subject: [PATCH 09/52] makefile --- Makefile | 48 +++++++++++++++++++++++++++++++++++++++++++++ scripts/print-ip.js | 9 +++++++++ 2 files changed, 57 insertions(+) create mode 100644 Makefile create mode 100644 scripts/print-ip.js diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..00785b2b --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# Makefile + +.PHONY: up down restart logs-api logs-web shell-api shell-web migrate makemigrations + +# Start the environment in the background +up: + @docker-compose up -d --build && \ + echo "" && \ + echo "Plancake Industries" && \ + echo " - Local: http://localhost:3000" && \ + echo " - Network: $$(node scripts/print-ip.js)" && \ + echo "" + +# Stop the environment completely +down: + docker-compose down + +# Restart the containers quickly +restart: + docker-compose restart + +# --- LOGS --- + +# Stream the Django backend logs +logs-api: + docker-compose logs -f backend + +# Stream the Next.js frontend logs +logs-web: + docker-compose logs -f frontend + +# --- SHELLS & COMMANDS --- + +# Open a terminal inside the Django container +shell-api: + docker-compose exec backend /bin/bash + +# Open a terminal inside the Next.js container +shell-web: + docker-compose exec frontend /bin/sh + +# Run Django migrations inside the running container +migrate: + docker-compose exec backend python manage.py migrate + +# Generate new Django migrations inside the running container +makemigrations: + docker-compose exec backend python manage.py makemigrations \ No newline at end of file diff --git a/scripts/print-ip.js b/scripts/print-ip.js new file mode 100644 index 00000000..89d562e5 --- /dev/null +++ b/scripts/print-ip.js @@ -0,0 +1,9 @@ +import os from "os"; + +for (const interfaces of Object.values(os.networkInterfaces())) { + for (const iface of interfaces ?? []) { + if (iface.family === "IPv4" && !iface.internal) { + console.log(`http://${iface.address}:3000`); + } + } +} From d5204cd317886f14dd094406d6c0e369bac65f64 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:01:20 -0400 Subject: [PATCH 10/52] switch celery and redis --- backend/api/settings.py | 4 ++-- docker-compose.yml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/api/settings.py b/backend/api/settings.py index 41c294cc..2771c178 100644 --- a/backend/api/settings.py +++ b/backend/api/settings.py @@ -166,10 +166,10 @@ class ThrottleScopes: "schedule": crontab(hour=0, minute=0), # Every day at midnight }, } -CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="redis://localhost:6379/1") +CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="redis://localhost:6379/0") # Live updates -LIVE_UPDATES_URL = env("LIVE_UPDATES_URL", default="redis://localhost:6379/0") +LIVE_UPDATES_URL = env("LIVE_UPDATES_URL", default="redis://localhost:6379/1") LIVE_UPDATES_HEARTBEAT_SECONDS = 1 MAX_LIVE_CONNECTIONS_EVENT = 25 MAX_LIVE_CONNECTIONS_GLOBAL = 500 diff --git a/docker-compose.yml b/docker-compose.yml index 5a1b8980..450feb15 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,8 @@ services: env_file: - ./backend/.env environment: - - LIVE_UPDATES_URL=redis://redis:6379/0 + - CELERY_BROKER_URL=redis://redis:6379/0 + - LIVE_UPDATES_URL=redis://redis:6379/1 depends_on: - redis From 7ace97f276abe2d9eabaee42c97f94af795e2787 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:01:28 -0400 Subject: [PATCH 11/52] update post baseurl --- frontend/src/lib/utils/api/server-fetch.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/utils/api/server-fetch.ts b/frontend/src/lib/utils/api/server-fetch.ts index 0739dd24..74fa8b42 100644 --- a/frontend/src/lib/utils/api/server-fetch.ts +++ b/frontend/src/lib/utils/api/server-fetch.ts @@ -70,7 +70,9 @@ export async function serverPost( body?: InferReq, options?: RequestInit, ): Promise> { - const baseUrl = process.env.NEXT_PUBLIC_API_URL; + // Inside serverGet and serverPost: + const baseUrl = + process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL; const url = `${baseUrl}${endpoint.url}`; const cookieString = await getAuthCookieString(); const forwardedHeaders = await getForwardedHeaders(); From 2bd10ff522075c68f7d8b4236ff2595ec328ee4b Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Mon, 29 Jun 2026 15:25:06 -0400 Subject: [PATCH 12/52] update dockerfiles and add dockerignore --- backend/.dockerignore | 55 +++++++++++++++++ backend/Dockerfile | 10 +++- frontend/.dockerignore | 129 ++++++++++++++++++++++++++++++++++++++++ frontend/Dockerfile | 41 +++++++++---- frontend/Dockerfile.dev | 2 + 5 files changed, 226 insertions(+), 11 deletions(-) create mode 100644 backend/.dockerignore create mode 100644 frontend/.dockerignore diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 00000000..081b2a71 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,55 @@ +# Python Bytecode +__pycache__/ +*.py[cod] +*.pyc +*.pyo +*$py.class +*.so + +# Documentation +*.md +docs/ + +# Virtual Environments +venv/ +.venv/ +env/ +.env/ + +# Git and OS files +.git/ +.DS_Store +Thumbs.db + +# Environment variables (only commit template files) +.env +.env*.local +.env.development +.env.test +.env.production.local + +# Docker configuration files (not needed inside build context) +Dockerfile* +.dockerignore +compose.yaml +compose.yml +docker-compose*.yaml +docker-compose*.yml + +# Logs and Databases +*.db +*.log +db.sqlite3 +db.sqlite3-journal + +# Testing and Coverage +.pytest_cache/ +.tox/ +coverage/ +htmlcov/ +.coverage +.coverage.* + +# Developer Tools +.vscode/ +.idea/ diff --git a/backend/Dockerfile b/backend/Dockerfile index ddfa63bd..ab1458a0 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,3 +1,5 @@ +# This Dockerfile is used to build the production and development image for the backend. + FROM python:3.13-slim # Prevent Python from writing pyc files and buffering stdout @@ -7,10 +9,16 @@ ENV PYTHONUNBUFFERED=1 WORKDIR /app COPY requirements.txt . + RUN pip install --upgrade pip \ && pip install --no-cache-dir -r requirements.txt COPY . . + +# Create a non-root user and switch to it +RUN addgroup --system appgroup && adduser --system --group user +USER user + EXPOSE 8000 -CMD ["uvicorn", "api.asgi:application", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file +CMD ["uvicorn", "api.asgi:application", "--host", "0.0.0.0", "--port", "8000"] diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 00000000..f3160296 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,129 @@ +# Dependencies (installed inside Docker, never copied) +node_modules/ +.pnpm-store/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Next.js build outputs (always generated during `next build`) +.next/ +out/ +dist/ +build/ +.vercel/ + +# Tests and testing output (not needed in production images) +coverage/ +.nyc_output/ +__tests__/ +__mocks__/ +jest/ +cypress/ +cypress/screenshots/ +cypress/videos/ +playwright-report/ +test-results/ +.vitest/ +vitest.config.* +jest.config.* +cypress.config.* +playwright.config.* +*.test.* +*.spec.* + +# Local development and editor files +.git/ +.gitignore +.gitattributes +.vscode/ +.idea/ +*.swp +*.swo +*~ +*.log + +# Environment variables (only commit template files) +.env +.env*.local +.env.development +.env.test +.env.production.local + +# Docker configuration files (not needed inside build context) +Dockerfile* +.dockerignore +compose.yaml +compose.yml +docker-compose*.yaml +docker-compose*.yml + +# Documentation +*.md +docs/ + +# CI/CD configuration files +.github/ +.gitlab-ci.yml +.travis.yml +.circleci/ +Jenkinsfile + +# Cache directories and temporary data +.cache/ +.parcel-cache/ +.eslintcache +.stylelintcache +.swc/ +.turbo/ +.tmp/ +.temp/ + +# TypeScript build metadata +*.tsbuildinfo + +# Sensitive or unnecessary configuration files +*.pem +.editorconfig +.prettierrc* +prettier.config.* +.eslintrc* +eslint.config.* +.stylelintrc* +stylelint.config.* +.babelrc* +*.iml +*.ipr +*.iws + +# OS-specific junk +.DS_Store +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +Desktop.ini + +# AI/ML tool metadata and configs +.cursor/ +.cursorrules +.copilot/ +.copilotignore +.github/copilot/ +.gemini/ +.anthropic/ +.kiro +.claude +AGENTS.md +.agents/ + +# AI-generated temp files +*.aider* +*.copilot* +*.chatgpt* +*.claude* +*.gemini* +*.openai* +*.anthropic* \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 310c3a66..40c17c13 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,7 +1,11 @@ # This Dockerfile is used to build the production image for the frontend. -# BUILD IMAGE -FROM node:22-slim AS builder +# The node version is specified as a build argument for ease of updating to the most +# stable version of Node.js in the future. +ARG NODE_VERSION=22-slim + +## INSTALL DEPENDENCIES +FROM node:${NODE_VERSION} AS dependencies WORKDIR /app @@ -9,21 +13,38 @@ WORKDIR /app COPY package.json package-lock.json* ./ RUN npm ci -# Copy project files and build +## BUILD IMAGE +FROM node:${NODE_VERSION} AS builder + +WORKDIR /app + +COPY --from=dependencies /app/node_modules ./node_modules + COPY . . + RUN npm run build -# PRODUCTION IMAGE -FROM node:22-slim AS runner +## RUN APPLICATION +FROM node:${NODE_VERSION} AS runner + WORKDIR /app ENV NODE_ENV=production -# Copy built assets from builder -COPY --from=builder /app/public ./public -COPY --from=builder /app/.next/standalone ./ -COPY --from=builder /app/.next/static ./.next/static +# Copy production assets +COPY --from=builder --chown=node:node /app/public ./public + +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown node:node .next + +# Automatically leverage output traces to reduce image size +COPY --from=builder --chown=node:node /app/.next/standalone ./ +COPY --from=builder --chown=node:node /app/.next/static ./.next/static + +# Use the non-root "node" user +USER node EXPOSE 3000 -CMD ["node", "server.js"] \ No newline at end of file +CMD ["node", "server.js"] diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index a094bb6e..83889793 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -7,6 +7,8 @@ WORKDIR /app COPY package.json package-lock.json* ./ RUN npm install +COPY . . + EXPOSE 3000 CMD ["npm", "run", "dev"] \ No newline at end of file From d449766822b73b4c42f3f82d9432ffa541fe5d82 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Mon, 29 Jun 2026 15:31:05 -0400 Subject: [PATCH 13/52] cleanup makefile --- Makefile | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 00785b2b..f94e6e6f 100644 --- a/Makefile +++ b/Makefile @@ -1,48 +1,50 @@ -# Makefile .PHONY: up down restart logs-api logs-web shell-api shell-web migrate makemigrations -# Start the environment in the background +# Starts the enviroment in the background and prints out the URLs for the local and +# network access. The command includes a build flag to ensure that the latest changes +# are reflected in the containers. up: - @docker-compose up -d --build && \ + docker compose up -d --build && \ echo "" && \ echo "Plancake Industries" && \ echo " - Local: http://localhost:3000" && \ echo " - Network: $$(node scripts/print-ip.js)" && \ echo "" -# Stop the environment completely +# Stops the environment completely and removes the connected containers, networks, +# and volumes. down: - docker-compose down + docker compose down -# Restart the containers quickly +# Restarts the containers without rebuilding them. Use this for things like env changes. restart: - docker-compose restart + docker compose restart # --- LOGS --- # Stream the Django backend logs logs-api: - docker-compose logs -f backend + docker compose logs -f backend # Stream the Next.js frontend logs logs-web: - docker-compose logs -f frontend + docker compose logs -f frontend # --- SHELLS & COMMANDS --- -# Open a terminal inside the Django container -shell-api: - docker-compose exec backend /bin/bash - -# Open a terminal inside the Next.js container +# Open a terminal inside the frontend container shell-web: - docker-compose exec frontend /bin/sh + docker compose exec frontend /bin/sh + +# Open a terminal inside the backend container +shell-api: + docker compose exec backend /bin/bash # Run Django migrations inside the running container migrate: - docker-compose exec backend python manage.py migrate + docker compose exec backend python manage.py migrate # Generate new Django migrations inside the running container makemigrations: - docker-compose exec backend python manage.py makemigrations \ No newline at end of file + docker compose exec backend python manage.py makemigrations From 7ce5ee563a92004bed800a6506e6ac5071bd5322 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Mon, 29 Jun 2026 15:35:27 -0400 Subject: [PATCH 14/52] add make url --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f94e6e6f..85459b56 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # network access. The command includes a build flag to ensure that the latest changes # are reflected in the containers. up: - docker compose up -d --build && \ + @docker compose up -d --build && \ echo "" && \ echo "Plancake Industries" && \ echo " - Local: http://localhost:3000" && \ @@ -33,6 +33,14 @@ logs-web: # --- SHELLS & COMMANDS --- +# Print out the URLs for the local and network access. +url: + @echo "" && \ + echo "Plancake Industries" && \ + echo " - Local: http://localhost:3000" && \ + echo " - Network: $$(node scripts/print-ip.js)" && \ + echo "" + # Open a terminal inside the frontend container shell-web: docker compose exec frontend /bin/sh From d74af0e4dfbd9a68cdaa30dd15306cd51c3afade Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:34:04 -0400 Subject: [PATCH 15/52] update readme with docker instructions --- Makefile | 4 ++-- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 85459b56..2d50e611 100644 --- a/Makefile +++ b/Makefile @@ -43,11 +43,11 @@ url: # Open a terminal inside the frontend container shell-web: - docker compose exec frontend /bin/sh + docker compose exec -it frontend /bin/sh # Open a terminal inside the backend container shell-api: - docker compose exec backend /bin/bash + docker compose exec -it backend /bin/bash # Run Django migrations inside the running container migrate: diff --git a/README.md b/README.md index 8d454565..2882aa8b 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,56 @@ A scheduling website that solves the logistics problem of figuring out when everyone is available to meet. -This is a monorepo holding both the frontend and backend code for the website. Each folder has its own README for setup. +## Project Structure + +This is a monorepo containing both the frontend client and the backend API service. + +```bash +plancake/ +├── backend/ # Django API server +└── frontend/ # Next.js application +``` + +## Getting Started + +This project can be run either natively or via Docker. + +#### `.env` Files + +Copy the contents of the respective `.env.example` files in each folder into its own `.env`. The same `env` setup is used both natively and in Docker. + +For more details about the database connection, take a look at the backend README linked below. + +### Native Setup + +For native setup, follow the setup instructions in these READMEs: + +- [Backend Setup](backend/README.md) +- [Frontend Setup](frontend/README.md) + +### Docker Setup + +The project uses Docker Compose and a set of Make commands that help setup the developement enviroment. It includes the frontend, backend, and Redis services required to run and support live updates. + +#### Make Commands + +| Task | Command | Description | +| :---------------- | :-------------------- | :---------------------------------------------------- | +| Start/Resume | `make up` | Starts the containers in the background with a build | +| Stop | `make down` | Stops and removes containers and networks | +| Restart | `make restart` | Restarts containers (useful for `.env` changes) | +| Backend Logs | `make logs-api` | Streams the backend Django logs | +| Frontend Logs | `make logs-web` | Streams the frontend Next.js logs | +| URLs | `make url` | Displays local and network URLs | +| API Shell | `make shell-api` | Opens a bash shell in the backend container | +| Frontend Shell | `make shell-web` | Opens a bash shell in the frontend container | +| Run Migrations | `make migrate` | Runs Django migrations | +| Create Migrations | `make makemigrations` | Creates new Django migration inside running container | + +_(Note: To exit log streams, just `Ctrl + C`. To exit shell sessions, type `exit` or `Ctrl + D`)_ + +#### Named Volumes + +Named volumes are used to prevent the build up with dangling volumes on your machine every time `make down` is run. It persists cached data (like `node_modules` and the redis cache) through each new container instance. + +If you ever need to completely wipe the data and start with a clean slate, the volumes must be removed with `docker compose down -v`. From a583ab7e1cd41cfda9bebb75933a0bb304659ce7 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Thu, 2 Jul 2026 13:02:55 -0400 Subject: [PATCH 16/52] add help command --- Makefile | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2d50e611..99efb142 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,19 @@ -.PHONY: up down restart logs-api logs-web shell-api shell-web migrate makemigrations +.PHONY: help up down restart logs-api logs-web shell-api shell-web migrate makemigrations + +help: + @echo "Usage: make " + @echo "" + @echo "Targets:" + @echo " up Starts the environment in the background and prints out the URLs for local and network access." + @echo " down Stops the environment completely and removes the connected containers, networks, and volumes." + @echo " restart Restarts the containers without rebuilding them. Use this for things like env changes." + @echo " logs-api Stream the Django backend logs." + @echo " logs-web Stream the Next.js frontend logs." + @echo " shell-api Open a terminal inside the backend container." + @echo " shell-web Open a terminal inside the frontend container." + @echo " migrate Run Django migrations inside the running container." + @echo " makemigrations Generate new Django migrations inside the running container." # Starts the enviroment in the background and prints out the URLs for the local and # network access. The command includes a build flag to ensure that the latest changes From b7bbf559f20d7180c34619628c2b4996f8f3943b Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:09:20 -0400 Subject: [PATCH 17/52] add backend start.sh --- backend/start.sh | 10 ++++++++++ docker-compose.yml | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 backend/start.sh diff --git a/backend/start.sh b/backend/start.sh new file mode 100644 index 00000000..56f387b4 --- /dev/null +++ b/backend/start.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +echo "Checking for Python dependency updates..." +pip install -r requirements.txt + +echo "Applying database migrations..." +python manage.py migrate + +echo "Starting Uvicorn server..." +exec uvicorn api.asgi:application --host 0.0.0.0 --port 8000 --reload diff --git a/docker-compose.yml b/docker-compose.yml index 450feb15..2985e134 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: backend: build: context: ./backend - command: uvicorn api.asgi:application --host 0.0.0.0 --port 8000 --reload + command: ["sh", "./start.sh"] volumes: - ./backend:/app ports: @@ -28,6 +28,7 @@ services: dockerfile: Dockerfile.dev ports: - "3000:3000" + command: ["sh", "./start.sh"] volumes: - ./frontend:/app - node_modules:/app/node_modules @@ -39,7 +40,6 @@ services: - INTERNAL_API_URL=http://backend:8000 # For Next.js to detect file changes in Docker - WATCHPACK_POLLING=true - command: ["sh", "./start.sh"] depends_on: - backend From ee1ba11a0d8e8cef7d5de28279cdcf0daed2a557 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:09:47 -0400 Subject: [PATCH 18/52] update package versions --- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e8ecfd3d..4538ff5b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "frontend", - "version": "0.2.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "0.2.0", + "version": "0.5.0", "hasInstallScript": true, "dependencies": { "@microsoft/fetch-event-source": "^2.0.1", diff --git a/frontend/package.json b/frontend/package.json index 7a25ed67..dfb139f5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "0.2.0", + "version": "0.5.0", "private": true, "scripts": { "dev": "next dev --turbopack", From 1fdd2aa861b22ce0fc5e5de3e93eb0dbe0209e3b Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:21:00 -0400 Subject: [PATCH 19/52] add image to registry --- .github/workflows/publish-images.yml | 72 ++++++++++++++++++++++++++++ Makefile | 38 ++++++++++++--- docker-compose.yml | 2 + 3 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/publish-images.yml diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml new file mode 100644 index 00000000..47c97a33 --- /dev/null +++ b/.github/workflows/publish-images.yml @@ -0,0 +1,72 @@ +name: Build and Publish Docker Images + +on: + push: + branches: ["main", "v*.*.*"] + +# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. +permissions: + contents: read + packages: write + attestations: write + id-token: write + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # --- BACKEND --- + - name: Extract metadata for Backend + id: meta-backend + uses: docker/metadata-action@v5 + with: + images: ghcr.io/plan-cake/plancake-backend + tags: | + type=ref,event=branch + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Backend + uses: docker/build-push-action@v5 + with: + context: ./backend + push: true + tags: ${{ steps.meta-backend.outputs.tags }} + labels: ${{ steps.meta-backend.outputs.labels }} + platforms: linux/amd64,linux/arm64 + + # --- FRONTEND --- + - name: Extract metadata for Frontend + id: meta-frontend + uses: docker/metadata-action@v5 + with: + images: ghcr.io/plan-cake/plancake-frontend + tags: | + type=ref,event=branch + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Frontend + uses: docker/build-push-action@v5 + with: + context: ./frontend + file: ./frontend/Dockerfile.dev + push: true + tags: ${{ steps.meta-frontend.outputs.tags }} + labels: ${{ steps.meta-frontend.outputs.labels }} + platforms: linux/amd64,linux/arm64 diff --git a/Makefile b/Makefile index 99efb142..cd7aadfc 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,27 @@ .PHONY: help up down restart logs-api logs-web shell-api shell-web migrate makemigrations +# Determine the Docker image tag based on the current Git branch. If the branch is +# 'main', use 'latest' as the tag. Otherwise, retrieve the version from the frontend +# package.json and use it as the tag. +CURRENT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) +ifeq ($(CURRENT_BRANCH),main) + IMAGE_TAG := latest +else + # Get the version from the frontend package.json for identifying the Docker image. + PKG_VERSION := $(shell node -p "require('./frontend/package.json').version") + IMAGE_TAG := v$(PKG_VERSION) +endif + +# --- COMMANDS --- + help: @echo "Usage: make " @echo "" @echo "Targets:" @echo " up Starts the environment in the background and prints out the URLs for local and network access." - @echo " down Stops the environment completely and removes the connected containers, networks, and volumes." + @echo " build Creates a full local rebuild from scratch (bypasses the registry) and prints out the URLs for local and network access." + @echo " down Stops the environment completely and removes the connected containers and networks." @echo " restart Restarts the containers without rebuilding them. Use this for things like env changes." @echo " logs-api Stream the Django backend logs." @echo " logs-web Stream the Next.js frontend logs." @@ -15,19 +30,28 @@ help: @echo " migrate Run Django migrations inside the running container." @echo " makemigrations Generate new Django migrations inside the running container." -# Starts the enviroment in the background and prints out the URLs for the local and -# network access. The command includes a build flag to ensure that the latest changes -# are reflected in the containers. +# Pulls the image from the registry and starts the containers in the background. up: - @docker compose up -d --build && \ + @IMAGE_TAG=$(IMAGE_TAG) docker compose pull + @IMAGE_TAG=$(IMAGE_TAG) docker compose up -d && \ + echo "" && \ + echo "Using image tag $(IMAGE_TAG)" && \ + echo "" && \ + echo "Plancake Industries" && \ + echo " - Local: http://localhost:3000" && \ + echo " - Network: $$(node scripts/print-ip.js)" && \ + echo "" + +# Creates a full local rebuild from scratch (bypasses the registry) +build: + @IMAGE_TAG=$(IMAGE_TAG) docker compose up -d --build && \ echo "" && \ echo "Plancake Industries" && \ echo " - Local: http://localhost:3000" && \ echo " - Network: $$(node scripts/print-ip.js)" && \ echo "" -# Stops the environment completely and removes the connected containers, networks, -# and volumes. +# Stops the environment completely and removes the connected containers and networks. down: docker compose down diff --git a/docker-compose.yml b/docker-compose.yml index 2985e134..68b4bc86 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: - "6379:6379" backend: + image: ghcr.io/plan-cake/plancake-backend:${IMAGE_TAG:-latest} build: context: ./backend command: ["sh", "./start.sh"] @@ -23,6 +24,7 @@ services: - redis frontend: + image: ghcr.io/plan-cake/plancake-frontend:${IMAGE_TAG:-latest} build: context: ./frontend dockerfile: Dockerfile.dev From cee9b54055e76a244c2dc8d6e36408ef26695f2c Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:27:09 -0400 Subject: [PATCH 20/52] Update publish-images.yml --- .github/workflows/publish-images.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 47c97a33..5c346198 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -3,6 +3,7 @@ name: Build and Publish Docker Images on: push: branches: ["main", "v*.*.*"] + workflow_dispatch: # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. permissions: From 31fcd5553fe9e653d688d851e4db789988a7d3fc Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:30:54 -0400 Subject: [PATCH 21/52] Update publish-images.yml --- .github/workflows/publish-images.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 5c346198..918dfe87 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -3,6 +3,7 @@ name: Build and Publish Docker Images on: push: branches: ["main", "v*.*.*"] + pull_request: workflow_dispatch: # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. From 33ed63645ad7dca47a73eeb2d1ecd35ae44dab05 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:48:42 -0400 Subject: [PATCH 22/52] point registry to personal namespace --- .github/workflows/publish-images.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 918dfe87..2514f916 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -3,7 +3,6 @@ name: Build and Publish Docker Images on: push: branches: ["main", "v*.*.*"] - pull_request: workflow_dispatch: # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. @@ -39,7 +38,7 @@ jobs: id: meta-backend uses: docker/metadata-action@v5 with: - images: ghcr.io/plan-cake/plancake-backend + images: ghcr.io/mirmirmirr/plancake-backend tags: | type=ref,event=branch type=raw,value=latest,enable={{is_default_branch}} @@ -58,7 +57,7 @@ jobs: id: meta-frontend uses: docker/metadata-action@v5 with: - images: ghcr.io/plan-cake/plancake-frontend + images: ghcr.io/mirmirmirr/plancake-frontend tags: | type=ref,event=branch type=raw,value=latest,enable={{is_default_branch}} From 202511150d421ea8ada3213676af5d17da675f39 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 4 Jul 2026 19:33:36 -0400 Subject: [PATCH 23/52] switch frontend to alpine --- frontend/Dockerfile | 2 +- frontend/Dockerfile.dev | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 40c17c13..5d109cda 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -2,7 +2,7 @@ # The node version is specified as a build argument for ease of updating to the most # stable version of Node.js in the future. -ARG NODE_VERSION=22-slim +ARG NODE_VERSION=22-alpine ## INSTALL DEPENDENCIES FROM node:${NODE_VERSION} AS dependencies diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index 83889793..9b77a1c6 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -1,6 +1,6 @@ # This Dockerfile is used to build the development image for the frontend. -FROM node:22-slim +FROM node:22-alpine WORKDIR /app From ce2a60272966f184cb66af985d600163ed2cce0f Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 4 Jul 2026 19:33:59 -0400 Subject: [PATCH 24/52] add concurrency and cache --- .github/workflows/publish-images.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 2514f916..e7036d77 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -3,7 +3,11 @@ name: Build and Publish Docker Images on: push: branches: ["main", "v*.*.*"] - workflow_dispatch: + +# Cancel any in-progress builds if a new commit is pushed to the same branch +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. permissions: @@ -51,6 +55,8 @@ jobs: tags: ${{ steps.meta-backend.outputs.tags }} labels: ${{ steps.meta-backend.outputs.labels }} platforms: linux/amd64,linux/arm64 + cache-from: type=gha,scope=backend-${{ github.ref_name }} + cache-to: type=gha,mode=max,scope=backend-${{ github.ref_name }} # --- FRONTEND --- - name: Extract metadata for Frontend @@ -71,3 +77,5 @@ jobs: tags: ${{ steps.meta-frontend.outputs.tags }} labels: ${{ steps.meta-frontend.outputs.labels }} platforms: linux/amd64,linux/arm64 + cache-from: type=gha,scope=backend-${{ github.ref_name }} + cache-to: type=gha,mode=max,scope=backend-${{ github.ref_name }} From 820de5bd229f40eec98109e9183960f55b872b3a Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 18 Jul 2026 09:24:00 -0400 Subject: [PATCH 25/52] cache fixes --- .github/workflows/publish-images.yml | 4 ++-- Makefile | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index e7036d77..7b861b80 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -77,5 +77,5 @@ jobs: tags: ${{ steps.meta-frontend.outputs.tags }} labels: ${{ steps.meta-frontend.outputs.labels }} platforms: linux/amd64,linux/arm64 - cache-from: type=gha,scope=backend-${{ github.ref_name }} - cache-to: type=gha,mode=max,scope=backend-${{ github.ref_name }} + cache-from: type=gha,scope=frontend-${{ github.ref_name }} + cache-to: type=gha,mode=max,scope=frontend-${{ github.ref_name }} diff --git a/Makefile b/Makefile index cd7aadfc..7c00f95f 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,8 @@ else IMAGE_TAG := v$(PKG_VERSION) endif +IMAGE_TAG := latest + # --- COMMANDS --- help: @@ -93,4 +95,4 @@ migrate: # Generate new Django migrations inside the running container makemigrations: - docker compose exec backend python manage.py makemigrations + docker compose exec backend python manage.py makemigrations api From 67ae67648c81322c15836dc2734b8b03dd34f601 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 18 Jul 2026 09:24:10 -0400 Subject: [PATCH 26/52] create clean up script --- .github/workflows/clean-up.yml | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/clean-up.yml diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml new file mode 100644 index 00000000..d71844fe --- /dev/null +++ b/.github/workflows/clean-up.yml @@ -0,0 +1,38 @@ +name: Weekly Image Cleanup + +on: + schedule: + - cron: "0 0 * * 0" # run every sunday at midnight + workflow_dispatch: + +jobs: + clean-registry: + runs-on: ubuntu-latest + permissions: + packages: write + + steps: + - name: Clean up Plancake Frontend images + uses: snok/container-retention-policy@v3 + with: + account-type: org + token: ${{ secrets.GITHUB_TOKEN }} + image-names: plancake-frontend + cut-off: 1 week ago + timestamp-to-use: updated_at + keep-at-least: 3 + skip-untagged: false + token: ${{ secrets.GITHUB_TOKEN }} + dry-run: true + + - name: Clean up Plancake Backend images + uses: snok/container-retention-policy@v3 + with: + account-type: org + token: ${{ secrets.GITHUB_TOKEN }} + image-names: plancake-backend + cut-off: 1 week ago + timestamp-to-use: updated_at + keep-at-least: 3 + skip-untagged: false + dry-run: true From 02f439b7ec5c6d6b13250d0d84158245e8c7e257 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 18 Jul 2026 09:26:33 -0400 Subject: [PATCH 27/52] remove extra token --- .github/workflows/clean-up.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml index d71844fe..1e7c0afb 100644 --- a/.github/workflows/clean-up.yml +++ b/.github/workflows/clean-up.yml @@ -22,7 +22,6 @@ jobs: timestamp-to-use: updated_at keep-at-least: 3 skip-untagged: false - token: ${{ secrets.GITHUB_TOKEN }} dry-run: true - name: Clean up Plancake Backend images From 191d8ce37021e4871124535d18bc75c0d9d7dcc8 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 18 Jul 2026 09:36:36 -0400 Subject: [PATCH 28/52] update snok version --- .github/workflows/clean-up.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml index 1e7c0afb..eb121d89 100644 --- a/.github/workflows/clean-up.yml +++ b/.github/workflows/clean-up.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Clean up Plancake Frontend images - uses: snok/container-retention-policy@v3 + uses: snok/container-retention-policy@v3.1.0 with: account-type: org token: ${{ secrets.GITHUB_TOKEN }} @@ -25,7 +25,7 @@ jobs: dry-run: true - name: Clean up Plancake Backend images - uses: snok/container-retention-policy@v3 + uses: snok/container-retention-policy@v3.1.0 with: account-type: org token: ${{ secrets.GITHUB_TOKEN }} From 611d01de71b787b38616ca0626d3d8a1633cda67 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 18 Jul 2026 09:42:04 -0400 Subject: [PATCH 29/52] fix clean up parameters --- .github/workflows/clean-up.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml index eb121d89..c44f4446 100644 --- a/.github/workflows/clean-up.yml +++ b/.github/workflows/clean-up.yml @@ -15,23 +15,21 @@ jobs: - name: Clean up Plancake Frontend images uses: snok/container-retention-policy@v3.1.0 with: - account-type: org + account: user token: ${{ secrets.GITHUB_TOKEN }} image-names: plancake-frontend - cut-off: 1 week ago + cut-off: 1w timestamp-to-use: updated_at - keep-at-least: 3 - skip-untagged: false + keep-n-most-recent: 3 dry-run: true - name: Clean up Plancake Backend images uses: snok/container-retention-policy@v3.1.0 with: - account-type: org + account: user token: ${{ secrets.GITHUB_TOKEN }} image-names: plancake-backend - cut-off: 1 week ago + cut-off: 1w timestamp-to-use: updated_at - keep-at-least: 3 - skip-untagged: false + keep-n-most-recent: 3 dry-run: true From c0fef42c8a7623566ad4638cca7e7df4490afa8a Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 18 Jul 2026 09:59:08 -0400 Subject: [PATCH 30/52] add package version check to merge-protect --- .github/workflows/merge-protect.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/merge-protect.yml b/.github/workflows/merge-protect.yml index 0e26f640..c3897388 100644 --- a/.github/workflows/merge-protect.yml +++ b/.github/workflows/merge-protect.yml @@ -19,3 +19,17 @@ jobs: echo "Error: You can only merge to main from version branches (e.g. v1.2.3)." exit 1 fi + + - name: Check package version + if: github.base_ref == 'main' + run: | + # Extract the version from package.json + PACKAGE_VERSION=$(jq -r '.version' frontend/package.json) + + # Extract the version from the branch name + BRANCH_VERSION="${{ github.head_ref#v }}" + + if [[ "$PACKAGE_VERSION" != "$BRANCH_VERSION" ]]; then + echo "Error: The version in package.json ($PACKAGE_VERSION) does not match the version in the branch name ($BRANCH_VERSION)." + exit 1 + fi From 9b527cdd9c575df959032467e276c6c7688a7235 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 18 Jul 2026 10:18:20 -0400 Subject: [PATCH 31/52] use npm ci in dockerfile --- frontend/Dockerfile.dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index 9b77a1c6..2f38accd 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -5,7 +5,7 @@ FROM node:22-alpine WORKDIR /app COPY package.json package-lock.json* ./ -RUN npm install +RUN npm ci COPY . . From 3b08dca109f9140194ae8437df8458262302df7a Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 18 Jul 2026 10:37:18 -0400 Subject: [PATCH 32/52] include cache fallback --- .github/workflows/publish-images.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 7b861b80..45935d8b 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -55,7 +55,9 @@ jobs: tags: ${{ steps.meta-backend.outputs.tags }} labels: ${{ steps.meta-backend.outputs.labels }} platforms: linux/amd64,linux/arm64 - cache-from: type=gha,scope=backend-${{ github.ref_name }} + cache-from: | + type=gha,scope=backend-${{ github.ref_name }} + type=gha,scope=backend-main cache-to: type=gha,mode=max,scope=backend-${{ github.ref_name }} # --- FRONTEND --- @@ -77,5 +79,7 @@ jobs: tags: ${{ steps.meta-frontend.outputs.tags }} labels: ${{ steps.meta-frontend.outputs.labels }} platforms: linux/amd64,linux/arm64 - cache-from: type=gha,scope=frontend-${{ github.ref_name }} + cache-from: | + type=gha,scope=frontend-${{ github.ref_name }} + type=gha,scope=frontend-main cache-to: type=gha,mode=max,scope=frontend-${{ github.ref_name }} From 05fa3e8bf7d03199f4e98b8b259dd4141545976e Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 18 Jul 2026 10:54:47 -0400 Subject: [PATCH 33/52] remove dry run --- .github/workflows/clean-up.yml | 4 ++-- frontend/Dockerfile.dev | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml index c44f4446..56b33d5e 100644 --- a/.github/workflows/clean-up.yml +++ b/.github/workflows/clean-up.yml @@ -21,7 +21,7 @@ jobs: cut-off: 1w timestamp-to-use: updated_at keep-n-most-recent: 3 - dry-run: true + dry-run: false - name: Clean up Plancake Backend images uses: snok/container-retention-policy@v3.1.0 @@ -32,4 +32,4 @@ jobs: cut-off: 1w timestamp-to-use: updated_at keep-n-most-recent: 3 - dry-run: true + dry-run: false diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index 2f38accd..fea9d4be 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -11,4 +11,4 @@ COPY . . EXPOSE 3000 -CMD ["npm", "run", "dev"] \ No newline at end of file +CMD ["npm", "run", "dev"] From db197b359b3ced2272dc315684ae2e9108c22dd2 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:30:54 -0400 Subject: [PATCH 34/52] create backend dev dockerfile --- backend/Dockerfile .dev | 20 ++++++++++++++++++++ docker-compose.yml | 1 + 2 files changed, 21 insertions(+) create mode 100644 backend/Dockerfile .dev diff --git a/backend/Dockerfile .dev b/backend/Dockerfile .dev new file mode 100644 index 00000000..c040a8d7 --- /dev/null +++ b/backend/Dockerfile .dev @@ -0,0 +1,20 @@ +# This Dockerfile is used to build the production and development image for the backend. + +FROM python:3.13-slim + +# Prevent Python from writing pyc files and buffering stdout +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --upgrade pip \ + && pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 8000 + +CMD ["uvicorn", "api.asgi:application", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker-compose.yml b/docker-compose.yml index 68b4bc86..dea4a7e5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,7 @@ services: image: ghcr.io/plan-cake/plancake-backend:${IMAGE_TAG:-latest} build: context: ./backend + dockerfile: Dockerfile.dev command: ["sh", "./start.sh"] volumes: - ./backend:/app From 3aa8d4f27a18b17f02a233a7b7be50e75e6f7242 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:31:18 -0400 Subject: [PATCH 35/52] clean up for release --- .github/workflows/clean-up.yml | 4 ++-- .github/workflows/publish-images.yml | 5 +++-- Makefile | 13 +++++-------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml index 56b33d5e..b45e8eb9 100644 --- a/.github/workflows/clean-up.yml +++ b/.github/workflows/clean-up.yml @@ -15,7 +15,7 @@ jobs: - name: Clean up Plancake Frontend images uses: snok/container-retention-policy@v3.1.0 with: - account: user + account: plan-cake token: ${{ secrets.GITHUB_TOKEN }} image-names: plancake-frontend cut-off: 1w @@ -26,7 +26,7 @@ jobs: - name: Clean up Plancake Backend images uses: snok/container-retention-policy@v3.1.0 with: - account: user + account: plan-cake token: ${{ secrets.GITHUB_TOKEN }} image-names: plancake-backend cut-off: 1w diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 45935d8b..1f28f820 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -42,7 +42,7 @@ jobs: id: meta-backend uses: docker/metadata-action@v5 with: - images: ghcr.io/mirmirmirr/plancake-backend + images: ghcr.io/plan-cake/plancake-backend tags: | type=ref,event=branch type=raw,value=latest,enable={{is_default_branch}} @@ -51,6 +51,7 @@ jobs: uses: docker/build-push-action@v5 with: context: ./backend + file: ./backend/Dockerfile.dev push: true tags: ${{ steps.meta-backend.outputs.tags }} labels: ${{ steps.meta-backend.outputs.labels }} @@ -65,7 +66,7 @@ jobs: id: meta-frontend uses: docker/metadata-action@v5 with: - images: ghcr.io/mirmirmirr/plancake-frontend + images: ghcr.io/plan-cake/plancake-frontend tags: | type=ref,event=branch type=raw,value=latest,enable={{is_default_branch}} diff --git a/Makefile b/Makefile index 7c00f95f..3854c2a1 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,17 @@ .PHONY: help up down restart logs-api logs-web shell-api shell-web migrate makemigrations -# Determine the Docker image tag based on the current Git branch. If the branch is -# 'main', use 'latest' as the tag. Otherwise, retrieve the version from the frontend -# package.json and use it as the tag. +# Determine the Docker image tag based on the current Git branch. If the branch is not +# "main", use the version from the frontend package.json to identify the Docker image. +# Otherwise, default to latest. CURRENT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) -ifeq ($(CURRENT_BRANCH),main) - IMAGE_TAG := latest -else +IMAGE_TAG := latest +ifneq ($(CURRENT_BRANCH),main) # Get the version from the frontend package.json for identifying the Docker image. PKG_VERSION := $(shell node -p "require('./frontend/package.json').version") IMAGE_TAG := v$(PKG_VERSION) endif -IMAGE_TAG := latest - # --- COMMANDS --- help: From 9fad22b7683630b694a8d343c63e261cd83de4d6 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:53:41 -0400 Subject: [PATCH 36/52] add readmes --- README.md | 76 +++++++++++++++++++++++--------- docs/docker.md | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 21 deletions(-) create mode 100644 docs/docker.md diff --git a/README.md b/README.md index 2882aa8b..2e8bd403 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,16 @@ A scheduling website that solves the logistics problem of figuring out when everyone is available to meet. +## Tech Stack + +| Layer | Technology | +| :--------------- | :----------------------------------- | +| Frontend | Next.js (React, TypeScript) | +| Backend | Django (ASGI, served via Uvicorn) | +| Database | PostgreSQL | +| Cache / Pub-Sub | Redis (live updates + Celery broker) | +| Background Tasks | Celery (worker + beat) | + ## Project Structure This is a monorepo containing both the frontend client and the backend API service. @@ -14,13 +24,22 @@ plancake/ ## Getting Started -This project can be run either natively or via Docker. +This project can be run either natively or via Docker. Docker is the fastest way to get a consistent environment running locally; native setup is useful if you need finer-grained control over each service (e.g. running Celery, debugging with your IDE's native tooling). + +### Prerequisites + +- [Docker](https://www.docker.com/) and Docker Compose (for the Docker setup) +- Node.js and Python (for native setup — see the linked READMEs below for versions) +- A PostgreSQL database (local, or a hosted option like [Supabase](https://supabase.com)) -#### `.env` Files +### `.env` Files -Copy the contents of the respective `.env.example` files in each folder into its own `.env`. The same `env` setup is used both natively and in Docker. +Copy the contents of the respective `.env.example` files in each folder into its own `.env`: -For more details about the database connection, take a look at the backend README linked below. +- [`backend/.env.example`](backend/.env.example) → `backend/.env` +- [`frontend/.env.example`](frontend/.env.example) → `frontend/.env` + +The same `.env` setup is used both natively and in Docker. For more details about the database connection, see the [Backend Setup](backend/README.md) guide. ### Native Setup @@ -31,27 +50,42 @@ For native setup, follow the setup instructions in these READMEs: ### Docker Setup -The project uses Docker Compose and a set of Make commands that help setup the developement enviroment. It includes the frontend, backend, and Redis services required to run and support live updates. +The project uses Docker Compose alongside a set of `make` commands that wrap common workflows. The Compose stack includes: + +| Service | Description | +| :--------- | :--------------------------------------------------------- | +| `frontend` | Next.js dev server, hot-reloaded via a bind mount | +| `backend` | Django API server (Uvicorn), hot-reloaded via a bind mount | +| `redis` | Backs live updates (pub/sub) and the Celery broker | + +> **Note:** PostgreSQL and Celery (worker/beat) are not included in the Compose stack. The backend connects to whatever database you configure in `backend/.env` (local or hosted), and scheduled background tasks (e.g. expired session cleanup) won't run unless you start Celery separately — see the [Backend Setup](backend/README.md) guide. + +For a deeper look at how the Docker setup works — architecture, images, CI/CD, and known gaps — see [`docs/docker.md`](docs/docker.md). + +#### Quick Start + +```bash +make up +``` + +This pulls the latest published images and starts the stack in the background. Once it's running: + +- Local: http://localhost:3000 +- Network: printed to the terminal, for testing on other devices on your network #### Make Commands -| Task | Command | Description | -| :---------------- | :-------------------- | :---------------------------------------------------- | -| Start/Resume | `make up` | Starts the containers in the background with a build | -| Stop | `make down` | Stops and removes containers and networks | -| Restart | `make restart` | Restarts containers (useful for `.env` changes) | -| Backend Logs | `make logs-api` | Streams the backend Django logs | -| Frontend Logs | `make logs-web` | Streams the frontend Next.js logs | -| URLs | `make url` | Displays local and network URLs | -| API Shell | `make shell-api` | Opens a bash shell in the backend container | -| Frontend Shell | `make shell-web` | Opens a bash shell in the frontend container | -| Run Migrations | `make migrate` | Runs Django migrations | -| Create Migrations | `make makemigrations` | Creates new Django migration inside running container | +The most commonly used commands: -_(Note: To exit log streams, just `Ctrl + C`. To exit shell sessions, type `exit` or `Ctrl + D`)_ +| Task | Command | Description | +| :----------- | :------------- | :---------------------------------------------------------------------------- | +| Start/Resume | `make up` | Pulls the latest images and starts the containers in the background | +| Full Rebuild | `make build` | Rebuilds images from source (bypasses the registry) and starts the containers | +| Stop | `make down` | Stops and removes containers and networks | +| Restart | `make restart` | Restarts containers without rebuilding (useful for `.env` changes) | -#### Named Volumes +See [`docs/docker.md`](docs/docker.md#makefile) for the full command list (logs, shells, migrations, and more) and run `make help` at any time to see it from the terminal. -Named volumes are used to prevent the build up with dangling volumes on your machine every time `make down` is run. It persists cached data (like `node_modules` and the redis cache) through each new container instance. +_(To exit log streams, press `Ctrl+C`. To exit shell sessions, type `exit` or press `Ctrl+D`.)_ -If you ever need to completely wipe the data and start with a clean slate, the volumes must be removed with `docker compose down -v`. +Named volumes are used to persist data like `node_modules` and the Redis cache across restarts — see [`docs/docker.md`](docs/docker.md#named-volumes) for details on wiping them. diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 00000000..e7820a79 --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,115 @@ +# Docker System Overview + +This document explains how Plancake's Docker setup is put together: the services, the images, the `make` workflow, and the CI pipelines that build and publish them. + +For quick-start commands, read alongside the [root README](../README.md). + +## Architecture + +``` + ┌────────────┐ ┌────────────┐ + :3000 ───▶ │ frontend │──────▶ │ backend │ ───▶ :8000 + │ (Next.js) │ │ (Django, │ + └────────────┘ │ Uvicorn) │ + └─────┬──────┘ + │ + ┌─────▼──────┐ + │ redis │ + └────────────┘ +``` + +- **frontend** — Next.js dev server. Talks to the backend over the internal Docker network (`http://backend:8000`). +- **backend** — Django served via Uvicorn. Talks to Redis for live updates and the Celery broker, and to whatever PostgreSQL database is configured in `backend/.env`. +- **redis** — backs two things: the pub/sub channel used for live updates on the results page, and the Celery broker (`CELERY_BROKER_URL`). + +**Not included in the Compose stack:** PostgreSQL and Celery (worker/beat). + +- The database is expected to already exist — locally installed, or hosted (e.g. Supabase) — and is configured entirely through `backend/.env`. Containerizing it wasn't necessary since most contributors are already pointed at a hosted dev database. +- Celery only drives the `daily_duties` scheduled task (session/token/reset-code cleanup at midnight). It's not required for day-to-day feature work, so it's left out to keep the stack lighter. If you need it, run it natively alongside the containers (`backend/README.md` has the commands) — its broker URL already matches redis's Docker port (`redis://localhost:6379/0`), so it'll happily connect to the containerized Redis. + +## Images + +### Backend (`backend/Dockerfile`) + +Single-stage image based on `python:3.13-slim`. Installs `requirements.txt`, copies the app, and drops to a non-root user before running Uvicorn. This is the production image. + +In Compose, this same image is used for local dev too — `docker-compose.yml` overrides its `command` to run `backend/start.sh` instead (which re-installs dependencies and runs with `--reload`), and bind-mounts the local `./backend` directory over `/app` so code changes are picked up live. + +### Frontend (`frontend/Dockerfile` and `frontend/Dockerfile.dev`) + +Two separate images, matching the prod/dev split: + +- **`Dockerfile`** — multi-stage production build (`dependencies` → `builder` → `runner`). Uses Next.js's standalone output to keep the final image small, and runs as the built-in non-root `node` user. +- **`Dockerfile.dev`** — single-stage, installs deps and runs `npm run dev` directly. This is what Compose builds locally, and also what's built and published by CI (see below). + +### `.dockerignore` + +Both `backend/` and `frontend/` have a `.dockerignore` to keep build contexts small — excluding things like `node_modules`, `.next`, virtualenvs, `__pycache__`, logs, and env files. Worth checking when adding new local-only directories so they don't bloat build context/image size. + +## `docker-compose.yml` + +- Both `backend` and `frontend` declare an `image:` (pointed at the registry) **and** a `build:` context. This means: + - `docker compose pull` / `make up` pulls the pre-built image for the given `IMAGE_TAG`. + - `docker compose up --build` / `make build` builds locally from source instead, ignoring the registry. +- `env_file` pulls in `backend/.env` and `frontend/.env` — the same files used for native setup. +- A few environment variables are set directly in the compose file rather than `.env`, because they depend on the container network rather than the developer's machine (e.g. `INTERNAL_API_URL=http://backend:8000`, `CELERY_BROKER_URL`, `LIVE_UPDATES_URL`). + +### Named Volumes + +Three named volumes are declared: `redis_data`, `node_modules`, and `next_cache`. They persist state across `make down`/`make up` cycles so you're not reinstalling `node_modules` or losing Redis data every restart, and so dangling anonymous volumes don't build up on your machine every time `make down` runs. + +If you ever need to wipe all data and start fresh, remove the volumes with: + +```bash +docker compose down -v +``` + +## `Makefile` + +The `Makefile` wraps the common Compose commands so contributors don't need to remember Compose flags or env vars. Run `make help` to see the full list. A few worth calling out: + +- `make up` vs `make build` — `up` pulls published images (fast, no local build); `build` builds from your working tree (needed if you've changed a Dockerfile or want to test uncommitted backend/frontend changes without publishing). +- `IMAGE_TAG` — determines which image tag is pulled/built. Currently pinned to `latest`; the intent (see the commented-out logic above it) is to eventually derive this from the branch/`package.json` version so non-`main` branches can pull their own tagged images. That branch-based logic isn't wired up yet — flagging this here as a known follow-up rather than finished behavior. +- `make shell-api` / `make shell-web` — open a shell in the running container for one-off debugging (`python manage.py shell`, inspecting `node_modules`, etc.) without needing to rebuild. + +This is the full list current supported commands: + +| Task | Command | Description | +| :---------------- | :-------------------- | :---------------------------------------------------------------------------- | +| Start/Resume | `make up` | Pulls the latest images and starts the containers in the background | +| Full Rebuild | `make build` | Rebuilds images from source (bypasses the registry) and starts the containers | +| Stop | `make down` | Stops and removes containers and networks | +| Restart | `make restart` | Restarts containers without rebuilding (useful for `.env` changes) | +| Backend Logs | `make logs-api` | Streams the backend Django logs | +| Frontend Logs | `make logs-web` | Streams the frontend Next.js logs | +| URLs | `make url` | Displays local and network URLs | +| API Shell | `make shell-api` | Opens a shell in the backend container | +| Frontend Shell | `make shell-web` | Opens a shell in the frontend container | +| Run Migrations | `make migrate` | Runs Django migrations inside the running container | +| Create Migrations | `make makemigrations` | Generates new Django migrations inside the running container | + +## CI/CD Workflows + +### `publish-images.yml` + +Runs on every push to `main` or a version branch (`v*.*.*`). Builds and pushes both images to GHCR using Buildx, multi-platform (`linux/amd64,linux/arm64`) via QEMU, with GitHub Actions cache (`cache-from`/`cache-to`) to speed up repeat builds. + +- Backend is built from `backend/Dockerfile` (the production image). +- Frontend is built from `frontend/Dockerfile.dev` — **not** the production `Dockerfile`. This was to get a working image published quickly during development; before this goes out as the "real" published image, it should probably be switched to build from the production Dockerfile instead. +- Tags: `type=ref,event=branch` (branch name) plus `latest` on the default branch, via `docker/metadata-action`. + +### `clean-up.yml` + +This cleaning job runs weekly every Sunday night and also can be manually enabled. It uses `snok/container-retention-policy` to delete old images from GHCR, keeping the 3 most recent per image and anything newer than a week. + +### `merge-protect.yml` + +Not Docker-specific, but lives with these workflows: gates PRs into `main` so they can only come from a version branch (`vX.Y.Z`) whose name matches `frontend/package.json`'s version. Keeps `main` (and therefore the `latest` tag published by `publish-images.yml`) in sync with an actual release version. + +## Known Gaps / Follow-ups + +- No Celery worker/beat service in Compose — daily cleanup task won't run unless started natively. +- No PostgreSQL service in Compose — a database must already exist and be reachable from `backend/.env`. +- Frontend CI publishes the dev image (`Dockerfile.dev`), not the production build. + +These aren't blockers for local development, but should be resolved before treating the published images as production-ready. From 9c7dd1a4efb3a28299559384ffdde642d225371e Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:44:07 -0400 Subject: [PATCH 37/52] add clean up concurrency group --- .github/workflows/clean-up.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml index b45e8eb9..0e03b8e1 100644 --- a/.github/workflows/clean-up.yml +++ b/.github/workflows/clean-up.yml @@ -5,6 +5,11 @@ on: - cron: "0 0 * * 0" # run every sunday at midnight workflow_dispatch: +# don't cancel in-progress runs, since this is a weekly cleanup job +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: clean-registry: runs-on: ubuntu-latest From ce123137f059a486cb9b255162fcf396c8c5465a Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:46:53 -0400 Subject: [PATCH 38/52] add extra security to prevent version tagged deletion --- .github/workflows/clean-up.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml index 0e03b8e1..c0ab1ede 100644 --- a/.github/workflows/clean-up.yml +++ b/.github/workflows/clean-up.yml @@ -26,6 +26,7 @@ jobs: cut-off: 1w timestamp-to-use: updated_at keep-n-most-recent: 3 + skip-tags: v*.*.* dry-run: false - name: Clean up Plancake Backend images @@ -37,4 +38,5 @@ jobs: cut-off: 1w timestamp-to-use: updated_at keep-n-most-recent: 3 + skip-tags: v*.*.* dry-run: false From 18c792f7b8a8852f307604ce74aae963b00cb2b6 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:52:18 -0400 Subject: [PATCH 39/52] move ${github.head_ref#v} from actions expression --- .github/workflows/merge-protect.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/merge-protect.yml b/.github/workflows/merge-protect.yml index c3897388..5199c2d2 100644 --- a/.github/workflows/merge-protect.yml +++ b/.github/workflows/merge-protect.yml @@ -8,6 +8,9 @@ on: # Having edited here updates the check when changing the base branch types: [synchronize, opened, reopened, edited] +env: + HEAD_REF: ${{ github.head_ref }} + jobs: check_branch: runs-on: ubuntu-latest @@ -15,7 +18,7 @@ jobs: - name: Check branch if: github.base_ref == 'main' run: | - if [[ ! "${{ github.head_ref }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ ! "$HEAD_REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "Error: You can only merge to main from version branches (e.g. v1.2.3)." exit 1 fi @@ -27,7 +30,7 @@ jobs: PACKAGE_VERSION=$(jq -r '.version' frontend/package.json) # Extract the version from the branch name - BRANCH_VERSION="${{ github.head_ref#v }}" + BRANCH_VERSION="${HEAD_REF#v}" if [[ "$PACKAGE_VERSION" != "$BRANCH_VERSION" ]]; then echo "Error: The version in package.json ($PACKAGE_VERSION) does not match the version in the branch name ($BRANCH_VERSION)." From 0f98da7a5822b9988e24f179e5c366dbb7b88a32 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:54:12 -0400 Subject: [PATCH 40/52] remove assetations --- .github/workflows/publish-images.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 1f28f820..415d1c9f 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -13,8 +13,6 @@ concurrency: permissions: contents: read packages: write - attestations: write - id-token: write jobs: build-and-push: From a8c0fb08347ea22e07da4d9b8461a083f8437f17 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:59:30 -0400 Subject: [PATCH 41/52] add dependabot and pin releases --- .github/dependabot.yml | 6 ++++++ .github/workflows/clean-up.yml | 4 ++-- .github/workflows/publish-images.yml | 16 ++++++++-------- 3 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..5ace4600 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml index c0ab1ede..d449c6b5 100644 --- a/.github/workflows/clean-up.yml +++ b/.github/workflows/clean-up.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Clean up Plancake Frontend images - uses: snok/container-retention-policy@v3.1.0 + uses: snok/container-retention-policy@d3bdcf5ce9b05f685154e4a16c39233b245e3d53 # v3.1.0 with: account: plan-cake token: ${{ secrets.GITHUB_TOKEN }} @@ -30,7 +30,7 @@ jobs: dry-run: false - name: Clean up Plancake Backend images - uses: snok/container-retention-policy@v3.1.0 + uses: snok/container-retention-policy@d3bdcf5ce9b05f685154e4a16c39233b245e3d53 # v3.1.0 with: account: plan-cake token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml index 415d1c9f..83a42179 100644 --- a/.github/workflows/publish-images.yml +++ b/.github/workflows/publish-images.yml @@ -20,16 +20,16 @@ jobs: steps: - name: Check out the repo - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -38,7 +38,7 @@ jobs: # --- BACKEND --- - name: Extract metadata for Backend id: meta-backend - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 with: images: ghcr.io/plan-cake/plancake-backend tags: | @@ -46,7 +46,7 @@ jobs: type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Backend - uses: docker/build-push-action@v5 + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 with: context: ./backend file: ./backend/Dockerfile.dev @@ -62,7 +62,7 @@ jobs: # --- FRONTEND --- - name: Extract metadata for Frontend id: meta-frontend - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 with: images: ghcr.io/plan-cake/plancake-frontend tags: | @@ -70,7 +70,7 @@ jobs: type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Frontend - uses: docker/build-push-action@v5 + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 with: context: ./frontend file: ./frontend/Dockerfile.dev From 5141c2594ef5b11cae2c693435d84f914f6902ca Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 1 Aug 2026 10:02:31 -0400 Subject: [PATCH 42/52] docker ignore .env.production --- backend/.dockerignore | 1 + frontend/.dockerignore | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/.dockerignore b/backend/.dockerignore index 081b2a71..e04d6f52 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -26,6 +26,7 @@ Thumbs.db .env*.local .env.development .env.test +.env.production .env.production.local # Docker configuration files (not needed inside build context) diff --git a/frontend/.dockerignore b/frontend/.dockerignore index f3160296..d8166316 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -49,6 +49,7 @@ playwright.config.* .env*.local .env.development .env.test +.env.production .env.production.local # Docker configuration files (not needed inside build context) From a56bb79d0915d90c4f1e954e22b19fa2e8c2129a Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 1 Aug 2026 10:08:42 -0400 Subject: [PATCH 43/52] fix dev dockerfile --- backend/{Dockerfile .dev => Dockerfile.dev} | 0 docs/docker.md | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename backend/{Dockerfile .dev => Dockerfile.dev} (100%) diff --git a/backend/Dockerfile .dev b/backend/Dockerfile.dev similarity index 100% rename from backend/Dockerfile .dev rename to backend/Dockerfile.dev diff --git a/docs/docker.md b/docs/docker.md index e7820a79..f48485be 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -29,11 +29,11 @@ For quick-start commands, read alongside the [root README](../README.md). ## Images -### Backend (`backend/Dockerfile`) +### Backend (`backend/Dockerfile` and `backend/Dockerfile.dev`) -Single-stage image based on `python:3.13-slim`. Installs `requirements.txt`, copies the app, and drops to a non-root user before running Uvicorn. This is the production image. +Single-stage image based on `python:3.13-slim`. Installs `requirements.txt`, copies the app, and drops to a non-root user before running Uvicorn. The production and dev images are very similar, with the only difference being the use of the non-root user. -In Compose, this same image is used for local dev too — `docker-compose.yml` overrides its `command` to run `backend/start.sh` instead (which re-installs dependencies and runs with `--reload`), and bind-mounts the local `./backend` directory over `/app` so code changes are picked up live. +In Compose, the `Dockerfile.dev` is used for local dev and `docker-compose.yml` overrides its `command` to run `backend/start.sh` instead (which re-installs dependencies and runs with `--reload`), and bind-mounts the local `./backend` directory over `/app` so code changes are picked up live. ### Frontend (`frontend/Dockerfile` and `frontend/Dockerfile.dev`) From 2a37844351d773d69402c4571a33fa9ccd637998 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 1 Aug 2026 10:10:16 -0400 Subject: [PATCH 44/52] add set -e to startup scripts --- backend/start.sh | 1 + frontend/start.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/start.sh b/backend/start.sh index 56f387b4..f7db9cf8 100644 --- a/backend/start.sh +++ b/backend/start.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -e echo "Checking for Python dependency updates..." pip install -r requirements.txt diff --git a/frontend/start.sh b/frontend/start.sh index 59a835f9..d11fefdd 100644 --- a/frontend/start.sh +++ b/frontend/start.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -e echo "Checking and syncing node_modules..." npm install From b7c5699e3ccc3b1c9c03e13e15f6150b6dccbdbe Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sat, 1 Aug 2026 10:13:12 -0400 Subject: [PATCH 45/52] update docker image tag description --- docs/docker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docker.md b/docs/docker.md index f48485be..991cd57f 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -69,7 +69,7 @@ docker compose down -v The `Makefile` wraps the common Compose commands so contributors don't need to remember Compose flags or env vars. Run `make help` to see the full list. A few worth calling out: - `make up` vs `make build` — `up` pulls published images (fast, no local build); `build` builds from your working tree (needed if you've changed a Dockerfile or want to test uncommitted backend/frontend changes without publishing). -- `IMAGE_TAG` — determines which image tag is pulled/built. Currently pinned to `latest`; the intent (see the commented-out logic above it) is to eventually derive this from the branch/`package.json` version so non-`main` branches can pull their own tagged images. That branch-based logic isn't wired up yet — flagging this here as a known follow-up rather than finished behavior. +- `IMAGE_TAG` — determines which image tag is pulled/built. On `main`, it's `latest`. On any other branch, it's derived from `frontend/package.json`'s version as `v`, so a branch like `v1.2.3` (with a matching `package.json` version) pulls its own tagged images instead of `latest`. - `make shell-api` / `make shell-web` — open a shell in the running container for one-off debugging (`python manage.py shell`, inspecting `node_modules`, etc.) without needing to rebuild. This is the full list current supported commands: From b0a919c4ba00fae796f9c73e42ba56eba026ae8c Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 2 Aug 2026 09:53:48 -0400 Subject: [PATCH 46/52] add baseURL fallback security --- frontend/src/lib/utils/api/server-fetch.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib/utils/api/server-fetch.ts b/frontend/src/lib/utils/api/server-fetch.ts index 74fa8b42..b947f0ed 100644 --- a/frontend/src/lib/utils/api/server-fetch.ts +++ b/frontend/src/lib/utils/api/server-fetch.ts @@ -4,6 +4,21 @@ import { getAuthCookieString } from "@/lib/utils/api/cookie-utils"; import { InferReq, InferRes } from "@/lib/utils/api/endpoints"; import { fetchJson } from "@/lib/utils/api/fetch-wrapper"; +/** + * Resolves the backend API base URL, throwing a configuration error if + * neither environment variable is set. + */ +function getApiBaseUrl(): string { + const baseUrl = + process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL; + if (!baseUrl) { + throw new Error( + "API base URL is not configured. Set INTERNAL_API_URL or NEXT_PUBLIC_API_URL.", + ); + } + return baseUrl; +} + /** * Extracts headers for User-Agent and X-Forwarded-For from the incoming request */ @@ -32,8 +47,7 @@ export async function serverGet( params?: InferReq, options?: RequestInit, ): Promise> { - const baseUrl = - process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL; + const baseUrl = getApiBaseUrl(); let queryString = ""; if (params && Object.keys(params).length > 0) { @@ -70,9 +84,7 @@ export async function serverPost( body?: InferReq, options?: RequestInit, ): Promise> { - // Inside serverGet and serverPost: - const baseUrl = - process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL; + const baseUrl = getApiBaseUrl(); const url = `${baseUrl}${endpoint.url}`; const cookieString = await getAuthCookieString(); const forwardedHeaders = await getForwardedHeaders(); From e067b4d69ab39577f81c754cbb9079914916a0b5 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 2 Aug 2026 09:59:02 -0400 Subject: [PATCH 47/52] update makefile phony list --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3854c2a1..e09e049e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -.PHONY: help up down restart logs-api logs-web shell-api shell-web migrate makemigrations +.PHONY: help up build down restart logs-api logs-web shell-api shell-web migrate makemigrations url # Determine the Docker image tag based on the current Git branch. If the branch is not # "main", use the version from the frontend package.json to identify the Docker image. From 08fe9a166f50681db2b353d73e414061ebf8c60d Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 2 Aug 2026 10:04:33 -0400 Subject: [PATCH 48/52] update docker docs --- docs/docker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docker.md b/docs/docker.md index 991cd57f..4472b564 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -69,7 +69,7 @@ docker compose down -v The `Makefile` wraps the common Compose commands so contributors don't need to remember Compose flags or env vars. Run `make help` to see the full list. A few worth calling out: - `make up` vs `make build` — `up` pulls published images (fast, no local build); `build` builds from your working tree (needed if you've changed a Dockerfile or want to test uncommitted backend/frontend changes without publishing). -- `IMAGE_TAG` — determines which image tag is pulled/built. On `main`, it's `latest`. On any other branch, it's derived from `frontend/package.json`'s version as `v`, so a branch like `v1.2.3` (with a matching `package.json` version) pulls its own tagged images instead of `latest`. +- `IMAGE_TAG` — determines which image tag is pulled/built. On `main`, it's `latest`. On any other branch, it's derived from `frontend/package.json`'s version as `v`. **This means `package.json`'s version must be kept up to date with whichever release image you want to pull** — bump it when starting work toward a new version, not just when publishing. - `make shell-api` / `make shell-web` — open a shell in the running container for one-off debugging (`python manage.py shell`, inspecting `node_modules`, etc.) without needing to rebuild. This is the full list current supported commands: From 4c58f358aac4fa2490c0561a73ddd76197e4b505 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 2 Aug 2026 10:11:00 -0400 Subject: [PATCH 49/52] bound redis to localhost --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index dea4a7e5..704c6669 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: volumes: - redis_data:/data ports: - - "6379:6379" + - "127.0.0.1:6379:6379" backend: image: ghcr.io/plan-cake/plancake-backend:${IMAGE_TAG:-latest} From 8ded5cf5f7eb576d3b7cba3b59dabbce2110e592 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 2 Aug 2026 10:12:22 -0400 Subject: [PATCH 50/52] update backend prod docker user creation --- backend/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index ab1458a0..74298237 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -16,8 +16,8 @@ RUN pip install --upgrade pip \ COPY . . # Create a non-root user and switch to it -RUN addgroup --system appgroup && adduser --system --group user -USER user +RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser +USER appuser EXPOSE 8000 From 561c16fba17017d03e239ae4377ef23b68d9a695 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 2 Aug 2026 10:15:43 -0400 Subject: [PATCH 51/52] Update print-ip.js --- scripts/print-ip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/print-ip.js b/scripts/print-ip.js index 89d562e5..ff03f94b 100644 --- a/scripts/print-ip.js +++ b/scripts/print-ip.js @@ -1,4 +1,4 @@ -import os from "os"; +const os = require("os"); for (const interfaces of Object.values(os.networkInterfaces())) { for (const iface of interfaces ?? []) { From 9228a293f49ab5e3d68bef8151f3302a5b00bf74 Mon Sep 17 00:00:00 2001 From: Miranda Zheng <123515762+mirmirmirr@users.noreply.github.com> Date: Sun, 2 Aug 2026 10:16:28 -0400 Subject: [PATCH 52/52] Update merge-protect.yml --- .github/workflows/merge-protect.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/merge-protect.yml b/.github/workflows/merge-protect.yml index 5199c2d2..7b7119c4 100644 --- a/.github/workflows/merge-protect.yml +++ b/.github/workflows/merge-protect.yml @@ -15,6 +15,9 @@ jobs: check_branch: runs-on: ubuntu-latest steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - name: Check branch if: github.base_ref == 'main' run: |