From f5a56081e180ddb2c191eb81057ae5673ea6c3af Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 7 Feb 2024 15:48:04 +0800 Subject: [PATCH] Support add task via PUT requests --- db.ts | 9 ++++- routes/api/health_check.ts | 3 +- routes/api/task.ts | 80 +++++++++++++++++++++++++++++++++++++- task_manager.ts | 9 ++++- 4 files changed, 95 insertions(+), 6 deletions(-) diff --git a/db.ts b/db.ts index 48cbe80..9eb025f 100644 --- a/db.ts +++ b/db.ts @@ -7,6 +7,7 @@ import { import { unescape } from "std/html/mod.ts"; import { join, resolve } from "std/path/mod.ts"; import { SqliteError } from "sqlite/mod.ts"; +import { SqliteError as Sqlite3Error } from "sqlite3/mod.ts"; import { Status } from "sqlite/src/constants.ts"; import { parse_bool, sleep, sure_dir_sync, try_remove_sync } from "./utils.ts"; import { Task, TaskType } from "./task.ts"; @@ -131,7 +132,9 @@ export enum UserPermission { None = 0, ReadGallery = 1 << 0, EditGallery = 1 << 1, - All = ~(~0 << 2), + DeleteGallery = 1 << 2, + ManageTasks = 1 << 3, + All = ~(~0 << 4), } export type User = { id: number; @@ -721,6 +724,10 @@ export class EhDb { if (e instanceof SqliteError) { if (e.code == Status.SqliteBusy) return false; } + if (e instanceof Sqlite3Error) { + // SQLITE_BUSY + if (e.code == 5) return false; + } throw e; } } diff --git a/routes/api/health_check.ts b/routes/api/health_check.ts index 44a8e9d..b007723 100644 --- a/routes/api/health_check.ts +++ b/routes/api/health_check.ts @@ -1,7 +1,8 @@ import { Handlers } from "$fresh/server.ts"; +import { return_data } from "../../server/utils.ts"; export const handler: Handlers = { GET(_req, _ctx) { - return new Response("OK"); + return return_data(true); }, }; diff --git a/routes/api/task.ts b/routes/api/task.ts index da927af..5ce1e99 100644 --- a/routes/api/task.ts +++ b/routes/api/task.ts @@ -6,9 +6,18 @@ import type { TaskServerSocketData, } from "../../server/task.ts"; import { ExitTarget } from "../../signal_handler.ts"; +import { get_string, parse_int } from "../../server/parse_form.ts"; +import { return_data, return_error } from "../../server/utils.ts"; +import type { DownloadConfig } from "../../tasks/download.ts"; +import type { ExportZipConfig } from "../../tasks/export_zip.ts"; +import { User, UserPermission } from "../../db.ts"; -export const handler: Handlers = { - GET(req, _ctx) { +export const handler: Handlers = { + GET(req, ctx) { + const u = ctx.state.user; + if (u && !u.is_admin && !(u.permissions & UserPermission.ManageTasks)) { + return return_error(403, "Permission denied."); + } const t = get_task_manager(); const { socket, response } = Deno.upgradeWebSocket(req); const handle = ( @@ -77,4 +86,71 @@ export const handler: Handlers = { }; return response; }, + async PUT(req, ctx) { + const u = ctx.state.user; + if (u && !u.is_admin && !(u.permissions & UserPermission.ManageTasks)) { + return return_error(403, "Permission denied."); + } + const t = get_task_manager(); + let form: FormData | null = null; + try { + form = await req.formData(); + } catch (_) { + return return_error(400, "Bad Request"); + } + const typ = await get_string(form.get("type")); + if (!typ) { + return return_error(1, "type is required"); + } + if (typ == "download") { + const gid = await parse_int(form.get("gid"), null); + const token = await get_string(form.get("token")); + if (gid === null) { + return return_error(2, "gid is required"); + } + if (!token) { + return return_error(3, "token is required"); + } + const cfg = await get_string(form.get("cfg")); + let dcfg: DownloadConfig | undefined = undefined; + if (cfg) { + try { + dcfg = JSON.parse(cfg); + } catch (_) { + return return_error(4, "cfg is invalid"); + } + } + try { + const task = t.add_download_task(gid, token, dcfg, true); + if (task === null) { + return return_error(6, "task is already in the list"); + } + return return_data(task, 201); + } catch (e) { + return return_error(500, e.message); + } + } else if (typ == "export_zip") { + const gid = await parse_int(form.get("gid"), null); + if (gid === null) { + return return_error(2, "gid is required"); + } + const cfg = await get_string(form.get("cfg")); + let dcfg: ExportZipConfig | undefined = undefined; + if (cfg) { + try { + dcfg = JSON.parse(cfg); + } catch (_) { + return return_error(4, "cfg is invalid"); + } + } + try { + const task = t.add_export_zip_task(gid, dcfg); + return return_data(task, 201); + } catch (e) { + return return_error(500, e.message); + } + } else { + return return_error(5, "unknown type"); + } + }, }; diff --git a/task_manager.ts b/task_manager.ts index 6a7c239..2d916f0 100644 --- a/task_manager.ts +++ b/task_manager.ts @@ -106,12 +106,17 @@ export class TaskManager extends EventTarget { get aborts() { return this.#abort.signal; } - async add_download_task(gid: number, token: string, cfg?: DownloadConfig) { + async add_download_task( + gid: number, + token: string, + cfg?: DownloadConfig, + mark_already = false, + ) { this.#check_closed(); const otask = await this.db.check_download_task(gid, token); if (otask !== undefined) { console.log("The task is already in list."); - return otask; + return mark_already ? null : otask; } const task: Task = { gid,