Files
ClaudeandClaude Opus 5.5 08d837d690 Shop Review B I-6, I-7: Archiv der bestaetigten Rechtstexte, G09 ohne Link-Ersatz
I-6: Neue Collection shopRechtstexteArchiv (ID r + 35 Hex aus sha256(shopId + hash)); das
Bestaetigen legt die Fassung samt Versand-/Zahlungstext und Gewaehrleistungstext ab, G09 zeigt
die Fassung zum rechtstexteHash der Bestellung (Rueckfall mit Warnung auf die aktuell
bestaetigte), loescheShopDaten loescht das Archiv mit.

I-7: Grenzen AGB 20000, Belehrung 12000, Formular 3000 plus Pruefung der maskierten Laenge beim
Speichern; G09 ersetzt die AGB nie mehr durch einen Link, sondern scheitert (Marker bleibt
offen, wird wiederholt). Test mit Texten in Hoechstlaenge gegen die Grenzen der Warteschlange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:58:31 +00:00

61 lines
2.8 KiB
JavaScript

import { createHash } from 'node:crypto'
import { config } from '../../config.js'
import { createDocument, getDocument } from '../appwriteAdmin.js'
/**
* Archiv der bestaetigten Rechtstexte eines Shops (Review B, I-6). Beim Bestaetigen wird die Fassung samt Hash abgelegt; G09 zeigt dem
* Kaeufer die Fassung zum Hash seiner Bestellung (bestellung.rechtstexteHash), nicht die beim Mailversand aktuelle. So passen Mail und
* Bestellung zusammen, und der Haendler kann spaeter belegen, welche Belehrung ein Kaeufer bekam (§ 356 Abs. 3 BGB).
* Eine Fassung je Dokument, ID = 'r' + die ersten 35 Hex-Zeichen von sha256(shopId + hash); dieselbe Fassung ein zweites Mal
* (409) ist kein Fehler. Inhalt (texte, JSON): agb, widerrufsbelehrung, widerrufsformular, impressumUrl, datenschutzUrl,
* versandUndZahlung, gewaehrleistung.
*/
const col = () => config.collections.shopRechtstexteArchiv
/** Dokument-ID der Fassung (hoechstens 36 Zeichen, nur Hex nach dem 'r'). */
export const archivId = (shopId, hash) => `r${createHash('sha256').update(`${String(shopId)}${String(hash)}`).digest('hex').slice(0, 35)}`
/**
* Legt die Fassung an, falls es sie noch nicht gibt.
* @param {{ shopId: string, customerId: string, hash: string, texte: object, at: string, von: string }} p
* @returns {Promise<{ angelegt: boolean }>}
*/
export async function archiviereRechtstexte({ shopId, customerId, hash, texte, at, von }) {
if (typeof shopId !== 'string' || shopId === '' || typeof hash !== 'string' || !/^[0-9a-f]{64}$/.test(hash)) throw new TypeError('archiviereRechtstexte: shopId oder hash fehlt')
try {
await createDocument(
col(),
{ shopId, customerId: String(customerId ?? ''), hash, texte: JSON.stringify(texte), bestaetigtAt: at, bestaetigtVon: String(von ?? '').slice(0, 255) },
archivId(shopId, hash),
)
return { angelegt: true }
} catch (err) {
if (err?.status === 409) return { angelegt: false }
throw err
}
}
/**
* Die archivierte Fassung zum Hash oder null, wenn es sie nicht gibt (zum Beispiel bei Bestellungen vor der Einfuehrung des Archivs).
* Andere Fehler (Datenbank) werfen, damit G09 nicht mit falschen Texten rausgeht, sondern wiederholt wird.
* @returns {Promise<object|null>}
*/
export async function holeArchivierteRechtstexte(shopId, hash) {
if (typeof shopId !== 'string' || shopId === '' || typeof hash !== 'string' || hash === '') return null
let doc
try {
doc = await getDocument(col(), archivId(shopId, hash))
} catch (err) {
if (err?.status === 404) return null
throw err
}
if (doc?.shopId !== shopId || doc?.hash !== hash) return null
try {
const texte = JSON.parse(doc.texte)
return texte !== null && typeof texte === 'object' && !Array.isArray(texte) ? texte : null
} catch {
return null
}
}