Funktionen: Stripe-Sitzungs-ID bis 128 Zeichen, keine Rohmeldungen beim Bestellen
Live-Fehler: cs_live-Sitzungs-IDs (~66 Zeichen) passten nicht in das Feld portalFeatures.stripeSessionId (64), die Bestellung brach nach dem Anlegen der Stripe-Sitzung ab und der Kunde sah die Appwrite-Meldung. - Setup-Skript: Feld 128 Zeichen, bestehendes Feld wird per ensureStringSize vergroessert - Fehler von Appwrite oder unerwartete Fehler: Log + verstaendlicher Hinweis Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4b7f73abba
commit
b45108c1de
4 files changed
+54
-7
No files matched your search
@@ -63,6 +63,24 @@ async function waitForAttributes(collectionId, keys, maxSec = 60) {
|
|||||||
|
|
||||||
const col = (id) => `/databases/${databaseId}/collections/${id}`
|
const col = (id) => `/databases/${databaseId}/collections/${id}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vergroessert ein bestehendes String-Attribut, falls es kleiner als size ist (ensureCollection
|
||||||
|
* ueberspringt vorhandene Attribute und prueft ihre Groesse nicht). Verkleinern tut es nie.
|
||||||
|
*/
|
||||||
|
async function ensureStringSize(collectionId, key, size) {
|
||||||
|
const attr = await aw(`${col(collectionId)}/attributes/${key}`)
|
||||||
|
if (attr.size >= size) {
|
||||||
|
console.log(`= ${collectionId}.${key} Groesse ${attr.size} (ok)`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await aw(`${col(collectionId)}/attributes/string/${key}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
body: { required: Boolean(attr.required), default: attr.default ?? null, size },
|
||||||
|
})
|
||||||
|
await waitForAttributes(collectionId, [key])
|
||||||
|
console.log(`+ ${collectionId}.${key} Groesse ${attr.size} -> ${size}`)
|
||||||
|
}
|
||||||
|
|
||||||
async function ensureCollection(id, name, attributes, indexes) {
|
async function ensureCollection(id, name, attributes, indexes) {
|
||||||
// Keine Dokument-Permissions: Zugriff ausschliesslich ueber den Server-API-Key
|
// Keine Dokument-Permissions: Zugriff ausschliesslich ueber den Server-API-Key
|
||||||
await ensure(`Collection ${id}`, () =>
|
await ensure(`Collection ${id}`, () =>
|
||||||
@@ -260,7 +278,7 @@ await ensureCollection(
|
|||||||
{ type: 'string', key: 'state', size: 16 },
|
{ type: 'string', key: 'state', size: 16 },
|
||||||
{ type: 'string', key: 'siteKey', size: 16 },
|
{ type: 'string', key: 'siteKey', size: 16 },
|
||||||
{ type: 'string', key: 'ticketId', size: 64 },
|
{ type: 'string', key: 'ticketId', size: 64 },
|
||||||
{ type: 'string', key: 'stripeSessionId', size: 64 },
|
{ type: 'string', key: 'stripeSessionId', size: 128 },
|
||||||
{ type: 'string', key: 'stripeInvoiceId', size: 64 },
|
{ type: 'string', key: 'stripeInvoiceId', size: 64 },
|
||||||
{ type: 'string', key: 'invoiceUrl', size: 500 },
|
{ type: 'string', key: 'invoiceUrl', size: 500 },
|
||||||
{ type: 'datetime', key: 'orderedAt' },
|
{ type: 'datetime', key: 'orderedAt' },
|
||||||
@@ -279,6 +297,8 @@ await ensureCollection(
|
|||||||
{ key: 'idx_session', type: 'key', attributes: ['stripeSessionId'] },
|
{ key: 'idx_session', type: 'key', attributes: ['stripeSessionId'] },
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
// Live-Fehler 30.09.2026: cs_live-Sitzungs-IDs (~66 Zeichen) passten nicht in 64
|
||||||
|
await ensureStringSize('portalFeatures', 'stripeSessionId', 128)
|
||||||
|
|
||||||
// News-Pinnwand: Beitraege je Projekt
|
// News-Pinnwand: Beitraege je Projekt
|
||||||
await ensureCollection(
|
await ensureCollection(
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import {
|
|||||||
DISABLED_MESSAGE,
|
DISABLED_MESSAGE,
|
||||||
assertOwnProject,
|
assertOwnProject,
|
||||||
backfillInvoiceLinks,
|
backfillInvoiceLinks,
|
||||||
getFeatureDoc, listCustomerFeatures,
|
getFeatureDoc,
|
||||||
|
listCustomerFeatures,
|
||||||
markOrdered,
|
markOrdered,
|
||||||
runActivation,
|
runActivation,
|
||||||
} from '../services/siteFeatures.js'
|
} from '../services/siteFeatures.js'
|
||||||
@@ -16,9 +17,13 @@ import {
|
|||||||
const router = Router()
|
const router = Router()
|
||||||
const DAY_MS = 24 * 60 * 60 * 1000
|
const DAY_MS = 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
function fail(res, err) {
|
// Eigene Fehler (httpError) tragen einen Text fuer den Kunden. Fehler von Appwrite (err.type) oder
|
||||||
const status = err.status >= 400 && err.status < 600 ? err.status : 500
|
// unerwartete Fehler werden nur geloggt; der Kunde sieht einen verstaendlichen Hinweis statt der Rohmeldung.
|
||||||
return res.status(status).json({ error: err.message || 'Interner Serverfehler' })
|
function fail(res, err, fallback) {
|
||||||
|
const own = err.status >= 400 && err.status < 500 && !err.type
|
||||||
|
if (own) return res.status(err.status).json({ error: err.message })
|
||||||
|
console.error('[funktionen]', err.status || '', err.type || '', err.message)
|
||||||
|
return res.status(500).json({ error: fallback })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zustaende der Funktionen je Projekt; geschlossene Einrichtungs-Tickets dieses Kunden werden vorher freigeschaltet
|
// Zustaende der Funktionen je Projekt; geschlossene Einrichtungs-Tickets dieses Kunden werden vorher freigeschaltet
|
||||||
@@ -34,7 +39,7 @@ router.get('/', requireSession, async (req, res) => {
|
|||||||
consentText: config.funktionen.consentText,
|
consentText: config.funktionen.consentText,
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
fail(res, err)
|
fail(res, err, 'Die Funktionen konnten gerade nicht geladen werden. Bitte lade die Seite gleich noch einmal.')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -101,7 +106,7 @@ router.post('/news/checkout', requireSession, async (req, res) => {
|
|||||||
})
|
})
|
||||||
res.json({ url: session.url })
|
res.json({ url: session.url })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
fail(res, err)
|
fail(res, err, 'Die Bestellung konnte gerade nicht gestartet werden. Bitte versuch es später noch einmal oder schreib uns im Chat.')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -395,3 +395,13 @@ test('ticket_details_nennen_die_anleitung', () => {
|
|||||||
const doc = [...aw.docs('workorders').values()].find((t) => String(t.topic).startsWith('News-Pinnwand einbauen'))
|
const doc = [...aw.docs('workorders').values()].find((t) => String(t.topic).startsWith('News-Pinnwand einbauen'))
|
||||||
assert.ok(doc.details.includes('Anleitung: docs/funktionen/news-einbau.md (Repo Webklar-Kundenbereich)'))
|
assert.ok(doc.details.includes('Anleitung: docs/funktionen/news-einbau.md (Repo Webklar-Kundenbereich)'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Live-Fehler 30.09.2026: Appwrite lehnte die (zu lange) Sitzungs-ID ab, der Kunde sah die rohe Meldung
|
||||||
|
test('checkout_datenbankfehler_ohne_rohe_meldung', async () => {
|
||||||
|
aw.seed('websiteProjects', { $id: 'p-db', customerId: 'c1', projectName: 'Eisdiele' })
|
||||||
|
aw.failNext({ collection: 'portalFeatures', method: 'PATCH', status: 400, message: 'Invalid document structure: Attribute "stripeSessionId" has invalid type.' })
|
||||||
|
const res = await checkout('p-db')
|
||||||
|
assert.equal(res.status, 500, JSON.stringify(res.body))
|
||||||
|
assert.doesNotMatch(res.body.error, /Invalid document structure|stripeSessionId/)
|
||||||
|
assert.match(res.body.error, /Bestellung konnte gerade nicht gestartet werden/)
|
||||||
|
})
|
||||||
@@ -299,3 +299,15 @@ test('schema_funktionen_news', async () => {
|
|||||||
assert.ok(news.includes(k), `newsPosts.${k}`)
|
assert.ok(news.includes(k), `newsPosts.${k}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Stripe-Checkout-IDs im Live-Modus sind ~66 Zeichen lang (cs_live_ + 58); 64 war zu knapp
|
||||||
|
test('schema_stripe_session_id_lang_genug', async () => {
|
||||||
|
const { readFileSync } = await import('node:fs')
|
||||||
|
const src = readFileSync(new URL('../scripts/setup-abo-collections.mjs', import.meta.url), 'utf8')
|
||||||
|
const block = src.slice(src.indexOf("'portalFeatures',"))
|
||||||
|
const m = block.match(/key: 'stripeSessionId', size: (\d+)/)
|
||||||
|
assert.ok(m, 'stripeSessionId im portalFeatures-Block')
|
||||||
|
assert.ok(Number(m[1]) >= 128, `stripeSessionId size ${m[1]} < 128`)
|
||||||
|
// bestehendes Feld in Produktion wird per Groessen-Update angehoben
|
||||||
|
assert.match(src, /ensureStringSize\('portalFeatures', 'stripeSessionId', 128\)/)
|
||||||
|
})
|
||||||
Reference in new issue
Block a user