diff --git a/fresh.gen.ts b/fresh.gen.ts index 33cd131..8b674f6 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -5,7 +5,7 @@ import config from "./deno.json" assert { type: "json" }; import * as $0 from "./routes/api/config.ts"; import * as $1 from "./routes/api/export/gallery/zip/[gid].ts"; -import * as $2 from "./routes/api/task/list.ts"; +import * as $2 from "./routes/api/task.ts"; import * as $3 from "./routes/index.tsx"; import * as $$0 from "./islands/Container.tsx"; import * as $$1 from "./islands/Settings.tsx"; @@ -14,7 +14,7 @@ const manifest = { routes: { "./routes/api/config.ts": $0, "./routes/api/export/gallery/zip/[gid].ts": $1, - "./routes/api/task/list.ts": $2, + "./routes/api/task.ts": $2, "./routes/index.tsx": $3, }, islands: { diff --git a/routes/api/task.ts b/routes/api/task.ts new file mode 100644 index 0000000..cc74d09 --- /dev/null +++ b/routes/api/task.ts @@ -0,0 +1,37 @@ +import { Handlers } from "$fresh/server.ts"; +import { get_task_manager } from "../../server.ts"; +import { Task } from "../../task.ts"; + +export const handler: Handlers = { + GET(req, _ctx) { + const t = get_task_manager(); + const { socket, response } = Deno.upgradeWebSocket(req); + const new_task_handle = (e: CustomEvent) => { + socket.send(JSON.stringify({ type: "new_task", detail: e.detail })); + }; + const removeListener = () => { + t.removeEventListener("new_task", new_task_handle); + }; + socket.onclose = () => { + removeListener(); + }; + socket.onerror = () => { + removeListener(); + console.error("WebSocket error."); + }; + socket.onmessage = (e) => { + try { + const d = JSON.parse(e.data); + if (d.type == "close") { + socket.close(); + } + } catch (_) { + null; + } + }; + socket.onopen = () => { + t.addEventListener("new_task", new_task_handle); + }; + return response; + }, +}; diff --git a/routes/api/task/list.ts b/routes/api/task/list.ts deleted file mode 100644 index d0819eb..0000000 --- a/routes/api/task/list.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Handlers } from "$fresh/server.ts"; -import { get_task_manager } from "../../../server.ts"; -import { Task } from "../../../task.ts"; - -export const handler: Handlers = { - async GET(_, _ctx) { - const t = get_task_manager(); - const tasks = await t.db.get_tasks_by_pid(Deno.pid); - return new Response(JSON.stringify(tasks), { - headers: { "Content-Type": "application/json" }, - }); - }, -}; diff --git a/task_manager.ts b/task_manager.ts index c058941..9297735 100644 --- a/task_manager.ts +++ b/task_manager.ts @@ -15,7 +15,11 @@ import { promiseState, PromiseStatus, sleep } from "./utils.ts"; export class AlreadyClosedError extends Error { } -export class TaskManager { +type EventMap = { + new_task: CustomEvent; +}; + +export class TaskManager extends EventTarget { #closed = false; cfg; client; @@ -25,6 +29,7 @@ export class TaskManager { #abort; #force_abort; constructor(cfg: Config) { + super(); this.cfg = cfg; this.#abort = new AbortController(); this.#force_abort = new AbortController(); @@ -40,6 +45,14 @@ export class TaskManager { abort(reason?: unknown) { this.#abort.abort(reason); } + // @ts-ignore Checked type + addEventListener( + type: T, + callback: ((e: EventMap[T]) => void | Promise) | null, + options?: boolean | AddEventListenerOptions, + ): void { + super.addEventListener(type, callback, options); + } get aborted() { return this.#abort.signal.aborted; } @@ -131,6 +144,18 @@ export class TaskManager { get force_aborts() { return this.#force_abort.signal; } + // @ts-ignore Checked type + removeEventListener( + type: T, + callback: ((e: EventMap[T]) => void | Promise) | null, + options?: boolean | EventListenerOptions, + ): void { + super.removeEventListener( + type, + callback, + options, + ); + } async run() { if (this.aborted || this.force_aborted) throw new AlreadyClosedError(); this.#check_closed();