chore: use shared postgres for family wishlist

This commit is contained in:
Vaka.pro
2026-04-25 17:25:28 +03:00
parent a7d5260ce3
commit 2adb03ff33
5 changed files with 46 additions and 42 deletions

View File

@@ -1,12 +1,14 @@
# ========================================== # ==========================================
# Database # Database
# ========================================== # ==========================================
POSTGRES_USER=wishlist DB_HOST=postgres_budget
POSTGRES_PASSWORD=change_me DB_PORT=5432
POSTGRES_DB=family_wishlist DB_NAME=db_family
# DATABASE_URL uses the docker-compose service name `postgres`. DB_USER=
# When running backend outside docker against docker-postgres use localhost:5432. DB_PASSWORD=
DATABASE_URL=postgresql://wishlist:change_me@postgres:5432/family_wishlist # Fill DATABASE_URL explicitly; .env files do not expand ${...} automatically for the app.
# For local host-based development, point it to localhost:5432 instead of postgres_budget.
DATABASE_URL=postgresql://<db_user>:<db_password>@postgres_budget:5432/db_family
# ========================================== # ==========================================
# Users (two fixed accounts) # Users (two fixed accounts)

View File

@@ -5,7 +5,7 @@ A small, private wishlist app for two users. Each user has their own profile, sl
- **Backend**: Node.js 20, Fastify 4, Prisma 5, PostgreSQL 16 - **Backend**: Node.js 20, Fastify 4, Prisma 5, PostgreSQL 16
- **Frontend**: React 18, Vite 5, Tailwind CSS, TanStack Query, React Hook Form + Zod - **Frontend**: React 18, Vite 5, Tailwind CSS, TanStack Query, React Hook Form + Zod
- **Monorepo**: pnpm workspaces - **Monorepo**: pnpm workspaces
- **Deploy**: Docker Compose (Postgres + backend + nginx-served frontend) - **Deploy**: Docker Compose (shared Postgres + backend + nginx-served frontend)
--- ---
@@ -32,7 +32,7 @@ apps/
packages/ packages/
shared/ zod schemas + DTO types shared between backend and frontend shared/ zod schemas + DTO types shared between backend and frontend
docker/ Dockerfiles + nginx.conf docker/ Dockerfiles + nginx.conf
docker-compose.yml prod stack (postgres + backend + frontend) docker-compose.yml prod stack (shared postgres + backend + frontend)
docker-compose.dev.yml dev helper (postgres only) docker-compose.dev.yml dev helper (postgres only)
.env.example full env template .env.example full env template
``` ```
@@ -127,10 +127,11 @@ Review the rest of `.env`:
- `USER1_USERNAME`, `USER1_SLUG`, `USER1_DISPLAY_NAME` - `USER1_USERNAME`, `USER1_SLUG`, `USER1_DISPLAY_NAME`
- `USER2_USERNAME`, `USER2_SLUG`, `USER2_DISPLAY_NAME` - `USER2_USERNAME`, `USER2_SLUG`, `USER2_DISPLAY_NAME`
- `POSTGRES_*` (used by Docker) - `DB_HOST=postgres_budget`, `DB_PORT=5432`, `DB_NAME=db_family`
- `DB_USER`, `DB_PASSWORD`, `DATABASE_URL` (sensitive values stay only in `.env`)
- `PUBLIC_APP_URL` (used for CORS in production) - `PUBLIC_APP_URL` (used for CORS in production)
### 3. Run everything via Docker ### 3. Run the shared Docker stack
```bash ```bash
docker compose up --build docker compose up --build
@@ -139,12 +140,18 @@ docker compose up --build
Opens: Opens:
- Frontend: http://localhost:8080 - Frontend: http://localhost:8080
- Backend API: http://localhost:8080/api (proxied by nginx) or http://localhost:3000 if you map `backend` - Backend API: http://localhost:8080/api (proxied by nginx) or http://localhost:3000 if you map `family-wishlist-backend`
- Postgres: internal only
Before first start, create a dedicated database and user for this project in the existing Postgres host:
- host: `postgres_budget`
- port: `5432`
- database: `db_family`
- user/password: set only in `.env` and do not commit them
On first start, the backend: On first start, the backend:
1. Runs `prisma db push` against the Postgres service (creates tables from `schema.prisma`; idempotent). 1. Runs `prisma db push` against the configured shared Postgres database (creates tables from `schema.prisma`; idempotent).
2. Seeds/upserts both users from env (public fields only — password hash stays in env). 2. Seeds/upserts both users from env (public fields only — password hash stays in env).
3. Starts Fastify on port 3000. 3. Starts Fastify on port 3000.
4. Registers the daily trash-purge cron (runs at 03:17 UTC, also once on startup). 4. Registers the daily trash-purge cron (runs at 03:17 UTC, also once on startup).
@@ -159,7 +166,7 @@ Run Postgres in a container, apps on the host:
```bash ```bash
docker compose -f docker-compose.dev.yml up -d docker compose -f docker-compose.dev.yml up -d
# Override DATABASE_URL to point to localhost: # Override DATABASE_URL to point to localhost:
# DATABASE_URL=postgresql://wishlist:change_me@localhost:5432/family_wishlist # DATABASE_URL=postgresql://<DB_USER>:<DB_PASSWORD>@localhost:5432/db_family
pnpm --filter @family-wishlist/backend prisma:push # apply schema (first time and on schema changes) pnpm --filter @family-wishlist/backend prisma:push # apply schema (first time and on schema changes)
pnpm --filter @family-wishlist/backend seed # upsert two users from env into DB pnpm --filter @family-wishlist/backend seed # upsert two users from env into DB
@@ -170,6 +177,9 @@ This starts both apps in parallel:
- Frontend: http://localhost:5173 (proxying `/api` and `/uploads` to http://localhost:3000) - Frontend: http://localhost:5173 (proxying `/api` and `/uploads` to http://localhost:3000)
- Backend: http://localhost:3000 - Backend: http://localhost:3000
- Dev Postgres: `localhost:5432` for local-only development data
The dev compose file stays isolated from the shared `postgres_budget` instance. Keep production credentials and local credentials in `.env`, and never hardcode them in compose files or source code.
### 5. Useful scripts ### 5. Useful scripts

