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

14 KiB
Raw Blame History

Развёртывание на виртуальной машине (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-devnpm run start = db:migrate (инициальная миграция → схема) + db:seed + watch (сервер через tsx + nodemon, порт 3000).
  • frontend-devnpm 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.