Rechtliches: Appwrite-Setup-Skript und Konstanten
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0105179e21
commit
1212173cb4
2 files changed
+187
-1
No files matched your search
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* WOMS Rechtliches – Appwrite-Setup (idempotent).
|
||||
* Legt die Sammlungen legalDocuments, legalVersions, providerContracts, den Bucket
|
||||
* legal-files und die festen Rechtstext-Karten an. Versionen und Dateien bekommen
|
||||
* bewusst keine Loeschrechte: das Archiv soll vollstaendig bleiben.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/setup-legal.mjs --dry-run
|
||||
* APPWRITE_API_KEY=... node scripts/setup-legal.mjs
|
||||
*/
|
||||
|
||||
const dryRun = process.argv.includes("--dry-run");
|
||||
const endpoint = process.env.APPWRITE_ENDPOINT || "https://appwrite.webklar.com/v1";
|
||||
const projectId = process.env.APPWRITE_PROJECT_ID || "6a1058610003c5a13a05";
|
||||
const databaseId = process.env.APPWRITE_DATABASE_ID || "woms-database";
|
||||
const apiKey = process.env.APPWRITE_API_KEY;
|
||||
|
||||
const READ_WRITE = ['read("users")', 'create("users")', 'update("users")'];
|
||||
const CRUD = [...READ_WRITE, 'delete("users")'];
|
||||
|
||||
const date = (key) => ({ key, type: "string", size: 10 });
|
||||
|
||||
export const COLLECTIONS = [
|
||||
{
|
||||
id: "legalDocuments",
|
||||
name: "Rechtstexte",
|
||||
perms: READ_WRITE,
|
||||
attrs: [
|
||||
{ key: "key", type: "string", size: 64, required: true },
|
||||
{ key: "title", type: "string", size: 200, required: true },
|
||||
{ key: "liveUrl", type: "string", size: 500 },
|
||||
{ key: "sort", type: "integer", min: 0, max: 1000 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "legalVersions",
|
||||
name: "Rechtstext-Versionen",
|
||||
perms: READ_WRITE,
|
||||
attrs: [
|
||||
{ key: "documentKey", type: "string", size: 64, required: true },
|
||||
{ key: "version", type: "string", size: 32, required: true },
|
||||
date("validFrom"),
|
||||
date("validTo"),
|
||||
{ key: "status", type: "enum", elements: ["draft", "current", "superseded"], required: true },
|
||||
{ key: "changeNote", type: "string", size: 5000 },
|
||||
{ key: "fileIds", type: "string[]", size: 64 },
|
||||
{ key: "source", type: "string", size: 300 },
|
||||
],
|
||||
indexes: [{ key: "idx_documentKey", type: "key", attributes: ["documentKey"] }],
|
||||
},
|
||||
{
|
||||
id: "providerContracts",
|
||||
name: "Anbieter-Verträge",
|
||||
perms: CRUD,
|
||||
attrs: [
|
||||
{ key: "provider", type: "string", size: 200, required: true },
|
||||
{ key: "purpose", type: "string", size: 300 },
|
||||
{ key: "cost", type: "float" },
|
||||
{ key: "costInterval", type: "enum", elements: ["month", "year"] },
|
||||
date("startDate"),
|
||||
{ key: "termMonths", type: "integer", min: 0, max: 1200 },
|
||||
{ key: "renewalMonths", type: "integer", min: 0, max: 1200 },
|
||||
{ key: "noticeValue", type: "integer", min: 0, max: 3650 },
|
||||
{ key: "noticeUnit", type: "enum", elements: ["days", "months"] },
|
||||
date("cancelledAt"),
|
||||
date("endsAt"),
|
||||
{ key: "note", type: "string", size: 5000 },
|
||||
{ key: "fileIds", type: "string[]", size: 64 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const BUCKET = {
|
||||
id: "legal-files",
|
||||
name: "Rechtliches Dateien",
|
||||
perms: ['read("users")', 'create("users")'],
|
||||
maxSize: 20 * 1024 * 1024,
|
||||
extensions: ["pdf", "html", "htm", "png", "jpg", "jpeg", "webp", "doc", "docx"],
|
||||
};
|
||||
|
||||
export const DOCUMENTS = [
|
||||
{ key: "agb", title: "AGB", liveUrl: "https://webklar.com/agb" },
|
||||
{ key: "abo-bedingungen", title: "Abo-Bedingungen", liveUrl: "https://project.webklar.com/abo-bedingungen.html" },
|
||||
{ key: "avv", title: "AVV (Auftragsverarbeitung)", liveUrl: "https://project.webklar.com/abo-bedingungen.html" },
|
||||
{ key: "datenschutz-website", title: "Datenschutz Website", liveUrl: "https://webklar.com/datenschutz" },
|
||||
{ key: "datenschutz-portal", title: "Datenschutz Kundenportal", liveUrl: "https://project.webklar.com/datenschutz.html" },
|
||||
{ key: "impressum", title: "Impressum", liveUrl: "https://webklar.com/impressum" },
|
||||
{ key: "widerruf", title: "Widerrufsbelehrung und -formular", liveUrl: "https://project.webklar.com/widerrufen.html" },
|
||||
].map((d, i) => ({ ...d, sort: i + 1 }));
|
||||
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
async function createAttribute(db, collectionId, a) {
|
||||
const req = Boolean(a.required);
|
||||
switch (a.type) {
|
||||
case "integer":
|
||||
return db.createIntegerAttribute(databaseId, collectionId, a.key, req, a.min, a.max);
|
||||
case "float":
|
||||
return db.createFloatAttribute(databaseId, collectionId, a.key, req);
|
||||
case "enum":
|
||||
return db.createEnumAttribute(databaseId, collectionId, a.key, a.elements, req);
|
||||
case "string[]":
|
||||
return db.createStringAttribute(databaseId, collectionId, a.key, a.size, req, undefined, true);
|
||||
default:
|
||||
return db.createStringAttribute(databaseId, collectionId, a.key, a.size, req);
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
if (dryRun) {
|
||||
for (const c of COLLECTIONS) {
|
||||
console.log(`Sammlung ${c.id} (${c.perms.join(", ")}): ${c.attrs.map((a) => a.key).join(", ")}`);
|
||||
}
|
||||
console.log(`Bucket ${BUCKET.id}: ${BUCKET.extensions.join(", ")}, max ${BUCKET.maxSize} Bytes`);
|
||||
for (const d of DOCUMENTS) console.log(`Rechtstext ${d.key}: ${d.title}`);
|
||||
return;
|
||||
}
|
||||
if (!apiKey) {
|
||||
console.error("APPWRITE_API_KEY required (oder --dry-run)");
|
||||
process.exit(1);
|
||||
}
|
||||
const { Client, Databases, Storage } = await import("node-appwrite");
|
||||
const client = new Client().setEndpoint(endpoint).setProject(projectId).setKey(apiKey);
|
||||
const db = new Databases(client);
|
||||
const storage = new Storage(client);
|
||||
|
||||
for (const c of COLLECTIONS) {
|
||||
try {
|
||||
await db.getCollection(databaseId, c.id);
|
||||
await db.updateCollection(databaseId, c.id, c.name, c.perms, false, true);
|
||||
console.log("Sammlung vorhanden:", c.id);
|
||||
} catch (e) {
|
||||
if (e.code !== 404) throw e;
|
||||
await db.createCollection(databaseId, c.id, c.name, c.perms, false, true);
|
||||
console.log("Sammlung angelegt:", c.id);
|
||||
}
|
||||
const existing = new Set((await db.listAttributes(databaseId, c.id)).attributes.map((a) => a.key));
|
||||
for (const a of c.attrs) {
|
||||
if (existing.has(a.key)) continue;
|
||||
await createAttribute(db, c.id, a);
|
||||
console.log(" Attribut", a.key);
|
||||
await sleep(400);
|
||||
}
|
||||
const indexes = new Set((await db.listIndexes(databaseId, c.id)).indexes.map((i) => i.key));
|
||||
for (const idx of c.indexes || []) {
|
||||
if (indexes.has(idx.key)) continue;
|
||||
await sleep(1500);
|
||||
await db.createIndex(databaseId, c.id, idx.key, idx.type, idx.attributes);
|
||||
console.log(" Index", idx.key);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await storage.getBucket(BUCKET.id);
|
||||
console.log("Bucket vorhanden:", BUCKET.id);
|
||||
} catch (e) {
|
||||
if (e.code !== 404) throw e;
|
||||
await storage.createBucket(BUCKET.id, BUCKET.name, BUCKET.perms, false, true, BUCKET.maxSize, BUCKET.extensions);
|
||||
console.log("Bucket angelegt:", BUCKET.id);
|
||||
}
|
||||
|
||||
await sleep(2000);
|
||||
for (const d of DOCUMENTS) {
|
||||
try {
|
||||
await db.getDocument(databaseId, "legalDocuments", d.key);
|
||||
} catch (e) {
|
||||
if (e.code !== 404) throw e;
|
||||
await db.createDocument(databaseId, "legalDocuments", d.key, d);
|
||||
console.log("Rechtstext angelegt:", d.key);
|
||||
}
|
||||
}
|
||||
console.log("Fertig.");
|
||||
}
|
||||
|
||||
run().catch((e) => {
|
||||
console.error(e.message || e);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in new issue
Block a user