Der Server bereitet Commits, Tickets und Arbeitseinträge für Kunden auf (server/services/customerProjectView.js), die Rohdaten bleiben intern: - keine Co-Authored-By-/Signed-off-by-Zeilen und KI-Hinweise, keine Commit-Kürzel, Kontonamen, Git-Links oder Branch - technische Commits (Preview-Config, Merges) ausgeblendet, bekannte Automatik-Texte übersetzt, "fix"/"upload" -> "Website aktualisiert" - Ticketstatus und Arbeitsarten auf Deutsch (Open -> Eingegangen, Remote -> Online erledigt, ...), Fortschritt in 4 Schritten - Akquise-Tickets auch in den Projektdetails verborgen - /api/projects ohne Gitea-URL, Repo- und Vorlagennamen Darstellung: Auftragskarte mit Status und Fortschrittsleiste, Zeitleiste "Letzte Änderungen" nach Tagen, Arbeiten mit Farbe/Symbol je Art und Arbeitszeit; der Tab "Fortschritt" nutzt dieselben Bausteine. In der App-Ansicht öffnen sich die Projektdetails als eigene Seite mit Zurück zu "Projekte". Tests: npm test (node --test, 22 Fälle). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
1.7 KiB
JavaScript
49 lines
1.7 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',
|
|
// Technische Commits blendet die Kundenansicht aus - etwas mehr holen, damit genug bleiben
|
|
limit: 30,
|
|
})
|
|
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')
|
|
})
|
|
}
|