Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8d3fcae
initial docker setup
mirmirmirr Jun 28, 2026
5cd14f8
adjust configs
mirmirmirr Jun 28, 2026
cb9fb35
add named volumes
mirmirmirr Jun 28, 2026
68fa8c6
remove version from compose
mirmirmirr Jun 28, 2026
ae695e8
update frontend env configs
mirmirmirr Jun 28, 2026
cff933c
update dockerfiles
mirmirmirr Jun 28, 2026
8b9d3ba
add trailing slash to destination
mirmirmirr Jun 29, 2026
8e101da
start scripts
mirmirmirr Jun 29, 2026
ea139bd
makefile
mirmirmirr Jun 29, 2026
d5204cd
switch celery and redis
mirmirmirr Jun 29, 2026
7ace97f
update post baseurl
mirmirmirr Jun 29, 2026
2bd10ff
update dockerfiles and add dockerignore
mirmirmirr Jun 29, 2026
d449766
cleanup makefile
mirmirmirr Jun 29, 2026
7ce5ee5
add make url
mirmirmirr Jun 29, 2026
d74af0e
update readme with docker instructions
mirmirmirr Jul 2, 2026
a583ab7
add help command
mirmirmirr Jul 2, 2026
b7bbf55
add backend start.sh
mirmirmirr Jul 4, 2026
ee1ba11
update package versions
mirmirmirr Jul 4, 2026
1fdd2aa
add image to registry
mirmirmirr Jul 4, 2026
cee9b54
Update publish-images.yml
mirmirmirr Jul 4, 2026
31fcd55
Update publish-images.yml
mirmirmirr Jul 4, 2026
33ed636
point registry to personal namespace
mirmirmirr Jul 4, 2026
2025111
switch frontend to alpine
mirmirmirr Jul 4, 2026
ce2a602
add concurrency and cache
mirmirmirr Jul 4, 2026
820de5b
cache fixes
mirmirmirr Jul 18, 2026
67ae676
create clean up script
mirmirmirr Jul 18, 2026
02f439b
remove extra token
mirmirmirr Jul 18, 2026
191d8ce
update snok version
mirmirmirr Jul 18, 2026
611d01d
fix clean up parameters
mirmirmirr Jul 18, 2026
c0fef42
add package version check to merge-protect
mirmirmirr Jul 18, 2026
9b527cd
use npm ci in dockerfile
mirmirmirr Jul 18, 2026
3b08dca
include cache fallback
mirmirmirr Jul 18, 2026
05fa3e8
remove dry run
mirmirmirr Jul 18, 2026
db197b3
create backend dev dockerfile
mirmirmirr Jul 23, 2026
3aa8d4f
clean up for release
mirmirmirr Jul 23, 2026
9fad22b
add readmes
mirmirmirr Jul 23, 2026
6cdd712
Merge branch 'v0.5.0' into docker
mirmirmirr Jul 25, 2026
9c7dd1a
add clean up concurrency group
mirmirmirr Aug 1, 2026
ce12313
add extra security to prevent version tagged deletion
mirmirmirr Aug 1, 2026
18c792f
move ${github.head_ref#v} from actions expression
mirmirmirr Aug 1, 2026
0f98da7
remove assetations
mirmirmirr Aug 1, 2026
a8c0fb0
add dependabot and pin releases
mirmirmirr Aug 1, 2026
5141c25
docker ignore .env.production
mirmirmirr Aug 1, 2026
a56bb79
fix dev dockerfile
mirmirmirr Aug 1, 2026
2a37844
add set -e to startup scripts
mirmirmirr Aug 1, 2026
b7c5699
update docker image tag description
mirmirmirr Aug 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
42 changes: 42 additions & 0 deletions .github/workflows/clean-up.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Weekly Image Cleanup

on:
schedule:
- 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:
Comment thread
mirmirmirr marked this conversation as resolved.
runs-on: ubuntu-latest
permissions:
packages: write

steps:
- name: Clean up Plancake Frontend images
uses: snok/container-retention-policy@d3bdcf5ce9b05f685154e4a16c39233b245e3d53 # v3.1.0
with:
account: plan-cake
token: ${{ secrets.GITHUB_TOKEN }}
image-names: plancake-frontend
cut-off: 1w
timestamp-to-use: updated_at
keep-n-most-recent: 3
skip-tags: v*.*.*
dry-run: false
Comment thread
mirmirmirr marked this conversation as resolved.

- name: Clean up Plancake Backend images
uses: snok/container-retention-policy@d3bdcf5ce9b05f685154e4a16c39233b245e3d53 # v3.1.0
with:
account: plan-cake
token: ${{ secrets.GITHUB_TOKEN }}
image-names: plancake-backend
cut-off: 1w
timestamp-to-use: updated_at
keep-n-most-recent: 3
skip-tags: v*.*.*
dry-run: false
19 changes: 18 additions & 1 deletion .github/workflows/merge-protect.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,31 @@ 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
steps:
- 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

- name: Check package version
if: github.base_ref == 'main'
run: |
# Extract the version from package.json
PACKAGE_VERSION=$(jq -r '.version' frontend/package.json)
Comment on lines +26 to +30

# Extract the version from the branch name
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)."
exit 1
fi
84 changes: 84 additions & 0 deletions .github/workflows/publish-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Build and Publish Docker Images

