diff --git a/api.yml b/api.yml index 731799f..b35c980 100644 --- a/api.yml +++ b/api.yml @@ -808,6 +808,15 @@ components: stack: type: string description: Stack trace + LogEntryApiResult: + description: Api response for getLog + allOf: + - $ref: "#/components/schemas/ApiResponse" + - type: object + required: [data] + properties: + data: + $ref: "#/components/schemas/LogEntry" LogEntries: description: Log entries type: object @@ -2652,6 +2661,90 @@ paths: $ref: "#/components/schemas/ApiResponseError" example: { "ok": false, "status": 403, "error": "Permission denied." } + /log/{id}: + parameters: + - name: id + in: path + schema: + type: integer + format: int64 + default: '' + required: true + description: Log entry ID + delete: + operationId: deleteLog + summary: Delete a log entry + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/ApiResponseTrue" + "400": + description: Bad request + content: + application/json: + schema: + $ref: "#/components/schemas/ApiResponseError" + example: + { "ok": false, "status": 1, "error": "id is required." } + "401": + description: Authorization information is missing or invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiResponseError" + example: { "ok": false, "status": 401, "error": "Unauthorized" } + "403": + description: Permission denied + content: + application/json: + schema: + $ref: "#/components/schemas/ApiResponseError" + example: + { "ok": false, "status": 403, "error": "Permission denied." } + get: + operationId: getLog + summary: Get a log entry + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/LogEntryApiResult" + "400": + description: Bad request + content: + application/json: + schema: + $ref: "#/components/schemas/ApiResponseError" + example: + { "ok": false, "status": 1, "error": "id is required." } + "401": + description: Authorization information is missing or invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiResponseError" + example: { "ok": false, "status": 401, "error": "Unauthorized" } + "403": + description: Permission denied + content: + application/json: + schema: + $ref: "#/components/schemas/ApiResponseError" + example: + { "ok": false, "status": 403, "error": "Permission denied." } + "404": + description: Not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiResponseError" + example: + { "ok": false, "status": 404, "error": "log not found." } /shared_token: delete: operationId: deleteSharedToken diff --git a/fresh.gen.ts b/fresh.gen.ts index d0d2ec1..e46a369 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -23,6 +23,7 @@ import * as $api_gallery_list from "./routes/api/gallery/list.ts"; import * as $api_gallery_meta_gids_ from "./routes/api/gallery/meta/[gids].ts"; import * as $api_health_check from "./routes/api/health_check.ts"; import * as $api_log from "./routes/api/log.ts"; +import * as $api_log_id_ from "./routes/api/log/[id].ts"; import * as $api_shared_token from "./routes/api/shared_token.ts"; import * as $api_shared_token_list from "./routes/api/shared_token/list.ts"; import * as $api_status from "./routes/api/status.ts"; @@ -74,6 +75,7 @@ const manifest = { "./routes/api/gallery/meta/[gids].ts": $api_gallery_meta_gids_, "./routes/api/health_check.ts": $api_health_check, "./routes/api/log.ts": $api_log, + "./routes/api/log/[id].ts": $api_log_id_, "./routes/api/shared_token.ts": $api_shared_token, "./routes/api/shared_token/list.ts": $api_shared_token_list, "./routes/api/status.ts": $api_status, diff --git a/routes/api/log/[id].ts b/routes/api/log/[id].ts new file mode 100644 index 0000000..553f199 --- /dev/null +++ b/routes/api/log/[id].ts @@ -0,0 +1,41 @@ +import { Handlers } from "$fresh/server.ts"; +import { return_data, return_error } from "../../../server/utils.ts"; +import { User, UserPermission } from "../../../db.ts"; +import { parse_big_int } from "../../../server/parse_form.ts"; +import { base_logger } from "../../../utils/logger.ts"; + +export const handler: Handlers = { + async GET(_req, ctx) { + const user = ctx.state.user; + if ( + user && !user.is_admin && + !(Number(user.permissions) & UserPermission.QueryLog) + ) { + return return_error(403, "Permission denied."); + } + const id = await parse_big_int(ctx.params["id"], null); + if (id === null) { + return return_error(1, "id is required."); + } + const log = base_logger.get_log(id); + if (!log) { + return return_error(404, "log not found."); + } + return return_data(log); + }, + async DELETE(_req, ctx) { + const user = ctx.state.user; + if ( + user && !user.is_admin && + !(Number(user.permissions) & UserPermission.QueryLog) + ) { + return return_error(403, "Permission denied."); + } + const id = await parse_big_int(ctx.params["id"], null); + if (id === null) { + return return_error(1, "id is required."); + } + base_logger.delete_log(id); + return return_data(true); + }, +}; diff --git a/utils/logger.ts b/utils/logger.ts index 1d88ac2..3f85038 100644 --- a/utils/logger.ts +++ b/utils/logger.ts @@ -150,6 +150,41 @@ class BaseLogger { ], ); } + clear( + type?: string | null, + min_level?: number | null, + max_level?: number | null, + deleted_level?: number[], + end_time?: Date | null, + ) { + if (!this.db) return; + const where = []; + const args: QueryParameterSet = []; + if (type) { + where.push("type = ?"); + args.push(type); + } + if (min_level) { + where.push("level >= ?"); + args.push(min_level); + } + if (max_level) { + where.push("level <= ?"); + args.push(max_level); + } + if (deleted_level) { + where.push( + "level IN (" + deleted_level.map(() => "?").join(",") + ")", + ); + args.push(...deleted_level); + } + if (end_time) { + where.push("time <= ?"); + args.push(end_time.getTime()); + } + const where_str = where.length ? " WHERE " + where.join(" AND ") : ""; + this.db.query(`DELETE FROM log${where_str};`, args); + } close() { this.db?.close(); this.db = undefined; @@ -201,6 +236,10 @@ class BaseLogger { debug(type: string, ...messages: unknown[]) { this.add(type, LogLevel.Debug, ...messages); } + delete_log(id: number | bigint) { + if (!this.db) return; + this.db.query("DELETE FROM log WHERE id = ?;", [id]); + } error(type: string, ...messages: unknown[]) { this.add(type, LogLevel.Error, ...messages); } @@ -240,6 +279,14 @@ class BaseLogger { ); } } + get_log(id: number | bigint) { + if (!this.db) return null; + const cur = this.#convert(this.db.queryEntries( + "SELECT * FROM log WHERE id = ?;", + [id], + )); + return cur.length ? cur[0] : null; + } get_logger(type: string) { return new Logger(this, type); }