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
@@ -17,6 +17,9 @@ import chatRoutes from './routes/chat.js'
|
||||
import aboRoutes from './routes/abo.js'
|
||||
import funktionenRoutes from './routes/funktionen.js'
|
||||
import termineRoutes from './routes/termine.js'
|
||||
import shopRoutes from './routes/shop.js'
|
||||
import shopWebhookRoutes from './routes/shopWebhook.js'
|
||||
import { shopSeiteRouter } from './routes/shopSeite.js'
|
||||
import zeitenRoutes from './routes/zeiten.js'
|
||||
import takeoverCreditRoutes from './routes/takeoverCredits.js'
|
||||
import { purgeExpiredUploads } from './services/takeoverCredits.js'
|
||||
@@ -26,6 +29,7 @@ import { purgeUnclaimedNewsImages } from './services/newsImages.js'
|
||||
import { runActivation } from './services/siteFeatures.js'
|
||||
import { runAboCheck, runAnalyseReconcile } from './services/analyse.js'
|
||||
import { fuenfMinutenJob, minutenJob, tagesbericht, taeglicherJob } from './services/termine/jobs.js'
|
||||
import { minutenJob as shopMinutenJob, stuendlicherJob as shopStuendlicherJob, taeglicherJob as shopTaeglicherJob } from './services/shop/jobs.js'
|
||||
import { zeitenAboJob, zeitenErinnerungsJob } from './services/zeiten/jobs.js'
|
||||
import giteaWebhookRoutes from './routes/webhook/gitea.js'
|
||||
import pushTimeRoutes from './routes/pushTime.js'
|
||||
@@ -39,6 +43,7 @@ import adminInvoiceClientsRoutes from './routes/admin/invoiceClients.js'
|
||||
import newsRoutes from './routes/news.js'
|
||||
import publicNewsRoutes from './routes/publicNews.js'
|
||||
import publicTermineRoutes from './routes/publicTermine.js'
|
||||
import publicShopRoutes from './routes/publicShop.js'
|
||||
import publicZeitenRoutes from './routes/publicZeiten.js'
|
||||
import adminAnalyseRoutes from './routes/admin/analyse.js'
|
||||
import { legalRouter } from './services/legalDocs.js'
|
||||
@@ -62,6 +67,8 @@ app.use(kundenansichtGuard)
|
||||
// Oeffentliche Schnittstelle des Terminplaners (Gaeste ohne Session): bringt Parser (16 KB) und Fehlerbehandlung mit und
|
||||
// haengt deshalb VOR dem globalen Parser (2 MB), der ihre Rumpfe sonst zuerst lesen wuerde
|
||||
app.use('/api/public/termine/v1', publicTermineRoutes)
|
||||
// Oeffentliche Schnittstelle des Webshops (Kaeufer ohne Session): wie die des Terminplaners mit eigenem Parser (16 KB), daher ebenfalls VOR dem globalen Parser
|
||||
app.use('/api/public/shop/v1', publicShopRoutes)
|
||||
// Uebernahmegutschrift: Nachweise kommen als Base64 im JSON (max. 10 MB Dateien) -> eigenes Limit,
|
||||
// muss vor dem globalen Parser stehen (der ueberspringt bereits geparste Bodies). Login-Pruefung
|
||||
// zuerst, damit niemand ohne Session grosse Bodies parsen laesst.
|
||||
@@ -83,6 +90,9 @@ app.use('/api/abo/takeover-credits', takeoverCreditRoutes)
|
||||
app.use('/api/abo', aboRoutes)
|
||||
app.use('/api/funktionen', funktionenRoutes)
|
||||
app.use('/api/termine', termineRoutes)
|
||||
// Stripe-Connect-Webhook des Webshops (ohne Sitzung): vor dem Router, braucht rawBody des globalen Parsers
|
||||
app.use('/api/shop/stripe-webhook', shopWebhookRoutes)
|
||||
app.use('/api/shop', shopRoutes)
|
||||
app.use('/api/zeiten', zeitenRoutes)
|
||||
app.use('/api/team', teamRoutes)
|
||||
app.use('/api/admin/gitea', adminGiteaRoutes)
|
||||
@@ -117,6 +127,8 @@ app.use('/embed/', (_req, res, next) => {
|
||||
})
|
||||
// Adressen ohne Fassung: aktuelle Fassung der Rechtstexte (Analyse und Terminplaner; vor express.static)
|
||||
app.use('/rechtliches', legalRouter())
|
||||
// Webshop-Seiten fuer Kaeufer: /shop/:siteKey und /shop/:siteKey/widerruf (vor express.static)
|
||||
app.use(shopSeiteRouter())
|
||||
// Abgeschaltetes Admin-Panel und Rechnungsanalyse: Links aus alten Team-Mails ins Ticketsystem
|
||||
app.use('/admin.html', legacyAdminRedirect.admin)
|
||||
app.use('/kalkulation.html', legacyAdminRedirect.kalkulation)
|
||||
@@ -225,6 +237,29 @@ const server = app.listen(config.port, '0.0.0.0', () => {
|
||||
setTimeout(termineTagesJob, 180 * 1000).unref()
|
||||
setInterval(termineTagesJob, 24 * 60 * 60 * 1000).unref()
|
||||
|
||||
// Webshop: jede Minute offene Bestellungen mit verstrichener Frist bei Stripe abgleichen (75 s nach dem Start beginnend), stuendlich nie
|
||||
// beanspruchte Artikelbilder aufraeumen (130 s), taeglich Kaeuferdaten anonymisieren, unbezahlte Bestellungen und lange abgeschaltete
|
||||
// Shops loeschen (200 s). Bei SHOP_ENABLED=off kehren Minuten- und Stundenjob sofort zurueck; der Tagesjob laeuft dann nur, wenn es
|
||||
// noch Shop-Dokumente gibt. Die Jobs melden Einzelfehler selbst (ohne Kaeuferdaten); hier faengt der Rahmen alles Unerwartete ab.
|
||||
const shopMinuten = () => shopMinutenJob(new Date()).catch((err) => console.warn('[shop] Minutenjob fehlgeschlagen:', err.message))
|
||||
setTimeout(() => {
|
||||
shopMinuten()
|
||||
setInterval(shopMinuten, 60 * 1000).unref()
|
||||
}, 75 * 1000).unref()
|
||||
const shopStunden = () => shopStuendlicherJob(new Date()).catch((err) => console.warn('[shop] Stundenjob fehlgeschlagen:', err.message))
|
||||
setTimeout(() => {
|
||||
shopStunden()
|
||||
setInterval(shopStunden, 60 * 60 * 1000).unref()
|
||||
}, 130 * 1000).unref()
|
||||
const shopTages = () =>
|
||||
shopTaeglicherJob(new Date())
|
||||
.then((r) => r && !r.aus && !r.uebersprungen && console.log(`[shop] Tagesjob: ${JSON.stringify(r)}`))
|
||||
.catch((err) => console.warn('[shop] Tagesjob fehlgeschlagen:', err.message))
|
||||
setTimeout(() => {
|
||||
shopTages()
|
||||
setInterval(shopTages, 24 * 60 * 60 * 1000).unref()
|
||||
}, 200 * 1000).unref()
|
||||
|
||||
// Oeffnungszeiten live: taeglich der Abo-Abgleich (4 Minuten nach dem Start beginnend; laeuft unabhaengig vom Schalter ZEITEN_ENABLED,
|
||||
// ohne Zeiten-Dokumente kostet er eine Abfrage) und stuendlich die Feiertags-Erinnerung (5 Minuten nach dem Start beginnend; nur bei
|
||||
// Schalter pilot/on und nur 08:00 bis 20:00 Berliner Zeit, sonst kehrt jeder Lauf sofort zurueck). Beide Jobs ueberlappen nicht und
|
||||
|
||||
Reference in new issue
Block a user