다시 작업. 디자인도 바꾸고 서버도 바꿈. Co-authored-by: monoid <jaeung@prelude.duckdns.org> Reviewed-on: https://git.prelude.duckdns.org/monoid/ionian/pulls/6
98 lines
3 KiB
TypeScript
98 lines
3 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { promises, type Stats } from "node:fs";
|
|
import path, { extname } from "node:path";
|
|
import type { DocumentBody } from "dbtype/api";
|
|
/**
|
|
* content file or directory referrer
|
|
*/
|
|
export interface ContentFile {
|
|
getHash(): Promise<string>;
|
|
createDocumentBody(): Promise<DocumentBody>;
|
|
readonly path: string;
|
|
readonly type: string;
|
|
}
|
|
export type ContentConstructOption = {
|
|
hash: string;
|
|
};
|
|
type ContentFileConstructor = (new (
|
|
path: string,
|
|
option?: ContentConstructOption,
|
|
) => ContentFile) & {
|
|
content_type: string;
|
|
};
|
|
export const createDefaultClass = (type: string): ContentFileConstructor => {
|
|
const cons = class implements ContentFile {
|
|
readonly path: string;
|
|
// type = type;
|
|
static content_type = type;
|
|
protected hash: string | undefined;
|
|
protected stat: Stats | undefined;
|
|
|
|
protected getStat(){
|
|
return this.stat;
|
|
}
|
|
|
|
constructor(path: string, option?: ContentConstructOption) {
|
|
this.path = path;
|
|
this.hash = option?.hash;
|
|
this.stat = undefined;
|
|
}
|
|
async createDocumentBody(): Promise<DocumentBody> {
|
|
console.log(`createDocumentBody: ${this.path}`);
|
|
const { base, dir, name } = path.parse(this.path);
|
|
|
|
const ret = {
|
|
title: name,
|
|
basepath: dir,
|
|
additional: {},
|
|
content_type: cons.content_type,
|
|
filename: base,
|
|
tags: [],
|
|
content_hash: await this.getHash(),
|
|
modified_at: await this.getMtime(),
|
|
} as DocumentBody;
|
|
return ret;
|
|
}
|
|
get type(): string {
|
|
return cons.content_type;
|
|
}
|
|
async getHash(): Promise<string> {
|
|
if (this.hash !== undefined) return this.hash;
|
|
this.stat = await promises.stat(this.path);
|
|
const hash = createHash("sha512");
|
|
hash.update(extname(this.path));
|
|
hash.update(this.stat.mode.toString());
|
|
// if(this.desc !== undefined)
|
|
// hash.update(JSON.stringify(this.desc));
|
|
hash.update(this.stat.size.toString());
|
|
this.hash = hash.digest("base64");
|
|
return this.hash;
|
|
}
|
|
async getMtime(): Promise<number> {
|
|
const oldStat = this.getStat();
|
|
if (oldStat !== undefined) return oldStat.mtimeMs;
|
|
await this.getHash();
|
|
const newStat = this.getStat();
|
|
if (newStat === undefined) throw new Error("stat is undefined");
|
|
return newStat.mtimeMs;
|
|
}
|
|
};
|
|
return cons;
|
|
};
|
|
const ContstructorTable: { [k: string]: ContentFileConstructor } = {};
|
|
export function registerContentReferrer(s: ContentFileConstructor) {
|
|
console.log(`registered content type: ${s.content_type}`);
|
|
ContstructorTable[s.content_type] = s;
|
|
}
|
|
export function createContentFile(type: string, path: string, option?: ContentConstructOption) {
|
|
const constructorMethod = ContstructorTable[type];
|
|
if (constructorMethod === undefined) {
|
|
console.log(`${type} are not in ${JSON.stringify(ContstructorTable)}`);
|
|
throw new Error("construction method of the content type is undefined");
|
|
}
|
|
return new constructorMethod(path, option);
|
|
}
|
|
export function getContentFileConstructor(type: string): ContentFileConstructor | undefined {
|
|
const ret = ContstructorTable[type];
|
|
return ret;
|
|
}
|