import 'dotenv/config'; import * as fs from 'fs'; import * as path from 'path'; import { PrismaClient } from '../src/generated/prisma/client.js'; import { PrismaPg } from '@prisma/adapter-pg'; const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }); const prisma = new PrismaClient({ adapter }); interface WeaponJson { name: string; trait: string; item_category: string; item_subcategory: string; bulk: string; url: string; summary: string; hands?: string; damage?: string; range?: string; weapon_category?: string; } interface ArmorJson { name: string; trait: string; item_category: string; item_subcategory: string; bulk: string; url: string; summary: string; ac?: string; dex_cap?: string; } interface ShieldJson { name: string; trait: string; item_category: string; item_subcategory: string; bulk: string; url: string; summary: string; ac?: string; hp?: string; // Format: "6 (3)" - HP und Broken Threshold hardness?: string; } interface EquipmentJson { name: string; trait: string; item_category: string; item_subcategory: string; bulk: string; url: string; summary: string; activation?: string; effect?: string; // Specific effect text for item variants (Lesser, Moderate, Greater, Major) } function parseTraits(traitString: string): string[] { if (!traitString || traitString.trim() === '') return []; return traitString.split(',').map(t => t.trim()).filter(t => t.length > 0); } function parseDamage(damageStr: string): { damage: string | null; damageType: string | null } { if (!damageStr || damageStr.trim() === '') return { damage: null, damageType: null }; // Parse strings like "1d8 S", "1d6 P", "2d6 B" const match = damageStr.match(/^(.+?)\s+([SPB])$/i); if (match) { return { damage: match[1].trim(), damageType: match[2].toUpperCase() }; } return { damage: damageStr, damageType: null }; } function parseNumber(str: string | undefined): number | null { if (!str || str.trim() === '') return null; const num = parseInt(str, 10); return isNaN(num) ? null : num; } function parseShieldHp(hpStr: string | undefined): { hp: number | null; bt: number | null } { if (!hpStr || hpStr.trim() === '') return { hp: null, bt: null }; // Format: "6 (3)" oder "12 (6)" const match = hpStr.match(/^(\d+)\s*\((\d+)\)$/); if (match) { return { hp: parseInt(match[1], 10), bt: parseInt(match[2], 10) }; } // Fallback: nur HP const num = parseInt(hpStr, 10); return { hp: isNaN(num) ? null : num, bt: null }; } async function seedWeapons() { const dataPath = path.join(__dirname, 'data', 'weapons.json'); const data: WeaponJson[] = JSON.parse(fs.readFileSync(dataPath, 'utf-8')); console.log(`⚔️ Importing ${data.length} weapons...`); let created = 0; let updated = 0; let errors = 0; for (const item of data) { try { const { damage, damageType } = parseDamage(item.damage || ''); // Check if exists first const existing = await prisma.equipment.findUnique({ where: { name: item.name } }); if (existing) { // Update with weapon-specific fields await prisma.equipment.update({ where: { name: item.name }, data: { hands: item.hands || existing.hands, damage: damage || existing.damage, damageType: damageType || existing.damageType, range: item.range || existing.range, weaponCategory: item.weapon_category || existing.weaponCategory, // Don't overwrite traits/summary if already set traits: existing.traits.length > 0 ? existing.traits : parseTraits(item.trait), summary: existing.summary || item.summary || null, }, }); updated++; } else { await prisma.equipment.create({ data: { name: item.name, traits: parseTraits(item.trait), itemCategory: item.item_category || 'Weapons', itemSubcategory: item.item_subcategory || null, bulk: item.bulk || null, url: item.url || null, summary: item.summary || null, hands: item.hands || null, damage, damageType, range: item.range || null, weaponCategory: item.weapon_category || null, }, }); created++; } } catch (error: any) { if (errors === 0) { // Print full error for first failure only console.log(` ⚠️ First error for "${item.name}":`); console.log(error.message); } errors++; } } console.log(` ✅ Created: ${created}, Updated: ${updated}, Errors: ${errors}`); } async function seedArmor() { const dataPath = path.join(__dirname, 'data', 'armor.json'); const data: ArmorJson[] = JSON.parse(fs.readFileSync(dataPath, 'utf-8')); console.log(`🛡️ Importing ${data.length} armor items...`); let created = 0; let updated = 0; let errors = 0; for (const item of data) { try { const existing = await prisma.equipment.findUnique({ where: { name: item.name } }); if (existing) { // Update with armor-specific fields await prisma.equipment.update({ where: { name: item.name }, data: { ac: parseNumber(item.ac) ?? existing.ac, dexCap: parseNumber(item.dex_cap) ?? existing.dexCap, traits: existing.traits.length > 0 ? existing.traits : parseTraits(item.trait), summary: existing.summary || item.summary || null, }, }); updated++; } else { await prisma.equipment.create({ data: { name: item.name, traits: parseTraits(item.trait), itemCategory: item.item_category || 'Armor', itemSubcategory: item.item_subcategory || null, bulk: item.bulk || null, url: item.url || null, summary: item.summary || null, ac: parseNumber(item.ac), dexCap: parseNumber(item.dex_cap), }, }); created++; } } catch (error: any) { if (errors < 3) { console.log(` ⚠️ Error for "${item.name}": ${error.message?.slice(0, 100)}`); } errors++; } } console.log(` ✅ Created: ${created}, Updated: ${updated}, Errors: ${errors}`); } async function seedShields() { const dataPath = path.join(__dirname, 'data', 'shields.json'); const data: ShieldJson[] = JSON.parse(fs.readFileSync(dataPath, 'utf-8')); console.log(`🛡️ Importing ${data.length} shields...`); let created = 0; let updated = 0; let errors = 0; for (const item of data) { try { const { hp, bt } = parseShieldHp(item.hp); const existing = await prisma.equipment.findUnique({ where: { name: item.name } }); if (existing) { await prisma.equipment.update({ where: { name: item.name }, data: { ac: parseNumber(item.ac) ?? existing.ac, shieldHp: hp ?? existing.shieldHp, shieldBt: bt ?? existing.shieldBt, shieldHardness: parseNumber(item.hardness) ?? existing.shieldHardness, traits: existing.traits.length > 0 ? existing.traits : parseTraits(item.trait), summary: existing.summary || item.summary || null, }, }); updated++; } else { await prisma.equipment.create({ data: { name: item.name, traits: parseTraits(item.trait), itemCategory: item.item_category || 'Shields', itemSubcategory: item.item_subcategory || null, bulk: item.bulk || null, url: item.url || null, summary: item.summary || null, ac: parseNumber(item.ac), shieldHp: hp, shieldBt: bt, shieldHardness: parseNumber(item.hardness), }, }); created++; } } catch (error: any) { if (errors < 3) { console.log(` ⚠️ Error for "${item.name}": ${error.message?.slice(0, 100)}`); } errors++; } } console.log(` ✅ Created: ${created}, Updated: ${updated}, Errors: ${errors}`); } async function seedEquipment() { const dataPath = path.join(__dirname, 'data', 'equipment.json'); const data: EquipmentJson[] = JSON.parse(fs.readFileSync(dataPath, 'utf-8')); console.log(`📦 Importing ${data.length} equipment items...`); let created = 0; let updated = 0; let errors = 0; for (const item of data) { try { await prisma.equipment.upsert({ where: { name: item.name }, update: { traits: parseTraits(item.trait), itemCategory: item.item_category || 'Equipment', itemSubcategory: item.item_subcategory || null, bulk: item.bulk || null, url: item.url || null, summary: item.summary || null, activation: item.activation || null, effect: item.effect || null, }, create: { name: item.name, traits: parseTraits(item.trait), itemCategory: item.item_category || 'Equipment', itemSubcategory: item.item_subcategory || null, bulk: item.bulk || null, url: item.url || null, summary: item.summary || null, activation: item.activation || null, effect: item.effect || null, }, }); created++; } catch (error) { // Item with same name already exists - count as update attempt updated++; } } console.log(` ✅ Created: ${created}, Duplicates: ${updated}, Errors: ${errors}`); } async function main() { console.log('🗃️ Seeding Pathfinder 2e Equipment Database...\n'); const startTime = Date.now(); // WICHTIG: Equipment zuerst, dann Waffen/Rüstung/Schilde um spezifische Felder zu ergänzen await seedEquipment(); await seedWeapons(); // Ergänzt damage, hands, weapon_category etc. await seedArmor(); // Ergänzt ac, dex_cap etc. await seedShields(); // Ergänzt shieldHp, shieldHardness, shieldBt etc. const totalCount = await prisma.equipment.count(); const duration = ((Date.now() - startTime) / 1000).toFixed(1); console.log(`\n✅ Equipment database seeded successfully!`); console.log(` Total items in database: ${totalCount}`); console.log(` Duration: ${duration}s`); } main() .catch((e) => { console.error('Equipment seeding failed:', e); process.exit(1); }) .finally(() => prisma.$disconnect());