- Fix seed-products.ts TS error (implicit any) and BOM handling - Re-run full seed to restore correct Persian encoding in DB - Fix productService query→search param mismatch - Fix IngredientWiki hardcoded port 4000→use settingsStore - Restore seed-products-data.json from git after accidental corruption
255 lines
12 KiB
TypeScript
255 lines
12 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
||
import * as fs from 'fs';
|
||
import * as path from 'path';
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
function slugify(text: string): string {
|
||
return text
|
||
.toLowerCase()
|
||
.replace(/\s+/g, '-')
|
||
.replace(/[^a-z0-9\u0600-\u06FF\-]/g, '')
|
||
.replace(/-+/g, '-')
|
||
.replace(/^-|-$/g, '');
|
||
}
|
||
|
||
function determineSuitableFor(name: string, description: string): string {
|
||
const text = (name + ' ' + description).toLowerCase();
|
||
const hasCat = text.includes('گربه') || text.includes('katzen') || text.includes('cat');
|
||
const hasDog = text.includes('سگ') || text.includes('hund') || text.includes('توله') || text.includes('dog');
|
||
if (hasCat && hasDog) return 'هر دو';
|
||
if (hasCat) return 'گربه';
|
||
if (hasDog) return 'سگ';
|
||
return 'هر دو';
|
||
}
|
||
|
||
function parseSize(size: string): { unit: string; packageSize: number } {
|
||
const mlMatch = size.match(/([\d.]+)\s*ml/i);
|
||
if (mlMatch) return { unit: 'ml', packageSize: parseFloat(mlMatch[1]) };
|
||
const gMatch = size.match(/([\d.]+)\s*g/i);
|
||
if (gMatch) return { unit: 'g', packageSize: parseFloat(gMatch[1]) };
|
||
const tabMatch = size.match(/([\d.]+)\s*(tablet|tab)/i);
|
||
if (tabMatch) return { unit: 'tablet', packageSize: parseFloat(tabMatch[1]) };
|
||
return { unit: 'g', packageSize: 100 };
|
||
}
|
||
|
||
async function findOrCreateCategory(slug: string): Promise<{ id: string; slug: string }> {
|
||
const CATEGORY_NAMES: Record<string, string> = {
|
||
'joints': 'مفاصل و استخوان',
|
||
'immune': 'تقویت سیستم ایمنی و گوارش',
|
||
'energy': 'ویتامینها و انرژیبخشها',
|
||
'special-care': 'مراقبتهای ویژه (پوست، دندان و چشم)',
|
||
'general': 'تقویت عمومی',
|
||
'nutrition': 'تغذیه تخصصی',
|
||
'supplements': 'مکملهای غذایی و درمانی',
|
||
};
|
||
const name = CATEGORY_NAMES[slug] || slug;
|
||
return await prisma.category.upsert({
|
||
where: { slug },
|
||
update: { name },
|
||
create: { slug, name, description: name },
|
||
});
|
||
}
|
||
|
||
async function main() {
|
||
console.log('Seeding products from seed-products-data.json...');
|
||
|
||
// 2. Read JSON data
|
||
const dataPath = path.join(__dirname, 'seed-products-data.json');
|
||
if (!fs.existsSync(dataPath)) {
|
||
console.error('seed-products-data.json not found at', dataPath);
|
||
process.exit(1);
|
||
}
|
||
|
||
const productsData = JSON.parse(fs.readFileSync(dataPath, 'utf-8').replace(/^\ufeff/, ''));
|
||
let variantCount = 0;
|
||
|
||
for (const item of productsData) {
|
||
const baseName = item.name;
|
||
const cleanDesc = item.description;
|
||
const cleanIngredients = item.ingredients || '';
|
||
const cleanDosage = item.usage_and_dosage || '';
|
||
const suitableFor = determineSuitableFor(baseName, cleanDesc);
|
||
const baseSlug = slugify(baseName);
|
||
const categorySlug = item.category_slug || 'supplements';
|
||
const category = await findOrCreateCategory(categorySlug);
|
||
|
||
for (const variant of item.variants) {
|
||
const artNo = variant.art_no;
|
||
const barcode = variant.barcode;
|
||
const sellPrice = variant.sell_price || 0;
|
||
const buyPrice = variant.buy_price || 0;
|
||
const sizeLabel = variant.size;
|
||
const { unit, packageSize } = parseSize(sizeLabel);
|
||
|
||
// Build variant-specific slug
|
||
const sizeSlug = slugify(sizeLabel);
|
||
const slug = `${baseSlug}-${sizeSlug}`;
|
||
|
||
// Build display name with size
|
||
const displayName = `${baseName} - ${sizeLabel}`;
|
||
const priceDisplay = `${sellPrice.toLocaleString()} T`;
|
||
|
||
const product = await prisma.product.upsert({
|
||
where: { artNo },
|
||
update: {
|
||
barcode,
|
||
slug,
|
||
productGroup: baseSlug,
|
||
name: displayName,
|
||
description: cleanDesc,
|
||
ingredients: cleanIngredients,
|
||
categoryId: category.id,
|
||
categorySlug: category.slug,
|
||
priceValue: sellPrice,
|
||
buyPrice,
|
||
priceDisplay,
|
||
unit,
|
||
packageSize,
|
||
dosageLogic: cleanDosage,
|
||
suitableFor,
|
||
imageUrl: `/products/${baseSlug}.png`,
|
||
},
|
||
create: {
|
||
artNo,
|
||
barcode,
|
||
slug,
|
||
productGroup: baseSlug,
|
||
name: displayName,
|
||
description: cleanDesc,
|
||
ingredients: cleanIngredients,
|
||
categoryId: category.id,
|
||
categorySlug: category.slug,
|
||
priceValue: sellPrice,
|
||
buyPrice,
|
||
priceDisplay,
|
||
unit,
|
||
packageSize,
|
||
dosageLogic: cleanDosage,
|
||
suitableFor,
|
||
imageUrl: `/products/${baseSlug}.png`,
|
||
}
|
||
});
|
||
|
||
// Seed ingredients (split by '،' or '، ')
|
||
await prisma.productIngredient.deleteMany({ where: { productId: product.id } });
|
||
if (cleanIngredients) {
|
||
const parts = cleanIngredients.split(/[،,]/).map((s: string) => s.trim()).filter(Boolean);
|
||
// Only take the first meaningful items (up to ~20)
|
||
const meaningful = parts.slice(0, 20);
|
||
for (const ing of meaningful) {
|
||
const truncated = ing.substring(0, 150);
|
||
if (truncated.length > 0) {
|
||
await prisma.productIngredient.create({
|
||
data: { productId: product.id, ingredient: truncated }
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
variantCount++;
|
||
}
|
||
}
|
||
|
||
console.log(`Seeded ${variantCount} product variants.`);
|
||
|
||
// Create admin user for blogs
|
||
const adminId = '12345678-1234-1234-1234-123456789012';
|
||
await prisma.user.upsert({
|
||
where: { id: adminId },
|
||
update: { role: 'ADMIN' },
|
||
create: {
|
||
id: adminId,
|
||
mobile: '09120000000',
|
||
email: 'admin@canino-iran.com',
|
||
firstName: 'Admin',
|
||
lastName: 'User',
|
||
role: 'ADMIN'
|
||
}
|
||
});
|
||
|
||
// 3. Seed some Blogs
|
||
const blog1Content = [
|
||
'<p><strong>تغذیه صحیح سگهای بالغ</strong> یکی از مهمترین عوامل در حفظ سلامت و افزایش طول عمر آنهاست. برند کانینا (Canina) با بیش از ۴۰ سال سابقه در تولید مکملهای دامپزشکی در آلمان، استاندارد طلایی تغذیه تکمیلی را تعریف کرده است.</p>',
|
||
'',
|
||
'<h2>اصول تغذیه سگ بالغ</h2>',
|
||
'<p>سگهای بالغ به رژیم غذایی متعادل حاوی پروتئین، چربی، کربوهیدرات، ویتامینها و مواد معدنی نیاز دارند. نسبت Ca:P (کلسیم به فسفر) باید در محدوده ۱.۲:۱ تا ۱.۵:۱ باشد. هرگونه عدم تعادل در این نسبت میتواند منجر به مشکلات کلیوی و استخوانی شود.</p>',
|
||
'',
|
||
'<h2>نقش مکملها در رژیم غذایی</h2>',
|
||
'<p>حتی با بهترین رژیم غذایی، ممکن است برخی مواد مغذی به اندازه کافی تأمین نشوند. محصولات کانینا مانند <strong>Petvital Vitamin-Tabs</strong> (کمپلکس کامل A تا K3) و <strong>Biotin-Tabs</strong> (۱ میلیون میکروگرم بیوتین) نیازهای تغذیهای را پوشش میدهند.</p>',
|
||
'',
|
||
'<h2>اسیدهای چرب ضروری</h2>',
|
||
'<p>روغن سالمون کانینا (Lachs-Öl) با ۱۴٪ امگا ۳ (EPA/DHA) به روش Cold Press استخراج میشود و تمام ویتامینهای حساس به حرارت را حفظ میکند. EPA پیشماده رزولوینهای ضدالتهابی و DHA ۳۰٪ وزن خشک مغز را تشکیل میدهد.</p>',
|
||
'',
|
||
'<h2>مراقبت از مفاصل</h2>',
|
||
'<p>با افزایش سن سگ، غضروف مفصلی تحلیل میرود. گلیکوزآمینوگلیکانهای صدف لب سبز (GAG) با جذب مولکولهای آب در ماتریکس غضروفی، خاصیت بافری مفاصل را بازیابی میکنند. <strong>CANHYDROX GAG</strong> و <strong>FLEXAN</strong> حاوی این ترکیبات حیاتی هستند.</p>',
|
||
'',
|
||
'<h2>هشدارهای مهم تغذیهای</h2>',
|
||
'<ul>',
|
||
'<li>مصرف همزمان ویتامین D3 با D2 مجاز نیست</li>',
|
||
'<li>روغن سیاه دانه در بارداری و شیردهی ممنوع است</li>',
|
||
'<li>روغنهای با غلظت بالا باید تدریجاً به رژیم اضافه شوند تا از پانکراتیت جلوگیری شود</li>',
|
||
'<li>تورین برای گربهها اسید آمینه ضروری است و بدن آنها قادر به سنتز آن نیست</li>',
|
||
'</ul>'
|
||
].join('\n');
|
||
|
||
const blog2Content = [
|
||
'<p><strong>کانینا (Canina)</strong> نماینده رسمی برند <strong>Canina pharma GmbH</strong> در ایران است. این شرکت آلمانی در سال ۱۹۸۴ توسط <strong>پتر و کریستین کرشنمایر</strong> در شهر برگیش گلادباخ تأسیس شد و به یکی از پیشگامان تولید مکملهای طبیعی دامپزشکی در اروپا تبدیل شده است.</p>',
|
||
'',
|
||
'<h2>فلسفه Canina: "مادر طبیعت بهترین نسخه را میداند"</h2>',
|
||
'<p>تمام محصولات کانینا بر پایه مواد اولیه طبیعی و با فرآیندهای پیشرفته تولید میشوند. این شرکت دارای گواهیهای معتبر <strong>IFS (International Featured Standard)</strong> و <strong>HACCP</strong> است و محصولات آن در بیش از ۴۰ کشور جهان توزیع میشود.</p>',
|
||
'',
|
||
'<h2>چرا مکملهای کانینا در ایران؟</h2>',
|
||
'<ul>',
|
||
'<li><strong>کیفیت آلمانی:</strong> تمام محصولات در کارخانه برگیش گلادباخ تحت نظارت دامپزشکان تولید میشوند</li>',
|
||
'<li><strong>بدون مواد مصنوعی:</strong> فاقد رنگ، طعم و نگهدارنده مصنوعی</li>',
|
||
'<li><strong>فرمولاسیون تخصصی:</strong> هر محصول برای نیاز خاصی مانند مفاصل، ایمنی، پوست و مو یا گوارش طراحی شده</li>',
|
||
'<li><strong>تأیید دامپزشکان:</strong> محصولات با مشورت دامپزشکان و متخصصان تغذیه حیوانات فرموله شدهاند</li>',
|
||
'</ul>',
|
||
'',
|
||
'<h2>محصولات پرفروش در ایران</h2>',
|
||
'<p><strong>مفاصل و استخوان:</strong> CANHYDROX GAG (استاندارد طلایی ارتوپدی)، FLEXAN (کلاژن خالص)، Petvital GAG و Velox Gelenkenergie (کنسانتره صدف لب سبز)</p>',
|
||
'<p><strong>سیستم ایمنی و گوارش:</strong> Immun-Booster (خمیر آغوز)، Moor-Tränke (عصاره پیت)، Ballaststoff Mix (فیبر گیاهی)</p>',
|
||
'<p><strong>پوست و مو:</strong> Lachs-Öl (روغن سالمون)، Biotin-Tabs، Seealgen (جلبک دریایی)</p>',
|
||
'<p><strong>قلب و انرژی:</strong> Herz Vital (ال-کارنیتین و زالزالک)، Energy-Gel (ویتامینهای گروه B)</p>',
|
||
'',
|
||
'<h2>تعهد به سلامت حیوانات</h2>',
|
||
'<p>کانینا به عنوان نماینده رسمی کانینا در ایران، متعهد به ارائه محصولات اصل با ضمانت اصالت و پروانه دامپزشکی است. تمام محصولات دارای بستهبندی اصلی آلمان و بارکد اختصاصی هستند.</p>'
|
||
].join('\n');
|
||
|
||
const blogs = [
|
||
{
|
||
title: 'راهنمای جامع تغذیه سگهای بالغ',
|
||
slug: 'adult-dog-nutrition-guide',
|
||
content: blog1Content,
|
||
authorId: adminId,
|
||
isPublished: true,
|
||
},
|
||
{
|
||
title: 'چرا مکملهای کانینا؟',
|
||
slug: 'why-canino-supplements',
|
||
content: blog2Content,
|
||
authorId: adminId,
|
||
isPublished: true,
|
||
}
|
||
];
|
||
|
||
for (const b of blogs) {
|
||
await prisma.blog.upsert({
|
||
where: { slug: b.slug },
|
||
update: {},
|
||
create: b
|
||
});
|
||
}
|
||
console.log('Seeded blogs.');
|
||
}
|
||
|
||
main()
|
||
.catch(e => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
})
|
||
.finally(async () => {
|
||
await prisma.$disconnect();
|
||
});
|