diff --git a/db.ts b/db.ts index e7d7106..1ce41b8 100644 --- a/db.ts +++ b/db.ts @@ -1432,6 +1432,22 @@ export class EhDb { [uid, client], ); } + list_shared_tokens( + type: T, + info: SharedTokenTypeMap[T] | null = null, + ) { + const where_sql = info === null ? "" : " AND info = ?"; + const args: QueryParameter[] = [type]; + if (info !== null) { + args.push(toJSON(info)); + } + return this.convert_shared_token( + this.db.queryEntries( + `SELECT * FROM shared_token WHERE type = ?${where_sql};`, + args, + ), + ); + } optimize() { this.db.execute("VACUUM;"); } @@ -1480,6 +1496,35 @@ export class EhDb { throw e; } } + update_shared_token( + token: string, + type: T, + expired: number | null | undefined = undefined, + info: SharedTokenTypeMap[T] | null = null, + ) { + if (expired === undefined && info === null) { + return this.get_shared_token(token); + } + const args: QueryParameter[] = []; + const set_sqls = []; + if (expired !== undefined) { + set_sqls.push("expired = ?"); + args.push(expired ? new Date(expired) : null); + } + if (info !== null) { + set_sqls.push("info = ?"); + args.push(toJSON(info)); + } + args.push(token); + args.push(type); + this.db.query( + `UPDATE shared_token SET ${ + set_sqls.join(", ") + } WHERE token = ? AND type = ?;`, + args, + ); + return this.get_shared_token(token); + } update_tags(tag: string, translated: string, intro: string) { const id = this.#tags.get(tag); if (id === undefined) { diff --git a/fresh.gen.ts b/fresh.gen.ts index 38bbfed..5d62e44 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -23,6 +23,7 @@ import * as $api_gallery_list from "./routes/api/gallery/list.ts"; import * as $api_gallery_meta_gids_ from "./routes/api/gallery/meta/[gids].ts"; import * as $api_health_check from "./routes/api/health_check.ts"; import * as $api_shared_token from "./routes/api/shared_token.ts"; +import * as $api_shared_token_list from "./routes/api/shared_token/list.ts"; import * as $api_status from "./routes/api/status.ts"; import * as $api_tag_id_ from "./routes/api/tag/[id].ts"; import * as $api_tag_rows from "./routes/api/tag/rows.ts"; @@ -71,6 +72,7 @@ const manifest = { "./routes/api/gallery/meta/[gids].ts": $api_gallery_meta_gids_, "./routes/api/health_check.ts": $api_health_check, "./routes/api/shared_token.ts": $api_shared_token, + "./routes/api/shared_token/list.ts": $api_shared_token_list, "./routes/api/status.ts": $api_status, "./routes/api/tag/[id].ts": $api_tag_id_, "./routes/api/tag/rows.ts": $api_tag_rows, diff --git a/routes/api/shared_token.ts b/routes/api/shared_token.ts index 836cf0a..57d4b66 100644 --- a/routes/api/shared_token.ts +++ b/routes/api/shared_token.ts @@ -20,6 +20,57 @@ export const handler: Handlers = { if (!st) return return_error(1, "No token."); return return_data(st); }, + async PATCH(req, ctx) { + const user = ctx.state.user; + let form: FormData | undefined; + try { + form = await req.formData(); + } catch (_) { + return return_error(400, "Bad Request"); + } + const typ = await get_string(form.get("type")); + const expired = await parse_int(form.get("expired"), null); + const token = await get_string(form.get("token")); + if (!token) { + return return_error(2, "token not specfied."); + } + if (typ == "gallery") { + if ( + user && !user.is_admin && + !(user.permissions & UserPermission.ShareGallery) + ) { + return return_error(403, "Permission denied."); + } + const m = get_task_manager(); + const st = m.db.update_shared_token( + token, + SharedTokenType.Gallery, + expired === 0 ? undefined : expired, + ); + if (!st) return return_error(404, "Not found"); + let flutter_base = import.meta.resolve("../../static/flutter") + .slice(7); + if (Deno.build.os === "windows") { + flutter_base = flutter_base.slice(1); + } + if (m.cfg.flutter_frontend) { + flutter_base = m.cfg.flutter_frontend; + } + const existed = await exists(flutter_base); + const host = get_host(req); + const base = host + (existed ? "/flutter" : "/api/"); + const token2 = encodeURIComponent(st.token); + const gid = st.info.gid; + const url = existed + ? `${base}/gallery/${gid}?share=${token2}` + : `https://dev.ehf.lifegpc.com/#/gallery/${gid}?base=${ + encodeURIComponent(base) + }&share=${token2}`; + return return_data({ url, token: st }); + } else { + return return_error(1, "Unknown type"); + } + }, async PUT(req, ctx) { const user = ctx.state.user; let form: FormData | undefined; diff --git a/routes/api/shared_token/list.ts b/routes/api/shared_token/list.ts new file mode 100644 index 0000000..4f4330e --- /dev/null +++ b/routes/api/shared_token/list.ts @@ -0,0 +1,51 @@ +import { Handlers } from "$fresh/server.ts"; +import { exists } from "@std/fs/exists"; +import { SharedTokenType, User, UserPermission } from "../../../db.ts"; +import { get_task_manager } from "../../../server.ts"; +import { get_string, parse_big_int } from "../../../server/parse_form.ts"; +import { get_host, return_data, return_error } from "../../../server/utils.ts"; + +export const handler: Handlers = { + async GET(req, ctx) { + const user = ctx.state.user; + const u = new URL(req.url); + const typ = await get_string(u.searchParams.get("type")); + if (typ == "gallery") { + if ( + user && !user.is_admin && + !(user.permissions & UserPermission.ShareGallery) + ) { + return return_error(403, "Permission denied."); + } + const gid = await parse_big_int(u.searchParams.get("gid"), null); + const m = get_task_manager(); + let flutter_base = import.meta.resolve("../../../static/flutter") + .slice(7); + if (Deno.build.os === "windows") { + flutter_base = flutter_base.slice(1); + } + if (m.cfg.flutter_frontend) { + flutter_base = m.cfg.flutter_frontend; + } + const existed = await exists(flutter_base); + const host = get_host(req); + const base = host + (existed ? "/flutter" : "/api/"); + const data = m.db.list_shared_tokens( + SharedTokenType.Gallery, + gid ? { gid } : null, + ).map((st) => { + const token2 = encodeURIComponent(st.token); + const gid = st.info.gid; + const url = existed + ? `${base}/gallery/${gid}?share=${token2}` + : `https://dev.ehf.lifegpc.com/#/gallery/${gid}?base=${ + encodeURIComponent(base) + }&share=${token2}`; + return { url, token: st }; + }); + return return_data(data); + } else { + return return_error(1, "Unknown type"); + } + }, +};