worldwide todo list: public todo list of 68 real, sourced problems (planet to street) at /projects/worldwide-todo. worldtodo schema (categories, tasks, change_requests), idempotent seed on deploy, public add/edit/delete requests with Turnstile + salted daily-rotating IP-hash rate limit, owner-only review queue (approve, edit & approve, reject) with live updates over /ws/worldtodo(-owner), JSON API, detail pages with OSM map and history; listed in projects, header, /start and sitemap
Some checks failed
Deploy production / deploy (push) Failing after 9s

This commit is contained in:
2026-10-26 01:25:59 +00:00
parent 0ee981a96b
commit 5e812271e1
58 changed files with 47689 additions and 9 deletions

View File

@@ -0,0 +1,32 @@
import { TURNSTILE_SECRET_KEY } from '$app/env/private';
import { PUBLIC_TURNSTILE_SITE_KEY } from '$app/env/public';
// Cloudflare Turnstile on the request form, checked server-side through Siteverify.
// Widget "stanrunge.dev worldwide todo list" (managed mode, domain stanrunge.dev).
// Without both keys, request submission is switched off and the form says so.
const SITEVERIFY = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
export const turnstileSiteKey = () => PUBLIC_TURNSTILE_SITE_KEY || '';
export const turnstileConfigured = () => !!(TURNSTILE_SECRET_KEY && PUBLIC_TURNSTILE_SITE_KEY);
export async function verifyTurnstile(
token: string,
fetcher: typeof fetch = fetch
): Promise<boolean> {
if (!turnstileConfigured() || !token || token.length > 2048) return false;
try {
const body = new URLSearchParams({ secret: TURNSTILE_SECRET_KEY, response: token });
const res = await fetcher(SITEVERIFY, {
method: 'POST',
body,
signal: AbortSignal.timeout(8000)
});
if (!res.ok) return false;
const data = (await res.json()) as { success?: boolean };
return data.success === true;
} catch (e) {
console.error('[worldtodo] turnstile siteverify failed', (e as Error).message);
return false;
}
}