Add initial Prisma migration and catalog-based product seed
This commit is contained in:
109
scripts/seed.js
109
scripts/seed.js
@@ -2,9 +2,98 @@ 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[^"]*"[^>]*href="([^"]+)"[^>]*>[\s\S]*?<div class="product-card__title">([\s\S]*?)<\/div>\s*<div class="product-card__type">([\s\S]*?)<\/div>/g;
|
||||
|
||||
let match;
|
||||
while ((match = cardRegex.exec(html)) !== null) {
|
||||
const href = match[1];
|
||||
const title = normalizeText(match[2]);
|
||||
const type = normalizeText(match[3]);
|
||||
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: {},
|
||||
@@ -47,24 +136,8 @@ const warehouseReserve = await prisma.warehouse.upsert({
|
||||
create: { code: 'SPB-01', name: 'Reserve warehouse' },
|
||||
});
|
||||
|
||||
const products = [
|
||||
{
|
||||
sku: 'FILM-100',
|
||||
name: 'Пленка прозрачная 100мкм',
|
||||
description: 'Готовая продукция для стандартной упаковки',
|
||||
isCustomizable: false,
|
||||
qtyMain: 1250,
|
||||
qtyReserve: 420,
|
||||
},
|
||||
{
|
||||
sku: 'FILM-200-COLOR',
|
||||
name: 'Пленка цветная 200мкм',
|
||||
description: 'Продукция с дополнительной окраской',
|
||||
isCustomizable: true,
|
||||
qtyMain: 860,
|
||||
qtyReserve: 230,
|
||||
},
|
||||
];
|
||||
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({
|
||||
|
||||
Reference in New Issue
Block a user