const { Pool } = require('pg') const DB_URL = process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/xflow' const pool = new Pool({ connectionString: DB_URL }) const CANVAS_ID = 'infrastructure' const OWNER_ID = 'GqbpMyUW0NaxjIy5nvCiXCiQu9t2' const nodes = [ { id: 'server', type: 'text', x: 0, y: 0, width: 420, height: 280, label: '🖥️ Сервер', text: `# 🖥️ Сервер (ВМ)\n\n- **IP (внутренний):** 10.0.0.120\n- **Jump-хост:** 178.238.78.98 (alex)\n- **ОС:** Debian GNU/Linux\n- **Nginx:** управляется FastPanel (не трогаем)\n- **Доступ:** \`ssh -J alex@178.238.78.98 root@10.0.0.120\`` }, { id: 'docker-network', type: 'text', x: 500, y: -200, width: 420, height: 200, label: '🐳 Docker сеть: infra', text: `# 🐳 Docker сеть: infra\n\nВсе контейнеры общаются внутри сети \`infra\`.\nМежду собой они обращаются по именам контейнеров.\n\nПример: приложение XFlow достучивается до базы\nпо адресу \`postgres:5432\`, а не через localhost.` }, { id: 'postgres', type: 'text', x: 500, y: 100, width: 420, height: 320, label: '🗄️ PostgreSQL (контейнер)', text: `# 🗄️ PostgreSQL (контейнер: postgres)\n\n- **Порт:** 5432 (проброшен на хост)\n- **Суперпользователь:** \`xflow / xflow_pass\`\n\n**Базы данных:**\n| База | Владелец | Назначение |\n|------|----------|------------|\n| \`xflow\` | xflow | Наш проект |\n| \`gitea\` | gitea | Gitea |\n| \`artbazar\` | gitea | Другой проект |\n| \`postgres\` | gitea | Не используем |\n\n⚠️ **Важно:** локальный Windows Postgres занимает порт 5432, поэтому туннель нужно пробрасывать на порт 5433!` }, { id: 'xflow-app', type: 'text', x: 500, y: 500, width: 420, height: 240, label: '🚀 XFlow (контейнер)', text: `# 🚀 XFlow (контейнер: xflow)\n\n- **Внутренний порт:** 3000\n- **Внешний порт:** 3002\n- **URL в проде:** https://a-crm.controlsystem.kz\n- **DATABASE_URL:** \`postgresql://xflow:xflow_pass@postgres:5432/xflow\`\n- **Деплой:** Gitea Actions → git pull → docker-compose restart\n- **Папка на сервере:** /opt/xflow` }, { id: 'gitea', type: 'text', x: -500, y: 100, width: 420, height: 240, label: '🦊 Gitea (контейнер)', text: `# 🦊 Gitea (контейнер: gitea)\n\n- **Порт:** 3001 → http://git.a-crm.controlsystem.kz\n- **SSH:** 2221\n- **БД:** \`gitea\` база внутри postgres-контейнера\n- **Репозиторий:** AVTEX/XFlow\n- **Runner:** gitea-runner (отдельный контейнер)\n- **Workflow:** .gitea/workflows/deploy.yml` }, { id: 'npb', type: 'text', x: -500, y: 450, width: 420, height: 160, label: '📦 NPB (контейнер)', text: `# 📦 NPB (другое приложение)\n\n- **npb-app:** порт 3000 (стандартный)\n- **npb:** порт 3003\n- ⚠️ Не трогаем — другой проект организации` }, { id: 'tunnel', type: 'text', x: -500, y: -300, width: 500, height: 320, label: '🔌 SSH Туннель (правильный)', text: `# 🔌 SSH Туннель к продовой БД\n\n**ПРАВИЛЬНАЯ команда:**\n\`\`\`bash\nssh -J alex@178.238.78.98 \\\\\n -o ServerAliveInterval=60 \\\\\n -L 5433:localhost:5432 \\\\\n root@10.0.0.120\n\`\`\`\n\n**Почему порт 5433?**\nПорт 5432 на Windows уже занят локальным PostgreSQL!\nЕсли пробрасывать на 5432 — туннель проваливается в локальную БД.\n\n**Локальный .env для работы с прод-БД:**\n\`\`\`\nDATABASE_URL=postgresql://xflow:xflow_pass\n @localhost:5433/xflow\n\`\`\`` }, { id: 'nginx', type: 'text', x: 0, y: -350, width: 420, height: 200, label: '🌐 Nginx (FastPanel)', text: `# 🌐 Nginx (управляется FastPanel)\n\n- Обратный прокси для всех сервисов\n- **a-crm.controlsystem.kz** → 3002 (XFlow)\n- **git.a-crm.controlsystem.kz** → 3001 (Gitea)\n- SSL/TLS сертификаты\n- ⚠️ Конфиги не трогаем вручную — только через FastPanel` }, { id: 'deploy-flow', type: 'text', x: 1000, y: 100, width: 420, height: 300, label: '🔄 Процесс деплоя', text: `# 🔄 Процесс деплоя\n\n1. **git push** → локальный компьютер → Gitea\n2. **Gitea Actions** запускает \`deploy.yml\`\n3. **Runner** (контейнер gitea-runner) выполняет:\n - \`git pull\` в /opt/xflow\n - \`npm ci && npm run build\`\n - \`docker-compose restart xflow\`\n4. **Приложение** перезапускается с новым кодом\n\n**Время деплоя:** ~2-3 минуты` } ] const edges = [ { id: 'e1', source: 'server', target: 'docker-network' }, { id: 'e2', source: 'docker-network', target: 'postgres' }, { id: 'e3', source: 'docker-network', target: 'xflow-app' }, { id: 'e4', source: 'docker-network', target: 'gitea' }, { id: 'e5', source: 'docker-network', target: 'npb' }, { id: 'e6', source: 'nginx', target: 'xflow-app' }, { id: 'e7', source: 'nginx', target: 'gitea' }, { id: 'e8', source: 'tunnel', target: 'postgres' }, { id: 'e9', source: 'gitea', target: 'deploy-flow' }, { id: 'e10', source: 'deploy-flow', target: 'xflow-app' }, { id: 'e11', source: 'xflow-app', target: 'postgres' } ] async function run() { try { // Проверяем существует ли карта const check = await pool.query('SELECT id FROM "Canvas" WHERE id = $1', [CANVAS_ID]) if (check.rows.length > 0) { await pool.query( 'UPDATE "Canvas" SET nodes = $1, edges = $2, name = $3, "updatedAt" = NOW() WHERE id = $4', [JSON.stringify(nodes), JSON.stringify(edges), '🖥️ Инфраструктура Сервера', CANVAS_ID] ) console.log('✅ Карта инфраструктуры обновлена!') } else { await pool.query( 'INSERT INTO "Canvas" (id, name, "ownerId", "isPublic", nodes, edges, "createdAt", "updatedAt") VALUES ($1, $2, $3, true, $4, $5, NOW(), NOW())', [CANVAS_ID, '🖥️ Инфраструктура Сервера', OWNER_ID, JSON.stringify(nodes), JSON.stringify(edges)] ) console.log('✅ Карта инфраструктуры создана!') } } catch (e) { console.error('❌ Ошибка:', e.message) } finally { await pool.end() } } run()