feat: modified file check

This commit is contained in:
monoid 2024-11-16 04:14:41 +09:00
parent 05c59937e9
commit 9f24f812c9
7 changed files with 62 additions and 10 deletions

View File

@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
import { promises, type Stats } from "node:fs"; import { promises, type Stats } from "node:fs";
import path, { extname } from "node:path"; import path, { extname } from "node:path";
import type { DocumentBody } from "dbtype"; import type { DocumentBody } from "dbtype";
import { oshash } from "src/util/oshash.ts"; import { oshash, OshashError } from "src/util/oshash.ts";
/** /**
* content file or directory referrer * content file or directory referrer
*/ */
@ -30,7 +30,7 @@ export const createDefaultClass = (type: string): ContentFileConstructor => {
protected hash: string | undefined; protected hash: string | undefined;
protected stat: Stats | undefined; protected stat: Stats | undefined;
protected getStat(){ protected getStat() {
return this.stat; return this.stat;
} }
@ -42,7 +42,7 @@ export const createDefaultClass = (type: string): ContentFileConstructor => {
async createDocumentBody(): Promise<DocumentBody> { async createDocumentBody(): Promise<DocumentBody> {
console.log(`createDocumentBody: ${this.path}`); console.log(`createDocumentBody: ${this.path}`);
const { base, dir, name } = path.parse(this.path); const { base, dir, name } = path.parse(this.path);
const ret = { const ret = {
title: name, title: name,
basepath: dir, basepath: dir,
@ -61,9 +61,17 @@ export const createDefaultClass = (type: string): ContentFileConstructor => {
} }
async getHash(): Promise<string> { async getHash(): Promise<string> {
if (this.hash !== undefined) return this.hash; if (this.hash !== undefined) return this.hash;
const hash = await oshash(this.path); try {
this.hash = hash.toString(); const hash = await oshash(this.path);
return this.hash; this.hash = hash.toString();
return this.hash;
} catch (error) {
if (error instanceof OshashError) {
// too short to hash
return "0";
}
throw error;
}
} }
async getMtime(): Promise<number> { async getMtime(): Promise<number> {
const oldStat = this.getStat(); const oldStat = this.getStat();

View File

@ -34,7 +34,8 @@ export class ContentDiffHandler {
diff diff
.on("create", (path) => this.OnCreated(path)) .on("create", (path) => this.OnCreated(path))
.on("delete", (path) => this.OnDeleted(path)) .on("delete", (path) => this.OnDeleted(path))
.on("change", (prev, cur) => this.OnChanged(prev, cur)); .on("change", (prev, cur) => this.OnChanged(prev, cur))
.on("modify", (path) => this.onModified(path));
} }
private async OnDeleted(cpath: string) { private async OnDeleted(cpath: string) {
const basepath = dirname(cpath); const basepath = dirname(cpath);
@ -126,4 +127,27 @@ export class ContentDiffHandler {
filename: cur_filename, filename: cur_filename,
}); });
} }
async onModified(path: string) {
const content = createContentFile(this.content_type, path);
// if it is in waiting list, update it.
if (this.waiting_list.hasByPath(path)) {
this.waiting_list.set(content);
return;
}
// if it is not in waiting list, check it in db.
const basepath = dirname(path);
const filename = basename(path);
const doc = await this.doc_cntr.findByPath(basepath, filename);
if (doc.length === 0) {
// if it is not in db, add it to waiting list. because it can happen that the file creation event occurs after the file modification event.
this.waiting_list.set(content);
return;
}
// if it is in db, update it.
await this.doc_cntr.update({
...doc[0],
content_hash: await content.getHash(),
modified_at: Date.now(),
});
}
} }

View File

@ -10,6 +10,7 @@ export interface DiffWatcherEvent {
create: (path: string) => void; create: (path: string) => void;
delete: (path: string) => void; delete: (path: string) => void;
change: (prev_path: string, cur_path: string) => void; change: (prev_path: string, cur_path: string) => void;
modify: (path: string) => void;
} }
export interface IDiffWatcher extends event.EventEmitter { export interface IDiffWatcher extends event.EventEmitter {
@ -22,4 +23,5 @@ export function linkWatcher(fromWatcher: IDiffWatcher, toWatcher: IDiffWatcher)
fromWatcher.on("create", (p) => toWatcher.emit("create", p)); fromWatcher.on("create", (p) => toWatcher.emit("create", p));
fromWatcher.on("delete", (p) => toWatcher.emit("delete", p)); fromWatcher.on("delete", (p) => toWatcher.emit("delete", p));
fromWatcher.on("change", (p, c) => toWatcher.emit("change", p, c)); fromWatcher.on("change", (p, c) => toWatcher.emit("change", p, c));
fromWatcher.on("modify", (p) => toWatcher.emit("modify", p));
} }

View File

@ -33,6 +33,9 @@ export class CommonDiffWatcher extends event.EventEmitter implements IDiffWatche
this.emit("delete", join(this.path, filename)); this.emit("delete", join(this.path, filename));
} }
} }
if (eventType === "change") {
this.emit("modify", join(this.path, filename));
}
}); });
} }
async setup(cntr: DocumentAccessor): Promise<void> { async setup(cntr: DocumentAccessor): Promise<void> {

View File

@ -47,6 +47,11 @@ export class RecursiveWatcher extends EventEmitter implements IDiffWatcher {
const cpath = path; const cpath = path;
// console.log("unlink ", cpath); // console.log("unlink ", cpath);
this.emit("delete", cpath); this.emit("delete", cpath);
})
.on("change", (path) => {
const cpath = path;
// console.log("change ", cpath);
this.emit("modify", cpath);
}); });
} }
if (option.watchDir) { if (option.watchDir) {

View File

@ -28,9 +28,11 @@ export class WatcherFilter extends EventEmitter implements IDiffWatcher {
return super.emit("create", cur); return super.emit("create", cur);
} }
return false; return false;
}if (!this.filter(arg[0])) { }
if (!this.filter(arg[0])) {
return false; return false;
}return super.emit(event, ...arg); }
return super.emit(event, ...arg);
} }
constructor(refWatcher: IDiffWatcher, filter: (filename: string) => boolean) { constructor(refWatcher: IDiffWatcher, filter: (filename: string) => boolean) {
super(); super();

View File

@ -1,4 +1,12 @@
import { open } from "fs/promises"; import { open } from "fs/promises";
export class OshashError extends Error {
constructor(message: string) {
super(message);
this.name = "OshashError";
}
}
/** /**
* get hash of file or directory. * get hash of file or directory.
* *
@ -22,7 +30,7 @@ export async function oshash(
if (st.size < minFileSize){ if (st.size < minFileSize){
if (st.size < 8) { if (st.size < 8) {
fd.close(); fd.close();
throw new Error("Too short to hash"); throw new OshashError("Too short to hash");
} }
// Although the original oshash algorithm should throw an exception, // Although the original oshash algorithm should throw an exception,
// just applying the hash function. // just applying the hash function.