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
task.md
# Go
# Go (go.sum is committed for reproducible, verified builds)
vendor/
go.sum
# Node
node_modules/
npm-debug.log
yarn-error.log
# Tooling
.playwright-mcp/
# FlowReader Specific
/_bmad-output/tmp/
+16 -15
View File
@@ -1,32 +1,33 @@
# Multi-stage Dockerfile for FlowReader
# Step 1: Build the React Frontend
FROM node:20-alpine AS web-builder
FROM node:22-alpine AS web-builder
WORKDIR /app/web
COPY web/package*.json ./
RUN npm install
# Reproducible install from the lockfile
RUN npm ci --no-audit --no-fund
COPY web/ ./
RUN npm run build
# Step 2: Build the Go Backend
FROM golang:1.24-alpine AS builder
FROM golang:1.26-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod tidy
RUN go mod download
# Copy the built frontend from Step 1
COPY --from=web-builder /app/web/dist ./web/dist
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /server ./cmd/server
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY cmd/ ./cmd/
COPY internal/ ./internal/
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-w -s" -o /server ./cmd/server
# Step 3: Final Production Image
FROM alpine:3.19
FROM alpine:3.22
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 the static files for the Go server to serve
COPY --from=builder /app/web/dist /app/web/dist
# Copy migration files for auto-migration
COPY --from=builder /app/migrations /app/migrations
# Static frontend and migrations (read-only for the app user)
COPY --from=web-builder /app/web/dist /app/web/dist
COPY migrations /app/migrations
USER flowreader
EXPOSE 8080
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` |
| `DATABASE_URL` | Connexion PostgreSQL | `postgres://...` |
| `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
Envie de mettre les mains dans le code ?
```bash
# Pré-requis : Go 1.22+, Node 20+, Docker
# Pré-requis : Go 1.26+, Node 22+, Docker
# 1. Lancer les services (DB)
make docker-up
+18 -5
View File
@@ -10,8 +10,19 @@ services:
- "8080:8080"
environment:
- PORT=8080
- DATABASE_URL=postgres://flowreader:flowreader@db:5432/flowreader?sslmode=disable
- OPENROUTER_API_KEY=votre_clef_ici
# 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
# Optional: AI summaries (OpenRouter). Leave empty to disable.
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
depends_on:
db:
condition: service_healthy
@@ -25,13 +36,15 @@ services:
container_name: flowreader-db
environment:
- POSTGRES_USER=flowreader
- POSTGRES_PASSWORD=flowreader
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-flowreader}
- POSTGRES_DB=flowreader
volumes:
# Persistance des données sur Unraid (chemin typique)
- /mnt/user/appdata/flowreader/postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
# Not published on the host: only the app container needs the database.
# Uncomment for local debugging only (and bind to 127.0.0.1).
# ports:
# - "127.0.0.1:5432:5432"
restart: unless-stopped
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U flowreader -d flowreader" ]
+16 -4
View File
@@ -7,7 +7,17 @@ services:
- "8080:8080"
environment:
- 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:
db:
condition: service_healthy
@@ -27,12 +37,14 @@ services:
image: postgres:16-alpine
environment:
- POSTGRES_USER=flowreader
- POSTGRES_PASSWORD=flowreader
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-flowreader}
- POSTGRES_DB=flowreader
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
# Not published on the host: only the app container needs the database.
# Uncomment for local debugging only (and bind to 127.0.0.1).
# ports:
# - "127.0.0.1:5432:5432"
restart: unless-stopped
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U flowreader -d flowreader" ]