ionian/packages/server/src/content/file.ts
monoid 8fece9090f BREAKING: Rework (#6)
다시 작업. 디자인도 바꾸고 서버도 바꿈.

Co-authored-by: monoid <jaeung@prelude.duckdns.org>
Reviewed-on: https://git.prelude.duckdns.org/monoid/ionian/pulls/6
2024-04-17 01:45:36 +09:00

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;
}