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>
62 lines
2.9 KiB
JavaScript
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
|
|
}
|