View File

@@ -1,11 +1,12 @@
services: services:
postgres: postgres:
image: postgres:16-alpine image: postgres:16-alpine
container_name: family-wishlist-postgres-dev
restart: unless-stopped restart: unless-stopped
environment: environment:
POSTGRES_USER: ${POSTGRES_USER} POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB} POSTGRES_DB: ${DB_NAME}
ports: ports:
- "5432:5432" - "5432:5432"
volumes: volumes:

View File

@@ -1,33 +1,16 @@
services: services:
postgres: family-wishlist-backend:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 10
backend:
build: build:
context: . context: .
dockerfile: docker/backend.Dockerfile dockerfile: docker/backend.Dockerfile
container_name: family-wishlist-backend
restart: unless-stopped restart: unless-stopped
env_file: .env env_file: .env
environment: environment:
NODE_ENV: production NODE_ENV: production
UPLOADS_DIR: /app/apps/backend/uploads UPLOADS_DIR: /app/apps/backend/uploads
BACKEND_PORT: 3000 BACKEND_PORT: 3000
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB} DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}
depends_on:
postgres:
condition: service_healthy
volumes: volumes:
- uploads:/app/apps/backend/uploads - uploads:/app/apps/backend/uploads
healthcheck: healthcheck:
@@ -42,18 +25,26 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
start_period: 20s start_period: 20s
networks:
- postgres_default
frontend: frontend:
build: build:
context: . context: .
dockerfile: docker/frontend.Dockerfile dockerfile: docker/frontend.Dockerfile
container_name: family-wishlist-frontend
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
backend: family-wishlist-backend:
condition: service_started condition: service_healthy
ports: ports:
- "8080:80" - "8080:80"
networks:
- postgres_default
volumes: volumes:
pgdata:
uploads: uploads:
networks:
postgres_default:
external: true

View File

@@ -13,7 +13,7 @@ server {
# API proxy # API proxy
location /api/ { location /api/ {
proxy_pass http://backend:3000/api/; proxy_pass http://family-wishlist-backend:3000/api/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
@@ -25,7 +25,7 @@ server {
# Uploaded files (images) # Uploaded files (images)
location /uploads/ { location /uploads/ {
proxy_pass http://backend:3000/uploads/; proxy_pass http://family-wishlist-backend:3000/uploads/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_cache_valid 200 1h; proxy_cache_valid 200 1h;