40227-vm/docs/deployment-vm.md
2026-06-10 18:27:19 +02:00

259 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Развёртывание на виртуальной машине (Flatlogic executor)
Так работает прод/превью Flatlogic: без Docker — PM2 + локальный PostgreSQL +
Cloudflare tunnel. В конце — справочная **структура файлов VM** (executor, pm2,
проект).
> Запуск **через Docker на хосте** (compose / single-image / staging) вынесен в
> отдельный документ: [`deployment-docker.md`](./deployment-docker.md).
Проект состоит из двух приложений:
- `frontend/` — Vite + React + TypeScript (SPA). Сборка → `frontend/dist/`.
- `backend/` — Express + Sequelize на TypeScript/ESM. Сборка → `backend/dist/`.
Проект живёт в `~/executor/workspace`, процессы — под PM2, БД — локальный
PostgreSQL, наружу — Cloudflare tunnel (`cloudflared`). Docker здесь не участвует.
## 1.1. Топология
```
Браузер
│ https://<subdomain>.dev.flatlogic.app
cloudflared (tunnel)
nginx :8080
├── / → frontend :3001 (Vite)
├── /api → backend :3000 (Express)
└── /api-docs → backend :3000
PostgreSQL :5432 (локальный)
```
- В `NODE_ENV=dev_stage` бэкенд слушает **3000** (`config.serverPort`), фронт — **3001**.
- Браузер открывает домен туннеля; фронт зовёт API относительным путём `/api`
(см. `frontend/src/shared/constants/api.ts`), nginx проксирует его на бэкенд —
тот же origin, поэтому CORS/CSRF не мешают.
## 1.2. Окружение (env)
**Инжектит платформа** в pm2-окружение процесса бэкенда (значения — секреты, в
репозиторий не коммитятся):
| Переменная | Назначение |
|---|---|
| `NODE_ENV=dev_stage` | режим (прод-подобный, но без жёстких проверок прода) |
| `SECRET_KEY` | подпись JWT (обязательна — иначе бэкенд не стартует) |
| `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASS` | подключение к локальному Postgres (`DB_PASS` = UUID проекта) |
| `GOOGLE_CLIENT_ID/SECRET`, `MS_CLIENT_ID/SECRET` | OAuth (опционально) |
| `SMTP_*`, `EMAIL_*`, `MAIL_*` | почта (опционально) |
| `CF_TUNNEL_*` | Cloudflare tunnel (для `cloudflared`, не для приложения) |
**Из закоммиченного `backend/.env`** (не секреты уровня прода):
- `PORT`, `SEED_ADMIN_EMAIL`, `SEED_ADMIN_PASSWORD`, `SEED_USER_PASSWORD`.
- `backend/src/config/load-env.ts` находит `backend/.env` одинаково и для `tsx`, и
для компилированного `dist`.
> `ALLOWED_ORIGINS` платформа НЕ задаёт. В `dev_stage` это допустимо: бэкенд не
> падает и рефлектит origin запроса (`config.auth.allowAllOrigins`). Жёсткая
> проверка `ALLOWED_ORIGINS` действует только при строгом `NODE_ENV=production`.
## 1.3. PM2-процессы
`pm2 status` на VM показывает (имена закреплены при провижининге):
| Процесс | Команда (cwd) | Порт |
|---|---|---|
| `frontend-dev` | `npm run dev -- --hostname 0.0.0.0 --port 3001` (`workspace/frontend`) | 3001 |
| `backend-dev` | `NODE_ENV=dev_stage npm run start` (`workspace/backend`) | 3000 |
| `fl-executor` | `executor.js` — агент Flatlogic (cable, git, AI-команды) | — |
| `fl-telemetry` | `telemetry-daemon.js` | — |
> ⚠️ `--hostname` — это флаг Next.js; Vite-CLI его отвергает (`Unknown option`).
> Поэтому `dev`/`start` фронта запускаются через обёртку `frontend/scripts/serve.mjs`,
> которая транслирует `--hostname`→`--host` и стартует Vite. Без неё фронт на VM
> не поднимется.
## 1.4. Деплой после `git pull`
PM2-команды зависимости **не ставят**. После подтягивания нового кода:
```bash
cd ~/executor/workspace/backend && npm ci
cd ~/executor/workspace/frontend && npm ci
```
Схема БД создаётся инициальной миграцией. На уже существующей БД `npm run start`
сам прогонит `db:migrate` (идемпотентно, `CREATE TABLE IF NOT EXISTS`) + `db:seed`.
Для гарантированно чистого состояния (рекомендуется после крупной миграции —
**сотрёт данные**):
```bash
cd ~/executor/workspace/backend && npm run db:reset # drop all tables → migrate → seed
```
Перезапуск процессов (или это делает executor после pull):
```bash
pm2 restart backend-dev frontend-dev --update-env
```
### Что делают команды запуска
- `backend-dev``npm run start` = `db:migrate` (инициальная миграция → схема) +
`db:seed` + `watch` (сервер через `tsx` + nodemon, порт 3000).
- `frontend-dev``npm run dev` (через `serve.mjs`) — Vite dev-сервер на 3001,
`allowedHosts: true` пускает домен туннеля.
## 1.5. nginx
Если nginx — системный сервис, его конфиг должен соответствовать `nginx.conf` из
репозитория (`/` → 3001, `/api` и `/api-docs` → 3000):
```bash
sudo cp ~/executor/workspace/nginx.conf /etc/nginx/nginx.conf
sudo nginx -t && sudo nginx -s reload
```
## 1.6. Проверка
```bash
curl -s -o /dev/null -w "front %{http_code}\n" http://127.0.0.1:3001/
curl -s -o /dev/null -w "api %{http_code}\n" http://127.0.0.1:3000/api-docs/
pm2 status
pm2 logs backend-dev --lines 50
```
## 1.7. Прод-режим на VM (компилированные сборки)
«Прод-режим» = **компилированные сборки при сохранённом `NODE_ENV=dev_stage`**
(минификация фронта, `node dist` бэка, source maps). Строгий `NODE_ENV=production`
использовать нельзя — он требует `ALLOWED_ORIGINS`, которого платформа не задаёт,
и бэкенд упадёт на старте.
Для перевода в прод нужно сменить pm2-команды (на стороне executor):
```bash
# Frontend
cd ~/executor/workspace/frontend && npm ci && npm run build
pm2 delete frontend-dev 2>/dev/null || true
FRONT_PORT=3001 pm2 start npm --name frontend --update-env -- run start
# Backend (NODE_ENV=dev_stage сохраняем)
cd ~/executor/workspace/backend && npm ci && npm run build
pm2 delete backend-dev 2>/dev/null || true
NODE_ENV=dev_stage pm2 start npm --name backend --update-env -- run start:production
pm2 save
```
- `frontend npm run start` = `vite preview` на `FRONT_PORT` (3001), отдаёт `dist/`.
- `backend npm run start:production` = `db:migrate:prod` + `db:seed:prod` +
`node --enable-source-maps dist/index.js` (порт 3000).
Чтобы executor сам пересобирал прод при каждом обновлении кода — добавить
`npm ci && npm run build` в его шаг рестарта сервисов (см. `vcs.js`, ~строка 412,
массив `const services = ['backend-dev', 'frontend-dev']`).
## 1.8. Траблшутинг
| Симптом | Причина / решение |
|---|---|
| Фронт не стартует, `Unknown option --hostname` | старый код без `serve.mjs`; обнови workspace (`git pull` + `npm ci`) |
| Бэкенд падает: `ALLOWED_ORIGINS must be configured` | запущен с `NODE_ENV=production`; на VM должно быть `dev_stage` |
| Бэкенд падает: `SECRET_KEY` required | платформа не прокинула `SECRET_KEY` в pm2-env |
| `tsx: not found` / `vite: not found` | не выполнен `npm ci` после pull |
| Сид падает: `Seeding requires SEED_*` | нет `backend/.env` (или переменных в нём) |
| 502 на домене | бэк/фронт не слушают 3000/3001, либо nginx-роутинг не совпадает |
---
# Часть 2. Структура файлов на VM (справочно)
Снимок боевой VM (`pool-saas-*`). Полезно для понимания, где что лежит.
## 2.1. Домашняя директория `~`
```
~/
├── executor/ # агент Flatlogic + сам проект (см. ниже)
├── .pm2/ # PM2: процессы, логи, pids
│ ├── logs/ # *-out.log, *-error.log по процессам
│ ├── pids/
│ └── dump.pm2 # сохранённый список процессов (pm2 save)
├── .bun/ .yarn/ .npm/ .cache/ # тулчейны/кэши
├── .codex/ .gemini/ .config/ # конфиги AI-CLI
├── .ssh/ .pki/
├── recipes.md
└── google-gemini-cli-0.17.1.tgz
```
## 2.2. `~/executor/` — агент Flatlogic
```
~/executor/
├── .env # конфиг агента и проекта (PROJECT_UUID, PROJECT_ID,
│ # CABLE_URL, DB_NAME=app_<id>, SUBDOMAIN, FRONT_PORT, ...)
├── executor.js # главный процесс агента (pm2: fl-executor):
│ # WebSocket-cable к flatlogic.com, приём команд,
│ # запуск AI-раннеров (gemini/codex), fs/git-операции
├── gemini.js / gemini-proc.js / opencode.js / opencode-proc.js # AI-раннеры
├── vcs.js и vcs/vcs.js # git: init/pull/push/commit, Gitea-зеркало,
│ # рестарт сервисов (массив ['backend-dev','frontend-dev'])
├── vm-tools.js # инструменты VM (команды от платформы)
├── activity-tracker.js # трекинг активности раннера
├── telemetry-daemon.js / telemetry-server.js / telemetry-file-watcher.js # телеметрия (pm2: fl-telemetry)
├── sentry.js # Sentry
├── config.js # WORKSPACE_ROOT и пр.
├── index.php # статичная страница-прелоадер («Analyzing your requirements…»)
├── setup_postgres_project.sh # создание роли/БД проекта в локальном Postgres
├── setup_mariadb_project.sh # то же для MariaDB
├── setup_workspace_permissions.sh
├── cleanup_vm.sh
├── AGENTS.md / README.md # инструкции для AI-агента (описывают шаблон проекта)
├── otel-local.yaml / schema.json / proto/
├── node_modules/ package.json package-lock.json
├── workspace/ # ◀── САМ ПРОЕКТ (git-репозиторий = этот репозиторий)
├── workspace_baseline.tar.gz # базовый снимок workspace
├── workspace_codegen/ # рабочая область кодогенерации
└── templates/ # шаблоны для новых проектов
├── app-templates/
└── frontend-tailwind-backend-nodejs/ # Next.js+Node шаблон (НЕ наш стек)
```
> Файлы `executor/` (агент) и `templates/` к запуску **нашего** проекта отношения
> не имеют — их менять не нужно. `index.php` — только прелоадер на время генерации.
## 2.3. `~/executor/workspace/` — проект
Это и есть данный git-репозиторий (`40227-vm`):
```
workspace/
├── frontend/ # Vite + React + TS → собирается в frontend/dist, сервится на :3001
├── backend/ # Express + Sequelize TS/ESM → backend/dist, сервится на :3000
│ ├── .env # PORT, SEED_* (закоммичен)
│ └── src/db/migrations/ # инициальная миграция (схема)
├── nginx.conf # роутинг / → 3001, /api → 3000 (для системного nginx)
├── Dockerfile / Dockerfile.dev / docker/ # альтернативный Docker-путь
├── 502.html
└── docs/ # в т.ч. этот файл
```
## 2.4. `~/executor/.env` — ключи (значения секретны/индивидуальны)
```
PROJECT_UUID, PROJECT_ID, CABLE_URL, GEMINI_MODEL, TELEMETRY_*, OTEL_*,
MAIL_*, SMTP_*, SUBDOMAIN, BASE_DOMAIN, FULL_DOMAIN, HOST_FQDN,
DB_NAME=app_<id>, DB_USER=app_<id>, DB_HOST=127.0.0.1, DB_PORT=5432,
FRONT_PORT=3001, SENTRY_DSN
```
Секреты приложения (`SECRET_KEY`, `DB_PASS`, OAuth/SMTP, токены git, `CF_TUNNEL_*`)
платформа кладёт прямо в **pm2-окружение** процессов `backend-dev`/`fl-executor`, а
не в `backend/.env`.