From 2cc3f1329c64b5830d1df3030631acc3a096f59c Mon Sep 17 00:00:00 2001 From: Huskydog9988 <39809509+Huskydog9988@users.noreply.github.com> Date: Wed, 14 May 2025 17:19:39 -0400 Subject: [PATCH] feat: fs object metadata cache and validation --- server/internal/objects/fsBackend.ts | 22 ++++++++++++++++++---- server/internal/objects/objectHandler.ts | 15 ++++++++++----- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/server/internal/objects/fsBackend.ts b/server/internal/objects/fsBackend.ts index 375026f..de0ea0a 100644 --- a/server/internal/objects/fsBackend.ts +++ b/server/internal/objects/fsBackend.ts @@ -1,5 +1,5 @@ import type { ObjectMetadata, ObjectReference, Source } from "./objectHandler"; -import { ObjectBackend } from "./objectHandler"; +import { ObjectBackend, objectMetadata } from "./objectHandler"; import fs from "fs"; import path from "path"; @@ -8,12 +8,15 @@ import { createHash } from "crypto"; import prisma from "../db/database"; import cacheHandler from "../cache"; import { systemConfig } from "../config/sys-conf"; +import { type } from "arktype"; export class FsObjectBackend extends ObjectBackend { private baseObjectPath: string; private baseMetadataPath: string; private hashStore = new FsHashStore(); + private metadataCache = + cacheHandler.createCache("ObjectMetadata"); constructor() { super(); @@ -102,17 +105,27 @@ export class FsObjectBackend extends ObjectBackend { const metadataPath = path.join(this.baseMetadataPath, `${id}.json`); if (!fs.existsSync(metadataPath)) return true; fs.rmSync(metadataPath); - // remove item from cache + // remove item from caches + await this.metadataCache.remove(id); await this.hashStore.delete(id); return true; } async fetchMetadata( id: ObjectReference, ): Promise { + const cacheResult = await this.metadataCache.get(id); + if (cacheResult !== null) return cacheResult; + const metadataPath = path.join(this.baseMetadataPath, `${id}.json`); if (!fs.existsSync(metadataPath)) return undefined; - const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8")); - return metadata as ObjectMetadata; + const metadataRaw = JSON.parse(fs.readFileSync(metadataPath, "utf-8")); + const metadata = objectMetadata(metadataRaw); + if (metadata instanceof type.errors) { + console.error("FsObjectBackend#fetchMetadata", metadata.summary); + return undefined; + } + await this.metadataCache.set(id, metadata); + return metadata; } async writeMetadata( id: ObjectReference, @@ -121,6 +134,7 @@ export class FsObjectBackend extends ObjectBackend { const metadataPath = path.join(this.baseMetadataPath, `${id}.json`); if (!fs.existsSync(metadataPath)) return false; fs.writeFileSync(metadataPath, JSON.stringify(metadata)); + await this.metadataCache.set(id, metadata); return true; } async fetchHash(id: ObjectReference): Promise { diff --git a/server/internal/objects/objectHandler.ts b/server/internal/objects/objectHandler.ts index edbf8ad..81cb11f 100644 --- a/server/internal/objects/objectHandler.ts +++ b/server/internal/objects/objectHandler.ts @@ -14,17 +14,22 @@ * anotherUserId:write */ +import { type } from "arktype"; import { parse as getMimeTypeBuffer } from "file-type-mime"; import type { Writable } from "stream"; import { Readable } from "stream"; import { getMimeType as getMimeTypeStream } from "stream-mime-type"; export type ObjectReference = string; -export type ObjectMetadata = { - mime: string; - permissions: string[]; - userMetadata: { [key: string]: string }; -}; + +export const objectMetadata = type({ + mime: "string", + permissions: "string[]", + userMetadata: { + "[string]": "string", + }, +}); +export type ObjectMetadata = typeof objectMetadata.infer; export enum ObjectPermission { Read = "read",