chore: move docker/ compose files and infra to repo root
Moved compose.yml, compose.prod.yml, DEPLOY.md, traefik/, and cron/ out of docker/ into the repo root (docker/ removed). Updated every reference: - Dockerfile: COPY paths for the cron stage - .dockerignore: dropped the now-unneeded docker/!docker-cron exclude+exception pair (cron/ is just a normal top-level dir now, no special-casing needed) - compose.prod.yml: build context ".." -> "." (Dockerfile and compose files are now siblings, not one level apart) - CLAUDE.md, DEPLOY.md, compose.yml, traefik/epicure.yml, cron/run-digest.sh, weekly-digest route.ts: path references in comments/ docs Verified: both compose files validate, and all three Dockerfile targets (runner/cron/migrator) build clean via a local --no-cache docker build from the new layout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,4 @@
|
|||||||
.git
|
.git
|
||||||
.env*
|
.env*
|
||||||
!.env.example
|
!.env.example
|
||||||
docker
|
|
||||||
!docker/cron
|
|
||||||
*.md
|
*.md
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
```bash
|
```bash
|
||||||
cp .env.example .env.local # fill in secrets (lives at repo root)
|
cp .env.example .env.local # fill in secrets (lives at repo root)
|
||||||
ln -s ../../.env.local apps/web/.env.local # Next.js reads from apps/web/, not root
|
ln -s ../../.env.local apps/web/.env.local # Next.js reads from apps/web/, not root
|
||||||
docker compose -f docker/compose.yml up -d # start postgres, redis, minio
|
docker compose -f compose.yml up -d # start postgres, redis, minio
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm db:generate # generate migrations from schema (first time)
|
pnpm db:generate # generate migrations from schema (first time)
|
||||||
pnpm db:migrate # apply migrations
|
pnpm db:migrate # apply migrations
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
1. Stacks → Add stack → **Repository**
|
1. Stacks → Add stack → **Repository**
|
||||||
2. Repository URL: this repo. Reference: branch to track (e.g. `main`)
|
2. Repository URL: this repo. Reference: branch to track (e.g. `main`)
|
||||||
3. Compose path: `docker/compose.prod.yml`
|
3. Compose path: `compose.prod.yml`
|
||||||
4. Environment variables (Portainer stack env, not committed):
|
4. Environment variables (Portainer stack env, not committed):
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -47,7 +47,7 @@ ANTHROPIC_API_KEY=
|
|||||||
OLLAMA_BASE_URL=
|
OLLAMA_BASE_URL=
|
||||||
```
|
```
|
||||||
|
|
||||||
Every var listed here must exist in `docker/compose.prod.yml`'s `web.environment` block to actually
|
Every var listed here must exist in `compose.prod.yml`'s `web.environment` block to actually
|
||||||
reach the container — Portainer stack env alone isn't enough, it only fills in `${...}` refs the
|
reach the container — Portainer stack env alone isn't enough, it only fills in `${...}` refs the
|
||||||
compose file declares.
|
compose file declares.
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ Both are idempotent — safe to re-run on every stack redeploy, not just the fir
|
|||||||
|
|
||||||
## Traefik (separate LXC, file provider)
|
## Traefik (separate LXC, file provider)
|
||||||
|
|
||||||
1. Copy `docker/traefik/epicure.yml` into the traefik LXC's dynamic config directory.
|
1. Copy `traefik/epicure.yml` into the traefik LXC's dynamic config directory.
|
||||||
2. Replace `HOST_DOMAIN` with the public hostname and `PORTAINER_LXC_IP` with the portainer LXC's network IP (must match `WEB_PORT` published in compose.prod.yml).
|
2. Replace `HOST_DOMAIN` with the public hostname and `PORTAINER_LXC_IP` with the portainer LXC's network IP (must match `WEB_PORT` published in compose.prod.yml).
|
||||||
3. Confirm `certResolver` name matches what's set in traefik's static config.
|
3. Confirm `certResolver` name matches what's set in traefik's static config.
|
||||||
4. Traefik picks it up automatically (file provider watches for changes) — no restart needed.
|
4. Traefik picks it up automatically (file provider watches for changes) — no restart needed.
|
||||||
+2
-2
@@ -63,7 +63,7 @@ CMD ["node", "apps/web/server.js"]
|
|||||||
# rather than `runner`, keeping the image tiny and independent of the Next build.
|
# rather than `runner`, keeping the image tiny and independent of the Next build.
|
||||||
FROM base AS cron
|
FROM base AS cron
|
||||||
RUN apk add --no-cache curl
|
RUN apk add --no-cache curl
|
||||||
COPY docker/cron/crontab /etc/crontabs/root
|
COPY cron/crontab /etc/crontabs/root
|
||||||
COPY docker/cron/run-digest.sh /usr/local/bin/run-digest.sh
|
COPY cron/run-digest.sh /usr/local/bin/run-digest.sh
|
||||||
RUN chmod +x /usr/local/bin/run-digest.sh
|
RUN chmod +x /usr/local/bin/run-digest.sh
|
||||||
CMD ["crond", "-f", "-l", "2"]
|
CMD ["crond", "-f", "-l", "2"]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
import { sendEmail, weeklyDigestHtml } from "@/lib/email";
|
import { sendEmail, weeklyDigestHtml } from "@/lib/email";
|
||||||
|
|
||||||
// Internal cron endpoint — triggered by the `digest-cron` container on a weekly
|
// Internal cron endpoint — triggered by the `digest-cron` container on a weekly
|
||||||
// schedule (see docker/compose.prod.yml). Not part of the public API surface;
|
// schedule (see compose.prod.yml). Not part of the public API surface;
|
||||||
// protected by a shared secret rather than user auth.
|
// protected by a shared secret rather than user auth.
|
||||||
//
|
//
|
||||||
// Computes, for every user: new followers / new comments / new ratings on
|
// Computes, for every user: new followers / new comments / new ratings on
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ services:
|
|||||||
|
|
||||||
migrate:
|
migrate:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
target: migrator
|
target: migrator
|
||||||
environment:
|
environment:
|
||||||
@@ -63,7 +63,7 @@ services:
|
|||||||
|
|
||||||
web:
|
web:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
# Explicit target: the Dockerfile's last stage is no longer `runner` now
|
# Explicit target: the Dockerfile's last stage is no longer `runner` now
|
||||||
# that `cron` was appended after it, and the default build target is
|
# that `cron` was appended after it, and the default build target is
|
||||||
@@ -124,11 +124,11 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
# Fires the weekly digest email send by POSTing to `web`'s internal cron
|
# Fires the weekly digest email send by POSTing to `web`'s internal cron
|
||||||
# route on a schedule (see docker/cron/crontab). Tiny alpine+curl+crond
|
# route on a schedule (see cron/crontab). Tiny alpine+curl+crond
|
||||||
# image — not part of the app build, just a periodic HTTP trigger.
|
# image — not part of the app build, just a periodic HTTP trigger.
|
||||||
digest-cron:
|
digest-cron:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
target: cron
|
target: cron
|
||||||
restart: always
|
restart: always
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Dev-only infra: postgres/redis/minio. The app and the `digest-cron` container
|
# Dev-only infra: postgres/redis/minio. The app and the `digest-cron` container
|
||||||
# (docker/compose.prod.yml) aren't run here — use `pnpm dev` locally, and hit
|
# (compose.prod.yml) aren't run here — use `pnpm dev` locally, and hit
|
||||||
# POST /api/internal/cron/weekly-digest with the CRON_SECRET header directly if
|
# POST /api/internal/cron/weekly-digest with the CRON_SECRET header directly if
|
||||||
# you need to test the weekly digest send without waiting for the schedule.
|
# you need to test the weekly digest send without waiting for the schedule.
|
||||||
services:
|
services:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Triggers the weekly digest email send on the `web` service. Run on a schedule
|
# Triggers the weekly digest email send on the `web` service. Run on a schedule
|
||||||
# by busybox crond (see docker/cron/crontab). Fails loudly (non-zero exit,
|
# by busybox crond (see cron/crontab). Fails loudly (non-zero exit,
|
||||||
# stderr) but crond just tries again next week — no retry loop here on purpose,
|
# stderr) but crond just tries again next week — no retry loop here on purpose,
|
||||||
# keep this script trivial.
|
# keep this script trivial.
|
||||||
set -eu
|
set -eu
|
||||||
@@ -17,7 +17,7 @@ http:
|
|||||||
epicure:
|
epicure:
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
servers:
|
servers:
|
||||||
- url: "http://PORTAINER_LXC_IP:3000" # match WEB_PORT from docker/compose.prod.yml env
|
- url: "http://PORTAINER_LXC_IP:3000" # match WEB_PORT from compose.prod.yml env
|
||||||
healthCheck:
|
healthCheck:
|
||||||
path: /
|
path: /
|
||||||
interval: 10s
|
interval: 10s
|
||||||
Reference in New Issue
Block a user