Expose file and page meta data directly
parent
7a627c2f35
commit
89f27f9e9c
|
@ -0,0 +1,78 @@
|
|||
import { Plug } from "../../plugos/plug.ts";
|
||||
import { FileMeta } from "../types.ts";
|
||||
import { FileData, FileEncoding, SpacePrimitives } from "./space_primitives.ts";
|
||||
import type { SysCallMapping } from "../../plugos/system.ts";
|
||||
|
||||
// Enriches the file list listing with custom metadata from the page index
|
||||
export class FileMetaSpacePrimitives implements SpacePrimitives {
|
||||
constructor(
|
||||
private wrapped: SpacePrimitives,
|
||||
private indexSyscalls: SysCallMapping,
|
||||
) {
|
||||
}
|
||||
|
||||
async fetchFileList(): Promise<FileMeta[]> {
|
||||
const list = await this.wrapped.fetchFileList();
|
||||
// Enrich the file list with custom meta data (for pages)
|
||||
const allFilesMap: Map<string, any> = new Map(
|
||||
list.map((fm) => [fm.name, fm]),
|
||||
);
|
||||
for (
|
||||
const { page, value } of await this.indexSyscalls["index.queryPrefix"](
|
||||
{} as any,
|
||||
"meta:",
|
||||
)
|
||||
) {
|
||||
const p = allFilesMap.get(`${page}.md`);
|
||||
if (p) {
|
||||
for (const [k, v] of Object.entries(value)) {
|
||||
if (
|
||||
["name", "lastModified", "size", "perm", "contentType"].includes(k)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
p[k] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...allFilesMap.values()];
|
||||
}
|
||||
|
||||
readFile(
|
||||
name: string,
|
||||
encoding: FileEncoding,
|
||||
): Promise<{ data: FileData; meta: FileMeta }> {
|
||||
return this.wrapped.readFile(name, encoding);
|
||||
}
|
||||
|
||||
getFileMeta(name: string): Promise<FileMeta> {
|
||||
return this.wrapped.getFileMeta(name);
|
||||
}
|
||||
|
||||
writeFile(
|
||||
name: string,
|
||||
encoding: FileEncoding,
|
||||
data: FileData,
|
||||
selfUpdate?: boolean | undefined,
|
||||
): Promise<FileMeta> {
|
||||
return this.wrapped.writeFile(name, encoding, data, selfUpdate);
|
||||
}
|
||||
|
||||
deleteFile(name: string): Promise<void> {
|
||||
return this.wrapped.deleteFile(name);
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
proxySyscall(plug: Plug<any>, name: string, args: any[]): Promise<any> {
|
||||
return this.wrapped.proxySyscall(plug, name, args);
|
||||
}
|
||||
|
||||
invokeFunction(
|
||||
plug: Plug<any>,
|
||||
env: string,
|
||||
name: string,
|
||||
args: any[],
|
||||
): Promise<any> {
|
||||
return this.wrapped.invokeFunction(plug, env, name, args);
|
||||
}
|
||||
}
|
|
@ -31,11 +31,7 @@ export class Space extends EventEmitter<SpaceEvents> {
|
|||
newPageList.forEach((meta) => {
|
||||
const pageName = meta.name;
|
||||
const oldPageMeta = this.pageMetaCache.get(pageName);
|
||||
const newPageMeta: PageMeta = {
|
||||
name: pageName,
|
||||
lastModified: meta.lastModified,
|
||||
perm: meta.perm,
|
||||
};
|
||||
const newPageMeta: PageMeta = { ...meta };
|
||||
if (
|
||||
!oldPageMeta &&
|
||||
(pageName.startsWith(plugPrefix) || !this.initialPageListLoad)
|
||||
|
|
|
@ -6,14 +6,14 @@ export type FileMeta = {
|
|||
contentType: string;
|
||||
size: number;
|
||||
perm: "ro" | "rw";
|
||||
};
|
||||
} & Record<string, any>;
|
||||
|
||||
export type PageMeta = {
|
||||
name: string;
|
||||
lastModified: number;
|
||||
lastOpened?: number;
|
||||
perm: "ro" | "rw";
|
||||
};
|
||||
} & Record<string, any>;
|
||||
|
||||
export type AttachmentMeta = {
|
||||
name: string;
|
||||
|
|
|
@ -60,20 +60,7 @@ export async function indexLinks({ name, tree }: IndexTreeEvent) {
|
|||
export async function pageQueryProvider({
|
||||
query,
|
||||
}: QueryProviderEvent): Promise<any[]> {
|
||||
let allPages = await space.listPages();
|
||||
const allPageMap: Map<string, any> = new Map(
|
||||
allPages.map((pm) => [pm.name, pm]),
|
||||
);
|
||||
for (const { page, value } of await index.queryPrefix("meta:")) {
|
||||
const p = allPageMap.get(page);
|
||||
if (p) {
|
||||
for (const [k, v] of Object.entries(value)) {
|
||||
p[k] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
allPages = [...allPageMap.values()];
|
||||
return applyQuery(query, allPages);
|
||||
return applyQuery(query, await space.listPages());
|
||||
}
|
||||
|
||||
export async function linkQueryProvider({
|
||||
|
|
|
@ -5,10 +5,18 @@ import { space } from "$sb/silverbullet-syscall/mod.ts";
|
|||
import { niceDate } from "$sb/lib/dates.ts";
|
||||
|
||||
const maxWidth = 70;
|
||||
|
||||
export function defaultJsonTransformer(_k: string, v: any) {
|
||||
if (v === undefined) {
|
||||
return "";
|
||||
}
|
||||
return "" + v;
|
||||
}
|
||||
|
||||
// Nicely format an array of JSON objects as a Markdown table
|
||||
export function jsonToMDTable(
|
||||
jsonArray: any[],
|
||||
valueTransformer: (k: string, v: any) => string = (_k, v) => "" + v,
|
||||
valueTransformer: (k: string, v: any) => string = defaultJsonTransformer,
|
||||
): string {
|
||||
const fieldWidths = new Map<string, number>();
|
||||
for (const entry of jsonArray) {
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
ensureTable as ensureStoreTable,
|
||||
storeSyscalls,
|
||||
} from "../plugos/syscalls/store.deno.ts";
|
||||
import { System } from "../plugos/system.ts";
|
||||
import { SysCallMapping, System } from "../plugos/system.ts";
|
||||
import { PageNamespaceHook } from "./hooks/page_namespace.ts";
|
||||
import { PlugSpacePrimitives } from "./hooks/plug_space_primitives.ts";
|
||||
import {
|
||||
|
@ -38,6 +38,7 @@ import { AssetBundlePlugSpacePrimitives } from "../common/spaces/asset_bundle_sp
|
|||
import assetSyscalls from "../plugos/syscalls/asset.ts";
|
||||
import { AssetBundle } from "../plugos/asset_bundle/bundle.ts";
|
||||
import { AsyncSQLite } from "../plugos/sqlite/async_sqlite.ts";
|
||||
import { FileMetaSpacePrimitives } from "../common/spaces/file_meta_space_primitives.ts";
|
||||
|
||||
export type ServerOptions = {
|
||||
port: number;
|
||||
|
@ -62,6 +63,7 @@ export class HttpServer {
|
|||
abortController?: AbortController;
|
||||
globalModules: Manifest;
|
||||
assetBundle: AssetBundle;
|
||||
indexSyscalls: SysCallMapping;
|
||||
|
||||
constructor(options: ServerOptions) {
|
||||
this.port = options.port;
|
||||
|
@ -84,17 +86,28 @@ export class HttpServer {
|
|||
const namespaceHook = new PageNamespaceHook();
|
||||
this.system.addHook(namespaceHook);
|
||||
|
||||
// The database used for persistence (SQLite)
|
||||
this.db = new AsyncSQLite(path.join(options.pagesPath, "data.db"));
|
||||
this.db.init().catch((e) => {
|
||||
console.error("Error initializing database", e);
|
||||
});
|
||||
|
||||
this.indexSyscalls = pageIndexSyscalls(this.db);
|
||||
|
||||
// The space
|
||||
try {
|
||||
this.spacePrimitives = new AssetBundlePlugSpacePrimitives(
|
||||
new EventedSpacePrimitives(
|
||||
new PlugSpacePrimitives(
|
||||
new DiskSpacePrimitives(options.pagesPath),
|
||||
namespaceHook,
|
||||
this.spacePrimitives = new FileMetaSpacePrimitives(
|
||||
new AssetBundlePlugSpacePrimitives(
|
||||
new EventedSpacePrimitives(
|
||||
new PlugSpacePrimitives(
|
||||
new DiskSpacePrimitives(options.pagesPath),
|
||||
namespaceHook,
|
||||
),
|
||||
this.eventHook,
|
||||
),
|
||||
this.eventHook,
|
||||
this.assetBundle,
|
||||
),
|
||||
this.assetBundle,
|
||||
this.indexSyscalls,
|
||||
);
|
||||
this.space = new Space(this.spacePrimitives);
|
||||
} catch (e: any) {
|
||||
|
@ -106,19 +119,13 @@ export class HttpServer {
|
|||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// The database used for persistence (SQLite)
|
||||
this.db = new AsyncSQLite(path.join(options.pagesPath, "data.db"));
|
||||
this.db.init().catch((e) => {
|
||||
console.error("Error initializing database", e);
|
||||
});
|
||||
|
||||
// The cron hook
|
||||
this.system.addHook(new DenoCronHook());
|
||||
|
||||
// Register syscalls available on the server side
|
||||
this.system.registerSyscalls(
|
||||
[],
|
||||
pageIndexSyscalls(this.db),
|
||||
this.indexSyscalls,
|
||||
storeSyscalls(this.db, "store"),
|
||||
fullTextSearchSyscalls(this.db, "fts"),
|
||||
spaceSyscalls(this.space),
|
||||
|
@ -308,10 +315,8 @@ export class HttpServer {
|
|||
this.addPasswordAuth(fsRouter);
|
||||
// File list
|
||||
fsRouter.get("/", async ({ response }) => {
|
||||
const list = await spacePrimitives.fetchFileList();
|
||||
// console.log("List", list);
|
||||
response.headers.set("Content-type", "application/json");
|
||||
response.body = JSON.stringify(list);
|
||||
response.body = JSON.stringify(await spacePrimitives.fetchFileList());
|
||||
});
|
||||
|
||||
fsRouter
|
||||
|
|
Loading…
Reference in New Issue