From e56d4e309d7413388bc8bc590318319009bf4abb Mon Sep 17 00:00:00 2001 From: Bivekich Date: Mon, 28 Jul 2025 09:34:43 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=81=D0=BA?= =?UTF-8?q?=D1=80=D0=B8=D0=BF=D1=82=D1=8B=20=D0=B2=20package.json=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=8B=20=D1=81=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B9=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85,=20=D0=B2=D0=BA=D0=BB=D1=8E=D1=87?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B8=D0=BD=D0=B8=D1=86=D0=B8=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8E=20=D0=B8=20=D1=81=D0=B1=D1=80?= =?UTF-8?q?=D0=BE=D1=81=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85.=20=D0=9E?= =?UTF-8?q?=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=81=D1=85?= =?UTF-8?q?=D0=B5=D0=BC=D0=B0=20Prisma=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B8=20=D0=B0=D0=B2=D1=82?= =?UTF-8?q?=D0=BE=D0=BC=D0=B0=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20seeding.=20=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=D0=BE=D0=BB=D0=B2=D0=B5=D1=80=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=B2=D1=81=D0=B5=D1=85=20=D0=BA=D0=B0=D1=82=D0=B5=D0=B3?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=B9=20=D1=82=D0=BE=D0=B2=D0=B0=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B2=20GraphQL.=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B2=20que?= =?UTF-8?q?ries.ts.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 6 +- prisma/schema.prisma | 6 ++ prisma/seed.js | 141 +++++++++++++++++++++++++++++++++++++++ src/graphql/queries.ts | 2 +- src/graphql/resolvers.ts | 16 +++++ src/lib/seed-init.ts | 141 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 prisma/seed.js create mode 100644 src/lib/seed-init.ts diff --git a/package.json b/package.json index f717c68..0a00ce9 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,15 @@ "name": "sferav", "version": "0.1.0", "private": true, + "type": "module", "scripts": { "dev": "next dev --turbopack", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "db:seed": "node prisma/seed.js", + "db:reset": "npx prisma db push --force-reset && npm run db:seed", + "postinstall": "npx prisma generate" }, "dependencies": { "@apollo/client": "^3.13.8", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 969f43d..ab75f3c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -2,6 +2,12 @@ generator client { provider = "prisma-client-js" } +// Конфигурация для автоматического seeding +generator seed { + provider = "prisma-client-js" + output = "./generated/client" +} + datasource db { provider = "postgresql" url = env("DATABASE_URL") diff --git a/prisma/seed.js b/prisma/seed.js new file mode 100644 index 0000000..a7d2429 --- /dev/null +++ b/prisma/seed.js @@ -0,0 +1,141 @@ +import { PrismaClient } from '@prisma/client' +import bcrypt from 'bcryptjs' + +const prisma = new PrismaClient() + +// Список базовых категорий товаров +const DEFAULT_CATEGORIES = [ + 'Одежда и обувь', + 'Косметика и парфюмерия', + 'Дом и сад', + 'Детские товары', + 'Спорт и отдых', + 'Электроника', + 'Книги', + 'Здоровье', + 'Автотовары', + 'Строительство и ремонт', + 'Продукты питания', + 'Зоотовары', + 'Дача, сад и огород', + 'Канцелярские товары', + 'Хобби и творчество', + 'Украшения и аксессуары', + 'Сумки и чемоданы', + 'Техника для дома', + 'Музыкальные инструменты', + 'Игры и игрушки' +] + +async function seedAdmin() { + try { + console.log('🔐 Создание администратора...') + + // Проверяем есть ли уже админ + const existingAdmin = await prisma.admin.findUnique({ + where: { username: 'admin' } + }) + + if (existingAdmin) { + console.log('ℹ️ Администратор уже существует, пропускаем создание') + return existingAdmin + } + + // Генерируем хеш пароля + const password = 'admin123' // Временный пароль + const hashedPassword = await bcrypt.hash(password, 12) + + // Создаем администратора + const admin = await prisma.admin.create({ + data: { + username: 'admin', + password: hashedPassword, + email: 'admin@sferav.com', + isActive: true + } + }) + + console.log('✅ Администратор создан:') + console.log(` Логин: ${admin.username}`) + console.log(` Пароль: ${password}`) + console.log(` Email: ${admin.email}`) + console.log(' ⚠️ Обязательно смените пароль после первого входа!') + + return admin + } catch (error) { + console.error('❌ Ошибка создания администратора:', error) + throw error + } +} + +async function seedCategories() { + try { + console.log('\n📂 Создание категорий...') + + let createdCount = 0 + let skippedCount = 0 + + for (const categoryName of DEFAULT_CATEGORIES) { + try { + // Проверяем существует ли категория + const existingCategory = await prisma.category.findUnique({ + where: { name: categoryName } + }) + + if (existingCategory) { + skippedCount++ + continue + } + + // Создаем категорию + await prisma.category.create({ + data: { + name: categoryName + } + }) + + createdCount++ + console.log(` ✅ Создана категория: ${categoryName}`) + } catch (error) { + console.log(` ⚠️ Ошибка создания категории "${categoryName}":`, error.message) + } + } + + console.log(`\n📊 Результат создания категорий:`) + console.log(` Создано: ${createdCount}`) + console.log(` Пропущено (уже существует): ${skippedCount}`) + console.log(` Всего категорий: ${DEFAULT_CATEGORIES.length}`) + + return { createdCount, skippedCount, total: DEFAULT_CATEGORIES.length } + } catch (error) { + console.error('❌ Ошибка создания категорий:', error) + throw error + } +} + +async function main() { + try { + console.log('🚀 Запуск инициализации базы данных...\n') + + // Создаем админа + await seedAdmin() + + // Создаем категории + await seedCategories() + + console.log('\n🎉 Инициализация базы данных завершена успешно!') + + } catch (error) { + console.error('\n💥 Ошибка инициализации базы данных:', error) + process.exit(1) + } finally { + await prisma.$disconnect() + } +} + +// Запускаем если скрипт вызван напрямую +if (import.meta.url === `file://${process.argv[1]}`) { + main() +} + +export { seedAdmin, seedCategories } \ No newline at end of file diff --git a/src/graphql/queries.ts b/src/graphql/queries.ts index ad0d876..047ea47 100644 --- a/src/graphql/queries.ts +++ b/src/graphql/queries.ts @@ -835,4 +835,4 @@ export const GET_SUPPLY_ORDERS = gql` } } } -` \ No newline at end of file +`; \ No newline at end of file diff --git a/src/graphql/resolvers.ts b/src/graphql/resolvers.ts index 9ab0839..05d921d 100644 --- a/src/graphql/resolvers.ts +++ b/src/graphql/resolvers.ts @@ -8,6 +8,7 @@ import { DaDataService } from "@/services/dadata-service"; import { MarketplaceService } from "@/services/marketplace-service"; import { WildberriesService } from "@/services/wildberries-service"; import { Prisma } from "@prisma/client"; +import "@/lib/seed-init"; // Автоматическая инициализация БД // Сервисы const smsService = new SmsService(); @@ -1241,6 +1242,21 @@ export const resolvers = { return scheduleRecords; }, + + // Получение всех категорий товаров + categories: async () => { + try { + const categories = await prisma.category.findMany({ + orderBy: { + name: 'asc' + } + }); + return categories; + } catch (error) { + console.error('Ошибка получения категорий:', error); + throw new GraphQLError('Не удалось получить категории'); + } + }, }, Mutation: { diff --git a/src/lib/seed-init.ts b/src/lib/seed-init.ts new file mode 100644 index 0000000..1a22521 --- /dev/null +++ b/src/lib/seed-init.ts @@ -0,0 +1,141 @@ +import { PrismaClient } from '@prisma/client' +import bcrypt from 'bcryptjs' + +const prisma = new PrismaClient() + +// Список базовых категорий товаров +const DEFAULT_CATEGORIES = [ + 'Одежда и обувь', + 'Косметика и парфюмерия', + 'Дом и сад', + 'Детские товары', + 'Спорт и отдых', + 'Электроника', + 'Книги', + 'Здоровье', + 'Автотовары', + 'Строительство и ремонт', + 'Продукты питания', + 'Зоотовары', + 'Дача, сад и огород', + 'Канцелярские товары', + 'Хобби и творчество', + 'Украшения и аксессуары', + 'Сумки и чемоданы', + 'Техника для дома', + 'Музыкальные инструменты', + 'Игры и игрушки' +] + +let isInitialized = false + +export async function ensureAdmin() { + try { + // Проверяем есть ли уже админ + const existingAdmin = await prisma.admin.findUnique({ + where: { username: 'admin' } + }) + + if (existingAdmin) { + return existingAdmin + } + + console.log('🔐 Создание администратора...') + + // Генерируем хеш пароля + const password = 'admin123' // Временный пароль + const hashedPassword = await bcrypt.hash(password, 12) + + // Создаем администратора + const admin = await prisma.admin.create({ + data: { + username: 'admin', + password: hashedPassword, + email: 'admin@sferav.com', + isActive: true + } + }) + + console.log('✅ Администратор создан:', admin.username) + return admin + } catch (error) { + console.error('❌ Ошибка создания администратора:', error) + return null + } +} + +export async function ensureCategories() { + try { + // Проверяем сколько категорий уже есть + const existingCount = await prisma.category.count() + + if (existingCount >= DEFAULT_CATEGORIES.length) { + return { created: 0, existing: existingCount } + } + + let createdCount = 0 + + for (const categoryName of DEFAULT_CATEGORIES) { + try { + // Проверяем существует ли категория + const existingCategory = await prisma.category.findUnique({ + where: { name: categoryName } + }) + + if (!existingCategory) { + // Создаем категорию + await prisma.category.create({ + data: { name: categoryName } + }) + createdCount++ + } + } catch (error) { + // Игнорируем ошибки дублирования + if (error.code !== 'P2002') { + console.error(`Ошибка создания категории "${categoryName}":`, error.message) + } + } + } + + if (createdCount > 0) { + console.log(`📂 Создано категорий: ${createdCount}`) + } + + return { created: createdCount, existing: existingCount } + } catch (error) { + console.error('❌ Ошибка создания категорий:', error) + return { created: 0, existing: 0 } + } +} + +export async function initializeDatabase() { + if (isInitialized) { + return + } + + try { + console.log('🚀 Проверка инициализации базы данных...') + + // Инициализируем админа и категории параллельно + const [admin, categories] = await Promise.all([ + ensureAdmin(), + ensureCategories() + ]) + + isInitialized = true + + if (admin || categories.created > 0) { + console.log('✨ Инициализация базы данных завершена') + } + + return { admin, categories } + } catch (error) { + console.error('💥 Ошибка инициализации базы данных:', error) + return null + } +} + +// Автоматическая инициализация при импорте модуля +if (typeof window === 'undefined') { // Только на сервере + initializeDatabase().catch(console.error) +} \ No newline at end of file