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' 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 || '', templateName: p.templateName || '', giteaRepoUrl: p.giteaRepoUrl || '', repoFullName: p.repoFullName || '', })) return res.json({ projects: sanitized }) } catch (err) { return res.status(500).json({ error: err.message || 'Projekte konnten nicht geladen werden' }) } }) /** Letzte Git-Pushes + Projektgroesse. */ router.get('/:id/git', requireSession, loadOwnProject, async (req, res) => { try { const { repo, commits } = await getProjectGitInsights(req.project) return res.json({ repo, 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' }) } }) /** Ticket-Arbeiten am Projekt (inkl. Git-Push-Worksheets, "Zeit offen" markiert). */ router.get('/:id/work', requireSession, loadOwnProject, async (req, res) => { try { const project = req.project if (!project.ticketId) { return res.json({ ticket: null, worksheets: [] }) } const ticket = await getDocument(config.collections.workorders, project.ticketId).catch(() => null) const worksheets = await listDocuments(config.collections.worksheets, [ Query.equal('workorderId', project.ticketId), Query.orderDesc('$createdAt'), Query.limit(50), ]).catch(() => []) const sanitized = worksheets // GIT-Eintraege einschliessen, sonstige reine Kommentare ausblenden .filter((ws) => ws.serviceType === 'GIT' || !ws.isComment) .map((ws) => ({ wsid: ws.wsid || '', date: ws.endDate || ws.startDate || '', time: ws.endTime || ws.startTime || '', employeeName: ws.employeeName || ws.employeeShort || '', serviceType: ws.serviceType || '', details: ws.details || '', totalTime: Number(ws.totalTime || 0), isGit: ws.serviceType === 'GIT', pending: ws.serviceType === 'GIT' && !ws.startTime, })) return res.json({ ticket: ticket ? { woid: ticket.woid || '', topic: ticket.topic || '', status: ticket.status || '' } : null, worksheets: sanitized, }) } catch (err) { return res.status(500).json({ error: err.message || 'Arbeiten konnten nicht geladen werden' }) } }) export default router