From fd680931bd2b0296a89ffb84d984c3800f70e796 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 24 May 2024 17:56:58 +0800 Subject: [PATCH] Add client config api --- db.ts | 42 +++++++++++++++++++++++ fresh.gen.ts | 2 ++ routes/api/_middleware.ts | 8 ++++- routes/api/client/config.ts | 68 +++++++++++++++++++++++++++++++++++++ server/i18ns.ts | 3 +- 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 routes/api/client/config.ts diff --git a/db.ts b/db.ts index 827d1c3..091e9a3 100644 --- a/db.ts +++ b/db.ts @@ -177,6 +177,12 @@ type TokenRaw = { client_version: string | null; client_platform: string | null; }; +export type ClientConfig = { + uid: number; + client: string; + name: string; + data: string; +}; const ALL_TABLES = [ "version", "task", @@ -189,6 +195,7 @@ const ALL_TABLES = [ "user", "token", "ehmeta", + "client_config", ]; const VERSION_TABLE = `CREATE TABLE version ( id TEXT, @@ -282,6 +289,13 @@ const EHMETA_TABLE = `CREATE TABLE ehmeta ( cached_time TEXT, PRIMARY KEY (gid) );`; +const CLIENT_CONFIG_TABLE = `CREATE TABLE client_config ( + uid INT, + client TEXT, + name TEXT, + data TEXT, + PRIMARY KEY (uid, client, name) +);`; function escape_fields(fields: string, namespace: string) { const fs = fields.split(","); @@ -508,6 +522,9 @@ export class EhDb { if (!this.#exist_table.has("ehmeta")) { this.db.execute(EHMETA_TABLE); } + if (!this.#exist_table.has("client_config")) { + this.db.execute(CLIENT_CONFIG_TABLE); + } this.#updateExistsTable(); } #read_version() { @@ -549,6 +566,12 @@ export class EhDb { ]); }); } + add_client_config(config: ClientConfig) { + this.db.queryEntries( + "INSERT OR REPLACE INTO client_config VALUES (:uid, :client, :name, :data);", + config, + ); + } add_ehmeta(data: GalleryMetadataSingle) { this.db.query( "INSERT OR REPLACE INTO ehmeta VALUES (?, ?, ?);", @@ -908,6 +931,12 @@ export class EhDb { return t; }); } + delete_client_config(uid: number, client: string, name: string) { + this.db.query( + "DELETE FROM client_config WHERE uid = ? AND client = ? AND name = ?;", + [uid, client, name], + ); + } delete_file(f: EhFile) { this.db.query("DELETE FROM file WHERE id = ?;", [f.id]); } @@ -966,6 +995,13 @@ export class EhDb { if (!this.#dblock) return; eval(`Deno.funlockSync(${this.#dblock.rid});`); } + get_client_config(uid: number, client: string, name: string) { + const d = this.db.queryEntries( + "SELECT * FROM client_config WHERE uid = ? AND client = ? AND name = ?;", + [uid, client, name], + ); + return d.length ? d[0] : null; + } get_ehmeta(gid: number) { const d = this.db.query<[string]>( "SELECT data FROM ehmeta WHERE gid = ?;", @@ -1265,6 +1301,12 @@ export class EhDb { get_user_count() { return this.db.query<[number]>("SELECT COUNT(*) FROM user;")[0][0]; } + list_client_configs(uid: number, client: string) { + return this.db.queryEntries( + "SELECT * FROM client_config WHERE uid = ? AND client = ?;", + [uid, client], + ); + } optimize() { this.db.execute("VACUUM;"); } diff --git a/fresh.gen.ts b/fresh.gen.ts index 6bd3d9a..35ba298 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -4,6 +4,7 @@ import * as $_middleware from "./routes/_middleware.ts"; import * as $api_middleware from "./routes/api/_middleware.ts"; +import * as $api_client_config from "./routes/api/client/config.ts"; import * as $api_config from "./routes/api/config.ts"; import * as $api_deploy_id from "./routes/api/deploy_id.ts"; import * as $api_eh_image_limit from "./routes/api/eh/image_limit.ts"; @@ -44,6 +45,7 @@ const manifest = { routes: { "./routes/_middleware.ts": $_middleware, "./routes/api/_middleware.ts": $api_middleware, + "./routes/api/client/config.ts": $api_client_config, "./routes/api/config.ts": $api_config, "./routes/api/deploy_id.ts": $api_deploy_id, "./routes/api/eh/image_limit.ts": $api_eh_image_limit, diff --git a/routes/api/_middleware.ts b/routes/api/_middleware.ts index 529851a..3bbe1f6 100644 --- a/routes/api/_middleware.ts +++ b/routes/api/_middleware.ts @@ -22,7 +22,13 @@ function handle_auth(req: Request, ctx: FreshContext) { if (u.pathname === "/api/health_check" && req.method === "GET") { return true; } - if (m.cfg.random_file_secret && (u.pathname == "/api/file/random" || u.pathname.match(/^\/api\/file\/\d+/) || u.pathname.match(/^\/api\/thumbnail\/\d+/)) && req.method === "GET" && u.searchParams.get("token")) { + if ( + m.cfg.random_file_secret && + (u.pathname == "/api/file/random" || + u.pathname.match(/^\/api\/file\/\d+/) || + u.pathname.match(/^\/api\/thumbnail\/\d+/)) && + req.method === "GET" && u.searchParams.get("token") + ) { return true; } return false; diff --git a/routes/api/client/config.ts b/routes/api/client/config.ts new file mode 100644 index 0000000..78e94e2 --- /dev/null +++ b/routes/api/client/config.ts @@ -0,0 +1,68 @@ +import { Handlers } from "$fresh/server.ts"; +import { User } from "../../../db.ts"; +import { get_task_manager } from "../../../server.ts"; +import { get_string } from "../../../server/parse_form.ts"; +import { return_data, return_error } from "../../../server/utils.ts"; + +export const handler: Handlers = { + async DELETE(req, ctx) { + const user = ctx.state.user; + if (!user) { + return return_error(403, "Permission denied."); + } + let d: FormData | null = null; + try { + d = await req.formData(); + } catch (_) { + return return_error(1, "Invalid parameters."); + } + const client = await get_string(d.get("client")); + if (!client) return return_error(2, "client is needed."); + const name = await get_string(d.get("name")); + if (!name) return return_error(2, "name is needed."); + const m = get_task_manager(); + m.db.delete_client_config(user.id, client, name); + return return_data({}); + }, + GET(req, ctx) { + const user = ctx.state.user; + if (!user) { + return return_error(403, "Permission denied."); + } + const d = new URL(req.url).searchParams; + const client = d.get("client"); + if (!client) return return_error(2, "client is needed."); + const name = d.get("name"); + const m = get_task_manager(); + if (name) { + const data = m.db.get_client_config(user.id, client, name); + if (data === null) return return_error(404, "Not found"); + return return_data(data.data); + } else { + return return_data( + m.db.list_client_configs(user.id, client).map((d) => d.name), + ); + } + }, + async PUT(req, ctx) { + const user = ctx.state.user; + if (!user) { + return return_error(403, "Permission denied."); + } + let d: FormData | null = null; + try { + d = await req.formData(); + } catch (_) { + return return_error(1, "Invalid parameters."); + } + const client = await get_string(d.get("client")); + if (!client) return return_error(2, "client is needed."); + const name = await get_string(d.get("name")); + if (!name) return return_error(2, "name is needed."); + const data = await get_string(d.get("data")); + if (data === null) return return_error(2, "data is needed."); + const m = get_task_manager(); + m.db.add_client_config({ uid: user.id, client, name, data }); + return return_data({}); + }, +}; diff --git a/server/i18ns.ts b/server/i18ns.ts index 1df3744..2312bc8 100644 --- a/server/i18ns.ts +++ b/server/i18ns.ts @@ -63,7 +63,8 @@ export function i18n_handle_request(req: Request) { const a = req.headers.get("Accept-Language"); const l = (a ? pick(LANGUAGES, a) || pick(LANGUAGES, a, { loose: true }) - : null) || "en"; + : null) || + "en"; const params = new URLSearchParams(); params.append("lang", l); for (const p of u.searchParams.entries()) {