Files
apollo-backend/scripts/seed.js
2026-03-31 12:31:45 +07:00

197 lines
5.3 KiB
JavaScript

import 'dotenv/config';
import { prisma } from '../src/prisma-client.js';
const CATALOG_PAGES = ['https://fregat.org/catalog/', 'https://fregat.org/catalog/page/2/'];
const managerEmail = 'manager@fregat.local';
const clientEmail = 'client@fregat.local';
function decodeHtmlEntities(value) {
return value
.replaceAll(' ', ' ')
.replaceAll('&', '&')
.replaceAll('"', '"')
.replaceAll(''', "'")
.replaceAll('«', '«')
.replaceAll('»', '»')
.replaceAll('—', '—')
.replaceAll('–', '-');
}
function normalizeText(value) {
return decodeHtmlEntities(value.replaceAll(/<[^>]*>/g, ' ').replaceAll(/\s+/g, ' ')).trim();
}
function skuFromHref(href, index) {
const pathname = href.replace(/^https?:\/\/[^/]+/i, '').split('?')[0] || '';
const segments = pathname.split('/').filter(Boolean);
const slug = segments[segments.length - 1] ?? '';
if (!slug) {
return `FREGAT-CATALOG-${String(index + 1).padStart(3, '0')}`;
}
const normalized = slug.toUpperCase().replaceAll(/[^A-Z0-9]+/g, '-').replace(/^-+|-+$/g, '');
return `FREGAT-${normalized || String(index + 1).padStart(3, '0')}`;
}
function parseCatalogCards(html) {
const cards = [];
const cardRegex = /<a([^>]*)class="[^"]*product-card[^"]*"([^>]*)>([\s\S]*?)<\/a>/g;
let match;
while ((match = cardRegex.exec(html)) !== null) {
const attrs = `${match[1]} ${match[2]}`;
const body = match[3];
const hrefMatch = attrs.match(/href="([^"]+)"/);
const titleMatch = body.match(/<div class="product-card__title">([\s\S]*?)<\/div>/);
const typeMatch = body.match(/<div class="product-card__type">([\s\S]*?)<\/div>/);
const href = hrefMatch?.[1] ?? '';
const title = normalizeText(titleMatch?.[1] ?? '');
const type = normalizeText(typeMatch?.[1] ?? '');
if (!title) {
continue;
}
cards.push({ href, title, type });
}
return cards;
}
async function loadCatalogProducts() {
const parsed = [];
for (const pageUrl of CATALOG_PAGES) {
const response = await fetch(pageUrl, { redirect: 'follow' });
if (!response.ok) {
throw new Error(`Catalog request failed: ${pageUrl} (${response.status})`);
}
const html = await response.text();
const cards = parseCatalogCards(html);
if (!cards.length) {
throw new Error(`No product cards found at ${pageUrl}`);
}
parsed.push(...cards);
}
const deduped = new Map();
for (const [index, card] of parsed.entries()) {
const sku = skuFromHref(card.href, index);
if (deduped.has(sku)) {
continue;
}
const descriptionParts = [];
if (card.type) {
descriptionParts.push(card.type);
}
descriptionParts.push(`Источник: ${card.href}`);
deduped.set(sku, {
sku,
name: card.title,
description: descriptionParts.join('. '),
isCustomizable: /логотип|печать/i.test(`${card.title} ${card.type}`),
qtyMain: 300 + index * 17,
qtyReserve: 120 + index * 9,
});
}
return [...deduped.values()];
}
const company = await prisma.company.upsert({
where: { inn: '7701000000' },
update: {},
create: {
name: 'Fregat Client LLC',
inn: '7701000000',
},
});
const manager = await prisma.user.upsert({
where: { email: managerEmail },
update: { fullName: 'Default Manager' },
create: {
email: managerEmail,
fullName: 'Default Manager',
role: 'MANAGER',
},
});
await prisma.user.upsert({
where: { email: clientEmail },
update: { fullName: 'Demo Client', companyId: company.id },
create: {
email: clientEmail,
fullName: 'Demo Client',
role: 'CLIENT',
companyId: company.id,
},
});
const warehouseMain = await prisma.warehouse.upsert({
where: { code: 'MSK-01' },
update: { name: 'Main warehouse' },
create: { code: 'MSK-01', name: 'Main warehouse' },
});
const warehouseReserve = await prisma.warehouse.upsert({
where: { code: 'SPB-01' },
update: { name: 'Reserve warehouse' },
create: { code: 'SPB-01', name: 'Reserve warehouse' },
});
const products = await loadCatalogProducts();
console.log(`Loaded ${products.length} products from fregat.org catalog.`);
for (const product of products) {
const dbProduct = await prisma.product.upsert({
where: { sku: product.sku },
update: {
name: product.name,
description: product.description,
isCustomizable: product.isCustomizable,
isActive: true,
},
create: {
sku: product.sku,
name: product.name,
description: product.description,
isCustomizable: product.isCustomizable,
isActive: true,
},
});
await prisma.productStock.upsert({
where: {
productId_warehouseId: {
productId: dbProduct.id,
warehouseId: warehouseMain.id,
},
},
update: { availableQty: product.qtyMain },
create: {
productId: dbProduct.id,
warehouseId: warehouseMain.id,
availableQty: product.qtyMain,
},
});
await prisma.productStock.upsert({
where: {
productId_warehouseId: {
productId: dbProduct.id,
warehouseId: warehouseReserve.id,
},
},
update: { availableQty: product.qtyReserve },
create: {
productId: dbProduct.id,
warehouseId: warehouseReserve.id,
availableQty: product.qtyReserve,
},
});
}
console.log('Seed complete. Use manager header x-user-id:', manager.id);
await prisma.$disconnect();