Добавлены новые скрипты в package.json для автоматизации работы с базой данных, включая инициализацию и сброс данных. Обновлена схема Prisma для поддержки автоматического seeding. Реализован новый резолвер для получения всех категорий товаров в GraphQL. Исправлен форматирование в queries.ts.
This commit is contained in:
@ -2,11 +2,15 @@
|
|||||||
"name": "sferav",
|
"name": "sferav",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"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": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.13.8",
|
"@apollo/client": "^3.13.8",
|
||||||
|
@ -2,6 +2,12 @@ generator client {
|
|||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Конфигурация для автоматического seeding
|
||||||
|
generator seed {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
output = "./generated/client"
|
||||||
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
|
141
prisma/seed.js
Normal file
141
prisma/seed.js
Normal file
@ -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 }
|
@ -835,4 +835,4 @@ export const GET_SUPPLY_ORDERS = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`;
|
@ -8,6 +8,7 @@ import { DaDataService } from "@/services/dadata-service";
|
|||||||
import { MarketplaceService } from "@/services/marketplace-service";
|
import { MarketplaceService } from "@/services/marketplace-service";
|
||||||
import { WildberriesService } from "@/services/wildberries-service";
|
import { WildberriesService } from "@/services/wildberries-service";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
|
import "@/lib/seed-init"; // Автоматическая инициализация БД
|
||||||
|
|
||||||
// Сервисы
|
// Сервисы
|
||||||
const smsService = new SmsService();
|
const smsService = new SmsService();
|
||||||
@ -1241,6 +1242,21 @@ export const resolvers = {
|
|||||||
|
|
||||||
return scheduleRecords;
|
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: {
|
Mutation: {
|
||||||
|
141
src/lib/seed-init.ts
Normal file
141
src/lib/seed-init.ts
Normal file
@ -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)
|
||||||
|
}
|
Reference in New Issue
Block a user