build: tighten Docker setup for local-stack parity
- Dockerfile: npm ci (uses package-lock for reproducible installs) - CMD now: migrate → seed (idempotent) → start. Gated by SEED_ON_BOOT. - docker-compose: name: helix, healthcheck on /, OAuth env defaults to empty so `docker compose up` works without a .env (public pages render; sign-in fails until OAuth is configured). - README: explicit "Run it locally — two ways" section. Docker first (production-like), native Node second. Documents port-conflict workaround and Gitea OAuth setup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,8 +9,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
python3 make g++ \
|
python3 make g++ \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY package.json package-lock.json* ./
|
COPY package.json package-lock.json ./
|
||||||
RUN npm install --include=dev
|
RUN npm ci --include=dev
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
@@ -39,5 +39,6 @@ COPY --from=build --chown=node:node /app/scripts ./scripts
|
|||||||
USER node
|
USER node
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# Run migrations then start the SvelteKit Node server.
|
# Run migrations + seed (idempotent INSERT OR IGNORE) then start the server.
|
||||||
CMD ["sh", "-c", "node scripts/migrate.js && node build"]
|
# Set SEED_ON_BOOT=false in production once you've added real content.
|
||||||
|
CMD ["sh", "-c", "node scripts/migrate.js && { [ \"$SEED_ON_BOOT\" = \"false\" ] || node scripts/seed.js; } && node build"]
|
||||||
|
|||||||
71
README.md
71
README.md
@@ -17,18 +17,57 @@ to the `RnD` Gitea organisation; viewing is open.
|
|||||||
- **Gitea OAuth2** for authoring (no passwords stored)
|
- **Gitea OAuth2** for authoring (no passwords stored)
|
||||||
- Pure **SVG + CSS** helix animation — no WebGL
|
- Pure **SVG + CSS** helix animation — no WebGL
|
||||||
|
|
||||||
## Local development
|
## Run it locally — two ways
|
||||||
|
|
||||||
|
### 1. Docker (production-like, recommended for testing)
|
||||||
|
|
||||||
|
Mirrors how the EVOLV stack is exercised locally. One command, named volume
|
||||||
|
for SQLite, healthcheck wired:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nvm use # picks up .nvmrc → Node 20
|
cd /path/to/helix
|
||||||
cp .env.example .env
|
docker compose up -d --build # build image + start container
|
||||||
# Fill in GITEA_CLIENT_ID and GITEA_CLIENT_SECRET (see "Gitea OAuth setup" below)
|
docker compose logs -f helix # tail logs
|
||||||
|
# → http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
If port 3000 is in use (Node-RED, another dev server, …) pick another:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
HELIX_PORT=3030 \
|
||||||
|
ORIGIN=http://localhost:3030 \
|
||||||
|
GITEA_REDIRECT_URI=http://localhost:3030/auth/gitea/callback \
|
||||||
|
docker compose up -d --build
|
||||||
|
# → http://localhost:3030
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps # status + health
|
||||||
|
docker inspect helix --format '{{.State.Health.Status}}'
|
||||||
|
docker compose exec helix sh # shell into the container
|
||||||
|
docker compose exec helix sqlite3 /data/helix.db # inspect the DB
|
||||||
|
docker compose down # stop (keeps the volume)
|
||||||
|
docker compose down -v # stop + DELETE all data
|
||||||
|
```
|
||||||
|
|
||||||
|
The SQLite database lives in the **`helix_helix-data`** named volume at
|
||||||
|
`/data/helix.db`. Migrations + the idempotent seed both run on every boot.
|
||||||
|
Once you have real content, set `SEED_ON_BOOT=false` in your env to stop
|
||||||
|
re-seeding the demo entries.
|
||||||
|
|
||||||
|
### 2. Native Node (faster dev loop, hot reload)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nvm use # .nvmrc → Node 20 (Tailwind v3 also works on 18)
|
||||||
|
cp .env.example .env # fill in Gitea OAuth — see below
|
||||||
|
|
||||||
npm install
|
npm install
|
||||||
npm run db:generate # produces drizzle/0000_*.sql from the schema
|
npm run db:generate # generate drizzle/0000_*.sql from the schema
|
||||||
npm run db:migrate # applies migrations to ./helix.db
|
npm run db:migrate # apply migrations to ./helix.db
|
||||||
npm run db:seed # adds example projects + posts so the landing page isn't empty
|
npm run db:seed # idempotent demo content
|
||||||
npm run dev # http://localhost:3000
|
npm run dev # http://localhost:5173
|
||||||
```
|
```
|
||||||
|
|
||||||
## Gitea OAuth setup
|
## Gitea OAuth setup
|
||||||
@@ -58,15 +97,23 @@ origins can be embedded inline on a project page. Add a hostname (no scheme,
|
|||||||
no path) and redeploy. Hosts not on the list render as a "Open in new tab" card
|
no path) and redeploy. Hosts not on the list render as a "Open in new tab" card
|
||||||
instead — never blindly iframed.
|
instead — never blindly iframed.
|
||||||
|
|
||||||
## Production deploy (Docker)
|
## Production deploy
|
||||||
|
|
||||||
|
Same compose file as local. On the production host:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env # fill in Gitea OAuth + ORIGIN
|
cp .env.example .env
|
||||||
|
# Set: ORIGIN=https://your-host, GITEA_REDIRECT_URI=https://your-host/auth/gitea/callback,
|
||||||
|
# GITEA_CLIENT_ID, GITEA_CLIENT_SECRET (from a separate prod OAuth app)
|
||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
The SQLite database lives in the `helix-data` named volume at `/data/helix.db`.
|
Put a TLS-terminating reverse proxy (nginx/caddy/traefik) in front of port 3000.
|
||||||
Back it up with `docker compose exec helix sqlite3 /data/helix.db .dump`.
|
Back up the volume periodically:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec helix sqlite3 /data/helix.db .dump | gzip > helix-backup-$(date +%F).sql.gz
|
||||||
|
```
|
||||||
|
|
||||||
## Project layout
|
## Project layout
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
name: helix
|
||||||
|
|
||||||
services:
|
services:
|
||||||
helix:
|
helix:
|
||||||
build: .
|
build:
|
||||||
|
context: .
|
||||||
container_name: helix
|
container_name: helix
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
@@ -11,12 +14,18 @@ services:
|
|||||||
ORIGIN: ${ORIGIN:-http://localhost:3000}
|
ORIGIN: ${ORIGIN:-http://localhost:3000}
|
||||||
DATABASE_URL: /data/helix.db
|
DATABASE_URL: /data/helix.db
|
||||||
GITEA_BASE_URL: ${GITEA_BASE_URL:-https://gitea.wbd-rd.nl}
|
GITEA_BASE_URL: ${GITEA_BASE_URL:-https://gitea.wbd-rd.nl}
|
||||||
GITEA_CLIENT_ID: ${GITEA_CLIENT_ID}
|
GITEA_CLIENT_ID: ${GITEA_CLIENT_ID:-}
|
||||||
GITEA_CLIENT_SECRET: ${GITEA_CLIENT_SECRET}
|
GITEA_CLIENT_SECRET: ${GITEA_CLIENT_SECRET:-}
|
||||||
GITEA_REDIRECT_URI: ${GITEA_REDIRECT_URI:-http://localhost:3000/auth/gitea/callback}
|
GITEA_REDIRECT_URI: ${GITEA_REDIRECT_URI:-http://localhost:3000/auth/gitea/callback}
|
||||||
GITEA_ALLOWED_ORG: ${GITEA_ALLOWED_ORG:-RnD}
|
GITEA_ALLOWED_ORG: ${GITEA_ALLOWED_ORG:-RnD}
|
||||||
volumes:
|
volumes:
|
||||||
- helix-data:/data
|
- helix-data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 4
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
helix-data:
|
helix-data:
|
||||||
|
|||||||
Reference in New Issue
Block a user