diff --git a/client.ts b/client.ts index 0724b45..330dbdc 100644 --- a/client.ts +++ b/client.ts @@ -1,6 +1,7 @@ import type { Config } from "./config.ts"; import { load_gallery_metadata } from "./page/GalleryMetadata.ts"; import { load_gallery_page } from "./page/GalleryPage.ts"; +import { load_home_overview_page } from "./page/HomeOverviewPage.ts"; import { load_mpv_page } from "./page/MPVPage.ts"; import { load_single_page } from "./page/SinglePage.ts"; import { RecoverableError } from "./task_manager.ts"; @@ -200,4 +201,24 @@ export class Client { } return load_mpv_page(await re.text(), this); } + /** + * Fetch home page overview + * @returns null if not logged in + */ + async fetchHomeOverviewPage() { + const url = `https://${this.host}/home.php`; + const re = await this.get(url); + if (re.redirected) { + const u = new URL(re.url); + if (u.pathname.startsWith("/login")) { + return null; + } + } + if (re.status != 200) { + throw new Error( + `Fetch ${url} failed, status ${re.status} ${re.statusText}`, + ); + } + return load_home_overview_page(await re.text()); + } } diff --git a/deno.json b/deno.json index bbb043d..215241a 100644 --- a/deno.json +++ b/deno.json @@ -3,15 +3,15 @@ "importMap": "./import_map.json", "tasks": { "cache": "deno cache main.ts server-dev.ts", - "server-dev": "deno run -A --unstable \"--watch=static/*.css,static/*.ts,static/*/,routes/,translation/\" server-dev.ts", - "server": "deno run -A --unstable server-run.ts", - "test": "deno test --allow-read=./ --allow-net --allow-write=./ --allow-run=tasklist.exe --allow-env --unstable", - "run": "deno run --allow-read=./ --allow-write=./ --allow-run=tasklist.exe --allow-env=DENO_DEPLOYMENT_ID --allow-net --unstable", - "compile": "deno compile --allow-read=./ --allow-write=./ --allow-run=tasklist.exe --allow-env=DENO_DEPLOYMENT_ID --allow-net --unstable", - "compile_full": "deno compile --allow-read --allow-write --allow-run=tasklist.exe --allow-env=DENO_DEPLOYMENT_ID --allow-net --unstable", + "server-dev": "deno run -A \"--watch=static/*.css,static/*.ts,static/*/,routes/,translation/\" server-dev.ts", + "server": "deno run -A server-run.ts", + "test": "deno test --allow-read=./ --allow-net --allow-write=./ --allow-run=tasklist.exe --allow-env", + "run": "deno run --allow-read=./ --allow-write=./ --allow-run=tasklist.exe --allow-env=DENO_DEPLOYMENT_ID,DOCKER,DB_USE_FFI --allow-ffi --allow-net", + "compile": "deno compile --allow-read=./ --allow-write=./ --allow-run=tasklist.exe --allow-env=DENO_DEPLOYMENT_ID --allow-net", + "compile_full": "deno compile --allow-read --allow-write --allow-run=tasklist.exe --allow-env=DENO_DEPLOYMENT_ID --allow-net", "fetch": "deno run --allow-read=./ --allow-write=./ --allow-net fetch_static_files.ts", "gen_meili_server_key": "deno run --allow-net scripts/gen_meili_server_key.ts", - "server-build": "deno run -A --unstable server-dev.ts build", + "server-build": "deno run -A server-dev.ts build", "prebuild": "deno run -A scripts/prebuild.ts" }, "fmt": { @@ -34,5 +34,6 @@ "tags": ["fresh", "recommended"] }, "exclude": ["_fresh"] - } + }, + "unstable": ["ffi", "fs"] } diff --git a/fresh.gen.ts b/fresh.gen.ts index 2942943..0ab8665 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -6,31 +6,32 @@ import * as $0 from "./routes/_middleware.ts"; import * as $1 from "./routes/api/_middleware.ts"; import * as $2 from "./routes/api/config.ts"; import * as $3 from "./routes/api/deploy_id.ts"; -import * as $4 from "./routes/api/exit.ts"; -import * as $5 from "./routes/api/export/gallery/zip/[gid].ts"; -import * as $6 from "./routes/api/file/[id].ts"; -import * as $7 from "./routes/api/file/random.ts"; -import * as $8 from "./routes/api/file/upload.ts"; -import * as $9 from "./routes/api/filemeta.ts"; -import * as $10 from "./routes/api/filemeta/[token].ts"; -import * as $11 from "./routes/api/files/[token].ts"; -import * as $12 from "./routes/api/gallery/[gid].ts"; -import * as $13 from "./routes/api/gallery/list.ts"; -import * as $14 from "./routes/api/health_check.ts"; -import * as $15 from "./routes/api/status.ts"; -import * as $16 from "./routes/api/tag/[id].ts"; -import * as $17 from "./routes/api/tag/rows.ts"; -import * as $18 from "./routes/api/task.ts"; -import * as $19 from "./routes/api/thumbnail/[id].ts"; -import * as $20 from "./routes/api/token.ts"; -import * as $21 from "./routes/api/user.ts"; -import * as $22 from "./routes/file/[id].ts"; -import * as $23 from "./routes/file/_middleware.ts"; -import * as $24 from "./routes/index.tsx"; -import * as $25 from "./routes/manifest.json.ts"; -import * as $26 from "./routes/thumbnail/[id].ts"; -import * as $27 from "./routes/thumbnail/_middleware.ts"; -import * as $28 from "./routes/upload.tsx"; +import * as $4 from "./routes/api/eh/image_limit.ts"; +import * as $5 from "./routes/api/exit.ts"; +import * as $6 from "./routes/api/export/gallery/zip/[gid].ts"; +import * as $7 from "./routes/api/file/[id].ts"; +import * as $8 from "./routes/api/file/random.ts"; +import * as $9 from "./routes/api/file/upload.ts"; +import * as $10 from "./routes/api/filemeta.ts"; +import * as $11 from "./routes/api/filemeta/[token].ts"; +import * as $12 from "./routes/api/files/[token].ts"; +import * as $13 from "./routes/api/gallery/[gid].ts"; +import * as $14 from "./routes/api/gallery/list.ts"; +import * as $15 from "./routes/api/health_check.ts"; +import * as $16 from "./routes/api/status.ts"; +import * as $17 from "./routes/api/tag/[id].ts"; +import * as $18 from "./routes/api/tag/rows.ts"; +import * as $19 from "./routes/api/task.ts"; +import * as $20 from "./routes/api/thumbnail/[id].ts"; +import * as $21 from "./routes/api/token.ts"; +import * as $22 from "./routes/api/user.ts"; +import * as $23 from "./routes/file/[id].ts"; +import * as $24 from "./routes/file/_middleware.ts"; +import * as $25 from "./routes/index.tsx"; +import * as $26 from "./routes/manifest.json.ts"; +import * as $27 from "./routes/thumbnail/[id].ts"; +import * as $28 from "./routes/thumbnail/_middleware.ts"; +import * as $29 from "./routes/upload.tsx"; import * as $$0 from "./islands/Container.tsx"; import * as $$1 from "./islands/Settings.tsx"; import * as $$2 from "./islands/TaskManager.tsx"; @@ -42,31 +43,32 @@ const manifest = { "./routes/api/_middleware.ts": $1, "./routes/api/config.ts": $2, "./routes/api/deploy_id.ts": $3, - "./routes/api/exit.ts": $4, - "./routes/api/export/gallery/zip/[gid].ts": $5, - "./routes/api/file/[id].ts": $6, - "./routes/api/file/random.ts": $7, - "./routes/api/file/upload.ts": $8, - "./routes/api/filemeta.ts": $9, - "./routes/api/filemeta/[token].ts": $10, - "./routes/api/files/[token].ts": $11, - "./routes/api/gallery/[gid].ts": $12, - "./routes/api/gallery/list.ts": $13, - "./routes/api/health_check.ts": $14, - "./routes/api/status.ts": $15, - "./routes/api/tag/[id].ts": $16, - "./routes/api/tag/rows.ts": $17, - "./routes/api/task.ts": $18, - "./routes/api/thumbnail/[id].ts": $19, - "./routes/api/token.ts": $20, - "./routes/api/user.ts": $21, - "./routes/file/[id].ts": $22, - "./routes/file/_middleware.ts": $23, - "./routes/index.tsx": $24, - "./routes/manifest.json.ts": $25, - "./routes/thumbnail/[id].ts": $26, - "./routes/thumbnail/_middleware.ts": $27, - "./routes/upload.tsx": $28, + "./routes/api/eh/image_limit.ts": $4, + "./routes/api/exit.ts": $5, + "./routes/api/export/gallery/zip/[gid].ts": $6, + "./routes/api/file/[id].ts": $7, + "./routes/api/file/random.ts": $8, + "./routes/api/file/upload.ts": $9, + "./routes/api/filemeta.ts": $10, + "./routes/api/filemeta/[token].ts": $11, + "./routes/api/files/[token].ts": $12, + "./routes/api/gallery/[gid].ts": $13, + "./routes/api/gallery/list.ts": $14, + "./routes/api/health_check.ts": $15, + "./routes/api/status.ts": $16, + "./routes/api/tag/[id].ts": $17, + "./routes/api/tag/rows.ts": $18, + "./routes/api/task.ts": $19, + "./routes/api/thumbnail/[id].ts": $20, + "./routes/api/token.ts": $21, + "./routes/api/user.ts": $22, + "./routes/file/[id].ts": $23, + "./routes/file/_middleware.ts": $24, + "./routes/index.tsx": $25, + "./routes/manifest.json.ts": $26, + "./routes/thumbnail/[id].ts": $27, + "./routes/thumbnail/_middleware.ts": $28, + "./routes/upload.tsx": $29, }, islands: { "./islands/Container.tsx": $$0, diff --git a/page/HomeOverviewPage.ts b/page/HomeOverviewPage.ts new file mode 100644 index 0000000..ef2a10c --- /dev/null +++ b/page/HomeOverviewPage.ts @@ -0,0 +1,71 @@ +import { DOMParser, Element } from "deno_dom/deno-dom-wasm-noinit.ts"; +import { initDOMParser } from "../utils.ts"; + +export class HomeOverviewPage { + dom; + doc; + #panel: Map | undefined; + #current_image_limit: number | undefined; + #max_image_limit: number | undefined; + constructor(html: string) { + const dom = (new DOMParser()).parseFromString(html, "text/html"); + if (!dom) { + throw Error("Failed to parse HTML document."); + } + this.dom = dom; + const doc = this.dom.documentElement; + if (!doc) { + throw Error("HTML document don't have a document element."); + } + this.doc = doc; + } + get panel() { + if (this.#panel === undefined) { + const panel = new Map(); + const keys = this.doc.querySelectorAll(".stuffbox > h2"); + for (const key of keys) { + const k = key as Element; + const v = k.nextElementSibling; + if (v) { + panel.set(k.innerText, v as Element); + } + } + this.#panel = panel; + return panel; + } else return this.#panel; + } + get current_image_limit() { + if (this.#current_image_limit === undefined) { + const panel = this.panel.get("Image Limits"); + if (!panel) { + throw Error('Failed to find panel "Image Limits".'); + } + const limits = panel.querySelectorAll("p:nth-child(1) > strong"); + if (!limits.length) { + throw Error("Failed to find limits."); + } + const current = limits[0] as Element; + const max = limits[1] as Element; + const current_image_limit = parseInt(current.innerText); + const max_image_limit = parseInt(max.innerText); + if (isNaN(current_image_limit) || isNaN(max_image_limit)) { + throw Error("Failed to parse limits."); + } + this.#current_image_limit = current_image_limit; + this.#max_image_limit = max_image_limit; + return current_image_limit; + } else return this.#current_image_limit; + } + get max_image_limit(): number { + if (this.#max_image_limit === undefined) { + this.current_image_limit; + // @ts-ignore Already assigned in current_image_limit. + return this.#max_image_limit; + } else return this.#max_image_limit; + } +} + +export async function load_home_overview_page(html: string) { + await initDOMParser(); + return new HomeOverviewPage(html); +} diff --git a/page/HomeOverviewPage_test.ts b/page/HomeOverviewPage_test.ts new file mode 100644 index 0000000..b02f135 --- /dev/null +++ b/page/HomeOverviewPage_test.ts @@ -0,0 +1,24 @@ +import { assert } from "std/assert/mod.ts"; +import { Client } from "../client.ts"; +import { load_settings } from "../config.ts"; +import { API_PERMISSION } from "../test_base.ts"; + +const MAX_IMAGE_LIMITS = [5000, 10000, 25000, 50000]; + +Deno.test({ + name: "HomeOverviewPage_test", + permissions: API_PERMISSION, +}, async () => { + const cfg = await load_settings("./config.json"); + const client = new Client(cfg); + const re = await client.fetchHomeOverviewPage(); + if (re === null) return; + console.log( + "Image limit:", + re.current_image_limit, + "/", + re.max_image_limit, + ); + assert(re.current_image_limit <= re.max_image_limit); + assert(MAX_IMAGE_LIMITS.includes(re.max_image_limit)); +}); diff --git a/routes/api/eh/image_limit.ts b/routes/api/eh/image_limit.ts new file mode 100644 index 0000000..7a75215 --- /dev/null +++ b/routes/api/eh/image_limit.ts @@ -0,0 +1,28 @@ +import { Handlers } from "$fresh/server.ts"; +import { User } from "../../../db.ts"; +import { get_task_manager } from "../../../server.ts"; +import { EHImageLimit } from "../../../server/eh.ts"; +import { return_data, return_error } from "../../../server/utils.ts"; + +export const handler: Handlers = { + async GET(_req, ctx) { + const user = ctx.state.user; + if (user && !user.is_admin) { + return return_error(403, "Permission denied."); + } + const m = get_task_manager(); + try { + const re = await m.client.fetchHomeOverviewPage(); + if (re === null) { + return return_error(1, "Not logged in."); + } + + return return_data({ + max: re.max_image_limit, + current: re.current_image_limit, + }); + } catch (e) { + return return_error(500, e.message); + } + }, +}; diff --git a/server/eh.ts b/server/eh.ts new file mode 100644 index 0000000..b5f41a4 --- /dev/null +++ b/server/eh.ts @@ -0,0 +1,4 @@ +export type EHImageLimit = { + current: number; + max: number; +};