228 lines
12 KiB
JavaScript
228 lines
12 KiB
JavaScript
const { Pool } = require('pg')
|
||
|
||
// XFlow DB (through SSH tunnel)
|
||
const DB_URL = 'postgresql://xflow:xflow_pass@localhost:5433/xflow'
|
||
const pool = new Pool({ connectionString: DB_URL })
|
||
|
||
const CANVAS_ID = 'npb-aipb-project-brief'
|
||
const CANVAS_NAME = '🎨 NPB/AIPB — Project Brief'
|
||
const OWNER_ID = 'GqbpMyUW0NaxjIy5nvCiXCiQu9t2'
|
||
|
||
// ─────────────────────────────────────────────
|
||
// NODES — merged from NPB.json + XFlow pre-prompt context
|
||
// ─────────────────────────────────────────────
|
||
const nodes = [
|
||
// ── CENTRAL NODE ──────────────────────────
|
||
{
|
||
id: 'main',
|
||
type: 'text',
|
||
x: -474, y: -603,
|
||
width: 450, height: 220,
|
||
label: '🎨 AIPB — AI Portrait Builder',
|
||
text: `# 🎨 AIPB\n\n**Генератор художественных портретов с ИИ.**\n\n- **Статус:** v1.1.0 Architecture\n- **Стек:** Nuxt 3, MediaPipe, Replicate, Firebase\n- **Концепт:** Парные экраны (Display + Remote)\n- **Фишка:** Управление жестами или с планшета`,
|
||
state: 'pending',
|
||
style: { background: '#1e293b', border: '#334155' }
|
||
},
|
||
|
||
// ── TECH STACK ────────────────────────────
|
||
{
|
||
id: 'tech-stack',
|
||
type: 'text',
|
||
x: -1049, y: -262,
|
||
width: 400, height: 380,
|
||
label: '🏗️ Tech Stack',
|
||
text: `# 🏗️ Стек AIPB\n\n- **Framework:** Nuxt 3 (SSR + Nitro)\n- **CV:** MediaPipe Hands (JS)\n- **AI APIs:** Replicate (SDXL), Face Swap\n- **Backend/Sync:** Firebase RTDB\n- **Auth:** Firebase Auth\n- **State:** Pinia + Firebase Listeners\n- **UI:** TailwindCSS + Animations`,
|
||
state: 'pending',
|
||
style: { background: '#1e293b', border: '#334155' }
|
||
},
|
||
|
||
// ── AUTH SYSTEM ───────────────────────────
|
||
{
|
||
id: 'auth-system',
|
||
type: 'text',
|
||
x: -1049, y: 180,
|
||
width: 400, height: 300,
|
||
label: '🔐 Auth Stack',
|
||
text: `# 🔐 Firebase Auth & Roles\n\n- **Admin:** Управление киосками, стата\n- **Kiosk (Service):** Авторизация самой точки\n- **Remote (Anonymous):** Временный токен для планшета\n- **Security:** Firebase Rules (доступ только к своей сессии)`,
|
||
state: 'pending',
|
||
style: { background: '#1e293b', border: '#334155' }
|
||
},
|
||
|
||
// ── DUAL DEVICE ───────────────────────────
|
||
{
|
||
id: 'dual-device',
|
||
type: 'text',
|
||
x: 100, y: -603,
|
||
width: 450, height: 320,
|
||
label: '📡 Синхронизация пар',
|
||
text: `# 📡 Dual-Device Sync\n\n- **Pairing:** Генерация PIN/QR кода на ПК\n- **Connection:** Сканирование с планшета\n- **Realtime:** Firebase RTDB для синхронизации State\n- **Latency:** <100ms отклик интерфейса`,
|
||
state: 'pending',
|
||
style: { background: '#0f766e', border: '#115e59' }
|
||
},
|
||
|
||
// ── KIOSK ARCHITECTURE ────────────────────
|
||
{
|
||
id: 'architecture',
|
||
type: 'text',
|
||
x: 600, y: -603,
|
||
width: 450, height: 350,
|
||
label: '🖥️ Kiosk Architecture',
|
||
text: `# 🖥️ Разделение UI\n\n**1. Display View (ТВ/ПК):**\n- Трансляция камеры\n- Красивые idle-анимации\n- Рендер ИИ и таймеров\n- QR-код для скачивания\n\n**2. Controller View (Планшет):**\n- Кнопки выбора (Пол, Стиль, Язык)\n- Кнопка спуска затвора\n- Ввод Email/Telegram`,
|
||
state: 'pending',
|
||
style: { background: '#0f766e', border: '#115e59' }
|
||
},
|
||
|
||
// ── USER FLOW ─────────────────────────────
|
||
{
|
||
id: 'user-flow',
|
||
type: 'text',
|
||
x: -128, y: 147,
|
||
width: 450, height: 400,
|
||
label: '🔄 User Flow (8 Steps)',
|
||
text: `# 🔄 Процесс генерации\n\n1. **Welcome** — Старт на планшете/жестом\n2. **Language** — Выбор языка\n3. **Gender** — Выбор пола (M/F)\n4. **Style** — Выбор стиля\n5. **Capture** — Фото с таймером (3с)\n6. **Confirm** — Проверка кадра на планшете\n7. **Loading** — Процесс ИИ генерации (на экране)\n8. **Result** — Сохранение и шеринг`,
|
||
state: 'pending',
|
||
style: { background: '#1e293b', border: '#334155' }
|
||
},
|
||
|
||
// ── GESTURE CONTROLS ──────────────────────
|
||
{
|
||
id: 'gestures',
|
||
type: 'text',
|
||
x: -566, y: -72,
|
||
width: 400, height: 300,
|
||
label: '👋 Gesture Controls',
|
||
text: `# 👋 Управление жестами\n\n- **✊ Fist:** Клик / Выбор\n- **✌️ V-sign:** Скролл вниз\n- **🖐️ Palm:** Пауза / Стоп\n\n*Альтернатива или дополнение к планшету*\n*Используется MediaPipe Hands (WASM в браузере)*`,
|
||
state: 'pending',
|
||
style: { background: '#1e293b', border: '#334155' }
|
||
},
|
||
|
||
// ── ROADMAP ───────────────────────────────
|
||
{
|
||
id: 'roadmap',
|
||
type: 'text',
|
||
x: 380, y: 147,
|
||
width: 400, height: 300,
|
||
label: '🚀 Roadmap',
|
||
text: `# 🚀 Планы (v1.2.0+)\n\n- [ ] Прямая интеграция с TG-ботом\n- [ ] Админ-панель со статистикой\n- [ ] Оффлайн фоллбэк для обрыва связи\n- [ ] Платные премиум-стили\n- [ ] Галерея работ сообщества`,
|
||
state: 'pending',
|
||
style: { background: '#1e293b', border: '#334155' }
|
||
},
|
||
|
||
// ── AI STYLES ─────────────────────────────
|
||
{
|
||
id: 'ai-styles',
|
||
type: 'text',
|
||
x: 850, y: 147,
|
||
width: 400, height: 320,
|
||
label: '🎭 AI Styles',
|
||
text: `# 🎭 Художественные стили\n\n- **Cinematic:** Кино-освещение\n- **Anime:** Стилизация под аниме\n- **Fantasy:** Магическая атмосфера\n- **Cyberpunk:** Неон и технологии\n- **Professional:** Студийное фото\n- **Artistic:** Масляная живопись\n\n*Провайдер: Replicate (SDXL)*`,
|
||
state: 'pending',
|
||
style: { background: '#312e81', border: '#3730a3' }
|
||
},
|
||
|
||
// ── INFRASTRUCTURE (SERVER) ───────────────
|
||
{
|
||
id: 'infrastructure',
|
||
type: 'text',
|
||
x: 850, y: -603,
|
||
width: 450, height: 400,
|
||
label: '🖥️ Инфраструктура сервера',
|
||
text: `# 🖥️ Сервер\n\n- **VM (Debian):** 10.0.0.120\n- **Jump-host:** alex@178.238.78.98\n- **Docker Network:** \`infra\`\n- **Services:**\n - \`postgres:5432\` (Shared PostgreSQL)\n - \`gitea:3001\` (Git server)\n - \`xflow:3002\` (XFlow CRM)\n - \`npb-app:3000\` (NPB/AIPB — наш проект)\n- **Reverse Proxy:** Nginx (FastPanel)\n\n**SSH-туннель к БД:**\n\`\`\`\nssh -J alex@178.238.78.98 -L 5432:localhost:5432 root@10.0.0.120\n\`\`\``,
|
||
state: 'pending',
|
||
style: { background: '#1c1917', border: '#44403c' }
|
||
},
|
||
|
||
// ── DEPLOYMENT ────────────────────────────
|
||
{
|
||
id: 'deployment',
|
||
type: 'text',
|
||
x: -474, y: 650,
|
||
width: 450, height: 300,
|
||
label: '🚀 Деплой & CI/CD',
|
||
text: `# 🔄 Деплой (Gitea Actions)\n\n1. \`git push\` → Gitea\n2. Gitea Actions запускает \`deploy.yml\`\n3. Runner делает \`git pull\` на сервере\n4. Пересобирает Docker-контейнер \`npb-app\`\n\n**Время деплоя:** ~3-5 минут\n\n⚠️ В PowerShell — команды по одной, без \`&&\``,
|
||
state: 'pending',
|
||
style: { background: '#14532d', border: '#166534' }
|
||
},
|
||
|
||
// ── KNOWN ISSUES ──────────────────────────
|
||
{
|
||
id: 'known-issues',
|
||
type: 'text',
|
||
x: -1049, y: 580,
|
||
width: 400, height: 300,
|
||
label: '🐛 Известные проблемы',
|
||
text: `# 🐛 Известные проблемы\n\n- **PWA:** Clear site data при non-precached-url ошибке\n- **Gitea push 403:** Токен мог истечь — проверить \`GITEA_TOKEN\` в .env\n- **AI Key:** Groq ключ может быть невалидным — обновить в .env\n- **Logs:** Убрать debug-логи из \`execute.post.ts\` перед продом`,
|
||
state: 'pending',
|
||
style: { background: '#450a0a', border: '#7f1d1d' }
|
||
},
|
||
|
||
// ── DEV TIPS ──────────────────────────────
|
||
{
|
||
id: 'dev-tips',
|
||
type: 'text',
|
||
x: 380, y: 580,
|
||
width: 450, height: 300,
|
||
label: '💡 Dev Tips',
|
||
text: `# 💡 Советы по разработке\n\n- **Локальный запуск:** \`npm run dev\`\n- **SSH-туннель к БД:**\n \`ssh -J alex@178.238.78.98 -L 5432:localhost:5432 root@10.0.0.120\`\n- **Список пользователей:** \`node scripts/list_users.cjs\`\n- **Тест AI:** \`node scripts/test_qwen.cjs\`\n- **Не использовать \`&&\` в PowerShell!**`,
|
||
state: 'pending',
|
||
style: { background: '#1e293b', border: '#334155' }
|
||
}
|
||
]
|
||
|
||
// ─────────────────────────────────────────────
|
||
// EDGES — connections between nodes
|
||
// ─────────────────────────────────────────────
|
||
const edges = [
|
||
// Main → top cluster
|
||
{ id: 'e1', source: 'main', target: 'dual-device' },
|
||
{ id: 'e2', source: 'dual-device', target: 'architecture' },
|
||
{ id: 'e3', source: 'architecture', target: 'infrastructure' },
|
||
|
||
// Main → left cluster
|
||
{ id: 'e4', source: 'main', target: 'tech-stack' },
|
||
{ id: 'e5', source: 'tech-stack', target: 'auth-system' },
|
||
|
||
// Main → center/bottom cluster
|
||
{ id: 'e6', source: 'main', target: 'gestures' },
|
||
{ id: 'e7', source: 'main', target: 'user-flow' },
|
||
{ id: 'e8', source: 'user-flow', target: 'roadmap' },
|
||
{ id: 'e9', source: 'roadmap', target: 'ai-styles' },
|
||
|
||
// Bottom
|
||
{ id: 'e10', source: 'main', target: 'deployment' },
|
||
{ id: 'e11', source: 'auth-system', target: 'known-issues' },
|
||
{ id: 'e12', source: 'deployment', target: 'dev-tips' }
|
||
]
|
||
|
||
// ─────────────────────────────────────────────
|
||
// DB UPSERT
|
||
// ─────────────────────────────────────────────
|
||
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_NAME, CANVAS_ID]
|
||
)
|
||
console.log('✅ NPB/AIPB Brief canvas UPDATED!')
|
||
} 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, CANVAS_NAME, OWNER_ID, JSON.stringify(nodes), JSON.stringify(edges)]
|
||
)
|
||
console.log('✅ NPB/AIPB Brief canvas CREATED!')
|
||
}
|
||
|
||
console.log(`\n📊 Stats: ${nodes.length} nodes, ${edges.length} edges`)
|
||
console.log(`🔗 Canvas ID: ${CANVAS_ID}`)
|
||
} catch (e) {
|
||
console.error('❌ Error:', e.message)
|
||
} finally {
|
||
await pool.end()
|
||
}
|
||
}
|
||
|
||
run()
|