sync: update from blackroad-operator 2026-03-14
Some checks failed
Autonomous Repo Agent / autonomous-build (push) Has been cancelled
BlackRoad AI Agents / agent-response (push) Has been cancelled
🔍 BlackRoad CodeQL Security Analysis / CodeQL Analysis (javascript) (push) Has been cancelled
🔍 BlackRoad CodeQL Security Analysis / CodeQL Analysis (python) (push) Has been cancelled
CI / Test (push) Has been cancelled
Deploy to Cloudflare Pages / Deploy to Cloudflare Pages (push) Has been cancelled
Trinity Compliance Check / check-compliance (push) Has been cancelled
Some checks failed
Autonomous Repo Agent / autonomous-build (push) Has been cancelled
BlackRoad AI Agents / agent-response (push) Has been cancelled
🔍 BlackRoad CodeQL Security Analysis / CodeQL Analysis (javascript) (push) Has been cancelled
🔍 BlackRoad CodeQL Security Analysis / CodeQL Analysis (python) (push) Has been cancelled
CI / Test (push) Has been cancelled
Deploy to Cloudflare Pages / Deploy to Cloudflare Pages (push) Has been cancelled
Trinity Compliance Check / check-compliance (push) Has been cancelled
Synced from BlackRoad-OS-Inc/blackroad-operator/orgs/core/blackroad-os-web BlackRoad OS — Pave Tomorrow. RoadChain-SHA2048: 13032509284e1f6c RoadChain-Identity: alexa@sovereign RoadChain-Full: 13032509284e1f6ca60f7004aa28e90fdc0fdae165e934d79f9ee91ee80caa9c42b57ad6c0ed9c400d303a39716259ad59602b6bc19ba3ea0720412c7957b64908250e99db1c5debc19331e7d473bb26d0c501cf1f02155ec53315372f62c0a36ca9d67d033e42c4d9683c2220eda4b4f4487eff9e474726e279d738e8a613870d38f5197ee4504b40c95ce73a1df4eb837b18bfce046609b29fbb4a7bdb83501806d25bfaa79be4f46f31b9616511733690a6b2a6257084c264223462161aca13e0608a59f5a0cc55f9835d640a1dde518b15c019a4ba62e8513cbbd58fd436d9e401fa12a1a8c82908b4688359b829c90e76067668e4793638a8d33fb9a77c
This commit is contained in:
@@ -2,85 +2,42 @@ import { NextResponse } from 'next/server';
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
// In-memory store for edge runtime (resets on cold start).
|
||||
// For persistence, bind a KV namespace named WORLDS_KV in wrangler.toml / Vercel env.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
declare const WORLDS_KV: any;
|
||||
|
||||
interface World {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
description: string;
|
||||
lore?: string;
|
||||
tags: string[];
|
||||
agent: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const url = new URL(request.url);
|
||||
const format = url.searchParams.get('format') || 'json';
|
||||
const limit = Math.min(parseInt(url.searchParams.get('limit') || '20'), 100);
|
||||
|
||||
// Try KV first
|
||||
try {
|
||||
if (typeof WORLDS_KV !== 'undefined') {
|
||||
const raw = await WORLDS_KV.get('worlds:index', 'json') as World[] | null;
|
||||
const worlds = (raw ?? []).slice(0, limit);
|
||||
return NextResponse.json({ worlds, total: worlds.length, source: 'kv' }, {
|
||||
headers: { 'Cache-Control': 'public, s-maxage=30, stale-while-revalidate=15' },
|
||||
});
|
||||
}
|
||||
} catch { /* fall through */ }
|
||||
const upstreamUrl =
|
||||
format === 'rss'
|
||||
? 'https://worlds.blackroad.io/rss'
|
||||
: format === 'atom'
|
||||
? 'https://worlds.blackroad.io/atom'
|
||||
: 'https://worlds.blackroad.io/';
|
||||
|
||||
// Fallback: try upstream worlds worker
|
||||
try {
|
||||
const res = await fetch('https://worlds.blackroad.io/', {
|
||||
const res = await fetch(upstreamUrl, {
|
||||
headers: { 'User-Agent': 'blackroad-web/1.0' },
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json() as { total: number; worlds: World[] };
|
||||
return NextResponse.json({ ...data, worlds: data.worlds.slice(0, limit), source: 'upstream' }, {
|
||||
headers: { 'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=30' },
|
||||
} as RequestInit);
|
||||
|
||||
if (!res.ok) throw new Error(`upstream ${res.status}`);
|
||||
|
||||
if (format === 'rss' || format === 'atom') {
|
||||
const text = await res.text();
|
||||
return new Response(text, {
|
||||
headers: {
|
||||
'Content-Type': format === 'rss' ? 'application/rss+xml' : 'application/atom+xml',
|
||||
'Cache-Control': 'public, s-maxage=120, stale-while-revalidate=60',
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch { /* fall through */ }
|
||||
|
||||
return NextResponse.json({ worlds: [], total: 0, source: 'empty' });
|
||||
}
|
||||
const data = await res.json() as { total: number; worlds: unknown[]; generated: string };
|
||||
const sliced = { ...data, worlds: data.worlds.slice(0, limit) };
|
||||
|
||||
export async function POST(request: Request) {
|
||||
let body: Partial<World>;
|
||||
try {
|
||||
body = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });
|
||||
return NextResponse.json(sliced, {
|
||||
headers: { 'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=30' },
|
||||
});
|
||||
} catch (e) {
|
||||
return NextResponse.json({ error: String(e), worlds: [] }, { status: 500 });
|
||||
}
|
||||
|
||||
if (!body.name || !body.description) {
|
||||
return NextResponse.json({ error: 'name and description required' }, { status: 400 });
|
||||
}
|
||||
|
||||
const world: World = {
|
||||
id: `world_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
|
||||
name: String(body.name),
|
||||
type: String(body.type || 'world'),
|
||||
description: String(body.description),
|
||||
lore: body.lore ? String(body.lore) : undefined,
|
||||
tags: Array.isArray(body.tags) ? body.tags : [],
|
||||
agent: String(body.agent || 'lucidia'),
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Persist to KV if available
|
||||
try {
|
||||
if (typeof WORLDS_KV !== 'undefined') {
|
||||
const existing = (await WORLDS_KV.get('worlds:index', 'json') as World[] | null) ?? [];
|
||||
const updated = [world, ...existing].slice(0, 500);
|
||||
await WORLDS_KV.put('worlds:index', JSON.stringify(updated), { expirationTtl: 60 * 60 * 24 * 90 });
|
||||
}
|
||||
} catch { /* KV not bound — world returned but not persisted */ }
|
||||
|
||||
return NextResponse.json(world, { status: 201 });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user