Add new settings

This commit is contained in:
2024-03-26 21:17:54 +08:00
parent 867756a70e
commit aa165bf5e4
5 changed files with 130 additions and 3 deletions

View File

@@ -34,6 +34,7 @@ export type ConfigType = {
download_timeout_check_interval: number; download_timeout_check_interval: number;
/** EH metadata cache time in hours */ /** EH metadata cache time in hours */
eh_metadata_cache_time: number; eh_metadata_cache_time: number;
random_file_secret?: string;
}; };
export enum ThumbnailMethod { export enum ThumbnailMethod {
@@ -200,6 +201,9 @@ export class Config {
get eh_metadata_cache_time() { get eh_metadata_cache_time() {
return this._return_number("eh_metadata_cache_time") || 168; return this._return_number("eh_metadata_cache_time") || 168;
} }
get random_file_secret() {
return this._return_string("random_file_secret");
}
to_json(): ConfigType { to_json(): ConfigType {
return { return {
cookies: typeof this.cookies === "string", cookies: typeof this.cookies === "string",
@@ -233,6 +237,7 @@ export class Config {
download_timeout_check_interval: download_timeout_check_interval:
this.download_timeout_check_interval, this.download_timeout_check_interval,
eh_metadata_cache_time: this.eh_metadata_cache_time, eh_metadata_cache_time: this.eh_metadata_cache_time,
random_file_secret: this.random_file_secret,
}; };
} }
} }

View File

@@ -22,6 +22,9 @@ function handle_auth(req: Request, ctx: FreshContext) {
if (u.pathname === "/api/health_check" && req.method === "GET") { if (u.pathname === "/api/health_check" && req.method === "GET") {
return true; 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; return false;
}; };
if (!token) return check(); if (!token) return check();

View File

@@ -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 { get_host, return_data, return_error } from "../../../server/utils.ts";
import type { EhFileExtend } from "../../../server/files.ts"; import type { EhFileExtend } from "../../../server/files.ts";
import { User, UserPermission } from "../../../db.ts"; import { User, UserPermission } from "../../../db.ts";
import { SortableURLSearchParams } from "../../../server/SortableURLSearchParams.ts";
export const handler: Handlers = { export const handler: Handlers = {
async GET(req, ctx) { async GET(req, ctx) {
@@ -21,13 +22,31 @@ export const handler: Handlers = {
return return_error(403, "Permission denied."); return return_error(403, "Permission denied.");
} }
const u = new URL(req.url); 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 data = await parse_bool(u.searchParams.get("data"), false);
const id = parseInt(ctx.params.id); 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 (isNaN(id)) {
if (data) return return_error(400, "Bad Request"); if (data) return return_error(400, "Bad Request");
return new Response("Bad Request", { status: 400 }); return new Response("Bad Request", { status: 400 });
} }
const m = get_task_manager();
const f = m.db.get_file(id); const f = m.db.get_file(id);
if (!f) { if (!f) {
if (data) return return_error(404, "File not found."); if (data) return return_error(404, "File not found.");

View File

@@ -1,8 +1,12 @@
import { Handlers } from "$fresh/server.ts"; import { Handlers } from "$fresh/server.ts";
import { get_task_manager } from "../../../server.ts"; import { get_task_manager } from "../../../server.ts";
import { parse_bool } from "../../../server/parse_form.ts"; import { parse_bool } from "../../../server/parse_form.ts";
import { SortableURLSearchParams } from "../../../server/SortableURLSearchParams.ts";
import { get_host } from "../../../server/utils.ts"; import { get_host } from "../../../server/utils.ts";
import { User, UserPermission } from "../../../db.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 = { export const handler: Handlers = {
async GET(req, ctx) { async GET(req, ctx) {
@@ -15,6 +19,48 @@ export const handler: Handlers = {
} }
const m = get_task_manager(); const m = get_task_manager();
const u = new URL(req.url); 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_nsfw = await parse_bool(u.searchParams.get("is_nsfw"), null);
const is_ad = await parse_bool(u.searchParams.get("is_ad"), null); const is_ad = await parse_bool(u.searchParams.get("is_ad"), null);
const thumb = await parse_bool(u.searchParams.get("thumb"), false); 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); const f = m.db.get_random_file(is_nsfw, is_ad, gids);
if (!f) return new Response("File not found.", { status: 404 }); 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"; 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}`); return Response.redirect(`${get_host(req)}/api/${t}/${f.id}`);
}, },
}; };

View File

@@ -36,10 +36,29 @@ export const handler: Handlers = {
return new Response("Permission denied", { status: 403 }); return new Response("Permission denied", { status: 403 });
} }
const id = parseInt(ctx.params.id); 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)) { if (isNaN(id)) {
return new Response("Bad Request", { status: 400 }); return new Response("Bad Request", { status: 400 });
} }
const m = get_task_manager();
const b = m.cfg.thumbnail_dir; const b = m.cfg.thumbnail_dir;
const method = m.cfg.thumbnail_method; const method = m.cfg.thumbnail_method;
await sure_dir(b); await sure_dir(b);
@@ -47,7 +66,6 @@ export const handler: Handlers = {
if (!f) { if (!f) {
return new Response("File not found.", { status: 404 }); 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 max = await parse_int(u.searchParams.get("max"), 1200);
const width = await parse_int(u.searchParams.get("width"), null); const width = await parse_int(u.searchParams.get("width"), null);
const height = await parse_int(u.searchParams.get("height"), null); const height = await parse_int(u.searchParams.get("height"), null);