const fs = require("fs"); const path = require("path"); async function createPlugin(ctx) { let activeOwnerEmail = null; return { async execute(action, input) { if (action === "getPolicy") { return getPolicy(ctx, activeOwnerEmail); } if (action === "addPersistentUser") { const email = normalizeEmail(input && input.email); if (!email) { throw new Error("E-Mail fehlt"); } const persistent = await readPersistentUsers(ctx); if (!persistent.includes(email)) { persistent.push(email); persistent.sort(); await writePersistentUsers(ctx, persistent); } await writePolicyFile(ctx, persistent, activeOwnerEmail); return { ok: true, persistentUsers: persistent, ownerEmail: activeOwnerEmail }; } if (action === "removePersistentUser") { const email = normalizeEmail(input && input.email); if (!email) { throw new Error("E-Mail fehlt"); } const persistent = await readPersistentUsers(ctx); const next = persistent.filter((entry) => entry !== email); await writePersistentUsers(ctx, next); await writePolicyFile(ctx, next, activeOwnerEmail); return { ok: true, persistentUsers: next, ownerEmail: activeOwnerEmail }; } if (action === "syncOwner") { activeOwnerEmail = normalizeEmail(input && input.ownerEmail); const persistent = await readPersistentUsers(ctx); await writePolicyFile(ctx, persistent, activeOwnerEmail); return { ok: true, ownerEmail: activeOwnerEmail, persistentUsers: persistent }; } if (action === "clearOwner") { activeOwnerEmail = null; const persistent = await readPersistentUsers(ctx); await writePolicyFile(ctx, persistent, activeOwnerEmail); return { ok: true, ownerEmail: null, persistentUsers: persistent }; } throw new Error(`Unknown action: ${action}`); }, async getStatus() { const policy = await getPolicy(ctx, activeOwnerEmail); return { ok: true, ownerEmail: policy.ownerEmail, persistentCount: policy.persistentUsers.length, effectiveCount: policy.effectiveUsers.length }; }, async health() { return { ok: true }; } }; } async function getPolicy(ctx, ownerEmail) { const persistentUsers = await readPersistentUsers(ctx); const effectiveUsers = buildEffectiveUsers(persistentUsers, ownerEmail); return { ok: true, ownerEmail: ownerEmail || null, persistentUsers, effectiveUsers }; } function buildEffectiveUsers(persistentUsers, ownerEmail) { const set = new Set(persistentUsers); if (ownerEmail) { set.add(ownerEmail); } return Array.from(set).sort(); } async function readPersistentUsers(ctx) { const filePath = persistentFilePath(ctx); if (!filePath || !fs.existsSync(filePath)) { return []; } const raw = await fs.promises.readFile(filePath, "utf8"); const entries = raw .split(/\r?\n/) .map((line) => normalizeEmail(line)) .filter(Boolean); return Array.from(new Set(entries)).sort(); } async function writePersistentUsers(ctx, users) { const filePath = persistentFilePath(ctx); if (!filePath) { return; } await fs.promises.mkdir(path.dirname(filePath), { recursive: true }); const payload = users.length ? `${users.join("\n")}\n` : ""; await fs.promises.writeFile(filePath, payload, "utf8"); } async function writePolicyFile(ctx, persistentUsers, ownerEmail) { const filePath = policyFilePath(ctx); if (!filePath) { return; } const effectiveUsers = buildEffectiveUsers(persistentUsers, ownerEmail); await fs.promises.mkdir(path.dirname(filePath), { recursive: true }); const payload = effectiveUsers.length ? `${effectiveUsers.join("\n")}\n` : ""; await fs.promises.writeFile(filePath, payload, "utf8"); } function policyFilePath(ctx) { return resolvePath(String(ctx.getSetting("policyFilePath", ctx.env.OPENWEBRX_ACCESS_POLICY_FILE || "./data/openwebrx-access-policy.txt"))); } function persistentFilePath(ctx) { return resolvePath(String(ctx.getSetting("persistentFilePath", ctx.env.OPENWEBRX_PERSISTENT_USERS_FILE || "./data/openwebrx-persistent-users.txt"))); } function normalizeEmail(value) { const email = String(value || "").trim().toLowerCase(); if (!email) return ""; if (!email.includes("@")) return ""; return email; } function resolvePath(value) { const trimmed = String(value || "").trim(); if (!trimmed) return ""; if (path.isAbsolute(trimmed)) return trimmed; return path.resolve(process.cwd(), trimmed); } module.exports = { createPlugin };