Gitea-Client herausgelöst, Konfiguration für Organisation und System-Konto, Test-Nachbauten

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-30 10:21:34 +00:00
1 parent ea71b3f1c3
commit 531ee2a02b
6 files changed
+824 -47

No files matched your search

+14
View File
@@ -235,6 +235,20 @@ export const config = {
templateOwner: process.env.GITEA_TEMPLATE_OWNER || 'knso',
templateRepo: process.env.GITEA_TEMPLATE_REPO || 'webklar-preview-template',
defaultOwner: process.env.GITEA_DEFAULT_OWNER || 'knso',
// Ziel-Organisation des Umzugs (siehe docs/superpowers/plans/2026-09-27-gitea-organisation-1-kundenportal.md)
org: process.env.GITEA_ORG || 'WEBklar',
// Login des System-Kontos, das automatische Commits/Pushes ausfuehrt (kein Arbeitsblatt dafuer)
systemUser: process.env.GITEA_SYSTEM_USER || 'webklar-system',
// Interne Repos, die trotz Zuweisung nicht nach GITEA_ORG umziehen
keepInPlace: (
process.env.GITEA_KEEP_IN_PLACE ||
'ANDJ/Emailsorter,ANDJ/Steuerungstechnik,JUSN/tickte-system,JUSN/Webklar,knso/eship,knso/Webklar-Kundenbereich'
)
.split(',')
.map((s) => s.trim())
.filter(Boolean),
// Beim Zuweisen eines Projekts werden Pushes der letzten so-vielen Tage nachgetragen
backfillDays: Number(process.env.GITEA_BACKFILL_DAYS) || 30,
},
cors: {
+2 -47
View File
@@ -13,53 +13,8 @@ import {
syncPreviewTls,
fetchPreviewConfig,
} from './previewDeploy.js'
function giteaHeaders() {
if (!config.gitea.apiToken) {
throw new Error('GITEA_API_TOKEN fehlt in .env')
}
return {
Authorization: `token ${config.gitea.apiToken}`,
'Content-Type': 'application/json',
}
}
async function giteaFetch(path, options = {}) {
const response = await fetch(`${config.gitea.baseUrl}/api/v1${path}`, {
...options,
headers: { ...giteaHeaders(), ...options.headers },
})
const text = await response.text()
let data = null
if (text) {
try {
data = JSON.parse(text)
} catch {
data = { message: text }
}
}
if (!response.ok) {
let message = data?.message || `Gitea ${response.status}`
if (response.status === 409 && /same name already exists/i.test(message)) {
message = 'Ein Gitea-Repository mit diesem Namen existiert bereits. Bitte einen anderen Subdomain-/Repo-Namen w�hlen.'
}
const error = new Error(message)
error.status = response.status
throw error
}
return data
}
function slugify(value) {
return String(value || '')
.toLowerCase()
.replace(/[^a-z0-9-]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 100)
}
import { giteaFetch } from './giteaClient.js'
import { slugify } from './slug.js'
export async function listAllAccessibleRepos() {
const repos = []
+57
View File
@@ -0,0 +1,57 @@
import { config } from '../config.js'
/**
* Baut die Authorization-/Content-Type-Header fuer einen Gitea-API-Aufruf.
* Wirft, wenn GITEA_API_TOKEN nicht gesetzt ist.
*/
function giteaHeaders() {
if (!config.gitea.apiToken) {
throw new Error('GITEA_API_TOKEN fehlt in .env')
}
return {
Authorization: `token ${config.gitea.apiToken}`,
'Content-Type': 'application/json',
}
}
/**
* Ruft `${config.gitea.baseUrl}/api/v1<path>` auf und liefert die geparste JSON-Antwort
* (oder null ohne Body). Eine Nicht-2xx-Antwort wirft eine Error mit .status; ein 409
* "same name already exists" wird in die deutsche Meldung uebersetzt (Subdomain-/Repo-Name).
*/
export async function giteaFetch(path, options = {}) {
const response = await fetch(`${config.gitea.baseUrl}/api/v1${path}`, {
...options,
headers: { ...giteaHeaders(), ...options.headers },
})
const text = await response.text()
let data = null
if (text) {
try {
data = JSON.parse(text)
} catch {
data = { message: text }
}
}
if (!response.ok) {
let message = data?.message || `Gitea ${response.status}`
if (response.status === 409 && /same name already exists/i.test(message)) {
message = 'Ein Gitea-Repository mit diesem Namen existiert bereits. Bitte einen anderen Subdomain-/Repo-Namen w�hlen.'
}
const error = new Error(message)
error.status = response.status
throw error
}
return data
}
/**
* Zerlegt "owner/repo" in [owner, repo]. Reines String-Splitting ohne Validierung -
* fuer nicht "owner/repo"-foermige Eingaben ist das Ergebnis entsprechend kurz/lang.
*/
export function splitRepo(fullName) {
return String(fullName || '').split('/')
}