From 8437bf1436d45d50c80e7d91d320738594fc8635 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 31 May 2024 14:24:57 +0800 Subject: [PATCH] Better handle bigint --- client.ts | 6 +- db.ts | 208 +++++++++++++------------ deno.json | 2 +- meilisearch.ts | 2 +- page/GalleryMetadata.ts | 2 +- pid_check.ts | 2 +- routes/api/_middleware.ts | 3 +- routes/api/eh/metadata.ts | 4 +- routes/api/export/gallery/zip/[gid].ts | 5 +- routes/api/file/[id].ts | 5 +- routes/api/file/random.ts | 11 +- routes/api/filemeta.ts | 33 +++- routes/api/gallery/[gid].ts | 11 +- routes/api/gallery/meta/[gids].ts | 11 +- routes/api/status.ts | 5 +- routes/api/tag/[id].ts | 5 +- routes/api/task.ts | 6 +- routes/api/thumbnail/[id].ts | 18 ++- routes/api/user.ts | 18 ++- routes/file/[id].ts | 5 +- routes/file/[verify]/[id].ts | 5 +- routes/thumbnail/[id].ts | 6 +- server/check_auth.ts | 29 ---- server/export_zip.ts | 13 +- server/files.ts | 12 +- server/parse_form.ts | 13 ++ server/parse_form_test.ts | 16 +- server/task.ts | 10 +- server/user.ts | 2 +- task.ts | 12 +- task_manager.ts | 14 +- tasks/download.ts | 6 +- tasks/export_zip.ts | 5 +- tasks/update_meili_search_data.ts | 2 +- utils.ts | 27 +++- utils_test.ts | 13 ++ 36 files changed, 320 insertions(+), 227 deletions(-) delete mode 100644 server/check_auth.ts diff --git a/client.ts b/client.ts index 196d2de..d357f16 100644 --- a/client.ts +++ b/client.ts @@ -6,7 +6,7 @@ import { load_mpv_page } from "./page/MPVPage.ts"; import { load_single_page } from "./page/SinglePage.ts"; import { RecoverableError, TimeoutError, toJSON } from "./utils.ts"; -export type GID = [number, string]; +export type GID = [number | bigint, string]; export class Client { cfg; @@ -189,7 +189,7 @@ export class Client { * @param page Page number * @returns */ - async fetchGalleryPage(gid: number, token: string, page?: number) { + async fetchGalleryPage(gid: number | bigint, token: string, page?: number) { let url = `https://${this.host}/g/${gid}/${token}/`; if (page) url += `?p=${page}`; const re = await this.get(url); @@ -206,7 +206,7 @@ export class Client { * @param token Token * @returns */ - async fetchMPVPage(gid: number, token: string) { + async fetchMPVPage(gid: number | bigint, token: string) { const url = `https://${this.host}/mpv/${gid}/${token}/`; const re = await this.get(url); if (re.status != 200) { diff --git a/db.ts b/db.ts index 127c92b..8e9eaf9 100644 --- a/db.ts +++ b/db.ts @@ -1,4 +1,4 @@ -import { Db } from "./utils/db_interface.ts"; +import { Db, QueryParameter } from "./utils/db_interface.ts"; import { compare as compare_ver, format as format_ver, @@ -33,97 +33,97 @@ export enum SqliteTransactionType { EXCLUSIVE = "EXCLUSIVE", } export type GMeta = { - gid: number; + gid: number | bigint; token: string; title: string; title_jpn: string; category: string; uploader: string; - posted: number; - filecount: number; - filesize: number; + posted: number | bigint; + filecount: number | bigint; + filesize: number | bigint; expunged: boolean; - rating: number; - parent_gid: number | null; + rating: number | bigint; + parent_gid: number | bigint | null; parent_key: string | null; - first_gid: number | null; + first_gid: number | bigint | null; first_key: string | null; }; export type GMetaRaw = { - gid: number; + gid: number | bigint; token: string; title: string; title_jpn: string; category: string; uploader: string; - posted: number; - filecount: number; - filesize: number; - expunged: number; - rating: number; - parent_gid: number | null; + posted: number | bigint; + filecount: number | bigint; + filesize: number | bigint; + expunged: number | bigint; + rating: number | bigint; + parent_gid: number | bigint | null; parent_key: string | null; - first_gid: number | null; + first_gid: number | bigint | null; first_key: string | null; }; export type PMeta = { - gid: number; - index: number; + gid: number | bigint; + index: number | bigint; token: string; name: string; - width: number; - height: number; + width: number | bigint; + height: number | bigint; }; export type ExtendedPMeta = { - gid: number; - index: number; + gid: number | bigint; + index: number | bigint; token: string; name: string; - width: number; - height: number; + width: number | bigint; + height: number | bigint; is_nsfw: boolean; is_ad: boolean; }; export type ExtendedPMetaRaw = { - gid: number; - index: number; + gid: number | bigint; + index: number | bigint; token: string; name: string; - width: number; - height: number; - is_nsfw: number | null; - is_ad: number | null; + width: number | bigint; + height: number | bigint; + is_nsfw: number | bigint | null; + is_ad: number | bigint | null; }; export type Tag = { - id: number; + id: number | bigint; tag: string; translated: string | undefined; intro: string | undefined; }; export type EhFile = { - id: number; + id: number | bigint; token: string; path: string; - width: number; - height: number; + width: number | bigint; + height: number | bigint; is_original: boolean; }; type EhFileRawV1 = { - id: number; - gid: number; + id: number | bigint; + gid: number | bigint; token: string; path: string; - width: number; - height: number; - is_original: number; + width: number | bigint; + height: number | bigint; + is_original: number | bigint; }; export type EhFileRaw = { - id: number; + id: number | bigint; token: string; path: string; - width: number; - height: number; - is_original: number; + width: number | bigint; + height: number | bigint; + is_original: number | bigint; }; export type EhFileMeta = { token: string; @@ -132,8 +132,8 @@ export type EhFileMeta = { }; export type EhFileMetaRaw = { token: string; - is_nsfw: number; - is_ad: number; + is_nsfw: number | bigint; + is_ad: number | bigint; }; export enum UserPermission { None = 0, @@ -144,22 +144,22 @@ export enum UserPermission { All = ~(~0 << 4), } export type User = { - id: number; + id: number | bigint; username: string; password: Uint8Array; is_admin: boolean; permissions: UserPermission; }; type UserRaw = { - id: number; + id: number | bigint; username: string; password: Uint8Array; - is_admin: number; + is_admin: number | bigint; permissions: UserPermission; }; export type Token = { - id: number; - uid: number; + id: number | bigint; + uid: number | bigint; token: string; expired: Date; http_only: boolean; @@ -171,12 +171,12 @@ export type Token = { client_platform: string | null; }; type TokenRaw = { - id: number; - uid: number; + id: number | bigint; + uid: number | bigint; token: string; expired: string; - http_only: number; - secure: number; + http_only: number | bigint; + secure: number | bigint; last_used: string; client: string | null; device: string | null; @@ -184,7 +184,7 @@ type TokenRaw = { client_platform: string | null; }; export type ClientConfig = { - uid: number; + uid: number | bigint; client: string; name: string; data: string; @@ -319,7 +319,7 @@ export class EhDb { #exist_table: Set = new Set(); #lock_file: string | undefined; #dblock_file: string | undefined; - #_tags: Map | undefined; + #_tags: Map | undefined; #base_path: string; #db_path: string; #use_ffi = false; @@ -547,7 +547,7 @@ export class EhDb { get #tags() { if (this.#_tags === undefined) { const tags = this.db.queryEntries("SELECT * FROM tag;"); - const re = new Map(); + const re = new Map(); tags.forEach((v) => re.set(v.tag, v.id)); this.#_tags = re; return re; @@ -590,7 +590,7 @@ export class EhDb { gmeta, ); } - async add_gtag(gid: number, tags: Set) { + async add_gtag(gid: number | bigint, tags: Set) { const otags = this.get_gtags(gid); const deleted: string[] = []; const added: string[] = []; @@ -703,7 +703,7 @@ export class EhDb { }); } add_token( - uid: number, + uid: number | bigint, added: number, http_only: boolean, secure: boolean, @@ -736,7 +736,7 @@ export class EhDb { return t; } add_user(user: User) { - if (user.id === 0) { + if (user.id === 0 || user.id === 0n) { this.db.query( "INSERT INTO user (username, password, is_admin, permissions) VALUES (?, ?, ?, ?);", [ @@ -778,18 +778,20 @@ export class EhDb { } } better_optimize() { - const d = this.db.query<[string, number]>( + const d = this.db.query<[string, number | bigint]>( "SELECT * FROM main.sqlite_sequence", ); d.forEach(([name, count]) => { - const c = this.db.query<[number]>( + const c = this.db.query<[number | bigint]>( `SELECT COUNT(*) FROM "${name}";`, )[0][0]; - if (c !== count) { - const d = this.db.query<[number]>(`SELECT id FROM "${name}";`); + if (c != count) { + const d = this.db.query<[number | bigint]>( + `SELECT id FROM "${name}";`, + ); d.forEach((d, i) => { const r = i + 1; - if (d[0] !== r) { + if (d[0] != r) { this.db.query( `UPDATE "${name}" SET id = ? WHERE id = ?;`, [r, d[0]], @@ -809,7 +811,7 @@ export class EhDb { } }); } - check_download_task(gid: number, token: string) { + check_download_task(gid: number | bigint, token: string) { return this.transaction(() => { const r = this.db.queryEntries( "SELECT * FROM task WHERE type = ? AND gid = ? AND token = ?;", @@ -836,8 +838,8 @@ export class EhDb { return r; }); } - check_update_meili_search_data_task(gid?: number) { - const args = [TaskType.UpdateMeiliSearchData]; + check_update_meili_search_data_task(gid?: number | bigint) { + const args: QueryParameter[] = [TaskType.UpdateMeiliSearchData]; let wsql = ""; if (gid !== undefined) { wsql = " AND gid = ?"; @@ -889,7 +891,7 @@ export class EhDb { } convert_file(f: EhFileRaw[]) { return f.map((m) => { - const b = m.is_original !== 0; + const b = m.is_original != 0; const t = m; t.is_original = b; return t; @@ -897,8 +899,8 @@ export class EhDb { } convert_filemeta(m: EhFileMetaRaw[]) { return m.map((m) => { - const n = m.is_nsfw !== 0; - const a = m.is_ad !== 0; + const n = m.is_nsfw != 0; + const a = m.is_ad != 0; const t = m; t.is_nsfw = n; t.is_ad = a; @@ -908,7 +910,7 @@ export class EhDb { convert_gmeta(m: GMetaRaw[]): GMeta[] { return m.map((m) => { if (m.expunged === undefined) return m; - const b = m.expunged !== 0; + const b = m.expunged != 0; const t = m; t.expunged = b; return t; @@ -917,8 +919,8 @@ export class EhDb { convert_token(m: TokenRaw[]) { return m.map((m) => { const e = new Date(m.expired); - const h = m.http_only !== 0; - const s = m.secure !== 0; + const h = m.http_only != 0; + const s = m.secure != 0; const t = m; const l = new Date(m.last_used); t.expired = e; @@ -930,14 +932,14 @@ export class EhDb { } convert_user(m: UserRaw[]) { return m.map((m) => { - const a = m.is_admin !== 0; + const a = m.is_admin != 0; const t = m; t.is_admin = a; if (t.is_admin) t.permissions = UserPermission.All; return t; }); } - delete_client_config(uid: number, client: string, name: string) { + delete_client_config(uid: number | bigint, client: string, name: string) { this.db.query( "DELETE FROM client_config WHERE uid = ? AND client = ? AND name = ?;", [uid, client, name], @@ -955,7 +957,7 @@ export class EhDb { console.log("Deleted ", f.path); }); } - delete_gallery(gid: number) { + delete_gallery(gid: number | bigint) { this.db.query("DELETE FROM gmeta WHERE gid = ?;", [gid]); this.db.query("DELETE FROM gtag WHERE gid = ?;", [gid]); const tokens = new Set( @@ -965,11 +967,11 @@ export class EhDb { ); this.db.query("DELETE FROM pmeta WHERE gid = ?;", [gid]); for (const token of tokens) { - const count = this.db.query<[number]>( + const count = this.db.query<[number | bigint]>( "SELECT COUNT(*) FROM pmeta WHERE token = ?;", [token], )[0][0]; - if (count === 0) this.delete_files(token); + if (count === 0 || count === 0n) this.delete_files(token); } } delete_task(task: Task) { @@ -977,7 +979,7 @@ export class EhDb { this.db.query("DELETE FROM task WHERE id = ?;", [task.id]); }); } - delete_task_by_id(id: number) { + delete_task_by_id(id: number | bigint) { return this.transaction(() => { this.db.query("DELETE FROM task WHERE id = ?;", [id]); }); @@ -985,12 +987,12 @@ export class EhDb { delete_token(token: string) { this.db.query("DELETE FROM token WHERE token = ?;", [token]); } - delete_user(id: number) { + delete_user(id: number | bigint) { this.db.query("DELETE FROM user WHERE id = ?;", [id]); this.db.query("DELETE FROM token WHERE uid = ?;", [id]); this.db.query("DELETE FROM client_config WHERE uid = ?;", [id]); } - delete_user_token(uid: number, excluded_token?: number) { + delete_user_token(uid: number | bigint, excluded_token?: number | bigint) { let where = ""; const args = [uid]; if (excluded_token) { @@ -1015,21 +1017,21 @@ export class EhDb { if (!this.#dblock) return; eval(`Deno.funlockSync(${this.#dblock.rid});`); } - get_client_config(uid: number, client: string, name: string) { + get_client_config(uid: number | bigint, 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) { + get_ehmeta(gid: number | bigint) { const d = this.db.query<[string]>( "SELECT data FROM ehmeta WHERE gid = ?;", [gid], ); return d.length ? JSON.parse(d[0][0]) as GalleryMetadataSingle : null; } - get_extended_pmeta(gid: number) { + get_extended_pmeta(gid: number | bigint) { return this.convert_extended_pmeta( this.db.queryEntries( "SELECT pmeta.*, filemeta.is_nsfw, filemeta.is_ad FROM pmeta LEFT JOIN filemeta ON filemeta.token = pmeta.token WHERE gid = ?;", @@ -1037,7 +1039,7 @@ export class EhDb { ), ); } - get_file(id: number) { + get_file(id: number | bigint) { const d = this.convert_file(this.db.queryEntries( "SELECT * FROM file WHERE id = ?;", [id], @@ -1058,10 +1060,12 @@ export class EhDb { )); } get_gallery_count() { - return this.db.query<[number]>("SELECT COUNT(*) FROM gmeta;")[0][0]; + return this.db.query<[number | bigint]>( + "SELECT COUNT(*) FROM gmeta;", + )[0][0]; } get_gids(offset = 0, limit = 20) { - return this.db.query<[number]>( + return this.db.query<[number | bigint]>( "SELECT gid FROM gmeta LIMIT ? OFFSET ?;", [limit, offset], ).map((n) => n[0]); @@ -1148,7 +1152,7 @@ export class EhDb { ), ); } - get_gmeta_by_gid(gid: number) { + get_gmeta_by_gid(gid: number | bigint) { const s = this.convert_gmeta( this.db.queryEntries( "SELECT * FROM gmeta WHERE gid = ?;", @@ -1157,7 +1161,7 @@ export class EhDb { ); return s.length ? s[0] : undefined; } - get_gtags(gid: number) { + get_gtags(gid: number | bigint) { return new Set( this.db.query<[string]>( "SELECT tag.tag FROM gtag INNER JOIN tag ON tag.id = gtag.id WHERE gid = ?;", @@ -1165,26 +1169,26 @@ export class EhDb { ).map((v) => v[0]), ); } - get_gtags_full(gid: number) { + get_gtags_full(gid: number | bigint) { return this.db.queryEntries( "SELECT tag.* FROM gtag INNER JOIN tag ON tag.id = gtag.id WHERE gid = ?;", [gid], ); } - get_pmeta(gid: number) { + get_pmeta(gid: number | bigint) { return this.db.queryEntries( "SELECT * FROM pmeta WHERE gid = ?;", [gid], ); } - get_pmeta_by_index(gid: number, index: number) { + get_pmeta_by_index(gid: number | bigint, index: number | bigint) { const s = this.db.queryEntries( 'SELECT * FROM pmeta WHERE gid = ? AND "index" = ?;', [gid, index], ); return s.length ? s[0] : undefined; } - get_pmeta_by_token(gid: number, token: string) { + get_pmeta_by_token(gid: number | bigint, token: string) { const s = this.db.queryEntries( "SELECT * FROM pmeta WHERE gid = ? AND token = ?;", [gid, token], @@ -1198,7 +1202,7 @@ export class EhDb { ); return s; } - get_pmeta_count(gid: number) { + get_pmeta_count(gid: number | bigint) { return this.db.query<[number]>( "SELECT COUNT(*) FROM pmeta WHERE gid = ?;", [gid], @@ -1246,7 +1250,7 @@ export class EhDb { ); return s.length ? s[0] : undefined; } - get_tag(id: number) { + get_tag(id: number | bigint) { const s = this.db.queryEntries( "SELECT * FROM tag WHERE id = ?;", [id], @@ -1266,7 +1270,7 @@ export class EhDb { ["rows:%"], ); } - async get_task(id: number) { + async get_task(id: number | bigint) { const s = await this.transaction(() => this.db.queryEntries("SELECT * FROM task WHERE id = ?;", [id]) ); @@ -1277,7 +1281,7 @@ export class EhDb { this.db.queryEntries("SELECT * FROM task;") ); } - get_tasks_by_pid(pid: number) { + get_tasks_by_pid(pid: number | bigint) { return this.transaction(() => this.db.queryEntries("SELECT * FROM task WHERE pid = ?;", [ pid, @@ -1300,7 +1304,7 @@ export class EhDb { ); return s.length ? s[0] : undefined; } - get_user(id: number) { + get_user(id: number | bigint) { const s = this.convert_user( this.db.queryEntries( "SELECT * FROM user WHERE id = ?;", @@ -1319,7 +1323,9 @@ export class EhDb { return s.length ? s[0] : undefined; } get_user_count() { - return this.db.query<[number]>("SELECT COUNT(*) FROM user;")[0][0]; + return this.db.query<[number | bigint]>( + "SELECT COUNT(*) FROM user;", + )[0][0]; } get_users(limit?: number, offset?: number) { let sql = ""; @@ -1336,7 +1342,7 @@ export class EhDb { this.db.queryEntries(`SELECT * FROM user${sql};`, args), ); } - list_client_configs(uid: number, client: string) { + list_client_configs(uid: number | bigint, client: string) { return this.db.queryEntries( "SELECT * FROM client_config WHERE uid = ? AND client = ?;", [uid, client], diff --git a/deno.json b/deno.json index f6e0c06..d3d4be2 100644 --- a/deno.json +++ b/deno.json @@ -33,7 +33,7 @@ "rules": { "tags": ["fresh", "recommended"] }, - "exclude": ["_fresh"] + "exclude": ["_fresh", "static/sw.js"] }, "unstable": ["ffi", "fs"] } diff --git a/meilisearch.ts b/meilisearch.ts index 2ee9aca..ddaefef 100644 --- a/meilisearch.ts +++ b/meilisearch.ts @@ -132,7 +132,7 @@ export class MeiliSearchServer { const gmeta = await this.gmeta; await this.waitTask(gmeta.deleteAllDocuments()); } - async updateGallery(...gids: number[]) { + async updateGallery(...gids: (number | bigint)[]) { const gmeta = await this.gmeta; const datas = gids.map((gid) => { const d = this.db.get_gmeta_by_gid(gid); diff --git a/page/GalleryMetadata.ts b/page/GalleryMetadata.ts index e80a884..37417fc 100644 --- a/page/GalleryMetadata.ts +++ b/page/GalleryMetadata.ts @@ -37,7 +37,7 @@ export type GalleryMetadataSingle = { class GalleryMetadata { obj; - map: Map; + map: Map; constructor(text: string) { this.obj = JSON.parse(text); this.map = new Map(); diff --git a/pid_check.ts b/pid_check.ts index 56f7fff..7db48e0 100644 --- a/pid_check.ts +++ b/pid_check.ts @@ -1,6 +1,6 @@ import { exists } from "@std/fs/exists"; -export async function check_running(pid: number) { +export async function check_running(pid: number | bigint) { if (Deno.build.os == "windows") { const cmd = new Deno.Command("tasklist.exe", { args: ["/NH", "/FI", `pid eq ${pid}`], diff --git a/routes/api/_middleware.ts b/routes/api/_middleware.ts index 3c8b11e..d6ca356 100644 --- a/routes/api/_middleware.ts +++ b/routes/api/_middleware.ts @@ -7,7 +7,8 @@ import type { Token } from "../../db.ts"; function handle_auth(req: Request, ctx: FreshContext) { if (req.method === "OPTIONS") return true; const m = get_task_manager(); - if (m.db.get_user_count() === 0) return true; + const c = m.db.get_user_count(); + if (c === 0 || c === 0n) return true; const u = new URL(req.url); let is_from_cookie = false; let token: string | null | undefined = req.headers.get("X-TOKEN"); diff --git a/routes/api/eh/metadata.ts b/routes/api/eh/metadata.ts index d6bec90..86b8e4a 100644 --- a/routes/api/eh/metadata.ts +++ b/routes/api/eh/metadata.ts @@ -68,9 +68,9 @@ export const handler: Handlers = { ); for (const [k, v] of metas.map) { if (typeof v === "string") { - data[k] = gen_error(2, v); + data[k.toString()] = gen_error(2, v); } else { - data[k] = gen_data(v); + data[k.toString()] = gen_data(v); m.db.add_ehmeta(v); } } diff --git a/routes/api/export/gallery/zip/[gid].ts b/routes/api/export/gallery/zip/[gid].ts index dfee49a..8ebde5b 100644 --- a/routes/api/export/gallery/zip/[gid].ts +++ b/routes/api/export/gallery/zip/[gid].ts @@ -4,6 +4,7 @@ import { get_export_zip_response } from "../../../../../server/export_zip.ts"; import { parse_bool, parse_int } from "../../../../../server/parse_form.ts"; import type { ExportZipConfig } from "../../../../../tasks/export_zip.ts"; import { User, UserPermission } from "../../../../../db.ts"; +import { isNumNaN, parseBigInt } from "../../../../../utils.ts"; export const handler: Handlers = { async GET(req, ctx) { @@ -11,8 +12,8 @@ export const handler: Handlers = { if (u && !u.is_admin && !(u.permissions & UserPermission.ReadGallery)) { return new Response("Permission denied", { status: 403 }); } - const gid = parseInt(ctx.params.gid); - if (isNaN(gid)) { + const gid = parseBigInt(ctx.params.gid); + if (isNumNaN(gid)) { return new Response("Bad Request", { status: 400 }); } const params = new URL(req.url).searchParams; diff --git a/routes/api/file/[id].ts b/routes/api/file/[id].ts index dcff977..ada68e0 100644 --- a/routes/api/file/[id].ts +++ b/routes/api/file/[id].ts @@ -11,6 +11,7 @@ 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"; +import { isNumNaN, parseBigInt } from "../../../utils.ts"; export const handler: Handlers = { async GET(req, ctx) { @@ -25,7 +26,7 @@ export const handler: Handlers = { 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); + const id = parseBigInt(ctx.params.id); if (token && m.cfg.random_file_secret) { const s = new SortableURLSearchParams(u.search, ["token"]); const r = encode( @@ -43,7 +44,7 @@ export const handler: Handlers = { return new Response("Invalid token", { status: 403 }); } } - if (isNaN(id)) { + if (isNumNaN(id)) { if (data) return return_error(400, "Bad Request"); return new Response("Bad Request", { status: 400 }); } diff --git a/routes/api/file/random.ts b/routes/api/file/random.ts index af0e20a..a560e67 100644 --- a/routes/api/file/random.ts +++ b/routes/api/file/random.ts @@ -146,7 +146,9 @@ export const handler: Handlers = { } if (svg) { const pmeta = m.db.get_pmeta_by_token_only(f.token); - let y = f.height + 17; + let y = typeof f.height === "bigint" + ? f.height + 17n + : f.height + 17; const lines = pmeta.map((d) => { const g = m.db.get_gmeta_by_gid(d.gid); const title = @@ -156,11 +158,14 @@ export const handler: Handlers = { }/flutter/gallery/${d.gid}/page/${d.index}">${ title ? title + " - " : title }${d.name} EH`; - y += 23; + typeof y === "bigint" ? y += 23n : y += 23; return t; }); + const le = pmeta.length * 23; const dom = ` ${lines.join("\n")} diff --git a/routes/api/filemeta.ts b/routes/api/filemeta.ts index c95571b..cf984da 100644 --- a/routes/api/filemeta.ts +++ b/routes/api/filemeta.ts @@ -1,9 +1,14 @@ import { Handlers } from "$fresh/server.ts"; import type { EhFileMeta } from "../../db.ts"; import { get_task_manager } from "../../server.ts"; -import { get_string, parse_bool, parse_int } from "../../server/parse_form.ts"; +import { + get_string, + parse_big_int, + parse_bool, +} from "../../server/parse_form.ts"; import { return_data, return_error } from "../../server/utils.ts"; import { User, UserPermission } from "../../db.ts"; +import { isNumNaN, parseBigInt } from "../../utils.ts"; export function get_filemeta(token: string) { const m = get_task_manager(); @@ -30,7 +35,7 @@ export function put_filemeta(d: EhFileMeta) { } export function put_gallery_filemeta( - gid: number, + gid: number | bigint, is_nsfw: boolean, is_ad: boolean, excludes: Set, @@ -84,7 +89,13 @@ export const handler: Handlers = { } m.db.add_filemeta(f); return return_data({}); - } else if (typeof b.gid === "number") { + } else if (typeof b.gid === "number" || typeof b.gid === "string") { + const gid: number | bigint = typeof b.gid === "string" + ? parseBigInt(b.gid) + : b.gid; + if (isNumNaN(gid)) { + return return_error(8, "Invalid gid."); + } const m = get_task_manager(); const excludes: Set = Array.isArray(b.excludes) && // @ts-ignore Any @@ -92,7 +103,7 @@ export const handler: Handlers = { ? new Set(b.excludes) : new Set(); const tokens = new Set( - m.db.get_pmeta(b.gid).map((d) => d.token), + m.db.get_pmeta(gid).map((d) => d.token), ); for (const token of tokens) { if (excludes.has(token)) continue; @@ -156,7 +167,7 @@ export const handler: Handlers = { const token = await get_string(d.get("token")); const is_nsfw = await parse_bool(d.get("is_nsfw"), null); const is_ad = await parse_bool(d.get("is_ad"), null); - const gid = await parse_int(d.get("gid"), null); + const gid = await parse_big_int(d.get("gid"), null); const tokens = await get_string(d.get("tokens")); if (token) { const m = get_task_manager(); @@ -231,7 +242,13 @@ export const handler: Handlers = { ) { return put_filemeta(b); } else return return_error(3, "Invalid parameters."); - } else if (typeof b.gid === "number") { + } else if (typeof b.gid === "number" || typeof b.gid === "string") { + const gid: number | bigint = typeof b.gid === "string" + ? parseBigInt(b.gid) + : b.gid; + if (isNumNaN(gid)) { + return return_error(8, "Invalid gid."); + } if ( typeof b.is_nsfw === "boolean" && typeof b.is_ad === "boolean" @@ -242,7 +259,7 @@ export const handler: Handlers = { ? new Set(b.excludes) : new Set(); return put_gallery_filemeta( - b.gid, + gid, b.is_nsfw, b.is_ad, excludes, @@ -293,7 +310,7 @@ export const handler: Handlers = { return return_error(3, "Invalid parameters."); } const token = await get_string(d.get("token")); - const gid = await parse_int(d.get("gid"), null); + const gid = await parse_big_int(d.get("gid"), null); const is_nsfw = await parse_bool(d.get("is_nsfw"), null); const is_ad = await parse_bool(d.get("is_ad"), null); const tokens = await get_string(d.get("tokens")); diff --git a/routes/api/gallery/[gid].ts b/routes/api/gallery/[gid].ts index c5c3175..f0f2d95 100644 --- a/routes/api/gallery/[gid].ts +++ b/routes/api/gallery/[gid].ts @@ -3,6 +3,7 @@ import { get_task_manager } from "../../../server.ts"; import type { GalleryData } from "../../../server/gallery.ts"; import { return_data, return_error } from "../../../server/utils.ts"; import { User, UserPermission } from "../../../db.ts"; +import { compareNum, isNumNaN, parseBigInt } from "../../../utils.ts"; export const handler: Handlers = { GET(_req, ctx) { @@ -10,8 +11,8 @@ export const handler: Handlers = { if (u && !u.is_admin && !(u.permissions & UserPermission.ReadGallery)) { return return_error(403, "Permission denied."); } - const gid = parseInt(ctx.params.gid); - if (isNaN(gid)) { + const gid = parseBigInt(ctx.params.gid); + if (isNumNaN(gid)) { return return_error(400, "Failed to parse gid."); } const m = get_task_manager(); @@ -19,9 +20,11 @@ export const handler: Handlers = { if (!meta) return return_error(404, "Gallery not found."); const data: GalleryData = { meta, - tags: m.db.get_gtags_full(gid).sort((a, b) => a.id - b.id), + tags: m.db.get_gtags_full(gid).sort((a, b) => + compareNum(a.id, b.id) + ), pages: m.db.get_extended_pmeta(gid).sort((a, b) => - a.index - b.index + compareNum(a.index, b.index) ), }; return return_data(data); diff --git a/routes/api/gallery/meta/[gids].ts b/routes/api/gallery/meta/[gids].ts index ebc7708..f2f3a27 100644 --- a/routes/api/gallery/meta/[gids].ts +++ b/routes/api/gallery/meta/[gids].ts @@ -8,6 +8,7 @@ import { return_error, } from "../../../../server/utils.ts"; import { GMeta, User, UserPermission } from "../../../../db.ts"; +import { isNumNaN, parseBigInt } from "../../../../utils.ts"; export const handler: Handlers = { GET(_req, ctx) { @@ -20,18 +21,18 @@ export const handler: Handlers = { return return_error(403, "Permission denied."); } const gids = new Set( - ctx.params.gids.split(",").map((v) => parseInt(v)).filter((v) => - !isNaN(v) + ctx.params.gids.split(",").map((v) => parseBigInt(v)).filter((v) => + !isNumNaN(v) ), ); const m = get_task_manager(); - const re: Record> = {}; + const re: Record> = {}; for (const gid of gids) { const meta = m.db.get_gmeta_by_gid(gid); if (meta) { - re[gid] = gen_data(meta); + re[gid.toString()] = gen_data(meta); } else { - re[gid] = gen_error(404, "Not found"); + re[gid.toString()] = gen_error(404, "Not found"); } } return return_data(re); diff --git a/routes/api/status.ts b/routes/api/status.ts index a04dd5b..700da77 100644 --- a/routes/api/status.ts +++ b/routes/api/status.ts @@ -21,8 +21,9 @@ async function check_ffmpeg_api() { export const handler: Handlers = { async GET(req, ctx) { const m = get_task_manager(); + const c = m.db.get_user_count(); const is_authed = ctx.state.user !== undefined || - m.db.get_user_count() === 0; + c === 0 || c === 0n; const ffmpeg_binary_enabled = await check_ffmpeg_binary( m.cfg.ffmpeg_path, ); @@ -50,7 +51,7 @@ export const handler: Handlers = { key: m.cfg.meili_search_api_key || m.cfg.meili_update_api_key, }; } - const no_user = m.db.get_user_count() === 0; + const no_user = c === 0 || c === 0n; const is_docker = isDocker(); return return_data({ ffmpeg_api_enabled, diff --git a/routes/api/tag/[id].ts b/routes/api/tag/[id].ts index d3a7b63..c991af6 100644 --- a/routes/api/tag/[id].ts +++ b/routes/api/tag/[id].ts @@ -8,6 +8,7 @@ import { return_data, return_error, } from "../../../server/utils.ts"; +import { isNumNaN, parseBigInt } from "../../../utils.ts"; export const handler: Handlers = { GET(_req, ctx) { @@ -18,10 +19,10 @@ export const handler: Handlers = { const ids = decodeURIComponent(ctx.params.id).split(","); const r: Record> = {}; for (const _id of ids) { - const id = parseInt(_id); + const id = parseBigInt(_id); let key: string | undefined; let tag: string | undefined; - if (isNaN(id)) { + if (isNumNaN(id)) { tag = _id; } const m = get_task_manager(); diff --git a/routes/api/task.ts b/routes/api/task.ts index b9d4493..06348cb 100644 --- a/routes/api/task.ts +++ b/routes/api/task.ts @@ -6,7 +6,7 @@ import type { TaskServerSocketData, } from "../../server/task.ts"; import { ExitTarget } from "../../signal_handler.ts"; -import { get_string, parse_int } from "../../server/parse_form.ts"; +import { get_string, parse_big_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"; @@ -111,7 +111,7 @@ export const handler: Handlers = { return return_error(1, "type is required"); } if (typ == "download") { - const gid = await parse_int(form.get("gid"), null); + const gid = await parse_big_int(form.get("gid"), null); const token = await get_string(form.get("token")); if (gid === null) { return return_error(2, "gid is required"); @@ -138,7 +138,7 @@ export const handler: Handlers = { return return_error(500, e.message); } } else if (typ == "export_zip") { - const gid = await parse_int(form.get("gid"), null); + const gid = await parse_big_int(form.get("gid"), null); if (gid === null) { return return_error(2, "gid is required"); } diff --git a/routes/api/thumbnail/[id].ts b/routes/api/thumbnail/[id].ts index fb2c774..5f61ee5 100644 --- a/routes/api/thumbnail/[id].ts +++ b/routes/api/thumbnail/[id].ts @@ -10,7 +10,7 @@ import { ThumbnailConfig, ThumbnailGenMethod, } from "../../../thumbnail/base.ts"; -import { sure_dir } from "../../../utils.ts"; +import { isNumNaN, parseBigInt, sure_dir } from "../../../utils.ts"; import { ThumbnailMethod } from "../../../config.ts"; import { fb_generate_thumbnail } from "../../../thumbnail/ffmpeg_binary.ts"; import { @@ -35,7 +35,7 @@ export const handler: Handlers = { ) { return new Response("Permission denied", { status: 403 }); } - const id = parseInt(ctx.params.id); + const id = parseBigInt(ctx.params.id); const m = get_task_manager(); const u = new URL(req.url); const token = u.searchParams.get("token"); @@ -56,7 +56,7 @@ export const handler: Handlers = { return new Response("Invalid token", { status: 403 }); } } - if (isNaN(id)) { + if (isNumNaN(id)) { return new Response("Bad Request", { status: 400 }); } const b = m.cfg.thumbnail_dir; @@ -85,20 +85,24 @@ export const handler: Handlers = { cfg.height = height; } else if (width !== null) { cfg.width = width; - cfg.height = Math.floor(f.height / f.width * width); + cfg.height = Math.floor(Number(f.height) / Number(f.width) * width); cfg.method = ThumbnailGenMethod.Unknown; } else if (height !== null) { cfg.height = height; - cfg.width = Math.floor(f.width / f.height * height); + cfg.width = Math.floor(Number(f.width) / Number(f.height) * height); cfg.method = ThumbnailGenMethod.Unknown; } else { if (f.width > f.height) { cfg.width = max; - cfg.height = Math.floor(f.height / f.width * max); + cfg.height = Math.floor( + Number(f.height) / Number(f.width) * max, + ); cfg.method = ThumbnailGenMethod.Unknown; } else { cfg.height = max; - cfg.width = Math.floor(f.width / f.height * max); + cfg.width = Math.floor( + Number(f.width) / Number(f.height) * max, + ); cfg.method = ThumbnailGenMethod.Unknown; } } diff --git a/routes/api/user.ts b/routes/api/user.ts index 0c7bafe..b888347 100644 --- a/routes/api/user.ts +++ b/routes/api/user.ts @@ -1,7 +1,12 @@ import { Handlers } from "$fresh/server.ts"; import { Token, User, UserPermission } from "../../db.ts"; import { get_task_manager } from "../../server.ts"; -import { get_string, parse_bool, parse_int } from "../../server/parse_form.ts"; +import { + get_string, + parse_big_int, + parse_bool, + parse_int, +} from "../../server/parse_form.ts"; import type { BUser } from "../../server/user.ts"; import { return_data, return_error } from "../../server/utils.ts"; import pbkdf2Hmac from "pbkdf2-hmac"; @@ -18,7 +23,7 @@ export const handler: Handlers = { } catch (_) { return return_error(3, "Invalid parameters."); } - const id = await parse_int(data.get("id"), null); + const id = await parse_big_int(data.get("id"), null); const username = await get_string(data.get("username")); if (id === null && !username) { return return_error(1, "user not specified."); @@ -44,7 +49,7 @@ export const handler: Handlers = { }, async GET(req, ctx) { const u = new URL(req.url); - const id = await parse_int(u.searchParams.get("id"), null); + const id = await parse_big_int(u.searchParams.get("id"), null); const username = u.searchParams.get("username"); const user = ctx.state.user; if (id === null && !username && !user) { @@ -78,7 +83,7 @@ export const handler: Handlers = { } catch (_) { return return_error(3, "Invalid parameters."); } - const id = await parse_int(data.get("id"), null); + const id = await parse_big_int(data.get("id"), null); const username = await get_string(data.get("username")); if (id === null && !username && !user) { return return_error(1, "user not specified."); @@ -139,7 +144,7 @@ export const handler: Handlers = { m.db.update_user(us); if (revoke_token) { const token = ctx.state.token; - let tid: number | undefined = undefined; + let tid: number | bigint | undefined = undefined; if (user && us.id == user.id && token) tid = token.id; m.db.delete_user_token(us.id, tid); } @@ -182,7 +187,8 @@ export const handler: Handlers = { "SHA-512", ), ); - if (m.db.get_user_count() === 0) { + const c = m.db.get_user_count(); + if (c === 0 || c === 0n) { m.db.add_root_user(name, hpassword); return return_data(0, 201); } else { diff --git a/routes/file/[id].ts b/routes/file/[id].ts index 189be40..cb5ca87 100644 --- a/routes/file/[id].ts +++ b/routes/file/[id].ts @@ -6,11 +6,12 @@ import { } from "../../server/get_file_response.ts"; import pbkdf2Hmac from "pbkdf2-hmac"; import { encodeBase64 as encode } from "@std/encoding/base64"; +import { isNumNaN, parseBigInt } from "../../utils.ts"; export const handler: Handlers = { async GET(req, ctx) { - const id = parseInt(ctx.params.id); - if (isNaN(id)) { + const id = parseBigInt(ctx.params.id); + if (isNumNaN(id)) { return new Response("Bad Request", { status: 400 }); } const m = get_task_manager(); diff --git a/routes/file/[verify]/[id].ts b/routes/file/[verify]/[id].ts index 073b3cc..661eb04 100644 --- a/routes/file/[verify]/[id].ts +++ b/routes/file/[verify]/[id].ts @@ -6,11 +6,12 @@ import { } from "../../../server/get_file_response.ts"; import pbkdf2Hmac from "pbkdf2-hmac"; import { encodeBase64 as encode } from "@std/encoding/base64"; +import { isNumNaN, parseBigInt } from "../../../utils.ts"; export const handler: Handlers = { async GET(req, ctx) { - const id = parseInt(ctx.params.id); - if (isNaN(id)) { + const id = parseBigInt(ctx.params.id); + if (isNumNaN(id)) { return new Response("Bad Request", { status: 400 }); } const m = get_task_manager(); diff --git a/routes/thumbnail/[id].ts b/routes/thumbnail/[id].ts index 185dcdf..dbd0512 100644 --- a/routes/thumbnail/[id].ts +++ b/routes/thumbnail/[id].ts @@ -3,7 +3,7 @@ import { exists } from "@std/fs/exists"; import { get_task_manager } from "../../server.ts"; import { parse_int } from "../../server/parse_form.ts"; import { generate_filename, ThumbnailConfig } from "../../thumbnail/base.ts"; -import { sure_dir } from "../../utils.ts"; +import { isNumNaN, parseBigInt, sure_dir } from "../../utils.ts"; import { get_file_response, GetFileResponseOptions, @@ -14,8 +14,8 @@ import { SortableURLSearchParams } from "../../server/SortableURLSearchParams.ts export const handler: Handlers = { async GET(req, ctx) { - const id = parseInt(ctx.params.id); - if (isNaN(id)) { + const id = parseBigInt(ctx.params.id); + if (isNumNaN(id)) { return new Response("Bad Request", { status: 400 }); } const m = get_task_manager(); diff --git a/server/check_auth.ts b/server/check_auth.ts deleted file mode 100644 index d008c3a..0000000 --- a/server/check_auth.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { get_task_manager } from "../server.ts"; -import { parse_cookies } from "./cookies.ts"; - -export function check_auth(req: Request) { - if (req.method === "OPTIONS") return true; - const m = get_task_manager(); - if (m.db.get_user_count() === 0) return true; - const u = new URL(req.url); - let token: string | null | undefined = req.headers.get("X-TOKEN"); - const cookies = parse_cookies(req.headers.get("Cookie")); - if (!token) { - token = cookies.get("token"); - } - const check = () => { - if (u.pathname === "/api/token" && req.method === "PUT") return true; - if (u.pathname === "/api/status" && req.method === "GET") return true; - return false; - }; - if (!token) return check(); - const t = m.db.get_token(token); - const now = (new Date()).getTime(); - if (!t || t.expired.getTime() < now) return check(); - const user = m.db.get_user(t.uid); - if (!user) { - m.db.delete_token(token); - return check(); - } - return true; -} diff --git a/server/export_zip.ts b/server/export_zip.ts index 425bbd8..5214327 100644 --- a/server/export_zip.ts +++ b/server/export_zip.ts @@ -1,16 +1,23 @@ import { Uint8ArrayReader, ZipWriter } from "zipjs/index.js"; import type { EhDb, PMeta } from "../db.ts"; import type { ExportZipConfig } from "../tasks/export_zip.ts"; -import { addZero, configureZipJs, limitFilename } from "../utils.ts"; +import { + addZero, + compareNum, + configureZipJs, + limitFilename, +} from "../utils.ts"; export function get_export_zip_response( - gid: number, + gid: number | bigint, db: EhDb, cfg: ExportZipConfig, ) { const gmeta = db.get_gmeta_by_gid(gid); if (!gmeta) return new Response("Gallery not found.", { status: 404 }); - const pmetas = db.get_pmeta(gid).sort((a, b) => a.index - b.index); + const pmetas = db.get_pmeta(gid).sort((a, b) => + compareNum(a.index, b.index) + ); const p = pmetas.length; const l = gmeta.filecount.toString().length; let c = 0; diff --git a/server/files.ts b/server/files.ts index fef3445..c3dc306 100644 --- a/server/files.ts +++ b/server/files.ts @@ -1,14 +1,14 @@ export type EhFileBasic = { - id: number; - width: number; - height: number; + id: number | bigint; + width: number | bigint; + height: number | bigint; is_original: boolean; }; export type EhFileExtend = { - id: number; - width: number; - height: number; + id: number | bigint; + width: number | bigint; + height: number | bigint; is_original: boolean; token: string; }; diff --git a/server/parse_form.ts b/server/parse_form.ts index 7155932..da1bee3 100644 --- a/server/parse_form.ts +++ b/server/parse_form.ts @@ -1,3 +1,5 @@ +import { isNumNaN, parseBigInt } from "../utils.ts"; + export async function get_string(value: FormDataEntryValue | null) { if (value === null) return null; return typeof value === "string" ? value : await value.text(); @@ -28,3 +30,14 @@ export async function parse_int( if (isNaN(n)) return def; return n; } + +export async function parse_big_int( + value: FormDataEntryValue | null, + def: T, +): Promise { + if (value === null) return def; + const v = typeof value === "string" ? value : await value.text(); + const n = parseBigInt(v); + if (isNumNaN(n)) return def; + return n; +} diff --git a/server/parse_form_test.ts b/server/parse_form_test.ts index 9a3dd92..3c9dae5 100644 --- a/server/parse_form_test.ts +++ b/server/parse_form_test.ts @@ -1,5 +1,5 @@ import { assertEquals } from "@std/assert"; -import { parse_bool, parse_int } from "./parse_form.ts"; +import { parse_big_int, parse_bool, parse_int } from "./parse_form.ts"; Deno.test("parse_bool_test", async () => { const f = new FormData(); @@ -27,3 +27,17 @@ Deno.test("parse_int_test", async () => { f.append("d", "-1"); assertEquals(await parse_int(f.get("d"), null), -1); }); + +Deno.test("parse_big_int_test", async () => { + const f = new FormData(); + f.append("a", "d"); + assertEquals(await parse_big_int(f.get("a"), null), null); + assertEquals(await parse_big_int(f.get("a"), 1), 1); + f.append("c", "1"); + assertEquals(await parse_big_int(f.get("c"), null), 1); + assertEquals(await parse_big_int(f.get("c"), 2), 1); + f.append("d", "-1"); + assertEquals(await parse_big_int(f.get("d"), null), -1); + f.append("b", "3152921504606847000"); + assertEquals(await parse_big_int(f.get("b"), null), 3152921504606847000n); +}); diff --git a/server/task.ts b/server/task.ts index 52262e3..8f8a28b 100644 --- a/server/task.ts +++ b/server/task.ts @@ -10,14 +10,18 @@ export type TaskServerSocketData = | { type: "tasks"; tasks: Task[]; - running: number[]; + running: (number | bigint)[]; } | { type: "ping" } | { type: "pong" }; type EventMap = { - new_download_task: { gid: number; token: string; cfg?: DownloadConfig }; - new_export_zip_task: { gid: number; cfg?: ExportZipConfig }; + new_download_task: { + gid: number | bigint; + token: string; + cfg?: DownloadConfig; + }; + new_export_zip_task: { gid: number | bigint; cfg?: ExportZipConfig }; }; export type TaskClientSocketData = diff --git a/server/user.ts b/server/user.ts index 70e47ca..3ba548e 100644 --- a/server/user.ts +++ b/server/user.ts @@ -1,7 +1,7 @@ import type { UserPermission } from "../db.ts"; export type BUser = { - id: number; + id: number | bigint; username: string; is_admin: boolean; permissions: UserPermission; diff --git a/task.ts b/task.ts index 77edaf6..739efc3 100644 --- a/task.ts +++ b/task.ts @@ -6,11 +6,11 @@ export enum TaskType { } export type Task = { - id: number; + id: number | bigint; type: T; - gid: number; + gid: number | bigint; token: string; - pid: number; + pid: number | bigint; details: string | null; }; @@ -43,12 +43,12 @@ export type TaskExportZipProgress = { }; export type TaskUpdateMeiliSearchDataProgress = { - total_gallery: number; + total_gallery: number | bigint; updated_gallery: number; }; export type TaskFixGalleryPageProgress = { - total_gallery: number; + total_gallery: number | bigint; checked_gallery: number; }; @@ -61,7 +61,7 @@ export type TaskProgressBasicType = { export type TaskProgress = { type: T; - task_id: number; + task_id: number | bigint; detail: TaskProgressBasicType[T]; }; diff --git a/task_manager.ts b/task_manager.ts index ab816c5..7bcfb05 100644 --- a/task_manager.ts +++ b/task_manager.ts @@ -55,7 +55,7 @@ export class TaskManager extends EventTarget { cfg; client; db; - running_tasks: Map; + running_tasks: Map; max_task_count; meilisearch?: MeiliSearchServer; #abort; @@ -108,7 +108,7 @@ export class TaskManager extends EventTarget { return this.#abort.signal; } async add_download_task( - gid: number, + gid: number | bigint, token: string, cfg?: DownloadConfig, mark_already = false, @@ -129,7 +129,7 @@ export class TaskManager extends EventTarget { }; return await this.#add_task(task); } - async add_export_zip_task(gid: number, cfg?: ExportZipConfig) { + async add_export_zip_task(gid: number | bigint, cfg?: ExportZipConfig) { const task: Task = { gid, token: "", @@ -180,7 +180,7 @@ export class TaskManager extends EventTarget { const ut = (await this.db.check_onetime_task()).map((t) => t.id); if (ut.length && !ut.includes(task.id)) return; let t = task; - if (task.pid !== Deno.pid) { + if (task.pid != Deno.pid) { const p = await this.db.set_task_pid(t); if (p == null) return; t = p; @@ -198,8 +198,8 @@ export class TaskManager extends EventTarget { } async check_running_tasks() { this.#check_closed(); - const removed_task: number[] = []; - const fataled_task: number[] = []; + const removed_task: (number | bigint)[] = []; + const fataled_task: (number | bigint)[] = []; for (const [id, task] of this.running_tasks) { const status = await promiseState(task.task); if (status.status == PromiseStatus.Fulfilled && status.value) { @@ -241,7 +241,7 @@ export class TaskManager extends EventTarget { } dispatchTaskProgressEvent( type: T, - task_id: number, + task_id: number | bigint, detail: TaskProgressBasicType[T], ) { return this.dispatchEvent("task_progress", { type, task_id, detail }); diff --git a/tasks/download.ts b/tasks/download.ts index cb4a82e..2147ad8 100644 --- a/tasks/download.ts +++ b/tasks/download.ts @@ -30,7 +30,7 @@ export type DownloadConfig = { download_original_img?: boolean; max_retry_count?: number; remove_previous_gallery?: boolean; - replaced_gallery?: { gid: number; token: string }[]; + replaced_gallery?: { gid: number | bigint; token: string }[]; }; export const DEFAULT_DOWNLOAD_CONFIG: DownloadConfig = {}; @@ -326,13 +326,13 @@ export async function download_task( if (f === undefined) throw Error("Failed to get file."); m.add_new_details({ downloaded: 0, - height: f.height, + height: Number(f.height), index: i.index, is_original: f.is_original, name: i.name, token: i.page_token, total: 0, - width: f.width, + width: Number(f.width), started: 0, speed: 0, last_updated: 0, diff --git a/tasks/export_zip.ts b/tasks/export_zip.ts index b788b9a..924fd69 100644 --- a/tasks/export_zip.ts +++ b/tasks/export_zip.ts @@ -4,6 +4,7 @@ import type { EhDb } from "../db.ts"; import { addZero, asyncForEach, + compareNum, configureZipJs, filterFilename, limitFilename, @@ -36,7 +37,7 @@ export async function export_zip( ? ecfg.jpn_title : cfg.export_zip_jpn_title; const progress: TaskExportZipProgress = { - total_page: g.filecount, + total_page: Number(g.filecount), added_page: 0, }; const sendEvent = () => { @@ -64,7 +65,7 @@ export async function export_zip( }); const l = g.filecount.toString().length; await asyncForEach( - db.get_pmeta(gid).sort((a, b) => a.index - b.index), + db.get_pmeta(gid).sort((a, b) => compareNum(a.index, b.index)), async (p) => { const f = db.get_files(p.token); const t = db.get_filemeta(p.token); diff --git a/tasks/update_meili_search_data.ts b/tasks/update_meili_search_data.ts index 0efa8c9..8317703 100644 --- a/tasks/update_meili_search_data.ts +++ b/tasks/update_meili_search_data.ts @@ -18,7 +18,7 @@ export async function update_meili_search_data( progress, ); }; - if (task.gid !== 0) { + if (task.gid != 0) { progress.total_gallery = 1; sendEvent(); await manager.meilisearch.updateGallery(task.gid); diff --git a/utils.ts b/utils.ts index 235dc26..1d11ed3 100644 --- a/utils.ts +++ b/utils.ts @@ -120,7 +120,7 @@ export async function asyncForEach( } } -export function addZero(n: string | number, len: number) { +export function addZero(n: string | number | bigint, len: number) { let s = n.toString(); while (s.length < len) s = "0" + s; return s; @@ -285,7 +285,28 @@ export function isDocker() { export function toJSON(obj: unknown) { return JSON.stringify( obj, - (_, value) => - typeof value === "bigint" ? parseInt(value.toString()) : value, + (_, value) => { + if (typeof value === "bigint") { + const s = value.toString(); + const t = parseInt(s); + if (Number.isSafeInteger(t)) return t; + return s; + } + return value; + }, ); } + +export function parseBigInt(str: string) { + const t = parseInt(str); + if (isNaN(t)) return t; + return Number.isSafeInteger(t) ? BigInt(str) : t; +} + +export function isNumNaN(num: number | bigint) { + return typeof num === "number" ? isNaN(num) : false; +} + +export function compareNum(num1: number | bigint, num2: number | bigint) { + return num1 == num2 ? 0 : num1 < num2 ? -1 : 1; +} diff --git a/utils_test.ts b/utils_test.ts index eeef861..2f44e34 100644 --- a/utils_test.ts +++ b/utils_test.ts @@ -6,6 +6,7 @@ import { asyncFilter, asyncForEach, calFileMd5, + compareNum, filterFilename, map, promiseState, @@ -166,4 +167,16 @@ Deno.test("toJSON_test", () => { toJSON([1099511627776n, { a: 45n }]), '[1099511627776,{"a":45}]', ); + assertEquals(toJSON([9007199254740992n]), '["9007199254740992"]'); +}); + +Deno.test("compareNum_test", () => { + assertEquals([3, 4n, 1, 2n, 11n, 5n].sort(compareNum), [ + 1, + 2n, + 3, + 4n, + 5n, + 11n, + ]); });