Initial commit: TopSysOps site + admin panel
Multi-page public site (Next.js), Node/Express backend with Postgres, and Vite admin/account app, restructured from a single-page layout into dedicated service, promotions, cases, about, FAQ, and blog pages for SEO. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013otXCiTZsxdZ4SJKZ9wUky
This commit is contained in:
commit
130c5382cd
108 files changed
+7146
No files matched your search
@@ -0,0 +1,167 @@
|
||||
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;
|
||||
Reference in new issue
Block a user