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

62 lines
2.9 KiB
JavaScript

import { config } from '../../config.js'
import { deleteDocument, listDocuments, Query } from '../appwriteAdmin.js'
import { loescheAlleBilder } from './bilder.js'
/**
* Alle Daten EINES Shops loeschen (Plan 2026-10-05-webshop.md, J3): Bestellungen, Artikel, Widerrufe, das Archiv der bestaetigten Rechtstexte, Artikelbilder und zuletzt das
* shopConfig-Dokument. Gemeinsamer Baustein des Tagesjobs (Shop seit 90 Tagen abgeschaltet, jobs.js) und der Loeschfristen
* (retention.js, Kunde faellig). Bewusst ohne Import von bestellungen.js/Stripe: retention.js darf nichts Schweres nachladen.
* Das Stripe-Konto gehoert dem Haendler und wird nie angefasst.
*
* Seitenweise (100 je Abfrage, immer die erste Seite neu, denn gelesene Dokumente sind danach weg). Am Deckel endet der Lauf
* (vollstaendig: false), der naechste setzt fort. Ein Fehler beim Loeschen wirft (sonst wuerde dieselbe Seite endlos neu gelesen).
*/
const SEITE = 100
const GRUPPEN = [
['shopOrders', 'bestellungen'],
['shopProducts', 'artikel'],
['shopWiderrufe', 'widerrufe'],
['shopRechtstexteArchiv', 'archiv'],
]
/**
* @param {{ shopId: string, siteKey?: string, deckel?: number, nochGueltig?: () => Promise<boolean> }} opt
* deckel: hoechstens so viele Dokumente (Bestellungen + Artikel + Widerrufe + Archiv); nochGueltig wird vor jeder Seite gefragt (false = abbrechen)
* @returns {Promise<{ bestellungen: number, artikel: number, widerrufe: number, archiv: number, geloescht: number, konfig: boolean, vollstaendig: boolean, abgebrochen: boolean }>}
* vollstaendig = alle Dokumente, Bilder und das shopConfig-Dokument sind weg
*/
export async function loescheShopDaten({ shopId, siteKey, deckel = Number.POSITIVE_INFINITY, nochGueltig = async () => true }) {
if (typeof shopId !== 'string' || shopId === '') throw new TypeError('loescheShopDaten: shopId fehlt')
const erg = { bestellungen: 0, artikel: 0, widerrufe: 0, archiv: 0, geloescht: 0, konfig: false, vollstaendig: false, abgebrochen: false }
for (const [name, feld] of GRUPPEN) {
const sammlung = config.collections[name]
for (;;) {
if (!(await nochGueltig())) return { ...erg, abgebrochen: true }
const rest = deckel - erg.geloescht
if (rest <= 0) return erg
const seite = await listDocuments(sammlung, [Query.equal('shopId', shopId), Query.limit(Math.min(SEITE, rest))])
if (seite.length === 0) break
for (const doc of seite) {
try {
await deleteDocument(sammlung, doc.$id)
} catch (err) {
if (err?.status !== 404) throw err
}
erg[feld] += 1
erg.geloescht += 1
}
}
}
if (!(await nochGueltig())) return { ...erg, abgebrochen: true }
if (siteKey) await loescheAlleBilder(siteKey)
try {
await deleteDocument(config.collections.shopConfig, shopId)
erg.konfig = true
} catch (err) {
if (err?.status !== 404) throw err
}
erg.vollstaendig = true
return erg
}