on:
push:
branches: ["main", "v*.*.*"]

# 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:
contents: read
packages: write

jobs:
build-and-push:
runs-on: ubuntu-latest

steps:
- name: Check out the repo
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0

- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0

- name: Log in to GitHub Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# --- BACKEND ---
- name: Extract metadata for Backend
id: meta-backend
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
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@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0
with:
context: ./backend
file: ./backend/Dockerfile.dev
push: true
Comment thread
mirmirmirr marked this conversation as resolved.
Comment thread
mirmirmirr marked this conversation as resolved.
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 }}
type=gha,scope=backend-main
cache-to: type=gha,mode=max,scope=backend-${{ github.ref_name }}

# --- FRONTEND ---
- name: Extract metadata for Frontend
id: meta-frontend
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
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@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0
with:
context: ./frontend
file: ./frontend/Dockerfile.dev
push: true
Comment thread
coderabbitai[bot] marked this conversation as resolved.
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 }}
type=gha,scope=frontend-main
cache-to: type=gha,mode=max,scope=frontend-${{ github.ref_name }}
95 changes: 95 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

.PHONY: help up down restart logs-api logs-web shell-api shell-web migrate makemigrations

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Makefile relevant lines =="
if [ -f Makefile ]; then
  nl -ba Makefile | sed -n '1,110p'
else
  echo "Makefile not found"
fi

echo
echo "== Makefile phony and target declarations =="
python3 - <<'PY'
from pathlib import Path
p = Path("Makefile")
if not p.exists():
    raise SystemExit
lines = p.read_text().splitlines()
for i, line in enumerate(lines, 1):
    if line.startswith(".PHONY"):
        print(f"{i}: {line}")
for i, line in enumerate(lines, 1):
    if line.startswith("build:") or line.startswith("url:") or line.startswith("help:") or line.startswith("up:"):
        print(f"{i}: {line}")
PY

echo
echo "== Check if make is available =="
command -v make || true

Repository: plan-cake/plancake

Length of output: 226


🏁 Script executed:

#!/bin/bash
set -u

echo "== Makefile existence and size =="
if [ -f Makefile ]; then
  wc -l Makefile
else
  echo "Makefile not found"
fi

echo
echo "== Makefile relevant lines =="
if [ -f Makefile ]; then
  cat -n Makefile | sed -n '1,110p'
fi

