129 lines
3.7 KiB
TypeScript
129 lines
3.7 KiB
TypeScript
import { FileSystemSource, ListDirOptions, VirtualDirectory, VirtualFile } from "./types";
|
|
import { NginxFS } from "./NginxFS";
|
|
import { FileStashFs } from "./FileStashFs";
|
|
import { CachePolicy, NoCacheEntryError } from "./FsCache";
|
|
|
|
type Config<T> = T extends FileSystemSource<any> ? ReturnType<T['getConfig']> : never;
|
|
|
|
const CACHE = Symbol('Cache');
|
|
export interface Fs {
|
|
get root(): VirtualDirectory[];
|
|
download(file: VirtualFile): Promise<File>;
|
|
listDir(dir: VirtualDirectory, options?: ListDirOptions): Promise<(VirtualDirectory|VirtualFile)[]>;
|
|
}
|
|
|
|
export class MountedFs implements Fs {
|
|
private filesystems: Map<string, FileSystemSource<any>> = new Map();
|
|
public readonly name: string = 'root';
|
|
|
|
constructor(filesystems: (Config<NginxFS> | Config<FileStashFs>)[]) {
|
|
for (const fsOptions of filesystems) {
|
|
if (fsOptions.type === 'nginx') {
|
|
this.filesystems.set(fsOptions.name, new NginxFS(fsOptions));
|
|
} else if (fsOptions.type === 'filestash') {
|
|
this.filesystems.set(fsOptions.name, new FileStashFs(fsOptions));
|
|
}
|
|
}
|
|
}
|
|
|
|
get root() {
|
|
return [...this.filesystems.values()].map(fs => fs.root);
|
|
}
|
|
|
|
download(file: VirtualFile) {
|
|
const fs = this.filesystems.get(file.source);
|
|
if (!fs) {
|
|
throw new Error(`Filesystem ${file.source} not found`);
|
|
}
|
|
return fs.download(file);
|
|
}
|
|
|
|
async listDir(dir: VirtualDirectory, options?: ListDirOptions) {
|
|
|
|
const fs = this.filesystems.get(dir.source);
|
|
if (!fs) {
|
|
throw new Error(`Filesystem ${dir.source} not found`);
|
|
}
|
|
const result = await fs.listDir(dir, options);
|
|
|
|
return result;
|
|
}
|
|
|
|
async *search(query: string, cancelToken?: AbortSignal) {
|
|
query = query.toLocaleLowerCase();
|
|
for (const fs of this.filesystems.values()) {
|
|
const dirQueue = [fs.root];
|
|
while (dirQueue.length > 0) {
|
|
const dir = dirQueue.shift()!;
|
|
try {
|
|
const children = await this.listDir(dir, {cache: CachePolicy.onlyCache});
|
|
for (const child of children) {
|
|
if (cancelToken?.aborted) {
|
|
return;
|
|
}
|
|
if (child.type === 'directory') {
|
|
dirQueue.push(child);
|
|
} else if (child.name.toLocaleLowerCase().includes(query)) {
|
|
yield child;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
if (!(err instanceof NoCacheEntryError)) {
|
|
console.warn(`Failed to list directory ${dir.path.join('/')}`, err);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async *buildIndex() {
|
|
let counter = 0;
|
|
for (const fs of this.filesystems.values()) {
|
|
const dirQueue = [fs.root];
|
|
while (dirQueue.length > 0) {
|
|
const dir = dirQueue.shift()!;
|
|
for (const child of await this.listDir(dir)) {
|
|
if (child.type === 'directory') {
|
|
dirQueue.push(child);
|
|
}
|
|
counter++;
|
|
}
|
|
yield counter
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// export class SearchableFilesystem implements Fs {
|
|
// public allFiles: VirtualFile[] = [];
|
|
|
|
// constructor(private fs: MountedFs) {}
|
|
|
|
// async precacheDir(dir: VirtualDirectory) {
|
|
// for (const child of await this.fs.listDir(dir, {cache: CachePolicy.useCache})) {
|
|
// if (child.type === 'file') {
|
|
// this.allFiles.push(child);
|
|
// } else {
|
|
// await this.precacheDir(child as VirtualDirectory);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// async search(query: string) {
|
|
// await Promise.all(this.fs.root.map((root) => this.precacheDir(root)));
|
|
// }
|
|
|
|
// get root() {
|
|
// return this.fs.root;
|
|
// }
|
|
|
|
// async listDir(dir: VirtualDirectory) {
|
|
// return this.fs.listDir(dir);
|
|
// }
|
|
|
|
// async download(file: VirtualFile) {
|
|
// return this.fs.download(file);
|
|
// }
|
|
// }
|