259 lines
14 KiB
Markdown
259 lines
14 KiB
Markdown
# Развёртывание на виртуальной машине (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`.
|