echo
echo "== Makefile phony and target declarations =="
if [ -f Makefile ]; then
  awk 'NR==FNR{for(i=1;i<=NF;i++) if($i=="\\") $(i+1)=$0} {print NR": "$0}' Makefile | \
  grep -E '^\s*[0-9]+: (\.PHONY|build:|url:|\s*build\s*:|\s*url\s*:)+'
  awk '{line=$0} NR==FNR{if(/\\[[:space:]]*$/){getline; line=line$0} else if(line~/^(\.PHONY|build:|url:|\s*build\s*:|\s*url\s*:)/ || $0~ /^(\.PHONY|build:|url:|\s*build\s*:|\s*url\s*:)/) print FNR": "$0}' Makefile Makefile
fi

echo
echo "== Check if make is available =="
command -v make || true

echo
echo "== Deterministic Makefile behavior probe with system make, if available =="
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
cat > "$tmp/Makefile" <<'EOF'
.PHONY: phony-only one-two

one-two: app
	`@echo` TARGET_RAN

phony-only:
	`@echo` PHONY_ONLY_RAN
EOF
touch "$tmp/app"
{
  echo "== with app present, target not marked .PHONY =="
  (cd "$tmp" && make --no-print-directory one-two || true)
  echo
  echo "== with app absent =="
  rm "$tmp/app"
  (cd "$tmp" && make --no-print-directory one-two || true)
} 2>&1 || true

Repository: plan-cake/plancake

Length of output: 4892


🌐 Web query:

GNU make .PHONY target exists file directory behavior make target skipped

💡 Result:

In GNU make, the.PHONY special target is used to explicitly declare that a target does not correspond to an actual file on the filesystem [1][2][3]. When a target is marked as.PHONY, make ignores the existence or modification time of any file with the same name and executes the associated recipe unconditionally every time that target is requested [4][1][5][3]. Key behaviors include: 1. Unconditional Execution: If you do not declare a target as.PHONY and a file with that same name happens to exist in the directory, make will treat it as a real file target [1][2]. If that file is up to date relative to its prerequisites, make will consider it "up to date" and skip the recipe [1][2][3]. Declaring it.PHONY forces execution regardless of the file's presence or age [4][2][3]. 2. Performance Optimization: Because make knows that a.PHONY target does not represent an actual file, it skips the implicit rule search for that target [2][6][3]. This provides a minor performance benefit by avoiding unnecessary filesystem lookups [1][2][3]. 3. Proper Usage: You should use.PHONY for targets that perform actions rather than building files (e.g., clean, all, install) [1][2]. Conversely, you should avoid using.PHONY for real file targets (like object files or executables), as doing so will cause them to be rebuilt unnecessarily every time you run make [7]. 4. Interaction with Directories: If a directory exists with the same name as your target, it is still treated as a real file/directory by make [8]. If you want a rule to build a directory (e.g., creating it with mkdir), do not mark it as.PHONY; instead, consider using order-only prerequisites if you only want the directory to exist before other files are created [8].

Citations:


Declare build and url as phony targets.

Both targets define Docker/printing actions rather than files, but they are only referenced from .PHONY for real files. If build or url exists, make build / make url can be skipped instead of running their recipes.

Proposed fix
-.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
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.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

Comment thread
mirmirmirr marked this conversation as resolved.

# 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)
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)
Comment on lines +9 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

make -n help

Repository: plan-cake/plancake

Length of output: 198


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Makefile lines 1-40:\n'
sed -n '1,45p' Makefile | cat -n

printf '\nMakefile indentation around conditional:\n'
python3 - <<'PY'
from pathlib import Path
p = Path("Makefile")
text = p.read_text(errors="replace").splitlines()
for i, line in enumerate(text[max(0,6):min(len(text),15)], start=max(0,6)+1):
    print(f"{i}: {line!r} chars before content: {len(line)-len(line.lstrip(chr(9)))} {'tab'*(len(line)-len(line.lstrip(chr(9)))>0) else 'spaces'}")
PY

Repository: plan-cake/plancake

Length of output: 2834


Remove the leading tabs from the conditional Makefile assignments.

