Projekt-Ablauf im Ticketsystem: Anlegen, Archivieren, Import repariert

- Neues Projekt ohne Subdomain: subdomain null statt '' (Unique-Index
  subdomain_unique, jedes weitere scheiterte mit "Document with the
  requested ID already exists"); ebenso Typ "Internes Projekt"
- Anlegen unter derselben Repo-Sperre wie der Webhook repository/created;
  upsertWebsiteProjectByRepo macht aus einem 409 ein Update
- Gitea-Beschreibung = Anzeigename; Art (kind) beim Anlegen aus dem Ticket
- Reaktivieren setzt status zurueck (vorher blieb 'archived' stehen)
- Import: archivierte Repos als archiviert, Archiv-Stand aus Gitea wird
  gespiegelt, ein fehlerhaftes Repo bricht den Lauf nicht mehr ab
- Abgleich ueberschreibt Projektnamen nicht mehr
- CORS erlaubt PUT (Preview-Variablen speichern)
- Vorlage zieht beim Zuweisen nie in die Org um
- Erinnerungs-Lauf ueberspringt archivierte Projekte
- kaputte Umlaute (U+FFFD) in drei Fehlermeldungen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-04 13:22:53 +00:00
1 parent 3a0b0089dd
commit e43cdb667b
13 files changed
+498 -83

No files matched your search

+32
View File
@@ -63,6 +63,7 @@ export async function startFakeGitea(options = {}) {
let transferDelayMs = 0
let responseDelayMs = 0
let transferNoopNext = false
let onGenerate = null
const pendingFailures = []
let nextRepoId = 1
@@ -229,6 +230,30 @@ export async function startFakeGitea(options = {}) {
send(res, 200, serializeRepo(found.record))
}
/**
* Repo aus Vorlage erzeugen wie Gitea (`POST /repos/{tplOwner}/{tpl}/generate`): 409, wenn es das
* Ziel schon gibt. `setOnGenerate(fn)` ruft fn(repo) nach dem Anlegen auf, BEVOR geantwortet
* wird - so laesst sich der sofort feuernde Webhook `repository/created` nachstellen.
*/
async function handleGenerate(match, searchParams, body, headers, res) {
const owner = body?.owner || ''
const name = body?.name || ''
if (!owner || !name) return send(res, 422, { message: 'owner und name fehlen' })
if (repos.has(keyFrom(owner, name))) {
return send(res, 409, { message: 'The repository with the same name already exists.' })
}
const record = addRepoInternal({ full_name: `${owner}/${name}`, description: body.description || '', private: body.private })
if (onGenerate) await onGenerate(serializeRepo(record))
send(res, 201, serializeRepo(record))
}
function handleGetBranches(match, searchParams, body, headers, res) {
const found = findRepoOrRedirect(match[1], match[2])
if (!found) return send(res, 404, { message: 'Not Found' })
if (found.redirectTo) return sendRepoRedirect(res, found.redirectTo, '/branches')
send(res, 200, [{ name: found.record.default_branch || 'main' }])
}
function handlePatchRepo(match, searchParams, body, headers, res) {
const found = findRepoOrRedirect(match[1], match[2])
if (!found) return send(res, 404, { message: 'Not Found' })
@@ -419,6 +444,8 @@ export async function startFakeGitea(options = {}) {
{ method: 'GET', re: /^\/api\/v1\/repos\/([^/]+)\/([^/]+)\/hooks$/, handler: handleGetRepoHooks },
{ method: 'DELETE', re: /^\/api\/v1\/repos\/([^/]+)\/([^/]+)\/hooks\/([^/]+)$/, handler: handleDeleteRepoHook },
{ method: 'GET', re: /^\/api\/v1\/repos\/([^/]+)\/([^/]+)\/activities\/feeds$/, handler: handleFeeds },
{ method: 'POST', re: /^\/api\/v1\/repos\/([^/]+)\/([^/]+)\/generate$/, handler: handleGenerate },
{ method: 'GET', re: /^\/api\/v1\/repos\/([^/]+)\/([^/]+)\/branches$/, handler: handleGetBranches },
{ method: 'GET', re: /^\/api\/v1\/repos\/([^/]+)\/([^/]+)\/contents\/(.+)$/, handler: handleGetContents },
{ method: 'POST', re: /^\/api\/v1\/repos\/([^/]+)\/([^/]+)\/contents\/(.+)$/, handler: handleWriteContents },
{ method: 'PUT', re: /^\/api\/v1\/repos\/([^/]+)\/([^/]+)\/contents\/(.+)$/, handler: handleWriteContents },
@@ -542,6 +569,10 @@ export async function startFakeGitea(options = {}) {
pendingFailures.push({ method, pathIncludes, status })
}
function setOnGenerate(fn) {
onGenerate = fn || null
}
function close() {
for (const socket of sockets) socket.destroy()
return new Promise((resolve) => server.close(() => resolve()))
@@ -549,6 +580,7 @@ export async function startFakeGitea(options = {}) {
return {
baseUrl,
setOnGenerate,
calls,
repo,
addRepo,