import { Handlers } from '$fresh/server.ts' import { DoneTask, DoneTaskModel } from '@homeman/models.ts' import { db, kv } from '@homeman/db.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' const DoneTaskPayload = DoneTaskModel.partial({ doneAt: true }) type DoneTaskPayload = z.infer export const handler: Handlers = { async POST(req, _ctx) { // a task is marked done let id: string | undefined if (req.headers.get('content-type')?.includes('json')) { id = DoneTaskPayload.parse(await req.json()).id } else { id = new URL(req.url).searchParams.get('id') || undefined } console.log('done task post:', id) if (!id) { return new Response( JSON.stringify({ message: "can't complete task with id empty string", }), { status: 400 }, ) } const newDoneTask: DoneTask = { id, doneAt: new Date(), } const res = await db.doneTasks.create({ data: newDoneTask, }) console.log('done task create result:', res) console.log(await db.doneTasks.findMany({})) await kv.set(['last_task_updated'], id) return new Response(JSON.stringify({ id })) }, async DELETE(req, _ctx) { let id: string | undefined if (req.headers.get('content-type')?.includes('json')) { id = DoneTaskPayload.parse(await req.json()).id } else { id = new URL(req.url).searchParams.get('id') || undefined } console.log('done task delete:', id) if (!id) { return new Response( JSON.stringify({ message: "can't uncomplete task with id empty string", }), { status: 400 }, ) } if (!id) { return new Response( JSON.stringify({ message: "can't complete task with id empty string", }), { status: 400 }, ) } await db.doneTasks.deleteMany({ where: { id, }, }) await kv.set(['last_task_updated'], id) return new Response(JSON.stringify({ id })) }, async GET(req, ctx) { // TODO: json or query params const accept = req.headers.get('accept') if (accept === 'text/event-stream') { console.log('Request for task event stream') let skipFirst = true const stream = kv.watch([['last_task_updated']]).getReader() const body = new ReadableStream({ async start(controller) { console.log( `Streaming task updates to ${JSON.stringify(ctx.remoteAddr)}...`, ) while (true) { try { const entries = await stream.read() for (const entry of entries.value || []) { if (skipFirst) { skipFirst = false continue } if (typeof entry.value !== 'string') { continue } const task = await db.doneTask.findFirst({ where: { id: entry.value }, }) const chunk = `data: ${ JSON.stringify({ id: entry.value, versionstamp: entry.versionstamp, value: task, }) }\n\n` console.log('todo event chunk:', chunk) controller.enqueue(new TextEncoder().encode(chunk)) } if (entries.done) { return } } catch (e) { console.error(`Error refreshing todo:`, e) } } }, cancel() { stream.cancel() console.log( `Closed todo updates stream to ${JSON.stringify(ctx.remoteAddr)}`, ) }, }) return new Response(body, { headers: { 'content-type': 'text/event-stream', }, }) } return new Response( JSON.stringify({ done: await db.doneTasks.findMany({}) }), ) }, }