mirror of
https://github.com/lifegpc/eh-downloader.git
synced 2026-07-08 01:31:00 +08:00
Update
This commit is contained in:
105
db.ts
105
db.ts
@@ -10,6 +10,7 @@ import { SqliteError } from "sqlite/mod.ts";
|
||||
import { Status } from "sqlite/src/constants.ts";
|
||||
import { sleep, sure_dir_sync, try_remove_sync } from "./utils.ts";
|
||||
import { Task, TaskType } from "./task.ts";
|
||||
import { generate as randomstring } from "randomstring";
|
||||
|
||||
type SqliteMaster = {
|
||||
type: string;
|
||||
@@ -126,6 +127,38 @@ export type EhFileMetaRaw = {
|
||||
is_nsfw: number;
|
||||
is_ad: number;
|
||||
};
|
||||
export enum UserPermisson {
|
||||
None = 0,
|
||||
ReadGallery = 1 << 0,
|
||||
EditGallery = 1 << 1,
|
||||
All = ~(~0 << 2),
|
||||
}
|
||||
export type User = {
|
||||
id: number;
|
||||
username: string;
|
||||
password: Uint8Array;
|
||||
is_admin: boolean;
|
||||
permissions: UserPermisson;
|
||||
};
|
||||
type UserRaw = {
|
||||
id: number;
|
||||
username: string;
|
||||
password: Uint8Array;
|
||||
is_admin: number;
|
||||
permissions: UserPermisson;
|
||||
};
|
||||
export type Token = {
|
||||
id: number;
|
||||
uid: number;
|
||||
token: string;
|
||||
expired: Date;
|
||||
};
|
||||
type TokenRaw = {
|
||||
id: number;
|
||||
uid: number;
|
||||
token: string;
|
||||
expired: string;
|
||||
};
|
||||
const ALL_TABLES = [
|
||||
"version",
|
||||
"task",
|
||||
@@ -135,6 +168,8 @@ const ALL_TABLES = [
|
||||
"gtag",
|
||||
"file",
|
||||
"filemeta",
|
||||
"user",
|
||||
"token",
|
||||
];
|
||||
const VERSION_TABLE = `CREATE TABLE version (
|
||||
id TEXT,
|
||||
@@ -201,6 +236,19 @@ const FILEMETA_TABLE = `CREATE TABLE filemeta (
|
||||
is_ad BOOLEAN,
|
||||
PRIMARY KEY (token)
|
||||
);`;
|
||||
const USER_TABLE = `CREATE TABLE user (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT,
|
||||
password BLOB,
|
||||
is_admin BOOLEAN,
|
||||
permissions INT
|
||||
);`;
|
||||
const TOKEN_TABLE = `CREATE TABLE token (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uid TEXT,
|
||||
token TEXT,
|
||||
expired TEXT
|
||||
);`;
|
||||
|
||||
export class EhDb {
|
||||
db;
|
||||
@@ -368,6 +416,12 @@ export class EhDb {
|
||||
if (!this.#exist_table.has("filemeta")) {
|
||||
this.db.execute(FILEMETA_TABLE);
|
||||
}
|
||||
if (!this.#exist_table.has("user")) {
|
||||
this.db.execute(USER_TABLE);
|
||||
}
|
||||
if (!this.#exist_table.has("token")) {
|
||||
this.db.execute(TOKEN_TABLE);
|
||||
}
|
||||
this.#updateExistsTable();
|
||||
}
|
||||
#read_version() {
|
||||
@@ -518,6 +572,19 @@ export class EhDb {
|
||||
)[0];
|
||||
});
|
||||
}
|
||||
add_token(uid: number, added: number): Token {
|
||||
let token = randomstring();
|
||||
while (this.get_token(token)) {
|
||||
token = randomstring();
|
||||
}
|
||||
this.db.query(
|
||||
"INSERT INTO token (uid, token, expired) VALUES (?, ?, ?);",
|
||||
[uid, token, new Date(added + 2592000000)],
|
||||
);
|
||||
const t = this.get_token(token);
|
||||
if (!t) throw Error("Failed to add token.");
|
||||
return t;
|
||||
}
|
||||
begin(type: SqliteTransactionType) {
|
||||
try {
|
||||
this.db.execute(`BEGIN ${type} TRANSACTION;`);
|
||||
@@ -666,6 +733,23 @@ export class EhDb {
|
||||
return t;
|
||||
});
|
||||
}
|
||||
convert_token(m: TokenRaw[]) {
|
||||
return m.map((m) => {
|
||||
const e = new Date(m.expired);
|
||||
const t = <Token> <unknown> m;
|
||||
t.expired = e;
|
||||
return t;
|
||||
});
|
||||
}
|
||||
convert_user(m: UserRaw[]) {
|
||||
return m.map((m) => {
|
||||
const a = m.is_admin !== 0;
|
||||
const t = <User> <unknown> m;
|
||||
t.is_admin = a;
|
||||
if (t.is_admin) t.permissions = UserPermisson.All;
|
||||
return t;
|
||||
});
|
||||
}
|
||||
delete_file(f: EhFile) {
|
||||
this.db.query("DELETE FROM file WHERE id = ?;", [f.id]);
|
||||
}
|
||||
@@ -876,6 +960,27 @@ export class EhDb {
|
||||
])
|
||||
);
|
||||
}
|
||||
get_token(token: string) {
|
||||
const s = this.convert_token(
|
||||
this.db.queryEntries<TokenRaw>(
|
||||
"SELECT * FROM token WHERE token = ?;",
|
||||
[token],
|
||||
),
|
||||
);
|
||||
return s.length ? s[0] : undefined;
|
||||
}
|
||||
get_user_by_name(name: string) {
|
||||
const s = this.convert_user(
|
||||
this.db.queryEntries<UserRaw>(
|
||||
"SELECT * FROM user WHERE username = ?;",
|
||||
[name],
|
||||
),
|
||||
);
|
||||
return s.length ? s[0] : undefined;
|
||||
}
|
||||
get_user_count() {
|
||||
return this.db.query<[number]>("SELECT COUNT(*) FROM user;")[0][0];
|
||||
}
|
||||
optimize() {
|
||||
this.db.execute("VACUUM;");
|
||||
}
|
||||
|
||||
18
fresh.gen.ts
18
fresh.gen.ts
@@ -15,10 +15,11 @@ import * as $9 from "./routes/api/filemeta/[token].ts";
|
||||
import * as $10 from "./routes/api/files/[token].ts";
|
||||
import * as $11 from "./routes/api/gallery/[gid].ts";
|
||||
import * as $12 from "./routes/api/gallery/list.ts";
|
||||
import * as $13 from "./routes/api/status.ts";
|
||||
import * as $14 from "./routes/api/task.ts";
|
||||
import * as $15 from "./routes/api/thumbnail/[id].ts";
|
||||
import * as $16 from "./routes/index.tsx";
|
||||
import * as $13 from "./routes/api/login.ts";
|
||||
import * as $14 from "./routes/api/status.ts";
|
||||
import * as $15 from "./routes/api/task.ts";
|
||||
import * as $16 from "./routes/api/thumbnail/[id].ts";
|
||||
import * as $17 from "./routes/index.tsx";
|
||||
import * as $$0 from "./islands/Container.tsx";
|
||||
import * as $$1 from "./islands/Settings.tsx";
|
||||
import * as $$2 from "./islands/TaskManager.tsx";
|
||||
@@ -38,10 +39,11 @@ const manifest = {
|
||||
"./routes/api/files/[token].ts": $10,
|
||||
"./routes/api/gallery/[gid].ts": $11,
|
||||
"./routes/api/gallery/list.ts": $12,
|
||||
"./routes/api/status.ts": $13,
|
||||
"./routes/api/task.ts": $14,
|
||||
"./routes/api/thumbnail/[id].ts": $15,
|
||||
"./routes/index.tsx": $16,
|
||||
"./routes/api/login.ts": $13,
|
||||
"./routes/api/status.ts": $14,
|
||||
"./routes/api/task.ts": $15,
|
||||
"./routes/api/thumbnail/[id].ts": $16,
|
||||
"./routes/index.tsx": $17,
|
||||
},
|
||||
islands: {
|
||||
"./islands/Container.tsx": $$0,
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"meilisearch": "https://esm.sh/[email protected]",
|
||||
"lodash/": "https://esm.sh/[email protected]/",
|
||||
"mime": "https://esm.sh/[email protected]",
|
||||
"ua-parser-js": "https://esm.sh/[email protected]"
|
||||
"ua-parser-js": "https://esm.sh/[email protected]",
|
||||
"pbkdf2-hmac": "https://esm.sh/[email protected]",
|
||||
"randomstring": "https://esm.sh/[email protected]"
|
||||
}
|
||||
}
|
||||
|
||||
45
routes/api/login.ts
Normal file
45
routes/api/login.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { decode } from "std/encoding/base64.ts";
|
||||
import { get_string, parse_int } from "../../server/parse_form.ts";
|
||||
import { return_data, return_error } from "../../server/utils.ts";
|
||||
import { get_task_manager } from "../../server.ts";
|
||||
import pbkdf2Hmac from "pbkdf2-hmac";
|
||||
import isEqual from "lodash/isEqual";
|
||||
|
||||
const USER_PASSWORD_ERROR = "Incorrect username or password.";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async POST(req, _ctx) {
|
||||
const data = await req.formData();
|
||||
const username = await get_string(data.get("username"));
|
||||
if (!username) return return_error(1, "username not specified.");
|
||||
const p = await get_string(data.get("password"));
|
||||
if (!p) return return_error(1, "password not specified.");
|
||||
let password = null;
|
||||
try {
|
||||
password = decode(p);
|
||||
} catch (_) {
|
||||
return return_error(2, "Failed to decode password with base64.");
|
||||
}
|
||||
if (password.length !== 64) {
|
||||
return return_error(2, "Password need 64 bytes.");
|
||||
}
|
||||
const t = await parse_int(data.get("t"), null);
|
||||
if (t === null) return return_error(1, "t not specified.");
|
||||
const now = (new Date()).getTime();
|
||||
if (t > now + 60000 || t < now - 60000) {
|
||||
return return_error(3, "Time is not corrected.");
|
||||
}
|
||||
const m = get_task_manager();
|
||||
const u = m.db.get_user_by_name(username);
|
||||
if (!u) return return_error(4, USER_PASSWORD_ERROR);
|
||||
const pa = new Uint8Array(
|
||||
await pbkdf2Hmac(u.password, t.toString(), 1000, 64),
|
||||
);
|
||||
if (!isEqual(pa, password)) {
|
||||
return return_error(4, USER_PASSWORD_ERROR);
|
||||
}
|
||||
const token = m.db.add_token(u.id, now);
|
||||
return return_data(token);
|
||||
},
|
||||
};
|
||||
@@ -19,10 +19,12 @@ export const handler: Handlers = {
|
||||
m.cfg.meili_update_api_key,
|
||||
}
|
||||
: undefined;
|
||||
const no_user = m.db.get_user_count() === 0;
|
||||
return return_data<StatusData>({
|
||||
ffmpeg_binary_enabled,
|
||||
meilisearch_enabled,
|
||||
meilisearch,
|
||||
no_user,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -5,4 +5,5 @@ export type StatusData = {
|
||||
host: string;
|
||||
key: string;
|
||||
};
|
||||
no_user: boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user