DOGFATHER UNIVERSE: erste lauffähige Website + Bewerbungs-Postfach + Hosting-Vorbereitung
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
/* =====================================================================
|
||||
DOGFATHER UNIVERSE — Bewerbungs-Postfach Worker
|
||||
Nimmt Bewerbungen von bewerben.html entgegen, speichert sie in KV,
|
||||
und gibt sie nur nach korrektem Code (serverseitig geprüft, per
|
||||
Secret gespeichert) an postfach.html zurück.
|
||||
|
||||
Endpoints:
|
||||
POST /submit { type: "creator"|"scout"|"kooperation", data: {...} }
|
||||
POST /postfach { code: "..." } -> Liste aller Bewerbungen
|
||||
GET /health -> einfacher Check ob der Worker läuft
|
||||
|
||||
Secrets/Bindings (siehe wrangler.toml + README.md):
|
||||
KV Binding: BEWERBUNGEN (Cloudflare KV Namespace)
|
||||
Secret: POSTFACH_CODE (mit `wrangler secret put POSTFACH_CODE` setzen)
|
||||
===================================================================== */
|
||||
|
||||
const ALLOWED_TYPES = ["creator", "scout", "kooperation"];
|
||||
|
||||
function corsHeaders() {
|
||||
// TODO: sobald die Domain final steht, statt "*" die echte Domain eintragen
|
||||
// (z.B. "https://dogfather-universe.example"), damit nur die eigene Seite
|
||||
// an die API senden kann.
|
||||
return {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type",
|
||||
};
|
||||
}
|
||||
|
||||
function json(data, status = 200) {
|
||||
return new Response(JSON.stringify(data), {
|
||||
status,
|
||||
headers: { "Content-Type": "application/json", ...corsHeaders() },
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSubmit(request, env) {
|
||||
let body;
|
||||
try {
|
||||
body = await request.json();
|
||||
} catch {
|
||||
return json({ ok: false, error: "Ungültiges JSON." }, 400);
|
||||
}
|
||||
|
||||
const { type, data } = body || {};
|
||||
if (!ALLOWED_TYPES.includes(type) || typeof data !== "object" || !data) {
|
||||
return json({ ok: false, error: "Ungültige Bewerbungsdaten." }, 400);
|
||||
}
|
||||
|
||||
// simple Server-seitige Validierung: keine leeren Kernfelder
|
||||
const values = Object.values(data).map((v) => String(v || "").trim());
|
||||
if (values.every((v) => v === "")) {
|
||||
return json({ ok: false, error: "Leere Bewerbung." }, 400);
|
||||
}
|
||||
|
||||
const id = crypto.randomUUID();
|
||||
const timestamp = new Date().toISOString();
|
||||
const key = `sub:${timestamp}:${id}`;
|
||||
|
||||
await env.BEWERBUNGEN.put(
|
||||
key,
|
||||
JSON.stringify({ id, type, data, timestamp })
|
||||
);
|
||||
|
||||
return json({ ok: true });
|
||||
}
|
||||
|
||||
async function handlePostfach(request, env) {
|
||||
let body;
|
||||
try {
|
||||
body = await request.json();
|
||||
} catch {
|
||||
return json({ ok: false, error: "Ungültiges JSON." }, 400);
|
||||
}
|
||||
|
||||
const code = (body && body.code) || "";
|
||||
if (!env.POSTFACH_CODE || code !== env.POSTFACH_CODE) {
|
||||
// bewusst generische Fehlermeldung, kein Hinweis ob Code existiert
|
||||
return json({ ok: false, error: "Falscher Code." }, 401);
|
||||
}
|
||||
|
||||
const list = await env.BEWERBUNGEN.list({ prefix: "sub:", limit: 500 });
|
||||
const items = await Promise.all(
|
||||
list.keys.map(async (k) => {
|
||||
const raw = await env.BEWERBUNGEN.get(k.name);
|
||||
return raw ? JSON.parse(raw) : null;
|
||||
})
|
||||
);
|
||||
|
||||
const bewerbungen = items
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => (a.timestamp < b.timestamp ? 1 : -1)); // neueste zuerst
|
||||
|
||||
return json({ ok: true, bewerbungen });
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (request.method === "OPTIONS") {
|
||||
return new Response(null, { headers: corsHeaders() });
|
||||
}
|
||||
|
||||
if (url.pathname === "/health") {
|
||||
return json({ ok: true, service: "dogfather-universe-postfach" });
|
||||
}
|
||||
|
||||
if (url.pathname === "/submit" && request.method === "POST") {
|
||||
return handleSubmit(request, env);
|
||||
}
|
||||
|
||||
if (url.pathname === "/postfach" && request.method === "POST") {
|
||||
return handlePostfach(request, env);
|
||||
}
|
||||
|
||||
return json({ ok: false, error: "Not found." }, 404);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user