From aa165bf5e47808df5c051badcc98fa38ba2ef744 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 26 Mar 2024 21:17:54 +0800 Subject: [PATCH] Add new settings --- config.ts | 5 +++ routes/api/_middleware.ts | 3 ++ routes/api/file/[id].ts | 21 ++++++++- routes/api/file/random.ts | 82 ++++++++++++++++++++++++++++++++++++ routes/api/thumbnail/[id].ts | 22 +++++++++- 5 files changed, 130 insertions(+), 3 deletions(-) diff --git a/config.ts b/config.ts index ab325f6..9d02d3d 100644 --- a/config.ts +++ b/config.ts @@ -34,6 +34,7 @@ export type ConfigType = { download_timeout_check_interval: number; /** EH metadata cache time in hours */ eh_metadata_cache_time: number; + random_file_secret?: string; }; export enum ThumbnailMethod { @@ -200,6 +201,9 @@ export class Config { get eh_metadata_cache_time() { return this._return_number("eh_metadata_cache_time") || 168; } + get random_file_secret() { + return this._return_string("random_file_secret"); + } to_json(): ConfigType { return { cookies: typeof this.cookies === "string", @@ -233,6 +237,7 @@ export class Config { download_timeout_check_interval: this.download_timeout_check_interval, eh_metadata_cache_time: this.eh_metadata_cache_time, + random_file_secret: this.random_file_secret, }; } } diff --git a/routes/api/_middleware.ts b/routes/api/_middleware.ts index d468b66..529851a 100644 --- a/routes/api/_middleware.ts +++ b/routes/api/_middleware.ts @@ -22,6 +22,9 @@ 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")) { + return true; + } return false; }; if (!token) return check(); diff --git a/routes/api/file/[id].ts b/routes/api/file/[id].ts index 23759ca..515e788 100644 --- a/routes/api/file/[id].ts +++ b/routes/api/file/[id].ts @@ -10,6 +10,7 @@ import { encodeBase64 as encode } from "std/encoding/base64.ts"; import { get_host, return_data, return_error } from "../../../server/utils.ts"; import type { EhFileExtend } from "../../../server/files.ts"; import { User, UserPermission } from "../../../db.ts"; +import { SortableURLSearchParams } from "../../../server/SortableURLSearchParams.ts"; export const handler: Handlers = { async GET(req, ctx) { @@ -21,13 +22,31 @@ export const handler: Handlers = { return return_error(403, "Permission denied."); } const u = new URL(req.url); + const m = get_task_manager(); + const token = u.searchParams.get("token"); const data = await parse_bool(u.searchParams.get("data"), false); const id = parseInt(ctx.params.id); + if (token && m.cfg.random_file_secret) { + const s = new SortableURLSearchParams(u.search, ["token"]); + const r = encode( + new Uint8Array( + await pbkdf2Hmac( + `${id}${s.toString2()}`, + m.cfg.random_file_secret, + 1000, + 64, + "SHA-512", + ), + ), + ); + if (token !== r) { + return new Response("Invalid token", { status: 403 }); + } + } if (isNaN(id)) { if (data) return return_error(400, "Bad Request"); return new Response("Bad Request", { status: 400 }); } - const m = get_task_manager(); const f = m.db.get_file(id); if (!f) { if (data) return return_error(404, "File not found."); diff --git a/routes/api/file/random.ts b/routes/api/file/random.ts index 947f366..e30992e 100644 --- a/routes/api/file/random.ts +++ b/routes/api/file/random.ts @@ -1,8 +1,12 @@ import { Handlers } from "$fresh/server.ts"; import { get_task_manager } from "../../../server.ts"; import { parse_bool } from "../../../server/parse_form.ts"; +import { SortableURLSearchParams } from "../../../server/SortableURLSearchParams.ts"; import { get_host } from "../../../server/utils.ts"; import { User, UserPermission } from "../../../db.ts"; +import pbkdf2Hmac from "pbkdf2-hmac"; +import { encodeBase64 as encode } from "std/encoding/base64.ts"; +import { return_data } from "../../../server/utils.ts"; export const handler: Handlers = { async GET(req, ctx) { @@ -15,6 +19,48 @@ export const handler: Handlers = { } const m = get_task_manager(); const u = new URL(req.url); + const token = u.searchParams.get("token"); + const action = u.searchParams.get("action"); + if (token && m.cfg.random_file_secret) { + const s = new SortableURLSearchParams(u.search, ["token"]); + const r = encode( + new Uint8Array( + await pbkdf2Hmac( + `${s.toString2()}`, + m.cfg.random_file_secret, + 1000, + 64, + "SHA-512", + ), + ), + ); + if (token !== r) { + return new Response("Invalid token", { status: 403 }); + } + } + if (action == "gentoken") { + if (!m.cfg.random_file_secret) { + return new Response("Random file secret is not enabled.", { + status: 400, + }); + } + const s = new SortableURLSearchParams(u.search, ["token", "action"]); + const token = encode( + new Uint8Array( + await pbkdf2Hmac( + `${s.toString2()}`, + m.cfg.random_file_secret, + 1000, + 64, + "SHA-512", + ), + ), + ); + const b = new URLSearchParams(u.search); + b.delete("action"); + b.set("token", token); + return return_data(`${get_host(req)}/api/file/random?${b}`); + } const is_nsfw = await parse_bool(u.searchParams.get("is_nsfw"), null); const is_ad = await parse_bool(u.searchParams.get("is_ad"), null); const thumb = await parse_bool(u.searchParams.get("thumb"), false); @@ -53,7 +99,43 @@ export const handler: Handlers = { } const f = m.db.get_random_file(is_nsfw, is_ad, gids); if (!f) return new Response("File not found.", { status: 404 }); + if (m.cfg.img_verify_secret && !thumb) { + const verify = encode( + new Uint8Array( + await pbkdf2Hmac( + `${f.id}`, + m.cfg.img_verify_secret, + 1000, + 64, + "SHA-512", + ), + ), + ); + const b = new URLSearchParams(); + b.append("verify", verify); + return Response.redirect( + `${get_host(req)}/file/${f.id}?${b}`, + ); + } const t = thumb ? "thumbnail" : "file"; + if (m.cfg.random_file_secret) { + const token = encode( + new Uint8Array( + await pbkdf2Hmac( + `${f.id}`, + m.cfg.random_file_secret, + 1000, + 64, + "SHA-512", + ), + ), + ); + const b = new URLSearchParams(); + b.append("token", token); + return Response.redirect( + `${get_host(req)}/api/${t}/${f.id}?${b}`, + ); + } return Response.redirect(`${get_host(req)}/api/${t}/${f.id}`); }, }; diff --git a/routes/api/thumbnail/[id].ts b/routes/api/thumbnail/[id].ts index be94af5..98fed3c 100644 --- a/routes/api/thumbnail/[id].ts +++ b/routes/api/thumbnail/[id].ts @@ -36,10 +36,29 @@ export const handler: Handlers = { return new Response("Permission denied", { status: 403 }); } const id = parseInt(ctx.params.id); + const m = get_task_manager(); + const u = new URL(req.url); + const token = u.searchParams.get("token"); + if (token && m.cfg.random_file_secret) { + const s = new SortableURLSearchParams(u.search, ["token"]); + const r = encode( + new Uint8Array( + await pbkdf2Hmac( + `${id}${s.toString2()}`, + m.cfg.random_file_secret, + 1000, + 64, + "SHA-512", + ), + ), + ); + if (token !== r) { + return new Response("Invalid token", { status: 403 }); + } + } if (isNaN(id)) { return new Response("Bad Request", { status: 400 }); } - const m = get_task_manager(); const b = m.cfg.thumbnail_dir; const method = m.cfg.thumbnail_method; await sure_dir(b); @@ -47,7 +66,6 @@ export const handler: Handlers = { if (!f) { return new Response("File not found.", { status: 404 }); } - const u = new URL(req.url); const max = await parse_int(u.searchParams.get("max"), 1200); const width = await parse_int(u.searchParams.get("width"), null); const height = await parse_int(u.searchParams.get("height"), null);