Skip to content

Commit da25b2f

Browse files
committed
feat: Add log entry retrieval and deletion API endpoints
1 parent 39b9748 commit da25b2f

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed

api.yml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,15 @@ components:
808808
stack:
809809
type: string
810810
description: Stack trace
811+
LogEntryApiResult:
812+
description: Api response for getLog
813+
allOf:
814+
- $ref: "#/components/schemas/ApiResponse"
815+
- type: object
816+
required: [data]
817+
properties:
818+
data:
819+
$ref: "#/components/schemas/LogEntry"
811820
LogEntries:
812821
description: Log entries
813822
type: object
@@ -2652,6 +2661,90 @@ paths:
26522661
$ref: "#/components/schemas/ApiResponseError"
26532662
example:
26542663
{ "ok": false, "status": 403, "error": "Permission denied." }
2664+
/log/{id}:
2665+
parameters:
2666+
- name: id
2667+
in: path
2668+
schema:
2669+
type: integer
2670+
format: int64
2671+
default: ''
2672+
required: true
2673+
description: Log entry ID
2674+
delete:
2675+
operationId: deleteLog
2676+
summary: Delete a log entry
2677+
responses:
2678+
"200":
2679+
description: OK
2680+
content:
2681+
application/json:
2682+
schema:
2683+
$ref: "#/components/schemas/ApiResponseTrue"
2684+
"400":
2685+
description: Bad request
2686+
content:
2687+
application/json:
2688+
schema:
2689+
$ref: "#/components/schemas/ApiResponseError"
2690+
example:
2691+
{ "ok": false, "status": 1, "error": "id is required." }
2692+
"401":
2693+
description: Authorization information is missing or invalid
2694+
content:
2695+
application/json:
2696+
schema:
2697+
$ref: "#/components/schemas/ApiResponseError"
2698+
example: { "ok": false, "status": 401, "error": "Unauthorized" }
2699+
"403":
2700+
description: Permission denied
2701+
content:
2702+
application/json:
2703+
schema:
2704+
$ref: "#/components/schemas/ApiResponseError"
2705+
example:
2706+
{ "ok": false, "status": 403, "error": "Permission denied." }
2707+
get:
2708+
operationId: getLog
2709+
summary: Get a log entry
2710+
responses:
2711+
"200":
2712+
description: OK
2713+
content:
2714+
application/json:
2715+
schema:
2716+
$ref: "#/components/schemas/LogEntryApiResult"
2717+
"400":
2718+
description: Bad request
2719+
content:
2720+
application/json:
2721+
schema:
2722+
$ref: "#/components/schemas/ApiResponseError"
2723+
example:
2724+
{ "ok": false, "status": 1, "error": "id is required." }
2725+
"401":
2726+
description: Authorization information is missing or invalid
2727+
content:
2728+
application/json:
2729+
schema:
2730+
$ref: "#/components/schemas/ApiResponseError"
2731+
example: { "ok": false, "status": 401, "error": "Unauthorized" }
2732+
"403":
2733+
description: Permission denied
2734+
content:
2735+
application/json:
2736+
schema:
2737+
$ref: "#/components/schemas/ApiResponseError"
2738+
example:
2739+
{ "ok": false, "status": 403, "error": "Permission denied." }
2740+
"404":
2741+
description: Not found
2742+
content:
2743+
application/json:
2744+
schema:
2745+
$ref: "#/components/schemas/ApiResponseError"
2746+
example:
2747+
{ "ok": false, "status": 404, "error": "log not found." }
26552748
/shared_token:
26562749
delete:
26572750
operationId: deleteSharedToken

