chore(deploy): Dockerfile + Dockge compose + deploy guide
This commit is contained in:
15
.dockerignore
Normal file
15
.dockerignore
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.git
|
||||||
|
**/node_modules
|
||||||
|
**/dist
|
||||||
|
**/.vite
|
||||||
|
data
|
||||||
|
**/data/*.db
|
||||||
|
**/data/*.db-*
|
||||||
|
e2e
|
||||||
|
**/test-results
|
||||||
|
**/playwright-report
|
||||||
|
docs
|
||||||
|
*.log
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
**/*.backup-*
|
||||||
88
DEPLOY.md
Normal file
88
DEPLOY.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Deploy to Dockge (fastest path: build on the host)
|
||||||
|
|
||||||
|
This is the quickest way to get Flashcard running on your Proxmox + Dockge setup.
|
||||||
|
Source comes from Gitea; Dockge builds the image locally and runs it. No container
|
||||||
|
registry, no Gitea Actions, no `docker login` required.
|
||||||
|
|
||||||
|
Repo: `https://gitea.hausmans.cloud/bert.hausmans/flashcards`
|
||||||
|
|
||||||
|
## What gets deployed
|
||||||
|
|
||||||
|
One container (`flashcard`) serving the API **and** the built frontend on port
|
||||||
|
`3000`, with a persistent named volume (`flashcard-data`) for the SQLite database.
|
||||||
|
Migrations run automatically on every container start.
|
||||||
|
|
||||||
|
## Step 1 — push the code to Gitea (from your dev machine)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/berthausmans/Documents/Development/flashcard
|
||||||
|
git remote add origin https://gitea.hausmans.cloud/bert.hausmans/flashcards.git # first time only
|
||||||
|
git push -u origin master
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2 — put the stack on the Dockge host
|
||||||
|
|
||||||
|
SSH into the host running Dockge. Dockge keeps stacks in `/opt/stacks` by default.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/stacks
|
||||||
|
git clone https://gitea.hausmans.cloud/bert.hausmans/flashcards.git flashcard
|
||||||
|
```
|
||||||
|
|
||||||
|
A "flashcard" stack now appears in the Dockge UI (it reads `compose.yaml`).
|
||||||
|
|
||||||
|
## Step 3 — configure environment in Dockge
|
||||||
|
|
||||||
|
Open the **flashcard** stack in Dockge and edit the `environment:` block:
|
||||||
|
|
||||||
|
- `APP_URL` → how you reach the app, e.g. `http://<dockge-host-ip>:3000`
|
||||||
|
(this is what verification/reset/invite e-mail links use — get it right).
|
||||||
|
- `COOKIE_SECURE` → leave `"false"` for now (plain HTTP). Flip to `"true"` only
|
||||||
|
after you put the app behind HTTPS.
|
||||||
|
- SMTP (Amazon SES) → fill in `SMTP_HOST` (e.g. `email-smtp.eu-west-1.amazonaws.com`),
|
||||||
|
`SMTP_USER`, `SMTP_PASS`, and `SMTP_FROM`. **Optional for first boot** — see Step 5.
|
||||||
|
|
||||||
|
## Step 4 — deploy
|
||||||
|
|
||||||
|
Click **Deploy** (Dockge runs `docker compose up -d --build`). First build takes a
|
||||||
|
few minutes (installs deps, builds the frontend, compiles better-sqlite3). When it's
|
||||||
|
up, open `http://<dockge-host-ip>:3000`.
|
||||||
|
|
||||||
|
## Step 5 — create the first account (becomes sysadmin)
|
||||||
|
|
||||||
|
Go to `/register` and sign up. The **first registered user is automatically the
|
||||||
|
sysadmin**.
|
||||||
|
|
||||||
|
- If SES is configured: click the verification link in your inbox.
|
||||||
|
- If you left SMTP empty: grab the verification link from the container logs —
|
||||||
|
in Dockge open the flashcard stack → Logs, and copy the
|
||||||
|
`…/verify-email?token=…` URL printed by the stub mailer.
|
||||||
|
|
||||||
|
Then log in. Done.
|
||||||
|
|
||||||
|
## Updating later
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/stacks/flashcard
|
||||||
|
git pull
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in Dockge click **Deploy** again (rebuilds + restarts). The `flashcard-data`
|
||||||
|
volume — and therefore all lessons, cards and accounts — persists across redeploys.
|
||||||
|
|
||||||
|
## Notes & gotchas
|
||||||
|
|
||||||
|
- **Data safety:** everything lives in the `flashcard-data` Docker volume. Don't
|
||||||
|
delete it. Back it up by copying `/data/flashcard.db` out of the container/volume.
|
||||||
|
- **Secure cookies need HTTPS.** With `COOKIE_SECURE=true` over plain HTTP the
|
||||||
|
browser silently discards the login cookie. Keep it `false` until you have TLS.
|
||||||
|
- **Mailpit** (`docker-compose.yml`) is dev-only and is ignored here — Dockge uses
|
||||||
|
`compose.yaml`.
|
||||||
|
- The backend runs via `tsx` (TypeScript runtime), matching dev. Migrations are
|
||||||
|
applied automatically by `docker-entrypoint.sh` before the server starts.
|
||||||
|
|
||||||
|
## Next step (optional, when you have time)
|
||||||
|
|
||||||
|
Move to the Gitea container registry + Gitea Actions so Dockge pulls a pre-built
|
||||||
|
image instead of building on the host. Ask and I'll add a `.gitea/workflows/build.yml`
|
||||||
|
plus a registry-based `compose.yaml`.
|
||||||
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Flashcard — single-container production image.
|
||||||
|
# Runs the Express API and serves the built React frontend on PORT (default 3000).
|
||||||
|
#
|
||||||
|
# The backend is launched with tsx (TypeScript runtime) because the shared
|
||||||
|
# package is consumed as TypeScript source; this matches how the app runs in dev
|
||||||
|
# and avoids a separate compile step for the shared workspace.
|
||||||
|
FROM node:22-bookworm-slim
|
||||||
|
|
||||||
|
# better-sqlite3 is a native module. Provide a toolchain in case no prebuilt
|
||||||
|
# binary exists for the target platform (most amd64/arm64 hosts use a prebuild).
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends python3 make g++ ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies first for better layer caching.
|
||||||
|
# Do NOT set NODE_ENV=production here: the image needs devDependencies
|
||||||
|
# (vite to build the frontend, tsx to run the backend).
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
COPY packages/shared/package.json packages/shared/package.json
|
||||||
|
COPY packages/backend/package.json packages/backend/package.json
|
||||||
|
COPY packages/frontend/package.json packages/frontend/package.json
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Copy the source and build the static frontend (served by the backend).
|
||||||
|
COPY . .
|
||||||
|
RUN npm -w @flashcard/frontend run build \
|
||||||
|
&& chmod +x /app/docker-entrypoint.sh
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||||
42
compose.yaml
Normal file
42
compose.yaml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Production stack for Dockge — builds the image on the host from this repo.
|
||||||
|
# Dockge prefers compose.yaml, so this file is used instead of the dev
|
||||||
|
# docker-compose.yml (which only runs Mailpit for local development).
|
||||||
|
services:
|
||||||
|
flashcard:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: flashcard:local
|
||||||
|
container_name: flashcard
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- flashcard-data:/data
|
||||||
|
environment:
|
||||||
|
NODE_ENV: production
|
||||||
|
PORT: "3000"
|
||||||
|
DB_PATH: /data/flashcard.db
|
||||||
|
|
||||||
|
# URL where users reach the app. Used in verification / reset / invite emails.
|
||||||
|
# CHANGE THIS to your real address, e.g. http://192.168.1.50:3000
|
||||||
|
# or https://flashcards.hausmans.cloud once behind a reverse proxy.
|
||||||
|
APP_URL: "http://CHANGE-ME:3000"
|
||||||
|
|
||||||
|
# Cookie security. Keep "false" when serving over plain HTTP — otherwise the
|
||||||
|
# browser drops the session cookie and login silently fails. Set to "true"
|
||||||
|
# only once the app is served over HTTPS (reverse proxy with TLS).
|
||||||
|
COOKIE_SECURE: "false"
|
||||||
|
|
||||||
|
# SMTP (Amazon SES). Leave SMTP_HOST empty to fall back to a stub mailer that
|
||||||
|
# prints verification/reset links to the container logs — handy for the very
|
||||||
|
# first sysadmin signup before SES is wired up.
|
||||||
|
SMTP_HOST: ""
|
||||||
|
SMTP_PORT: "587"
|
||||||
|
SMTP_SECURE: "false"
|
||||||
|
SMTP_USER: ""
|
||||||
|
SMTP_PASS: ""
|
||||||
|
SMTP_FROM: "Flashcard <noreply@hausmans.cloud>"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
flashcard-data:
|
||||||
9
docker-entrypoint.sh
Executable file
9
docker-entrypoint.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
cd /app
|
||||||
|
|
||||||
|
echo "[entrypoint] Applying database migrations…"
|
||||||
|
node_modules/.bin/tsx packages/backend/src/db/migrate.ts
|
||||||
|
|
||||||
|
echo "[entrypoint] Starting Flashcard on port ${PORT:-3000}…"
|
||||||
|
exec node_modules/.bin/tsx packages/backend/src/index.ts
|
||||||
Reference in New Issue
Block a user