0.1.0Updated a month ago
import { join } from "https://deno.land/std@0.216.0/path/mod.ts";
import { existsSync } from "https://deno.land/std@0.216.0/fs/mod.ts";
import { AssetStoreBase, FileStats, UploadableDataTypes } from "./asset_store_base.ts";

export interface FileStoreOptions {
  root_path: string
}

export class FileStore extends AssetStoreBase {
  readonly root_path: string

  constructor(namespace: string, options: FileStoreOptions) {
    super(namespace);

    this.root_path = options.root_path;
  }

  private cleaned_key(key: string) {
    if(key.includes('../')) throw new Error("Can't traverse upwards in a file store");

    return this.stripped_key(key);
  }

  override Get(key: string) {
    key = this.cleaned_key(key);
    const path = join(this.root_path, key);

    if(this.cache.has(key)) return this.cache.get(key);

    if(!existsSync(path, { isFile: true })) return null;

    return this.cache.set(key, Deno.readFileSync(path).buffer);
  }
  override async Upload(key: string, data: UploadableDataTypes) {
    key = this.cleaned_key(key);
    const path = join(this.root_path, key);

    const array_buffer = await this.ToArrayBuffer(key, data);

    if(existsSync(path, { isDirectory: true })) return this.namespaced_key(key);

    if(array_buffer) Deno.writeFileSync(path, new Uint8Array(array_buffer));

    return this.namespaced_key(key);
  }
  override Info(key: string): FileStats | Promise<FileStats> {
    key = this.cleaned_key(key);
    const path = join(this.root_path, key);

    if(!existsSync(path))  return {
      size: 0,
      etag: key,
      lastModified: new Date(0),
      type: "null"
    };

    const stats = Deno.statSync(path);

    return {
      size: stats.size,
      lastModified: stats.mtime!,
      type: "file",
      etag: stats.uid!.toString()
    }
  }
  override Delete(key: string): void | Promise<void> {
    key = this.cleaned_key(key);
    const path = join(this.root_path, key);

    if(!existsSync(path, { isFile: true })) return;

    Deno.removeSync(path);
  }
};