import { Router } from 'express' import { config } from '../config.js' import { getDocument, listDocuments, Query } from '../services/appwriteAdmin.js' import { getSessionCustomerId, requireSession } from '../middleware/session.js' import { getProjectGitInsights, getProjectFileOverview } from '../services/projectInsights.js' import { customerChanges, customerOrder, customerWorkEntries, workTotalMinutes } from '../services/customerProjectView.js' const router = Router() /** * Laedt das Projekt und stellt sicher, dass es dem eingeloggten Kunden gehoert. * Fremde/unbekannte IDs antworten einheitlich mit 404 (kein Enumeration-Leak). */ async function loadOwnProject(req, res, next) { const customerId = getSessionCustomerId(req) const project = await getDocument(config.collections.websiteProjects, req.params.id).catch(() => null) if (!project || !customerId || project.customerId !== customerId) { return res.status(404).json({ error: 'Projekt nicht gefunden' }) } req.project = project return next() } router.get('/', requireSession, async (req, res) => { const customerId = getSessionCustomerId(req) if (!customerId) { return res.status(401).json({ error: 'Nicht angemeldet' }) } try { const projects = await listDocuments(config.collections.websiteProjects, [ Query.equal('customerId', customerId), Query.orderDesc('$createdAt'), ]) const sanitized = projects.map((p) => ({ id: p.$id, projectName: p.projectName || '', subdomain: p.subdomain || '', previewUrl: p.previewUrl || '', liveDomain: p.liveDomain || '', status: p.status || '', provisioningStatus: p.provisioningStatus || '', updatedAt: p.updatedAt || p.$updatedAt || '', })) return res.json({ projects: sanitized }) } catch (err) { return res.status(500).json({ error: err.message || 'Projekte konnten nicht geladen werden' }) } }) /** Letzte Aenderungen in Kundensprache (ohne Commit-Kuerzel, Kontonamen, KI-Hinweise). */ router.get('/:id/git', requireSession, loadOwnProject, async (req, res) => { try { const { commits } = await getProjectGitInsights(req.project) return res.json({ changes: customerChanges(commits) }) } catch (err) { return res.status(500).json({ error: err.message || 'Git-Daten konnten nicht geladen werden' }) } }) /** Datei-Uebersicht (Top-Level, Groessensummen). */ router.get('/:id/files', requireSession, loadOwnProject, async (req, res) => { try { const summary = await getProjectFileOverview(req.project) if (!summary) return res.json({ totalFiles: 0, totalSizeBytes: 0, truncated: false, topLevel: [] }) return res.json(summary) } catch (err) { return res.status(500).json({ error: err.message || 'Datei-Uebersicht konnte nicht geladen werden' }) } }) /** * Auftrag zum Projekt (Status auf Deutsch) und dokumentierte Arbeiten. Git-Pushes ohne * Arbeitszeit stehen schon unter "Letzte Änderungen"; interne Tickets (Akquise) bleiben verborgen. */ router.get('/:id/work', requireSession, loadOwnProject, async (req, res) => { try { const project = req.project const ticket = project.ticketId ? await getDocument(config.collections.workorders, project.ticketId).catch(() => null) : null const order = customerOrder(ticket) if (!order) { return res.json({ order: null, entries: [] }) } // Alle Blaetter, damit "Arbeitszeit gesamt" stimmt; lange Listen klappt der Browser zu (PD_ENTRIES_SHOWN) const worksheets = await listDocuments(config.collections.worksheets, [ Query.equal('workorderId', project.ticketId), Query.orderDesc('$createdAt'), Query.limit(5000), ]).catch(() => []) const entries = customerWorkEntries(worksheets, { includeUpdates: false }) return res.json({ order, entries, totalMinutes: workTotalMinutes(entries) }) } catch (err) { return res.status(500).json({ error: err.message || 'Arbeiten konnten nicht geladen werden' }) } }) export default router