PKG_VERSION := ... and IMAGE_TAG := ... must not be indented with tabs at the top level; tab-indented lines are parsed as recipes, which makes make fail before targets can run.

endif
Comment on lines +4 to +13

# --- COMMANDS ---

help:
@echo "Usage: make <target>"
@echo ""
@echo "Targets:"
@echo " up Starts the environment in the background and prints out the URLs for local and network access."
@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."
@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."

# Pulls the image from the registry and starts the containers in the background.
up:
@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 and networks.
down:
docker compose down

# Restarts the containers without rebuilding them. Use this for things like env changes.
restart:
docker compose restart
Comment on lines +57 to +59

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Makefile around restart target =="
nl -ba Makefile | sed -n '45,68p'

echo
echo "== Search for restart target / docker compose usage =="
rg -n "restart:|docker compose|FORCE|env changes" Makefile .github -S || true

echo
echo "== Local repository version files if any =="
git ls-files | rg '(^|/)(Makefile|compose.*\.ya?ml|\.env|docker-compose\.ya?ml)$' || true

Repository: plan-cake/plancake

Length of output: 233


🌐 Web query:

Docker Compose restart up --force-recreate environment variables .env documentation

💡 Result:

When using the docker compose up command, the --force-recreate flag instructs Docker Compose to stop and recreate all containers, even if their configuration or image has not changed [1][2]. This is particularly useful when you need to ensure that environment variable changes or other configuration updates are applied to the containers [3]. Regarding environment variables and the.env file: 1. Behavior with --force-recreate: By default, Docker Compose compares the current configuration (including environment variables) of running containers with the desired configuration in your compose.yaml [1]. If it detects a difference, it automatically recreates them [1][3]. However, using --force-recreate guarantees that the containers are replaced regardless of whether Compose detects a change, ensuring that the environment defined by your current.env file or other environment settings is applied [1][2]. 2..env File Precedence: Docker Compose automatically loads a default.env file from the project directory [4][5]. If you need to use a different file, you can specify it using the --env-file option [4][6]. 3. Precedence Rules: Environment variables are resolved based on a specific order of precedence [5][7]. Generally, variables defined in the environment attribute of your compose.yaml file take precedence over those loaded from an env_file [5][8]. If you are changing environment variables and the container does not seem to update, ensure you are running the command in the same directory as your compose.yaml or specify the path to your configuration file correctly [4]. If the container persists old state (e.g., due to anonymous volumes), you may also need to consider flags like --renew-anon-volumes to fully refresh the container's state [9].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Files matching relevant names =="
git ls-files 2>/dev/null || true \
  | grep -E '(^|/)(Makefile|compose.*\.ya?ml|docker-compose\.ya?ml|\.env(_[^/]+)?)$' || true

echo
echo "== Makefile around restart target =="
awk 'NR>=45 && NR<=70 { printf "%6d\t%s\n", NR, $0 }' Makefile 2>/dev/null || true

echo
echo "== Search restart / docker compose / env changes =="
grep -n -- "restart:|docker compose|env changes" Makefile Makefile.lock 2>/dev/null | head -50 || true

Repository: plan-cake/plancake

Length of output: 16153


Recreate containers when applying environment changes.

docker compose restart only restarts existing containers and does not apply changed .env values or updated Compose configuration, contradicting the target comment. Use docker compose up -d --force-recreate instead.


# --- 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 ---

# 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 -it frontend /bin/sh

# Open a terminal inside the backend container
shell-api:
docker compose exec -it backend /bin/bash
Comment on lines +86 to +87

# 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 api
88 changes: 87 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,90 @@

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.
## 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.

```bash
plancake/
├── backend/ # Django API server
└── frontend/ # Next.js application
```

## Getting Started

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

Copy the contents of the respective `.env.example` files in each folder into its own `.env`:

- [`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

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 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

The most commonly used 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) |

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.

_(To exit log streams, press `Ctrl+C`. To exit shell sessions, type `exit` or press `Ctrl+D`.)_

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.
Loading
Loading