chore(deploy): non-root image, reproducible builds, private database

- Dockerfile: Go 1.26, npm ci, go mod verify, non-root user, alpine 3.22.
- Compose: Postgres no longer published on the host, password and new
  security settings read from .env, GOMEMLIMIT.
- README: document new environment variables and reading shortcuts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Antigravity AgentandClaude Opus 5.5 committed 2026-10-09 07:34:09 +02:00
1 parent 360f3cf012
commit 9eaed0aea4
5 files changed
+72 -27

No files matched your search

+4 -2
View File
@@ -26,14 +26,16 @@
Thumbs.db Thumbs.db
task.md task.md
# Go # Go (go.sum is committed for reproducible, verified builds)
vendor/ vendor/
go.sum
# Node # Node
node_modules/ node_modules/
npm-debug.log npm-debug.log
yarn-error.log yarn-error.log
# Tooling
.playwright-mcp/
# FlowReader Specific # FlowReader Specific
/_bmad-output/tmp/ /_bmad-output/tmp/
+16 -15
View File
@@ -1,32 +1,33 @@
# Multi-stage Dockerfile for FlowReader # Multi-stage Dockerfile for FlowReader
# Step 1: Build the React Frontend # Step 1: Build the React Frontend
FROM node:20-alpine AS web-builder FROM node:22-alpine AS web-builder
WORKDIR /app/web WORKDIR /app/web
COPY web/package*.json ./ COPY web/package*.json ./
RUN npm install # Reproducible install from the lockfile
RUN npm ci --no-audit --no-fund
COPY web/ ./ COPY web/ ./
RUN npm run build RUN npm run build
# Step 2: Build the Go Backend # Step 2: Build the Go Backend
FROM golang:1.24-alpine AS builder FROM golang:1.26-alpine AS builder
WORKDIR /app WORKDIR /app
COPY . . COPY go.mod go.sum ./
RUN go mod tidy RUN go mod download && go mod verify
RUN go mod download COPY cmd/ ./cmd/
# Copy the built frontend from Step 1 COPY internal/ ./internal/
COPY --from=web-builder /app/web/dist ./web/dist RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-w -s" -o /server ./cmd/server
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /server ./cmd/server
# Step 3: Final Production Image # Step 3: Final Production Image
FROM alpine:3.19 FROM alpine:3.22
WORKDIR /app WORKDIR /app
RUN apk add --no-cache wget ca-certificates RUN apk add --no-cache wget ca-certificates tzdata \
&& adduser -D -H -u 10001 flowreader
COPY --from=builder /server /app/server COPY --from=builder /server /app/server
# Copy the static files for the Go server to serve # Static frontend and migrations (read-only for the app user)
COPY --from=builder /app/web/dist /app/web/dist COPY --from=web-builder /app/web/dist /app/web/dist
# Copy migration files for auto-migration COPY migrations /app/migrations
COPY --from=builder /app/migrations /app/migrations
USER flowreader
EXPOSE 8080 EXPOSE 8080
CMD ["/app/server"] CMD ["/app/server"]
+18 -1
View File
@@ -87,13 +87,30 @@ Un template dédié est fourni pour les utilisateurs d'Unraid.
| `PORT` | Port du serveur | `8080` | | `PORT` | Port du serveur | `8080` |
| `DATABASE_URL` | Connexion PostgreSQL | `postgres://...` | | `DATABASE_URL` | Connexion PostgreSQL | `postgres://...` |
| `OPENROUTER_API_KEY` | Clé pour les résumés IA | *(Optionnel)* | | `OPENROUTER_API_KEY` | Clé pour les résumés IA | *(Optionnel)* |
| `OPENROUTER_MODEL` | Modèle utilisé pour les résumés | `google/gemini-2.0-flash-001` |
| `POSTGRES_PASSWORD` | Mot de passe PostgreSQL (compose, à mettre dans `.env`) | `flowreader` — **à changer** |
| `REGISTRATION_ENABLED` | `false` pour fermer les inscriptions (le 1er compte, admin, reste possible) | `true` |
| `TRUSTED_PROXIES` | IP/CIDR du reverse proxy (ex. `172.16.0.0/12`) pour le rate-limit par IP client | *(vide)* |
| `COOKIE_SECURE` | Forcer le cookie `Secure` (HTTPS derrière proxy) | auto |
| `WS_ALLOWED_ORIGINS` | Origines WebSocket supplémentaires | *(vide)* |
> 🔒 La base PostgreSQL n'est plus exposée sur l'hôte par défaut. Changez `POSTGRES_PASSWORD`
> **avant** le premier démarrage (il n'est appliqué qu'à la création du volume).
### Lecture
- Raccourcis clavier : `j`/`k` naviguer, `o` ouvrir, `m` lu/non lu, `s` favori, `v` original,
`/` rechercher, `u` non lus/tous, `r` actualiser, `Espace` page suivante puis article suivant,
`?` aide.
- Bouton **Aa** dans le lecteur : thème (clair, sépia, sombre, auto), police (serif, sans,
Atkinson Hyperlegible), taille, interligne et largeur de colonne.
## 🛠️ Développement ## 🛠️ Développement
Envie de mettre les mains dans le code ? Envie de mettre les mains dans le code ?
```bash ```bash
# Pré-requis : Go 1.22+, Node 20+, Docker # Pré-requis : Go 1.26+, Node 22+, Docker
# 1. Lancer les services (DB) # 1. Lancer les services (DB)
make docker-up make docker-up
+18 -5
View File
@@ -10,8 +10,19 @@ services:
- "8080:8080" - "8080:8080"
environment: environment:
- PORT=8080 - PORT=8080
- DATABASE_URL=postgres://flowreader:flowreader@db:5432/flowreader?sslmode=disable # Set a strong password in a .env file next to this compose file.
- OPENROUTER_API_KEY=votre_clef_ici # Note: POSTGRES_PASSWORD only applies when the volume is first created.
- DATABASE_URL=postgres://flowreader:${POSTGRES_PASSWORD:-flowreader}@db:5432/flowreader?sslmode=disable
# Close public sign-ups once your account exists (the first account is admin).
- REGISTRATION_ENABLED=${REGISTRATION_ENABLED:-true}
# Behind a reverse proxy (Traefik, NPM, Caddy…): its IP/CIDR, so rate
# limiting sees real client IPs. Leave empty when exposed directly.
- TRUSTED_PROXIES=${TRUSTED_PROXIES:-}
# Force Secure cookies when served over HTTPS behind a proxy.
- COOKIE_SECURE=${COOKIE_SECURE:-}
- GOMEMLIMIT=48MiB
# Optional: AI summaries (OpenRouter). Leave empty to disable.
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
@@ -25,13 +36,15 @@ services:
container_name: flowreader-db container_name: flowreader-db
environment: environment:
- POSTGRES_USER=flowreader - POSTGRES_USER=flowreader
- POSTGRES_PASSWORD=flowreader - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-flowreader}
- POSTGRES_DB=flowreader - POSTGRES_DB=flowreader
volumes: volumes:
# Persistance des données sur Unraid (chemin typique) # Persistance des données sur Unraid (chemin typique)
- /mnt/user/appdata/flowreader/postgres_data:/var/lib/postgresql/data - /mnt/user/appdata/flowreader/postgres_data:/var/lib/postgresql/data
ports: # Not published on the host: only the app container needs the database.
- "5432:5432" # Uncomment for local debugging only (and bind to 127.0.0.1).
# ports:
# - "127.0.0.1:5432:5432"
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: [ "CMD-SHELL", "pg_isready -U flowreader -d flowreader" ] test: [ "CMD-SHELL", "pg_isready -U flowreader -d flowreader" ]
+16 -4
View File
@@ -7,7 +7,17 @@ services:
- "8080:8080" - "8080:8080"
environment: environment:
- PORT=8080 - PORT=8080
- DATABASE_URL=postgres://flowreader:flowreader@db:5432/flowreader?sslmode=disable # Set a strong password in a .env file next to this compose file.
# Note: POSTGRES_PASSWORD only applies when the volume is first created.
- DATABASE_URL=postgres://flowreader:${POSTGRES_PASSWORD:-flowreader}@db:5432/flowreader?sslmode=disable
# Close public sign-ups once your account exists (the first account is admin).
- REGISTRATION_ENABLED=${REGISTRATION_ENABLED:-true}
# Behind a reverse proxy (Traefik, NPM, Caddy…): its IP/CIDR, so rate
# limiting sees real client IPs. Leave empty when exposed directly.
- TRUSTED_PROXIES=${TRUSTED_PROXIES:-}
# Force Secure cookies when served over HTTPS behind a proxy.
- COOKIE_SECURE=${COOKIE_SECURE:-}
- GOMEMLIMIT=48MiB
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
@@ -27,12 +37,14 @@ services:
image: postgres:16-alpine image: postgres:16-alpine
environment: environment:
- POSTGRES_USER=flowreader - POSTGRES_USER=flowreader
- POSTGRES_PASSWORD=flowreader - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-flowreader}
- POSTGRES_DB=flowreader - POSTGRES_DB=flowreader
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
ports: # Not published on the host: only the app container needs the database.
- "5432:5432" # Uncomment for local debugging only (and bind to 127.0.0.1).
# ports:
# - "127.0.0.1:5432:5432"
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: [ "CMD-SHELL", "pg_isready -U flowreader -d flowreader" ] test: [ "CMD-SHELL", "pg_isready -U flowreader -d flowreader" ]