14 KiB
Развёртывание на виртуальной машине (Flatlogic executor)
Так работает прод/превью Flatlogic: без Docker — PM2 + локальный PostgreSQL + Cloudflare tunnel. В конце — справочная структура файлов VM (executor, pm2, проект).
Запуск через Docker на хосте (compose / single-image / staging) вынесен в отдельный документ:
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-команды зависимости не ставят. После подтягивания нового кода:
cd ~/executor/workspace/backend && npm ci
cd ~/executor/workspace/frontend && npm ci
Схема БД создаётся инициальной миграцией. На уже существующей БД npm run start
сам прогонит db:migrate (идемпотентно, CREATE TABLE IF NOT EXISTS) + db:seed.
Для гарантированно чистого состояния (рекомендуется после крупной миграции —
сотрёт данные):
cd ~/executor/workspace/backend && npm run db:reset # drop all tables → migrate → seed
Перезапуск процессов (или это делает executor после pull):
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):
sudo cp ~/executor/workspace/nginx.conf /etc/nginx/nginx.conf
sudo nginx -t && sudo nginx -s reload
1.6. Проверка
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):
# 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.