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>
61 lines
2.8 KiB
JavaScript
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
|
|
}
|
|
}
|