feat: add query with gid
This commit is contained in:
parent
a92ddadaf0
commit
18a137e441
@ -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>;
|
||||
|
@ -21,6 +21,7 @@ export interface Document {
|
||||
modified_at: number;
|
||||
title: string;
|
||||
pagenum: number;
|
||||
gid: number | null;
|
||||
}
|
||||
|
||||
export interface Permissions {
|
||||
|
@ -23,7 +23,7 @@ export async function up(db: Kysely<any>) {
|
||||
.addColumn('filename', 'varchar(512)', col => col.notNull())
|
||||
.addColumn('content_hash', 'varchar')
|
||||
.addColumn('additional', 'json')
|
||||
.addColumn("pagenum", "integer", col => col.notNull())
|
||||
.addColumn("pagenum", "integer", col => col.notNull())
|
||||
.addColumn('created_at', 'integer', col => col.notNull())
|
||||
.addColumn('modified_at', 'integer', col => col.notNull())
|
||||
.addColumn('deleted_at', 'integer')
|
||||
@ -62,13 +62,27 @@ export async function up(db: Kysely<any>) {
|
||||
})
|
||||
.execute();
|
||||
|
||||
await db
|
||||
.insertInto('schema_migration')
|
||||
.values({
|
||||
version: '0.0.1',
|
||||
dirty: false,
|
||||
})
|
||||
.execute();
|
||||
await db
|
||||
.insertInto('schema_migration')
|
||||
.values({
|
||||
version: '0.0.1',
|
||||
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>) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ class SqliteDocumentAccessor implements DocumentAccessor {
|
||||
.executeTakeFirst() as { id: number };
|
||||
const id = id_lst.id;
|
||||
|
||||
if (tags.length > 0){
|
||||
if (tags.length > 0) {
|
||||
// add tags
|
||||
await trx.insertInto("tags")
|
||||
.values(tags.map((x) => ({ name: x })))
|
||||
@ -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")
|
||||
@ -322,7 +336,7 @@ class SqliteDocumentAccessor implements DocumentAccessor {
|
||||
.values(tags.map((x) => ({ tag_name: x, doc_id: c.id }))
|
||||
)
|
||||
.execute();
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ export function getKysely() {
|
||||
log: (event) => {
|
||||
if (event.level === "error") {
|
||||
console.error("Query failed : ", {
|
||||
durationMs: event.queryDurationMillis,
|
||||
error: event.error,
|
||||
sql: event.query.sql,
|
||||
params: event.query.parameters,
|
||||
});
|
||||
durationMs: event.queryDurationMillis,
|
||||
error: event.error,
|
||||
sql: event.query.sql,
|
||||
params: event.query.parameters,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -52,7 +52,7 @@ export class ContentDiffHandler {
|
||||
console.log("its not in waiting_list and db!!!: ", cpath);
|
||||
return;
|
||||
}
|
||||
console.log("deleted ",cpath, dbc[0].content_hash)
|
||||
console.log("deleted ",cpath, dbc[0].content_hash)
|
||||
const content_hash = dbc[0].content_hash;
|
||||
// When a path is changed, it takes into account when the
|
||||
// creation event occurs first and the deletion occurs, not
|
||||
@ -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(),
|
||||
});
|
||||
}
|
||||
|
@ -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[];
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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));
|
||||
|
5
packages/server/src/util/gid.ts
Normal file
5
packages/server/src/util/gid.ts
Normal 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]);
|
||||
}
|
Loading…
Reference in New Issue
Block a user