feat: add query with gid

This commit is contained in:
monoid 2024-12-27 18:37:06 +09:00
parent a92ddadaf0
commit 18a137e441
11 changed files with 77 additions and 32 deletions

View File

@ -11,6 +11,7 @@ export const DocumentBodySchema = z.object({
additional: z.record(z.unknown()),
tags: z.array(z.string()),
pagenum: z.number().int(),
gid: z.number().nullable(),
});
export type DocumentBody = z.infer<typeof DocumentBodySchema>;

View File

@ -21,6 +21,7 @@ export interface Document {
modified_at: number;
title: string;
pagenum: number;
gid: number | null;
}
export interface Permissions {

View File

@ -69,6 +69,20 @@ export async function up(db: Kysely<any>) {
dirty: false,
})
.execute();
// create indexes
await db.schema.createIndex("index_document_basepath_filename")
.on("document")
.columns(["basepath", "filename"])
.execute();
await db.schema.createIndex("index_document_content_hash")
.on("document")
.columns(["content_hash"])
.execute();
await db.schema.createIndex("index_document_created_at")
.on("document")
.columns(["created_at"])
.execute();
}
export async function down(db: Kysely<any>) {

View File

@ -3,6 +3,7 @@ import { promises, type Stats } from "node:fs";
import path, { extname } from "node:path";
import type { DocumentBody } from "dbtype";
import { oshash, OshashError } from "src/util/oshash.ts";
import { extractGidFromFilename } from "src/util/gid.ts";
/**
* content file or directory referrer
*/
@ -53,6 +54,7 @@ export const createDefaultClass = (type: string): ContentFileConstructor => {
content_hash: await this.getHash(),
modified_at: await this.getMtime(),
pagenum: 0,
gid: extractGidFromFilename(base),
} as DocumentBody;
return ret;
}

View File

@ -202,6 +202,19 @@ class SqliteDocumentAccessor implements DocumentAccessor {
tags: JSON.parse(x.tags ?? "[]"), //?.map((x: { tag_name: string }) => x.tag_name) ?? [],
}));
}
async findByGidList(gid_list: number[]) {
const result = await this.kysely
.selectFrom("document")
.selectAll()
.where("gid", "in", gid_list)
.execute();
return result.map((x) => ({
...x,
content_hash: x.content_hash ?? "",
tags: [],
additional: {},
}));
}
async findByPath(path: string, filename?: string): Promise<Document[]> {
const results = await this.kysely
.selectFrom("document")
@ -216,6 +229,7 @@ class SqliteDocumentAccessor implements DocumentAccessor {
additional: {},
}));
}
async update(c: Partial<Document> & { id: number }) {
const { id, tags, additional, ...rest } = c;
const r = await this.kysely.updateTable("document")

View File

@ -62,14 +62,12 @@ export class ContentDiffHandler {
if (cf) {
// if a path is changed, update the changed path.
console.log("update path from", cpath, "to", cf.path);
const newFilename = basename(cf.path);
const newBasepath = dirname(cf.path);
const new_doc = createContentFile(this.content_type, cf.path);
this.waiting_list.deleteByHash(content_hash);
await this.doc_cntr.update({
id: dbc[0].id,
deleted_at: null,
filename: newFilename,
basepath: newBasepath,
...dbc[0],
...new_doc.createDocumentBody(),
});
return;
}
@ -91,21 +89,17 @@ export class ContentDiffHandler {
if (c !== undefined) {
await this.doc_cntr.update({
id: c.id,
deleted_at: null,
filename: filename,
basepath: basepath,
...content.createDocumentBody(),
});
}
if (this.waiting_list.hasByHash(hash)) {
console.log("Hash Conflict!!!");
console.log("Hash Conflict!!!", cpath);
}
this.waiting_list.set(content);
}
private async OnChanged(prev_path: string, cur_path: string) {
const prev_basepath = dirname(prev_path);
const prev_filename = basename(prev_path);
const cur_basepath = dirname(cur_path);
const cur_filename = basename(cur_path);
console.log("modify", cur_path, "from", prev_path);
const c = this.waiting_list.getByPath(prev_path);
if (c !== undefined) {
@ -123,8 +117,7 @@ export class ContentDiffHandler {
await this.doc_cntr.update({
...doc[0],
basepath: cur_basepath,
filename: cur_filename,
...createContentFile(this.content_type, cur_path).createDocumentBody(),
});
}
async onModified(path: string) {
@ -146,7 +139,7 @@ export class ContentDiffHandler {
// if it is in db, update it.
await this.doc_cntr.update({
...doc[0],
content_hash: await content.getHash(),
...content.createDocumentBody(),
modified_at: Date.now(),
});
}

View File

@ -1,5 +1,5 @@
import { ConfigManager } from "../../util/configRW.ts";
import ComicSchema from "./ComicConfig.schema.json";
import ComicSchema from "./ComicConfig.schema.json" assert { type: "json" };
export interface ComicConfig {
watch: string[];
}

View File

@ -55,6 +55,12 @@ export interface DocumentAccessor {
* if you call this function with filename, its return array length is 0 or 1.
*/
findByPath: (basepath: string, filename?: string) => Promise<Document[]>;
/**
* find by gid list
* @param gid_list
* @returns Document list
*/
findByGidList: (gid_list: number[]) => Promise<Document[]>;
/**
* find deleted content
*/

View File

@ -213,6 +213,15 @@ function getRescanDocumentHandler(controller: DocumentAccessor) {
export const getContentRouter = (controller: DocumentAccessor) => {
const ret = new Router();
ret.get("/search", PerCheck(Per.QueryContent), ContentQueryHandler(controller));
ret.get("/_gid", PerCheck(Per.QueryContent), async (ctx, next) => {
const gid_list = ParseQueryArray(ctx.query.gid).map((x) => Number.parseInt(x))
if (gid_list.some((x) => Number.isNaN(x))) {
return sendError(400, "gid is not a number");
}
const r = await controller.findByGidList(gid_list);
ctx.body = r;
ctx.type = "json";
});
ret.get("/:num(\\d+)", PerCheck(Per.QueryContent), ContentIDHandler(controller));
ret.all("/:num(\\d+)/(.*)", PerCheck(Per.QueryContent), ContentHandler(controller));
ret.post("/:num(\\d+)", AdminOnly, UpdateContentHandler(controller));

View File

@ -0,0 +1,5 @@
export function extractGidFromFilename(filename: string) {
const match = filename.match(/^([0-9]+)(:?\.|\s)/);
if (match === null) return null;
return Number.parseInt(match[1]);
}