Kunden-Dashboard: - Tab-Navigation: Projekte | Was wurde gemacht | Rechnungen | Chat - Projekt-Details pro Projekt: letzte Git-Commits (Titel+Beschreibung), Projektgroesse, Datei-Uebersicht (Top-Level aggregiert), Ticket-Arbeiten - Rechnungen: gestylte Liste mit Status-Pillen, Ansehen/Zahlen-Link, PDF-Download ueber Server-Proxy (IDOR-geschuetzt) - Chat mit dem Webklar-Team (Polling, Ungelesen-Badge, viewAs blockiert) Admin-Dashboard: - Chat-Tab: Konversationsliste + Thread + Antwortfeld, Ungelesen-Badge Backend: - giteaAdmin: getRepoInfo, listRepoCommits, getRepoTreeSummary - projectInsights: 5-min-Cache, Invalidierung per Gitea-Webhook - /api/projects/:id/git|files|work mit Ownership-Check (404) - /api/chat/* (Kunde) + /api/portal-admin/chats/* (Admin), portalMessages-Collection - mailer: E-Mail-Benachrichtigung an Admins bei Kundennachricht (15-min-Throttle) - gitWorksheet: dd.mm.yyyy, voller Commit-Body, startTime leer (Zeit-Nachtrag), Auto-Webpage-Ticket bei Push auf Projekt ohne Ticket (ensureProjectTicket) - customerActivity: Git-Push-Eintraege fuer Kunden sichtbar Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
48 lines
1.6 KiB
JavaScript
48 lines
1.6 KiB
JavaScript
import { getRepoInfo, listRepoCommits, getRepoTreeSummary } from './giteaAdmin.js'
|
|
|
|
// In-Memory-Cache fuer Gitea-Abfragen (Commits/Groesse/Dateibaum) pro Repo.
|
|
// Wird vom Gitea-Webhook nach jedem Push invalidiert.
|
|
const cache = new Map() // key: `${repoFullName}:${kind}` -> { data, expiresAt }
|
|
const TTL_MS = 5 * 60 * 1000
|
|
|
|
async function cached(repoFullName, kind, loader) {
|
|
const key = `${repoFullName}:${kind}`
|
|
const hit = cache.get(key)
|
|
if (hit && hit.expiresAt > Date.now()) return hit.data
|
|
const data = await loader()
|
|
cache.set(key, { data, expiresAt: Date.now() + TTL_MS })
|
|
return data
|
|
}
|
|
|
|
export function invalidateProjectCache(repoFullName) {
|
|
for (const key of cache.keys()) {
|
|
if (key.startsWith(`${repoFullName}:`)) cache.delete(key)
|
|
}
|
|
}
|
|
|
|
/** Repo-Info + letzte Commits fuer die Projekt-Detailansicht im Kundenportal. */
|
|
export async function getProjectGitInsights(project) {
|
|
const repoFullName = project?.repoFullName
|
|
if (!repoFullName) return { repo: null, commits: [] }
|
|
|
|
return cached(repoFullName, 'git', async () => {
|
|
const repo = await getRepoInfo(repoFullName)
|
|
const commits = await listRepoCommits(repoFullName, {
|
|
branch: repo?.defaultBranch || 'main',
|
|
limit: 20,
|
|
})
|
|
return { repo, commits }
|
|
})
|
|
}
|
|
|
|
/** Aggregierte Datei-Uebersicht (Top-Level + Groessensummen). */
|
|
export async function getProjectFileOverview(project) {
|
|
const repoFullName = project?.repoFullName
|
|
if (!repoFullName) return null
|
|
|
|
return cached(repoFullName, 'files', async () => {
|
|
const repo = await getRepoInfo(repoFullName)
|
|
return getRepoTreeSummary(repoFullName, repo?.defaultBranch || 'main')
|
|
})
|
|
}
|