apps/glyph Public The code is public; the app answers only to 1 person who can change it and the crew.

events: emit id and status only, never prompts or SVG

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Pike committed 65c469b Browse files
3 files changed +40 −3
src/actions.ts +1 −1
@@ -149,7 +149,7 @@ function start(
149149 "INSERT INTO drawing (prompt, kind, author_id, author_name) VALUES (?1, ?2, ?3, ?4) RETURNING *",
150150 )
151151 .get(prompt, kind, who.id, who.name)!;
152 emit("drawing.added", drawing);
152 emit("drawing.added", { id: drawing.id, status: drawing.status });
153153 kick(db, drawing.id, prompt, kind);
154154 return ok(drawing);
155155 }
src/draw.ts +2 −2
@@ -55,7 +55,7 @@ export const settled = (): Promise<unknown> => Promise.all([...running]);
5555 /** Fill in drawing `id` in the background. */
5656 export function kick(db: Database, id: number, prompt: string, kind: Kind): void {
5757 const job = (async () => {
58 let row: unknown;
58 let row: { id: number; status: string } | null;
5959 try {
6060 const svgs = await drawer(prompt, kind);
6161 row = db
@@ -67,7 +67,7 @@ export function kick(db: Database, id: number, prompt: string, kind: Kind): void
6767 .query("UPDATE drawing SET status = 'failed', error = ?1, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = ?2 AND status = 'drawing' RETURNING *")
6868 .get(message.slice(0, 500), id);
6969 }
70 if (row !== null) emit("drawing.changed", row);
70 if (row !== null) emit("drawing.changed", { id: row.id, status: row.status });
7171 })().catch((error) => console.error("drawing", id, error));
7272 running.add(job);
7373 void job.finally(() => running.delete(job));
src/server.test.ts +37
@@ -394,3 +394,40 @@ test("what the web expects: robots.txt, and security.txt from the forge that run
394394 );
395395 }
396396 });
397
398 test("another person's prompt and SVG never reach /events, live or replayed", async () => {
399 const secret = "zq-private-prompt-7";
400 const live = await fetch(base + "/events", { headers: as("u2", "bob") });
401 const reader = live.body!.getReader();
402 let heard = "";
403 const pump = (async () => {
404 for (;;) {
405 const { value, done } = await reader.read();
406 if (done) return;
407 heard += new TextDecoder().decode(value);
408 }
409 })();
410 await api(
411 "/api/drawings",
412 { method: "POST", ...jsonBody({ prompt: secret }) },
413 as("u1", "alice"),
414 );
415 await settled();
416 await Bun.sleep(50);
417 const replay = await (async () => {
418 const late = await fetch(base + "/events?since=0", { headers: as("u2", "bob") });
419 const late_reader = late.body!.getReader();
420 const text = new TextDecoder().decode((await late_reader.read()).value);
421 await late_reader.cancel();
422 return text;
423 })();
424 await reader.cancel();
425 await pump;
426 expect(heard).toContain("drawing.changed");
427 expect(replay).toContain("drawing.changed");
428 for (const text of [heard, replay]) {
429 expect(text).not.toContain(secret);
430 expect(text).not.toContain("<svg");
431 expect(text).not.toContain("Test Mark");
432 }
433 });
↑↓ navigate ↵ open esc close

Keyboard shortcuts