/** * Erstellt Dummy-Daten: eine Dummy-Filiale mit Mitarbeitern, Lagerstandorten und Geräten. * Voraussetzung: npm run setup bereits ausgeführt (Collections existieren). * * Ausführung: node scripts/seed-dummy-data.js */ import { Client, Databases, Users, Teams, ID, Query } from 'node-appwrite'; import { readFileSync } from 'fs'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); function loadEnv() { const envPath = resolve(__dirname, '..', '.env'); try { const lines = readFileSync(envPath, 'utf-8').split('\n'); for (const line of lines) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const eqIdx = trimmed.indexOf('='); if (eqIdx === -1) continue; const key = trimmed.slice(0, eqIdx).trim(); const value = trimmed.slice(eqIdx + 1).trim(); process.env[key] = value; } } catch { console.error('Fehler: .env nicht gefunden. Bitte .env aus .env.example anlegen.'); process.exit(1); } } loadEnv(); const ENDPOINT = process.env.APPWRITE_ENDPOINT; const PROJECT_ID = process.env.VITE_APPWRITE_PROJECT_ID; const API_KEY = process.env.APPWRITE_API_KEY; const DATABASE_ID = process.env.VITE_APPWRITE_DATABASE_ID || 'defekttrack_db'; const DUMMY_PASSWORD = 'Dummy1234!'; if (!ENDPOINT || !PROJECT_ID || !API_KEY || API_KEY === 'YOUR_API_KEY_HERE') { console.error('Bitte APPWRITE_ENDPOINT, VITE_APPWRITE_PROJECT_ID und APPWRITE_API_KEY in .env setzen.'); process.exit(1); } const client = new Client() .setEndpoint(ENDPOINT) .setProject(PROJECT_ID) .setKey(API_KEY); const databases = new Databases(client); const users = new Users(client); const teams = new Teams(client); /** Prüft ob die Dummy-Filiale bereits existiert */ async function getOrCreateDummyLocation() { const res = await databases.listDocuments(DATABASE_ID, 'locations', [ Query.equal('name', ['Dummy-Filiale München']), Query.limit(1), ]); if (res.documents.length > 0) { console.log(`Dummy-Filiale existiert bereits: "${res.documents[0].name}" (${res.documents[0].$id})`); return res.documents[0]; } const loc = await databases.createDocument(DATABASE_ID, 'locations', ID.unique(), { name: 'Dummy-Filiale München', address: 'Musterstraße 42, 80331 München', isActive: true, }); console.log(`Filiale erstellt: "${loc.name}" (${loc.$id})`); return loc; } /** Erstellt oder findet einen Appwrite-User und users_meta */ async function ensureUser(email, name, role, locationId) { let userId; const metaCheck = await databases.listDocuments(DATABASE_ID, 'users_meta', [ Query.equal('userName', [name]), Query.equal('locationId', [locationId]), Query.limit(1), ]); if (metaCheck.documents.length > 0) { userId = metaCheck.documents[0].userId; const userCheck = await users.get(userId).catch(() => null); if (userCheck) { console.log(` User existiert: ${name} (${email})`); return userId; } } try { const user = await users.create(ID.unique(), email, undefined, DUMMY_PASSWORD, name); userId = user.$id; console.log(` User erstellt: ${name} (${email})`); } catch (err) { if (err.code === 409) { const list = await users.list([Query.equal('email', [email])]); if (list.users.length > 0) { userId = list.users[0].$id; console.log(` User existiert: ${name}`); } else throw err; } else throw err; } try { await teams.createMembership(role, [], email, userId, undefined, `${ENDPOINT}/auth/confirm`); } catch (err) { if (err.code !== 409) console.warn(` Team ${role}:`, err.message); } const existingMeta = await databases.listDocuments(DATABASE_ID, 'users_meta', [ Query.equal('userId', [userId]), Query.limit(1), ]); if (existingMeta.documents.length === 0) { await databases.createDocument(DATABASE_ID, 'users_meta', ID.unique(), { userId, locationId, userName: name, role, mustChangePassword: false, }); console.log(` users_meta erstellt für ${name}`); } return userId; } /** Erstellt Lagerstandorte für die Filiale, gibt alle zurück */ async function createLagerstandorte(locationId) { const names = ['Lager A - EG', 'Lager B - Keller', 'Reparaturwerkstatt']; const existing = await databases.listDocuments(DATABASE_ID, 'lagerstandorte', [ Query.equal('locationId', [locationId]), Query.limit(20), ]); const existingNames = new Set(existing.documents.map((d) => d.name)); for (const name of names) { if (existingNames.has(name)) { console.log(` Lagerstandort existiert: ${name}`); continue; } await databases.createDocument(DATABASE_ID, 'lagerstandorte', ID.unique(), { name, locationId, isActive: true, }); existingNames.add(name); console.log(` Lagerstandort erstellt: ${name}`); } const res = await databases.listDocuments(DATABASE_ID, 'lagerstandorte', [ Query.equal('locationId', [locationId]), Query.limit(20), ]); return res.documents; } /** Erstellt Dummy-Mitarbeiter */ async function createDummyMitarbeiter(locationId) { const mitarbeiter = [ { email: 'dummy-filialleiter@defekttrack.local', name: 'Max Mustermann', role: 'filialleiter' }, { email: 'dummy-service1@defekttrack.local', name: 'Lisa Schmidt', role: 'service' }, { email: 'dummy-service2@defekttrack.local', name: 'Thomas Weber', role: 'service' }, { email: 'dummy-service3@defekttrack.local', name: 'Anna Becker', role: 'service' }, { email: 'dummy-lager@defekttrack.local', name: 'Peter Krause', role: 'lager' }, ]; const userNames = []; for (const m of mitarbeiter) { await ensureUser(m.email, m.name, m.role, locationId); userNames.push(m.name); await new Promise((r) => setTimeout(r, 300)); } return userNames; } /** Erstellt Dummy-Geräte (Assets) */ async function createDummyAssets(locationId, lagerstandorte, userNames) { const statuses = ['offen', 'in_bearbeitung', 'entsorgt']; const prioritaeten = ['hoch', 'mittel', 'niedrig']; const geraete = [ { erl: 'ERL-001', serien: 'SN-100001', artikel: 'ART-101', bezeichnung: 'Laptop Dell Latitude 5520', defekt: 'Display defekt' }, { erl: 'ERL-002', serien: 'SN-100002', artikel: 'ART-102', bezeichnung: 'Monitor LG 27"', defekt: 'Kein Bild' }, { erl: 'ERL-003', serien: 'SN-100003', artikel: 'ART-103', bezeichnung: 'Tastatur Logitech MX', defekt: 'Tasten kleben' }, { erl: 'ERL-004', serien: 'SN-100004', artikel: 'ART-104', bezeichnung: 'Maus Microsoft', defekt: 'Scroller defekt' }, { erl: 'ERL-005', serien: 'SN-100005', artikel: 'ART-105', bezeichnung: 'Drucker HP LaserJet', defekt: 'Papierstau' }, { erl: 'ERL-006', serien: 'SN-100006', artikel: 'ART-106', bezeichnung: 'Router Cisco', defekt: 'Keine Verbindung' }, { erl: 'ERL-007', serien: 'SN-100007', artikel: 'ART-107', bezeichnung: 'Smartphone Samsung', defekt: 'Akku schwach' }, { erl: 'ERL-008', serien: 'SN-100008', artikel: 'ART-108', bezeichnung: 'Tablet iPad', defekt: 'Display Riss' }, { erl: 'ERL-009', serien: 'SN-100009', artikel: 'ART-109', bezeichnung: 'Switch Netgear', defekt: 'LED defekt' }, { erl: 'ERL-010', serien: 'SN-100010', artikel: 'ART-110', bezeichnung: 'Festplatte SSD 1TB', defekt: 'Lesefehler' }, { erl: 'ERL-011', serien: 'SN-100011', artikel: 'ART-111', bezeichnung: 'USB-Hub 4-Port', defekt: 'Port 3 defekt' }, { erl: 'ERL-012', serien: 'SN-100012', artikel: 'ART-112', bezeichnung: 'Webcam Logitech', defekt: 'Mikrofon rauscht' }, { erl: 'ERL-013', serien: 'SN-100013', artikel: 'ART-113', bezeichnung: 'Headset Jabra', defekt: 'Kabelbruch' }, { erl: 'ERL-014', serien: 'SN-100014', artikel: 'ART-114', bezeichnung: 'Kabel HDMI 2m', defekt: 'Kontakt defekt' }, { erl: 'ERL-015', serien: 'SN-100015', artikel: 'ART-115', bezeichnung: 'Laptop Lenovo ThinkPad', defekt: 'Lüfter laut' }, ]; const existing = await databases.listDocuments(DATABASE_ID, 'assets', [ Query.startsWith('erlNummer', ['ERL-00']), Query.limit(50), ]); const existingErl = new Set(existing.documents.map((d) => d.erlNummer)); let created = 0; for (let i = 0; i < geraete.length; i++) { const g = geraete[i]; if (existingErl.has(g.erl)) continue; const ls = lagerstandorte[i % lagerstandorte.length]; const zustaendig = userNames[i % userNames.length]; const status = statuses[i % statuses.length]; const prio = prioritaeten[i % prioritaeten.length]; await databases.createDocument(DATABASE_ID, 'assets', ID.unique(), { erlNummer: g.erl, seriennummer: g.serien, artikelNr: g.artikel, bezeichnung: g.bezeichnung, defekt: g.defekt, lagerstandortId: ls?.$id || '', zustaendig, status, prio, kommentar: i % 3 === 0 ? 'Dummy-Eintrag für Testzwecke' : '', createdBy: 'Seed-Script', lastEditedBy: 'Seed-Script', }); created++; console.log(` Gerät erstellt: ${g.erl} (${g.bezeichnung}) - ${status}`); if (created % 3 === 0) await new Promise((r) => setTimeout(r, 200)); } if (created === 0) console.log(' Alle Dummy-Geräte existieren bereits.'); return created; } async function main() { console.log('=== DefektTrack Dummy-Daten Seed ==='); console.log(''); const loc = await getOrCreateDummyLocation(); const locationId = loc.$id; console.log(''); console.log('--- Dummy-Mitarbeiter ---'); const userNames = await createDummyMitarbeiter(locationId); console.log(''); console.log('--- Lagerstandorte ---'); const lsList = await createLagerstandorte(locationId); console.log(''); console.log('--- Dummy-Geräte ---'); await createDummyAssets(locationId, lsList, userNames); console.log(''); console.log('=== Dummy-Daten Seed abgeschlossen ==='); console.log(''); console.log('Filiale: Dummy-Filiale München'); console.log('Mitarbeiter-Logins (Passwort: Dummy1234!):'); console.log(' - dummy-filialleiter@defekttrack.local (Max Mustermann, Filialleiter)'); console.log(' - dummy-service1@defekttrack.local (Lisa Schmidt)'); console.log(' - dummy-service2@defekttrack.local (Thomas Weber)'); console.log(' - dummy-service3@defekttrack.local (Anna Becker)'); console.log(' - dummy-lager@defekttrack.local (Peter Krause)'); console.log(''); } main().catch((err) => { console.error('Seed fehlgeschlagen:', err); process.exit(1); });