a control panel for my server

feat: add redaction

dunkirk.sh e431de6f f7c2ec16

verified
Changed files
+64
src
+29
src/flags.ts
··· 35 35 name: string; 36 36 description: string; 37 37 paths: string[]; // The paths this flag blocks 38 + redact?: Record<string, string[]>; // path -> fields to strip from JSON 38 39 } 39 40 40 41 export interface ServiceDefinition { ··· 147 148 148 149 return false; 149 150 } 151 + 152 + // Get fields to redact from a JSON response based on host and path 153 + export function getRedactions(host: string, path: string): string[] { 154 + const config = getConfig(); 155 + const fields: string[] = []; 156 + 157 + for (const [serviceId, service] of Object.entries(config.services)) { 158 + if (!host.includes(serviceId) && !serviceId.includes(host)) { 159 + continue; 160 + } 161 + 162 + for (const [flagId, flag] of Object.entries(service.flags)) { 163 + if (!getFlagStatus(flagId)) { 164 + continue; 165 + } 166 + 167 + if (flag.redact) { 168 + for (const [redactPath, redactFields] of Object.entries(flag.redact)) { 169 + if (path === redactPath || path.startsWith(redactPath + "?")) { 170 + fields.push(...redactFields); 171 + } 172 + } 173 + } 174 + } 175 + } 176 + 177 + return fields; 178 + }
+35
src/index.ts
··· 19 19 setFlag, 20 20 getFlagDefinition, 21 21 shouldBlock, 22 + getRedactions, 22 23 } from "./flags"; 23 24 24 25 import homepage from "../public/index.html"; ··· 55 56 } 56 57 57 58 return c.text("OK", 200); 59 + }); 60 + 61 + // Proxy JSON endpoint and redact configured fields 62 + app.get("/proxy/*", async (c) => { 63 + const host = c.req.header("X-Orig-Host") || c.req.header("Host") || ""; 64 + const origPath = c.req.header("X-Orig-Path") || ""; 65 + const backendUrl = c.req.header("X-Backend-Url"); 66 + 67 + if (!backendUrl) { 68 + return c.text("Missing X-Backend-Url header", 400); 69 + } 70 + 71 + const res = await fetch(backendUrl); 72 + if (!res.ok) { 73 + return c.text("Backend error", res.status); 74 + } 75 + 76 + const data = await res.json(); 77 + 78 + // Redact fields based on config 79 + const fieldsToRedact = getRedactions(host, origPath); 80 + for (const field of fieldsToRedact) { 81 + if (field in data) { 82 + if (Array.isArray(data[field])) { 83 + data[field] = []; 84 + } else if (typeof data[field] === "object" && data[field] !== null) { 85 + data[field] = {}; 86 + } else { 87 + delete data[field]; 88 + } 89 + } 90 + } 91 + 92 + return c.json(data); 58 93 }); 59 94 60 95 app.get("/auth/login", (c) => {