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) => {
|
newPageList.forEach((meta) => {
|
||||||
const pageName = meta.name;
|
const pageName = meta.name;
|
||||||
const oldPageMeta = this.pageMetaCache.get(pageName);
|
const oldPageMeta = this.pageMetaCache.get(pageName);
|
||||||
const newPageMeta: PageMeta = {
|
const newPageMeta: PageMeta = { ...meta };
|
||||||
name: pageName,
|
|
||||||
lastModified: meta.lastModified,
|
|
||||||
perm: meta.perm,
|
|
||||||
};
|
|
||||||
if (
|
if (
|
||||||
!oldPageMeta &&
|
!oldPageMeta &&
|
||||||
(pageName.startsWith(plugPrefix) || !this.initialPageListLoad)
|
(pageName.startsWith(plugPrefix) || !this.initialPageListLoad)
|
||||||
|
|
|
@ -6,14 +6,14 @@ export type FileMeta = {
|
||||||
contentType: string;
|
contentType: string;
|
||||||
size: number;
|
size: number;
|
||||||
perm: "ro" | "rw";
|
perm: "ro" | "rw";
|
||||||
};
|
} & Record<string, any>;
|
||||||
|
|
||||||
export type PageMeta = {
|
export type PageMeta = {
|
||||||
name: string;
|
name: string;
|
||||||
lastModified: number;
|
lastModified: number;
|
||||||
lastOpened?: number;
|
lastOpened?: number;
|
||||||
perm: "ro" | "rw";
|
perm: "ro" | "rw";
|
||||||
};
|
} & Record<string, any>;
|
||||||
|
|
||||||
export type AttachmentMeta = {
|
export type AttachmentMeta = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -60,20 +60,7 @@ export async function indexLinks({ name, tree }: IndexTreeEvent) {
|
||||||
export async function pageQueryProvider({
|
export async function pageQueryProvider({
|
||||||
query,
|
query,
|
||||||
}: QueryProviderEvent): Promise<any[]> {
|
}: QueryProviderEvent): Promise<any[]> {
|
||||||
let allPages = await space.listPages();
|
return applyQuery(query, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function linkQueryProvider({
|
export async function linkQueryProvider({
|
||||||
|
|
|
@ -5,10 +5,18 @@ import { space } from "$sb/silverbullet-syscall/mod.ts";
|
||||||
import { niceDate } from "$sb/lib/dates.ts";
|
import { niceDate } from "$sb/lib/dates.ts";
|
||||||
|
|
||||||
const maxWidth = 70;
|
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
|
// Nicely format an array of JSON objects as a Markdown table
|
||||||
export function jsonToMDTable(
|
export function jsonToMDTable(
|
||||||
jsonArray: any[],
|
jsonArray: any[],
|
||||||
valueTransformer: (k: string, v: any) => string = (_k, v) => "" + v,
|
valueTransformer: (k: string, v: any) => string = defaultJsonTransformer,
|
||||||
): string {
|
): string {
|
||||||
const fieldWidths = new Map<string, number>();
|
const fieldWidths = new Map<string, number>();
|
||||||
for (const entry of jsonArray) {
|
for (const entry of jsonArray) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ import {
|
||||||
ensureTable as ensureStoreTable,
|
ensureTable as ensureStoreTable,
|
||||||
storeSyscalls,
|
storeSyscalls,
|
||||||
} from "../plugos/syscalls/store.deno.ts";
|
} 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 { PageNamespaceHook } from "./hooks/page_namespace.ts";
|
||||||
import { PlugSpacePrimitives } from "./hooks/plug_space_primitives.ts";
|
import { PlugSpacePrimitives } from "./hooks/plug_space_primitives.ts";
|
||||||
import {
|
import {
|
||||||
|
@ -38,6 +38,7 @@ import { AssetBundlePlugSpacePrimitives } from "../common/spaces/asset_bundle_sp
|
||||||
import assetSyscalls from "../plugos/syscalls/asset.ts";
|
import assetSyscalls from "../plugos/syscalls/asset.ts";
|
||||||
import { AssetBundle } from "../plugos/asset_bundle/bundle.ts";
|
import { AssetBundle } from "../plugos/asset_bundle/bundle.ts";
|
||||||
import { AsyncSQLite } from "../plugos/sqlite/async_sqlite.ts";
|
import { AsyncSQLite } from "../plugos/sqlite/async_sqlite.ts";
|
||||||
|
import { FileMetaSpacePrimitives } from "../common/spaces/file_meta_space_primitives.ts";
|
||||||
|
|
||||||
export type ServerOptions = {
|
export type ServerOptions = {
|
||||||
port: number;
|
port: number;
|
||||||
|
@ -62,6 +63,7 @@ export class HttpServer {
|
||||||
abortController?: AbortController;
|
abortController?: AbortController;
|
||||||
globalModules: Manifest;
|
globalModules: Manifest;
|
||||||
assetBundle: AssetBundle;
|
assetBundle: AssetBundle;
|
||||||
|
indexSyscalls: SysCallMapping;
|
||||||
|
|
||||||
constructor(options: ServerOptions) {
|
constructor(options: ServerOptions) {
|
||||||
this.port = options.port;
|
this.port = options.port;
|
||||||
|
@ -84,17 +86,28 @@ export class HttpServer {
|
||||||
const namespaceHook = new PageNamespaceHook();
|
const namespaceHook = new PageNamespaceHook();
|
||||||
this.system.addHook(namespaceHook);
|
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
|
// The space
|
||||||
try {
|
try {
|
||||||
this.spacePrimitives = new AssetBundlePlugSpacePrimitives(
|
this.spacePrimitives = new FileMetaSpacePrimitives(
|
||||||
new EventedSpacePrimitives(
|
new AssetBundlePlugSpacePrimitives(
|
||||||
new PlugSpacePrimitives(
|
new EventedSpacePrimitives(
|
||||||
new DiskSpacePrimitives(options.pagesPath),
|
new PlugSpacePrimitives(
|
||||||
namespaceHook,
|
new DiskSpacePrimitives(options.pagesPath),
|
||||||
|
namespaceHook,
|
||||||
|
),
|
||||||
|
this.eventHook,
|
||||||
),
|
),
|
||||||
this.eventHook,
|
this.assetBundle,
|
||||||
),
|
),
|
||||||
this.assetBundle,
|
this.indexSyscalls,
|
||||||
);
|
);
|
||||||
this.space = new Space(this.spacePrimitives);
|
this.space = new Space(this.spacePrimitives);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
@ -106,19 +119,13 @@ export class HttpServer {
|
||||||
Deno.exit(1);
|
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
|
// The cron hook
|
||||||
this.system.addHook(new DenoCronHook());
|
this.system.addHook(new DenoCronHook());
|
||||||
|
|
||||||
// Register syscalls available on the server side
|
// Register syscalls available on the server side
|
||||||
this.system.registerSyscalls(
|
this.system.registerSyscalls(
|
||||||
[],
|
[],
|
||||||
pageIndexSyscalls(this.db),
|
this.indexSyscalls,
|
||||||
storeSyscalls(this.db, "store"),
|
storeSyscalls(this.db, "store"),
|
||||||
fullTextSearchSyscalls(this.db, "fts"),
|
fullTextSearchSyscalls(this.db, "fts"),
|
||||||
spaceSyscalls(this.space),
|
spaceSyscalls(this.space),
|
||||||
|
@ -308,10 +315,8 @@ export class HttpServer {
|
||||||
this.addPasswordAuth(fsRouter);
|
this.addPasswordAuth(fsRouter);
|
||||||
// File list
|
// File list
|
||||||
fsRouter.get("/", async ({ response }) => {
|
fsRouter.get("/", async ({ response }) => {
|
||||||
const list = await spacePrimitives.fetchFileList();
|
|
||||||
// console.log("List", list);
|
|
||||||
response.headers.set("Content-type", "application/json");
|
response.headers.set("Content-type", "application/json");
|
||||||
response.body = JSON.stringify(list);
|
response.body = JSON.stringify(await spacePrimitives.fetchFileList());
|
||||||
});
|
});
|
||||||
|
|
||||||
fsRouter
|
fsRouter
|
||||||
|
|
Loading…
Reference in New Issue