feat: add containerized application delivery #11
Conventional commits / commits (pull_request) Successful in 10s
Conventional commits / commits (pull_request) Successful in 10s
This commit was merged in pull request #12.
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
# Copy to a restricted, untracked file. Never commit filled values.
|
||||||
|
# Full release image references; use published version tags, not latest.
|
||||||
|
FRONTEND_IMAGE=git.personal.ksv741.ru/wealthledger/frontend:0.1.0
|
||||||
|
BACKEND_IMAGE=git.personal.ksv741.ru/wealthledger/backend:0.1.0
|
||||||
|
FRONTEND_PORT=8080
|
||||||
|
FRONTEND_ORIGIN=https://wealthledger.example.com
|
||||||
|
# Absolute directory, pre-created with owner UID/GID 1000:1000 and mode 0700.
|
||||||
|
DATA_DIRECTORY=/srv/wealthledger/data
|
||||||
|
# Generate once and keep across updates. Store an independent key backup.
|
||||||
|
JWT_SECRET=
|
||||||
|
TOKEN_ENCRYPTION_KEY=
|
||||||
|
LOG_LEVEL=info
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Local-only fixture values. Never use this file for a server deployment.
|
||||||
|
# Base-file interpolation happens before overrides; these references are replaced.
|
||||||
|
FRONTEND_IMAGE=wealthledger-frontend:development
|
||||||
|
BACKEND_IMAGE=wealthledger-backend:development
|
||||||
|
DATA_DIRECTORY=/unused-development-volume
|
||||||
|
FRONTEND_PORT=5173
|
||||||
|
FRONTEND_ORIGIN=http://localhost:5173
|
||||||
|
JWT_SECRET=local-development-only-not-a-server-secret
|
||||||
|
TOKEN_ENCRYPTION_KEY=MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=
|
||||||
|
LOG_LEVEL=info
|
||||||
@@ -3,6 +3,10 @@
|
|||||||
!/.gitignore
|
!/.gitignore
|
||||||
!/AGENTS.md
|
!/AGENTS.md
|
||||||
!/README.md
|
!/README.md
|
||||||
|
!/compose.yaml
|
||||||
|
!/compose.development.yaml
|
||||||
|
!/.env.compose.example
|
||||||
|
!/.env.development.example
|
||||||
!/.ai/
|
!/.ai/
|
||||||
/.ai/*
|
/.ai/*
|
||||||
!/.ai/conventions/
|
!/.ai/conventions/
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ git clone ssh://git@git.personal.ksv741.ru:2222/WealthLedger/specs.git specs
|
|||||||
|
|
||||||
Root отслеживает входные документы, собственные engineering conventions и исторический migration
|
Root отслеживает входные документы, собственные engineering conventions и исторический migration
|
||||||
inventory в `.ai/`, npm/release-конфигурацию, changelog,
|
inventory в `.ai/`, npm/release-конфигурацию, changelog,
|
||||||
один CI workflow с commitlint. Точный allowlist — в `.gitignore`.
|
Compose configuration, безопасные env examples и один CI workflow с commitlint. Точный allowlist — в `.gitignore`.
|
||||||
Остальные root-пути игнорируются по умолчанию:
|
Остальные root-пути игнорируются по умолчанию:
|
||||||
вложенные repos, `.env`, базы и WAL, импорты выписок, PDF, `references/`,
|
вложенные repos, `.env`, базы и WAL, импорты выписок, PDF, `references/`,
|
||||||
`design/`, `todo/` и локальные настройки инструментов.
|
`design/`, `todo/` и локальные настройки инструментов.
|
||||||
@@ -53,6 +53,35 @@ inventory в `.ai/`, npm/release-конфигурацию, changelog,
|
|||||||
Не запускайте очистку workspace через `git clean -fdx`: ignored-пути содержат
|
Не запускайте очистку workspace через `git clean -fdx`: ignored-пути содержат
|
||||||
локальные данные. Правила tracking не удаляют файлы и вложенные `.git`.
|
локальные данные. Правила tracking не удаляют файлы и вложенные `.git`.
|
||||||
|
|
||||||
|
## Запуск через Docker Compose
|
||||||
|
|
||||||
|
Нужны Docker Engine и Compose >= 2.24.4, а для development — checkout
|
||||||
|
`frontend/` и `backend/` рядом с root. Компоненты собираются независимо:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --env-file .env.development.example \
|
||||||
|
-f compose.yaml -f compose.development.yaml up -d --build --wait --wait-timeout 180
|
||||||
|
```
|
||||||
|
|
||||||
|
Откройте `http://localhost:5173`: Vite HMR и `/api/` используют один origin,
|
||||||
|
Nest работает в watch mode. Host `node_modules` и `.env` не монтируются.
|
||||||
|
Данные development хранятся отдельно в named volume `development-data` проекта `wealthledger-dev`;
|
||||||
|
обычные restart/down их сохраняют. Изменение dependencies/config требует rebuild.
|
||||||
|
|
||||||
|
Production использует `compose.yaml` и restricted env по `.env.compose.example`:
|
||||||
|
две ссылки на опубликованные версии images, persistent absolute host data path,
|
||||||
|
постоянные JWT/encryption secrets и публичный HTTPS origin. Каталог данных
|
||||||
|
должен существовать и принадлежать UID/GID 1000:1000. Только frontend публикует
|
||||||
|
`127.0.0.1:8080`; серверный Caddy направляет туда весь домен. Backend port
|
||||||
|
остаётся внутренним, migration service использует тот же backend image.
|
||||||
|
|
||||||
|
Полная процедура установки, независимых обновлений, stopped-backend backup
|
||||||
|
и explicit restore — [документация](https://git.personal.ksv741.ru/WealthLedger/docs),
|
||||||
|
страница `architecture/deployment.md`.
|
||||||
|
Перед backend update обязательны остановка, backup и успешная migration.
|
||||||
|
Image rollback не восстанавливает старую базу. Публикация component images
|
||||||
|
принадлежит их Gitea workflows; root CI остаётся только commitlint.
|
||||||
|
|
||||||
## Подготовка и запуск
|
## Подготовка и запуск
|
||||||
|
|
||||||
Нужны nvm и Node из `.nvmrc` каждого npm-repo (сейчас 24.21.0, npm 11.19).
|
Нужны nvm и Node из `.nvmrc` каждого npm-repo (сейчас 24.21.0, npm 11.19).
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Requires Compose >= 2.24.4 for !override. Always use a separate project name.
|
||||||
|
name: wealthledger-dev
|
||||||
|
|
||||||
|
x-development-backend: &development-backend
|
||||||
|
image: wealthledger-backend:development
|
||||||
|
pull_policy: build
|
||||||
|
build:
|
||||||
|
context: ./backend
|
||||||
|
target: development
|
||||||
|
read_only: false
|
||||||
|
environment:
|
||||||
|
NODE_ENV: development
|
||||||
|
volumes: !override
|
||||||
|
- development-data:/data
|
||||||
|
|
||||||
|
services:
|
||||||
|
migration:
|
||||||
|
<<: *development-backend
|
||||||
|
|
||||||
|
backend:
|
||||||
|
<<: *development-backend
|
||||||
|
healthcheck:
|
||||||
|
test: [CMD, node, -e, 'fetch("http://127.0.0.1:3000/api/v1/auth/me").then(async r=>{await r.arrayBuffer();process.exit(r.status===401?0:1)}).catch(()=>process.exit(1))']
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
start_period: 20s
|
||||||
|
retries: 6
|
||||||
|
volumes: !override
|
||||||
|
- development-data:/data
|
||||||
|
- ./backend/src:/app/src:ro
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: wealthledger-frontend:development
|
||||||
|
pull_policy: build
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
target: development
|
||||||
|
read_only: false
|
||||||
|
volumes:
|
||||||
|
- ./frontend/src:/app/src:ro
|
||||||
|
- ./frontend/public:/app/public:ro
|
||||||
|
environment:
|
||||||
|
VITE_API_BASE_URL: ''
|
||||||
|
API_PROXY_TARGET: http://backend:3000
|
||||||
|
healthcheck:
|
||||||
|
test: [CMD, node, -e, 'fetch("http://127.0.0.1:8080/").then(async r=>{await r.arrayBuffer();process.exit(r.ok?0:1)}).catch(()=>process.exit(1))']
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
start_period: 10s
|
||||||
|
retries: 6
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
development-data:
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
name: wealthledger
|
||||||
|
|
||||||
|
x-backend: &backend
|
||||||
|
image: ${BACKEND_IMAGE:?Set versioned backend image}
|
||||||
|
environment:
|
||||||
|
NODE_ENV: production
|
||||||
|
PORT: '3000'
|
||||||
|
DATABASE_URL: file:/data/wealthledger.db
|
||||||
|
FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:?Set browser-visible origin}
|
||||||
|
JWT_SECRET: ${JWT_SECRET:?Set persistent JWT secret}
|
||||||
|
TOKEN_ENCRYPTION_KEY: ${TOKEN_ENCRYPTION_KEY:?Set persistent encryption key}
|
||||||
|
LOG_LEVEL: ${LOG_LEVEL:-info}
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: ${DATA_DIRECTORY:?Set persistent absolute host data directory}
|
||||||
|
target: /data
|
||||||
|
bind:
|
||||||
|
create_host_path: false
|
||||||
|
read_only: true
|
||||||
|
init: true
|
||||||
|
security_opt: [no-new-privileges:true]
|
||||||
|
cap_drop: [ALL]
|
||||||
|
|
||||||
|
services:
|
||||||
|
migration:
|
||||||
|
<<: *backend
|
||||||
|
command: [node, node_modules/prisma/build/index.js, migrate, deploy]
|
||||||
|
restart: 'no'
|
||||||
|
healthcheck:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
backend:
|
||||||
|
<<: *backend
|
||||||
|
scale: 1
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
migration:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: ${FRONTEND_IMAGE:?Set versioned frontend image}
|
||||||
|
ports:
|
||||||
|
- '127.0.0.1:${FRONTEND_PORT:-8080}:8080'
|
||||||
|
read_only: true
|
||||||
|
tmpfs: [/tmp]
|
||||||
|
security_opt: [no-new-privileges:true]
|
||||||
|
cap_drop: [ALL]
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
backend:
|
||||||
|
condition: service_healthy
|
||||||
Reference in New Issue
Block a user