fresh.gen.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import * as $api_gallery_list from "./routes/api/gallery/list.ts";
2323
import * as $api_gallery_meta_gids_ from "./routes/api/gallery/meta/[gids].ts";
2424
import * as $api_health_check from "./routes/api/health_check.ts";
2525
import * as $api_log from "./routes/api/log.ts";
26+
import * as $api_log_id_ from "./routes/api/log/[id].ts";
2627
import * as $api_shared_token from "./routes/api/shared_token.ts";
2728
import * as $api_shared_token_list from "./routes/api/shared_token/list.ts";
2829
import * as $api_status from "./routes/api/status.ts";
@@ -74,6 +75,7 @@ const manifest = {
7475
"./routes/api/gallery/meta/[gids].ts": $api_gallery_meta_gids_,
7576
"./routes/api/health_check.ts": $api_health_check,
7677
"./routes/api/log.ts": $api_log,
78+
"./routes/api/log/[id].ts": $api_log_id_,
7779
"./routes/api/shared_token.ts": $api_shared_token,
7880
"./routes/api/shared_token/list.ts": $api_shared_token_list,
7981
"./routes/api/status.ts": $api_status,

routes/api/log/[id].ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Handlers } from "$fresh/server.ts";
2+
import { return_data, return_error } from "../../../server/utils.ts";
3+
import { User, UserPermission } from "../../../db.ts";
4+
import { parse_big_int } from "../../../server/parse_form.ts";
5+
import { base_logger } from "../../../utils/logger.ts";
6+
7+
export const handler: Handlers = {
8+
async GET(_req, ctx) {
9+
const user = <User | undefined> ctx.state.user;
10+
if (
11+
user && !user.is_admin &&
12+
!(Number(user.permissions) & UserPermission.QueryLog)
13+
) {
14+
return return_error(403, "Permission denied.");
15+
}
16+
const id = await parse_big_int(ctx.params["id"], null);
17+
if (id === null) {
18+
return return_error(1, "id is required.");
19+
}
20+
const log = base_logger.get_log(id);
21+
if (!log) {
22+
return return_error(404, "log not found.");
23+
}
24+
return return_data(log);
25+
},
26+
async DELETE(_req, ctx) {
27+
const user = <User | undefined> ctx.state.user;
28+
if (
29+
user && !user.is_admin &&
30+
!(Number(user.permissions) & UserPermission.QueryLog)
31+
) {
32+
return return_error(403, "Permission denied.");
33+
}
34+
const id = await parse_big_int(ctx.params["id"], null);
35+
if (id === null) {
36+
return return_error(1, "id is required.");
37+
}
38+
base_logger.delete_log(id);
39+
return return_data(true);
40+
},
41+
};

utils/logger.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,41 @@ class BaseLogger {
150150
],
151151
);
152152
}
153+
clear(
154+
type?: string | null,
155+
min_level?: number | null,
156+
max_level?: number | null,
157+
deleted_level?: number[],
158+
end_time?: Date | null,
159+
) {
160+
if (!this.db) return;
161+
const where = [];
162+
const args: QueryParameterSet = [];
163+
if (type) {
164+
where.push("type = ?");
165+
args.push(type);
166+
}
167+
if (min_level) {
168+
where.push("level >= ?");
169+
args.push(min_level);
170+
}
171+
if (max_level) {
172+
where.push("level <= ?");
173+
args.push(max_level);
174+
}
175+
if (deleted_level) {
176+
where.push(
177+
"level IN (" + deleted_level.map(() => "?").join(",") + ")",
178+
);
179+
args.push(...deleted_level);
180+
}
181+
if (end_time) {
182+
where.push("time <= ?");
183+
args.push(end_time.getTime());
184+
}
185+
const where_str = where.length ? " WHERE " + where.join(" AND ") : "";
186+
this.db.query(`DELETE FROM log${where_str};`, args);
187+
}
153188
close() {
154189
this.db?.close();
155190
this.db = undefined;
@@ -201,6 +236,10 @@ class BaseLogger {
201236
debug(type: string, ...messages: unknown[]) {
202237
this.add(type, LogLevel.Debug, ...messages);
203238
}
239+
delete_log(id: number | bigint) {
240+
if (!this.db) return;
241+
this.db.query("DELETE FROM log WHERE id = ?;", [id]);
242+
}
204243
error(type: string, ...messages: unknown[]) {
205244
this.add(type, LogLevel.Error, ...messages);
206245
}
@@ -240,6 +279,14 @@ class BaseLogger {
240279
);
241280
}
242281
}
282+
get_log(id: number | bigint) {
283+
if (!this.db) return null;
284+
const cur = this.#convert(this.db.queryEntries<LogEntryRaw>(
285+
"SELECT * FROM log WHERE id = ?;",
286+
[id],
287+
));
288+
return cur.length ? cur[0] : null;
289+
}
243290
get_logger(type: string) {
244291
return new Logger(this, type);
245292
}

0 commit comments

Comments
 (0)