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);
|
||||||
|
});
|
||||||
+8
-1
@@ -1,4 +1,4 @@
|
|||||||
import { Client, Account, Databases, ID, Query } from 'appwrite'
|
import { Client, Account, Databases, Storage, ID, Query } from 'appwrite'
|
||||||
|
|
||||||
/** Ticket-System Appwrite-Projekt (Fallback wenn keine .env beim Build) */
|
/** Ticket-System Appwrite-Projekt (Fallback wenn keine .env beim Build) */
|
||||||
const DEFAULT_PROJECT_ID = '6a1058610003c5a13a05'
|
const DEFAULT_PROJECT_ID = '6a1058610003c5a13a05'
|
||||||
@@ -23,6 +23,7 @@ const client = new Client().setEndpoint(endpoint).setProject(projectId)
|
|||||||
|
|
||||||
export const account = new Account(client)
|
export const account = new Account(client)
|
||||||
export const databases = new Databases(client)
|
export const databases = new Databases(client)
|
||||||
|
export const storage = new Storage(client)
|
||||||
|
|
||||||
export const DATABASE_ID = import.meta.env.VITE_APPWRITE_DATABASE_ID || 'woms-database'
|
export const DATABASE_ID = import.meta.env.VITE_APPWRITE_DATABASE_ID || 'woms-database'
|
||||||
|
|
||||||
@@ -35,8 +36,14 @@ export const COLLECTIONS = {
|
|||||||
LEADS: 'leads',
|
LEADS: 'leads',
|
||||||
LEAD_SETTINGS: 'leadSettings',
|
LEAD_SETTINGS: 'leadSettings',
|
||||||
LEAD_PIPELINE: 'leadPipeline',
|
LEAD_PIPELINE: 'leadPipeline',
|
||||||
|
LEGAL_DOCUMENTS: 'legalDocuments',
|
||||||
|
LEGAL_VERSIONS: 'legalVersions',
|
||||||
|
PROVIDER_CONTRACTS: 'providerContracts',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Dateien im Bereich Rechtliches (Versionen und Anbieter-Vertraege) */
|
||||||
|
export const LEGAL_BUCKET_ID = 'legal-files'
|
||||||
|
|
||||||
/** Prüft ob vermutlich eine Session existiert (cookieFallback oder Same-Origin-Cookie). */
|
/** Prüft ob vermutlich eine Session existiert (cookieFallback oder Same-Origin-Cookie). */
|
||||||
export function hasAppwriteSession() {
|
export function hasAppwriteSession() {
|
||||||
if (typeof window === 'undefined' || !projectId) return false
|
if (typeof window === 'undefined' || !projectId) return false
|
||||||
|
|||||||
Reference in new issue
Block a user