const bcrypt = require('bcryptjs'); const pool = require('./db'); async function ensureAdmin() { const { rows } = await pool.query('SELECT COUNT(*)::int AS c FROM admin_users'); if (rows[0].c > 0) return; const username = process.env.ADMIN_USERNAME || 'admin'; const password = process.env.ADMIN_PASSWORD; if (!password) { console.warn('[seed] ADMIN_PASSWORD is not set in .env - no admin user was created. Set it and restart the backend.'); return; } const hash = await bcrypt.hash(password, 12); await pool.query('INSERT INTO admin_users (username, password_hash) VALUES ($1, $2)', [username, hash]); console.log(`[seed] created admin user "${username}" - please log in and change the password from the admin panel`); } async function ensureContent() { const { rows } = await pool.query('SELECT COUNT(*)::int AS c FROM service_categories'); if (rows[0].c > 0) return; const categories = [ { slug: 'ai', title: 'ИИ и автоматизация', description: 'Внедрение ИИ, боты и ассистенты, автоматизация процессов', icon: 'Bot', items: [ ['Бот / ИИ-ассистент', 'от 20 000 ₽', '/проект', 'Автоматизация бизнес-процессов'], ['Интеграция с сервисами', 'от 25 000 ₽', '/проект', 'Подключение CRM, мессенджеров, API'], ] }, { slug: 'support', title: 'IT-поддержка', description: 'Удалённое и выездное сопровождение инфраструктуры', icon: 'Headphones', items: [ ['Базовый тариф', 'от 30 000 ₽', '/мес', 'Удалённая поддержка, график 5/2'], ['Премиум тариф', 'от 100 000 ₽', '/мес', 'Полное сопровождение, выезды, 24/7'], ] }, { slug: 'security', title: 'Кибербезопасность', description: 'Аудит, мониторинг и защита от угроз', icon: 'ShieldCheck', items: [ ['Аудит и мониторинг', 'от 50 000 ₽', '/мес', 'Комплексная защита данных и систем'], ['Внедрение IDS/IPS', 'от 30 000 ₽', '/проект', 'Обнаружение и предотвращение вторжений'], ] }, { slug: 'db', title: 'Администрирование БД', description: 'Настройка, оптимизация, резервное копирование', icon: 'Database', items: [ ['Настройка и оптимизация', 'от 3 000 ₽', '/час', 'MySQL, PostgreSQL и другие СУБД'], ['Резервное копирование', 'от 4 000 ₽', '/день', 'Бэкапы и восстановление данных'], ] }, { slug: 'network', title: 'Сетевая инфраструктура', description: 'Проектирование и поддержка корпоративных сетей', icon: 'Network', items: [ ['Настройка сервера', 'от 25 000 ₽', '', 'Подбор и настройка оборудования'], ['Почтовый сервер (ваше оборудование)', 'от 20 000 ₽', '', 'Развёртывание с нуля'], ['Почтовый сервер (наше оборудование)', 'от 30 000 ₽', '/мес', 'Аренда и обслуживание'], ['Проектирование сети', 'от 20 000 ₽', '/проект', 'Монтаж и настройка'], ['Управление оборудованием', 'от 6 000 ₽', '/мес', 'Роутеры и коммутаторы'], ] }, { slug: 'cloud', title: 'Облачные решения', description: 'Миграция и оптимизация затрат на облако', icon: 'Cloud', items: [ ['Миграция в облако', 'от 25 000 ₽', '/проект', 'Перенос и настройка сервисов'], ] }, { slug: 'development', title: 'Разработка под ключ', description: 'От быстрого MVP до продакшн-разработки и сопровождения', icon: 'Rocket', items: [ ['Лендинг', 'от 20 000 ₽', '', 'Простая посадочная страница под продукт или услугу - без формы-бота и админки, 1 неделя'], ['MVP «Быстрый старт»', 'от 120 000 ₽', '', 'Посадочная страница, форма/бот для заявок, простая админка - 2-3 недели'], ['MVP Pro', 'от 450 000 ₽', '', 'Личный кабинет с авторизацией, оплата, ИИ-ассистент - 4-6 недель'], ['Продакшн-разработка', 'от 1 200 000 ₽', '', 'Архитектура, тесты, DevOps, масштабирование - от 2 месяцев, точная смета после дискавери'], ['Discovery-воркшоп', 'от 30 000 ₽', '', 'Фиксация ТЗ и оценки перед стартом проекта - 1-2 дня'], ['Сопровождение проекта', 'от 40 000 ₽', '/мес', 'Ежемесячные доработки уже готовых продуктов'], ] }, { slug: 'partners', title: 'White-label и партнёрство', description: 'Готовые решения для других компаний и программы сотрудничества', icon: 'Handshake', items: [ ['White-label «Кабинет + ИИ-ассистент»', 'от 500 000 ₽', '', 'Личный кабинет с авторизацией и ИИ-чатом под брендом вашей компании'], ['IT-мониторинг сайта', 'от 10 000 ₽', '/мес', 'Аптайм, скорость загрузки, уведомления об инцидентах'], ['Реферальная программа', '10%', 'от суммы контракта', 'Бонус вам за рекомендацию нас новому клиенту'], ['Партнёрская программа', 'по договорённости', '', 'Комиссия для хостинг-провайдеров и поставщиков оборудования за рекомендации'], ] }, { slug: 'extra', title: 'Дополнительные услуги', description: 'Разовые и почасовые работы вне основных тарифов', icon: 'Server', items: [ ['Экстренный выезд специалиста', '10 000 ₽', '', 'В течение 2 часов, по Санкт-Петербургу'], ['Выезд специалиста', '5 000 ₽', '', 'В течение дня, по Санкт-Петербургу'], ['Почасовые работы', 'от 3 000 ₽', '/час', 'Иные работы по IT-инфраструктуре'], ['Удалённая работа', 'от 15 000 ₽', '/мес', 'Аренда мощностей для 1-5 сотрудников'], ] }, ]; for (let i = 0; i < categories.length; i++) { const cat = categories[i]; const { rows: catRows } = await pool.query( 'INSERT INTO service_categories (slug, title, description, icon, sort_order) VALUES ($1,$2,$3,$4,$5) RETURNING id', [cat.slug, cat.title, cat.description, cat.icon, i] ); const categoryId = catRows[0].id; for (let j = 0; j < cat.items.length; j++) { const [name, price, period, note] = cat.items[j]; await pool.query( 'INSERT INTO service_items (category_id, name, price_text, period, note, sort_order) VALUES ($1,$2,$3,$4,$5,$6)', [categoryId, name, price, period, note, j] ); } } const promotions = [ ['Бонус для новых клиентов', '15 000 ₽', null, 'Бонус на любые услуги при заключении договора на IT-аутсорсинг, от 10 рабочих мест.'], ['Год без забот', 'от 20 000 ₽/мес', 'от 30 000 ₽', 'IT-аутсорсинг базовый 5/2 при оплате за 12 месяцев - скидка 30%.'], ['Премиум 24/7', 'от 80 000 ₽/мес', 'от 100 000 ₽', 'Полное сопровождение при договоре от 6 месяцев - скидка 20%.'], ['Почтовый сервер под ключ', '35 000 ₽', 'от 50 000 ₽', 'SPF/DKIM/DMARC, антиспам, веб-почта, 3 месяца сопровождения.'], ['Бот + ИИ-ассистент', '35 000 ₽', 'от 45 000 ₽', 'Telegram-бот с обучением на ваших данных (RAG).'], ]; for (let i = 0; i < promotions.length; i++) { const [title, price, oldPrice, desc] = promotions[i]; await pool.query( 'INSERT INTO promotions (title, price_text, old_price_text, description, sort_order) VALUES ($1,$2,$3,$4,$5)', [title, price, oldPrice, desc, i] ); } console.log('[seed] inserted starter services and promotions - edit or delete them from the admin panel any time'); } async function ensureCasesAndFaqs() { const { rows } = await pool.query('SELECT COUNT(*)::int AS c FROM cases'); if (rows[0].c === 0) { const cases = [ ['Миграция инфраструктуры розничной сети', 'Сетевая инфраструктура', 'завершён', 'ok', 'Перенесли серверы 12 магазинов на новое оборудование без остановки продаж, снизили время отклика сети.'], ['ИИ-ассистент для отдела поддержки', 'ИИ и автоматизация', 'завершён', 'ok', 'Внедрили Telegram-бота с RAG на базе базы знаний компании - снизили нагрузку на операторов.'], ['Аудит кибербезопасности финтех-проекта', 'Кибербезопасность', 'в работе', 'warn', 'Проводим комплексный аудит защищённости и внедряем систему мониторинга угроз 24/7.'], ]; for (let i = 0; i < cases.length; i++) { const [title, tag, status, tone, description] = cases[i]; await pool.query( 'INSERT INTO cases (title, tag, status, tone, description, sort_order) VALUES ($1,$2,$3,$4,$5,$6)', [title, tag, status, tone, description, i] ); } } const { rows: faqRows } = await pool.query('SELECT COUNT(*)::int AS c FROM faqs'); if (faqRows[0].c === 0) { const faqs = [ ['Как быстро вы реагируете на заявки?', 'Для клиентов на поддержке 24/7 среднее время реакции - около 15 минут. Для разовых заявок мы связываемся в течение рабочего дня.'], ['Работаете ли вы полностью удалённо?', 'Да, большинство работ выполняется удалённо. Выезд специалиста доступен по Санкт-Петербургу.'], ['Что входит в базовый тариф IT-аутсорсинга?', 'Удалённая поддержка инфраструктуры по графику 5/2: мониторинг, устранение инцидентов, консультации.'], ['Как происходит оплата услуг?', 'По разовым работам - по факту выполнения или по предоплате для крупных проектов. По абонентскому обслуживанию - ежемесячно.'], ['Можно ли заказать разовую услугу без абонемента?', 'Да, доступны разовые работы: настройка сервера, аудит безопасности, разработка бота и другие услуги из прайс-листа.'], ['Как обеспечивается конфиденциальность данных?', 'Мы подписываем NDA перед началом работ и используем защищённые каналы доступа к вашей инфраструктуре.'], ]; for (let i = 0; i < faqs.length; i++) { const [question, answer] = faqs[i]; await pool.query('INSERT INTO faqs (question, answer, sort_order) VALUES ($1,$2,$3)', [question, answer, i]); } } } async function ensureContactInfo() { const { rows } = await pool.query('SELECT COUNT(*)::int AS c FROM contact_info'); if (rows[0].c > 0) return; await pool.query( 'INSERT INTO contact_info (id, address, phone, email, hours, map_embed) VALUES (1, $1, $2, $3, $4, $5)', [ 'г. Санкт-Петербург, ул. Вавиловых, д. 10, к1', '+7 (988) 845-47-65', 'info@top-sysops.ru', 'Пн-Пт: 8:00-18:00', 'https://yandex.ru/map-widget/v1/?um=constructor%3Aefc560cea4e680eb7a1e2826c633bc8b25312218ff4c0e69e571089cbb4372a5&source=constructor', ] ); } async function seed() { await ensureAdmin(); await ensureContent(); await ensureCasesAndFaqs(); await ensureContactInfo(); } module.exports = seed;