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