import { Client, Databases, Teams, Users, ID, Permission, Role, 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'); 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; } } 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'; 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 teamsService = new Teams(client); const users = new Users(client); const TEAM_ROLES = ['admin', 'firmenleiter', 'filialleiter', 'service', 'lager']; async function createDatabase() { try { const db = await databases.create(DATABASE_ID, 'DefektTrack DB'); console.log(`Datenbank erstellt: ${db.$id}`); } catch (err) { if (err.code === 409) { console.log(`Datenbank existiert bereits: ${DATABASE_ID}`); } else { throw err; } } } async function createLocationsCollection() { const COLLECTION_ID = 'locations'; try { await databases.createCollection( DATABASE_ID, COLLECTION_ID, 'Standorte', [ Permission.read(Role.users()), Permission.create(Role.team('admin')), Permission.update(Role.team('admin')), Permission.delete(Role.team('admin')), ] ); console.log('Collection erstellt: locations'); } catch (err) { if (err.code === 409) { console.log('Collection existiert bereits: locations'); return; } throw err; } await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'name', 128, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'address', 256, false, ''); await databases.createBooleanAttribute(DATABASE_ID, COLLECTION_ID, 'isActive', false, true); console.log(' Attribute fuer locations erstellt (name, address, isActive)'); } async function createUsersMetaCollection() { const COLLECTION_ID = 'users_meta'; try { await databases.createCollection( DATABASE_ID, COLLECTION_ID, 'Benutzer-Metadaten', [ Permission.read(Role.users()), Permission.create(Role.team('admin')), Permission.update(Role.users()), Permission.delete(Role.team('admin')), ] ); console.log('Collection erstellt: users_meta'); } catch (err) { if (err.code === 409) { console.log('Collection existiert bereits: users_meta'); return; } throw err; } await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'userId', 64, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'locationId', 64, false, ''); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'userName', 128, false, ''); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'role', 32, true); await databases.createBooleanAttribute(DATABASE_ID, COLLECTION_ID, 'mustChangePassword', false, true); console.log(' Attribute fuer users_meta erstellt (userId, locationId, userName, role, mustChangePassword)'); } async function createLagerstandorteCollection() { const COLLECTION_ID = 'lagerstandorte'; try { await databases.createCollection( DATABASE_ID, COLLECTION_ID, 'Lagerstandorte', [ Permission.read(Role.users()), Permission.create(Role.team('admin')), Permission.update(Role.team('admin')), Permission.delete(Role.team('admin')), Permission.create(Role.team('filialleiter')), Permission.update(Role.team('filialleiter')), Permission.delete(Role.team('filialleiter')), ] ); console.log('Collection erstellt: lagerstandorte'); } catch (err) { if (err.code === 409) { console.log('Collection existiert bereits: lagerstandorte'); return; } throw err; } await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'name', 128, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'locationId', 64, true); await databases.createBooleanAttribute(DATABASE_ID, COLLECTION_ID, 'isActive', false, true); console.log(' Attribute fuer lagerstandorte erstellt (name, locationId, isActive)'); } async function createAssetsCollection() { const COLLECTION_ID = 'assets'; try { await databases.createCollection( DATABASE_ID, COLLECTION_ID, 'Assets', [ Permission.read(Role.users()), Permission.create(Role.users()), Permission.update(Role.users()), Permission.delete(Role.team('admin')), Permission.delete(Role.team('filialleiter')), ] ); console.log('Collection erstellt: assets'); } catch (err) { if (err.code === 409) { console.log('Collection existiert bereits: assets'); return; } throw err; } await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'erlNummer', 64, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'seriennummer', 128, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'artikelNr', 64, false, ''); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'bezeichnung', 256, false, ''); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'defekt', 1024, false, ''); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'lagerstandortId', 64, false, ''); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'zustaendig', 128, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'status', 32, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'prio', 16, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'kommentar', 2048, false, ''); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'createdBy', 128, false, ''); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'lastEditedBy', 128, false, ''); console.log(' Attribute fuer assets erstellt (erlNummer, seriennummer, artikelNr, bezeichnung, defekt, lagerstandortId, zustaendig, status, prio, kommentar, createdBy, lastEditedBy)'); } async function createAuditLogsCollection() { const COLLECTION_ID = 'audit_logs'; try { await databases.createCollection( DATABASE_ID, COLLECTION_ID, 'Audit Logs', [ Permission.read(Role.users()), Permission.create(Role.users()), ] ); console.log('Collection erstellt: audit_logs'); } catch (err) { if (err.code === 409) { console.log('Collection existiert bereits: audit_logs'); return; } throw err; } await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'assetId', 64, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'action', 64, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'details', 2048, false, ''); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'userId', 64, true); await databases.createStringAttribute(DATABASE_ID, COLLECTION_ID, 'userName', 128, true); await new Promise((r) => setTimeout(r, 2000)); try { await databases.createIndex(DATABASE_ID, COLLECTION_ID, 'idx_assetId', 'key', ['assetId'], ['ASC']); console.log(' Index erstellt: idx_assetId'); } catch (err) { if (err.code === 409) console.log(' Index existiert bereits: idx_assetId'); else throw err; } console.log(' Attribute fuer audit_logs erstellt (assetId, action, details, userId, userName)'); } async function createTeams() { for (const role of TEAM_ROLES) { try { await teamsService.create(role, role.charAt(0).toUpperCase() + role.slice(1)); console.log(`Team erstellt: ${role}`); } catch (err) { if (err.code === 409) { console.log(`Team existiert bereits: ${role}`); } else { throw err; } } } } async function createDefaultLocation() { const existing = await databases.listDocuments(DATABASE_ID, 'locations', [Query.limit(1)]); if (existing.documents.length > 0) { console.log(`Filiale existiert bereits: "${existing.documents[0].name}"`); return existing.documents[0].$id; } await new Promise((r) => setTimeout(r, 2000)); const loc = await databases.createDocument(DATABASE_ID, 'locations', ID.unique(), { name: 'Hauptfiliale', address: '', isActive: true, }); console.log(`Filiale erstellt: "Hauptfiliale" (${loc.$id})`); return loc.$id; } async function createAdminUser(defaultLocationId) { const ADMIN_EMAIL = 'admin@defekttrack.local'; const ADMIN_PASSWORD = 'Admin1234!'; const ADMIN_NAME = 'Administrator'; let userId; try { const user = await users.create(ID.unique(), ADMIN_EMAIL, undefined, ADMIN_PASSWORD, ADMIN_NAME); userId = user.$id; console.log(`Admin-User erstellt: ${ADMIN_EMAIL} (ID: ${userId})`); } catch (err) { if (err.code === 409) { console.log(`Admin-User existiert bereits: ${ADMIN_EMAIL}`); const userList = await users.list([Query.equal('email', [ADMIN_EMAIL])]); if (userList.users.length > 0) { userId = userList.users[0].$id; } else { console.log(' Konnte bestehenden Admin nicht finden, ueberspringe Team-Zuordnung.'); return; } } else { throw err; } } try { await teamsService.createMembership('admin', [], ADMIN_EMAIL, userId, undefined, `${ENDPOINT}/auth/confirm`); console.log(' Admin dem Team "admin" hinzugefuegt'); } catch (err) { if (err.code === 409) { console.log(' Admin ist bereits im Team "admin"'); } else { console.warn(' Team-Membership Warnung:', err.message); } } try { await databases.createDocument(DATABASE_ID, 'users_meta', ID.unique(), { userId, locationId: defaultLocationId || '', userName: ADMIN_NAME, role: 'admin', mustChangePassword: false, }); console.log(' users_meta Dokument fuer Admin erstellt'); } catch (err) { if (err.code === 409) { console.log(' users_meta Dokument existiert bereits'); } else { console.warn(' users_meta Warnung:', err.message); } } } async function main() { console.log('=== DefektTrack Appwrite Setup ==='); console.log(`Endpoint: ${ENDPOINT}`); console.log(`Projekt: ${PROJECT_ID}`); console.log(''); await createDatabase(); console.log(''); await createLocationsCollection(); console.log(''); await createUsersMetaCollection(); console.log(''); await createLagerstandorteCollection(); console.log(''); await createAssetsCollection(); console.log(''); await createAuditLogsCollection(); console.log(''); await createTeams(); console.log(''); const defaultLocationId = await createDefaultLocation(); console.log(''); await createAdminUser(defaultLocationId); console.log(''); console.log('=== Setup abgeschlossen ==='); console.log(''); console.log('Admin-Login:'); console.log(' E-Mail: admin@defekttrack.local'); console.log(' Passwort: Admin1234!'); console.log(''); console.log('Vergiss nicht, den API-Key aus .env zu entfernen oder sicher aufzubewahren.'); } main().catch((err) => { console.error('Setup fehlgeschlagen:', err); process.exit(1); });