Merge remote-tracking branch 'origin/main' into feature/oeffnungszeiten
Zusammenführung von TP2-Abschluss und Webshop (origin/main cab9c33) mit Öffnungszeiten live. Beide
Funktionen bleiben vollständig: jeder Konflikt ist als Vereinigung gelöst, Reihenfolge der Funktionsschlüssel
überall news, termine, shop, zeiten.
Konfliktdateien (28): public/app.js, dashboard.html, funktionen.js, widerrufen.html; scripts/stripe-products.mjs;
server/config.js, index.js, middleware/kundenansichtGuard.js, routes/abo.js, routes/admin/funktionenAdmin.js,
routes/funktionen.js, services/customerAdmin.js, customerViewLog.js, featureRegistry.js, retention.js,
siteFeatures.js, stripeBilling.js; test/customerViewLog, featureFreischaltung, featureRegistry, funktionenRoutes,
funktionenTermine, kundenansichtGuard, mailTemplates, publicForms, siteFeatures, stripeProductsScript (je
.test.js) und test/support/schema.js.
Entscheidungen: Mailvorlagen G01–G15, K01–K30, T01–T21 lückenlos (K24–K26 Webshop, K27–K30 Öffnungszeiten);
Stripe-Katalog mit shopSetup und zeitenSetup; Widerruf kennt shop und zeiten; ABO_FUNKTIONEN bleibt analyse
und termine (kein zeiten); activate() schreibt die Zeiten-Felder nur über extra; kein Halter von
feature-activate:<id> ruft disable() auf; die Aufbewahrungs-Löschung löscht Zeiten-Daten vor dem Abschalten und
hält den Kunden bei einem Fehler (held); die Admin-Antwort trägt ersatzTicket und warnung; Hash-Routen webshop
und zeiten, Ansichten view-webshop und view-zeiten; assertOwnProject behält die Meldung von main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
e87140e5ba
245 files changed
+38799
-1050
No files matched your search
@@ -804,7 +804,75 @@ async function runDeployViaRunner(repoFullName, branch, previewConfig, { project
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gehoert `subdomain` schon einem ANDEREN Projekt? Ein kopiertes Repo (git clone + push, Vorlage)
|
||||
* bringt die .webklar-preview.json des Originals mit - ohne diese Pruefung ueberschrieb sein erster
|
||||
* Push die Preview des Originals (Olzinger -> HSK-Vorlage, 04.10.2026). Ein in Gitea umbenanntes
|
||||
* oder umgezogenes Repo ist nicht fremd: Gitea leitet den alten Namen auf `repoFullName` weiter.
|
||||
* Ein Projekt ohne Repo (z. B. die Relaunch-Vorschau) haelt seine Subdomain ebenfalls.
|
||||
* @returns {Promise<string>} Repo-Name bzw. Projekt des fremden Besitzers, sonst ''
|
||||
*/
|
||||
export async function foreignSubdomainOwner(repoFullName, subdomain) {
|
||||
const own = String(repoFullName || '').toLowerCase()
|
||||
// ohne Repo gibt es nichts zu klonen - und ein Projekt ohne Repo sperrt sich nicht selbst
|
||||
if (!subdomain || !own) return ''
|
||||
const docs = await listDocuments(config.collections.websiteProjects, [
|
||||
Query.equal('subdomain', subdomain),
|
||||
Query.limit(5),
|
||||
])
|
||||
for (const doc of docs) {
|
||||
const other = String(doc.repoFullName || '')
|
||||
if (!other) return `Projekt „${doc.projectName || doc.$id}“ (ohne Repo)`
|
||||
if (other.toLowerCase() === own) continue
|
||||
let resolved = ''
|
||||
try {
|
||||
resolved = String((await giteaFetch(`/repos/${other}`))?.full_name || '')
|
||||
} catch {
|
||||
// geloescht oder Gitea nicht erreichbar: im Zweifel fremd
|
||||
}
|
||||
if (resolved.toLowerCase() !== own) return other
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Ergebnis fuer einen gesperrten Deploy oder null. `failOpen`: ist die Pruefung selbst nicht
|
||||
* moeglich (Appwrite weg), trotzdem deployen - so nutzt es runDeploy; der Webhook prueft vorher
|
||||
* streng, und die WOMS-Wege lesen das Projekt ohnehin zuerst aus Appwrite.
|
||||
*/
|
||||
export async function foreignSubdomainBlock(repoFullName, subdomain, { failOpen = false } = {}) {
|
||||
let owner
|
||||
try {
|
||||
owner = await foreignSubdomainOwner(repoFullName, subdomain)
|
||||
} catch (err) {
|
||||
if (!failOpen) throw err
|
||||
console.error(`[previewDeploy] ${repoFullName}: Besitzer der Subdomain "${subdomain}" nicht pruefbar, deploye trotzdem:`, err.message)
|
||||
return null
|
||||
}
|
||||
if (!owner) return null
|
||||
console.warn(`[previewDeploy] ${repoFullName}: Subdomain "${subdomain}" gehoert ${owner} - kein Deploy`)
|
||||
return { deployed: false, blocked: true, reason: 'subdomain_foreign', subdomain, owner }
|
||||
}
|
||||
|
||||
/**
|
||||
* Muss runDeploy den Besitzer der Subdomain pruefen? Nicht, wenn der Aufrufer es schon getan hat
|
||||
* (`subdomainChecked`, der Webhook) oder wenn es die schon vergebene Subdomain des uebergebenen
|
||||
* Projekts selbst ist.
|
||||
*/
|
||||
function needsOwnerCheck(repoFullName, subdomain, { project, subdomainChecked } = {}) {
|
||||
if (subdomainChecked) return false
|
||||
const same = (a, b) => String(a || '').toLowerCase() === String(b || '').toLowerCase()
|
||||
return !(project?.subdomain && same(project.subdomain, subdomain) && same(project.repoFullName, repoFullName))
|
||||
}
|
||||
|
||||
export async function runDeploy(repoFullName, branch, previewConfig, options = {}) {
|
||||
// Vor dem Runner pruefen: ein gesperrter Aufruf belegt dort keinen Platz und laesst so den
|
||||
// Verfolger eines laufenden Baus desselben Repos in Ruhe.
|
||||
const target = previewConfig.subdomain || repoFullName.split('/').pop()
|
||||
if (needsOwnerCheck(repoFullName, target, options)) {
|
||||
const blocked = await foreignSubdomainBlock(repoFullName, target, { failOpen: true })
|
||||
if (blocked) return blocked
|
||||
}
|
||||
if (config.previewRunner.enabled) {
|
||||
return runDeployViaRunner(repoFullName, branch, previewConfig, options)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user