1
0

Space System refactor

This commit is contained in:
Zef Hemel 2022-11-26 14:15:38 +01:00
parent 80a2d4e58a
commit eff0277be0
7 changed files with 290 additions and 223 deletions

33
cmd/invokeFunction.ts Normal file
View File

@ -0,0 +1,33 @@
import { SpaceSystem } from "../server/space_system.ts";
import assetBundle from "../dist/asset_bundle.json" assert { type: "json" };
import { path } from "../plugos/deps.ts";
import { AssetBundle, AssetJson } from "../plugos/asset_bundle/bundle.ts";
export async function invokeFunction(
options: any,
pagesPath: string,
functionName: string,
...args: string[]
) {
console.log("Going to invoke funciton", functionName, "with args", args);
const spaceSystem = new SpaceSystem(
new AssetBundle(assetBundle as AssetJson),
pagesPath,
path.join(pagesPath, options.db),
);
await spaceSystem.start();
const [plugName, funcName] = functionName.split(".");
const plug = spaceSystem.system.loadedPlugs.get(plugName);
if (!plug) {
console.error("Plug not found", plugName);
Deno.exit(1);
}
await plug.invoke(funcName, args);
Deno.exit(0);
}

View File

@ -14,7 +14,7 @@ import {
storeSyscalls, storeSyscalls,
} from "../plugos/syscalls/store.deno.ts"; } from "../plugos/syscalls/store.deno.ts";
import { System } from "../plugos/system.ts"; import { System } from "../plugos/system.ts";
import { Manifest, SilverBulletHooks } from "../common/manifest.ts"; import { SilverBulletHooks } from "../common/manifest.ts";
import { loadMarkdownExtensions } from "../common/markdown_ext.ts"; import { loadMarkdownExtensions } from "../common/markdown_ext.ts";
import buildMarkdown from "../common/parser.ts"; import buildMarkdown from "../common/parser.ts";
import { DiskSpacePrimitives } from "../common/spaces/disk_space_primitives.ts"; import { DiskSpacePrimitives } from "../common/spaces/disk_space_primitives.ts";
@ -29,15 +29,12 @@ import {
} from "../server/syscalls/index.ts"; } from "../server/syscalls/index.ts";
import spaceSyscalls from "../server/syscalls/space.ts"; import spaceSyscalls from "../server/syscalls/space.ts";
import { Command } from "https://deno.land/x/cliffy@v0.25.2/command/command.ts";
import assetBundle from "../dist/asset_bundle.json" assert { type: "json" }; import assetBundle from "../dist/asset_bundle.json" assert { type: "json" };
import { AssetBundle, AssetJson } from "../plugos/asset_bundle/bundle.ts"; import { AssetBundle, AssetJson } from "../plugos/asset_bundle/bundle.ts";
import { path } from "../server/deps.ts"; import { path } from "../server/deps.ts";
import { AsyncSQLite } from "../plugos/sqlite/async_sqlite.ts"; import { AsyncSQLite } from "../plugos/sqlite/async_sqlite.ts";
import { AssetBundlePlugSpacePrimitives } from "../common/spaces/asset_bundle_space_primitives.ts"; import { AssetBundlePlugSpacePrimitives } from "../common/spaces/asset_bundle_space_primitives.ts";
import assetSyscalls from "../plugos/syscalls/asset.ts"; import assetSyscalls from "../plugos/syscalls/asset.ts";
import { faBullseye } from "https://esm.sh/v96/@fortawesome/free-solid-svg-icons@6.2.0/index.d.ts";
export async function publishCommand(options: { export async function publishCommand(options: {
index: boolean; index: boolean;

View File

@ -12,11 +12,14 @@ export function serveCommand(options: any, folder: string) {
port, port,
"serving pages from", "serving pages from",
pagesPath, pagesPath,
"with db file",
options.db,
); );
const httpServer = new HttpServer({ const httpServer = new HttpServer({
port: port, port: port,
pagesPath: pagesPath, pagesPath: pagesPath,
dbPath: path.join(pagesPath, options.db),
assetBundle: new AssetBundle(assetBundle as AssetJson), assetBundle: new AssetBundle(assetBundle as AssetJson),
password: options.password, password: options.password,
}); });

View File

@ -1,164 +1,41 @@
import { Application, path, Router } from "./deps.ts"; import { Application, path, Router } from "./deps.ts";
import { Manifest, SilverBulletHooks } from "../common/manifest.ts"; import { Manifest } from "../common/manifest.ts";
import { loadMarkdownExtensions } from "../common/markdown_ext.ts";
import buildMarkdown from "../common/parser.ts";
import { DiskSpacePrimitives } from "../common/spaces/disk_space_primitives.ts";
import { EventedSpacePrimitives } from "../common/spaces/evented_space_primitives.ts";
import { Space } from "../common/spaces/space.ts";
import { SpacePrimitives } from "../common/spaces/space_primitives.ts"; import { SpacePrimitives } from "../common/spaces/space_primitives.ts";
import { markdownSyscalls } from "../common/syscalls/markdown.ts";
import { parseYamlSettings } from "../common/util.ts";
import { createSandbox } from "../plugos/environments/deno_sandbox.ts";
import { EndpointHook } from "../plugos/hooks/endpoint.ts"; import { EndpointHook } from "../plugos/hooks/endpoint.ts";
import { EventHook } from "../plugos/hooks/event.ts";
import { DenoCronHook } from "../plugos/hooks/cron.deno.ts";
import { esbuildSyscalls } from "../plugos/syscalls/esbuild.ts";
import { eventSyscalls } from "../plugos/syscalls/event.ts";
import fileSystemSyscalls from "../plugos/syscalls/fs.deno.ts";
import {
ensureFTSTable,
fullTextSearchSyscalls,
} from "../plugos/syscalls/fulltext.sqlite.ts";
import sandboxSyscalls from "../plugos/syscalls/sandbox.ts";
import shellSyscalls from "../plugos/syscalls/shell.deno.ts";
import {
ensureTable as ensureStoreTable,
storeSyscalls,
} from "../plugos/syscalls/store.deno.ts";
import { SysCallMapping, System } from "../plugos/system.ts";
import { PageNamespaceHook } from "./hooks/page_namespace.ts";
import { PlugSpacePrimitives } from "./hooks/plug_space_primitives.ts";
import {
ensureTable as ensureIndexTable,
pageIndexSyscalls,
} from "./syscalls/index.ts";
import spaceSyscalls from "./syscalls/space.ts";
import { systemSyscalls } from "./syscalls/system.ts";
import { AssetBundlePlugSpacePrimitives } from "../common/spaces/asset_bundle_space_primitives.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 { SpaceSystem } from "./space_system.ts";
import { FileMetaSpacePrimitives } from "../common/spaces/file_meta_space_primitives.ts"; import { parseYamlSettings } from "../common/util.ts";
export type ServerOptions = { export type ServerOptions = {
port: number; port: number;
pagesPath: string; pagesPath: string;
dbPath: string;
assetBundle: AssetBundle; assetBundle: AssetBundle;
password?: string; password?: string;
}; };
const indexRequiredKey = "$spaceIndexed";
const staticLastModified = new Date().toUTCString(); const staticLastModified = new Date().toUTCString();
export class HttpServer { export class HttpServer {
app: Application; app: Application;
system: System<SilverBulletHooks>; systemBoot: SpaceSystem;
private space: Space;
private eventHook: EventHook;
private db: AsyncSQLite;
private port: number; private port: number;
password?: string; password?: string;
settings: { [key: string]: any } = {}; settings: { [key: string]: any } = {};
spacePrimitives: SpacePrimitives;
abortController?: AbortController; abortController?: AbortController;
globalModules: Manifest;
assetBundle: AssetBundle;
indexSyscalls: SysCallMapping;
constructor(options: ServerOptions) { constructor(options: ServerOptions) {
this.port = options.port; this.port = options.port;
this.app = new Application(); //{ serverConstructor: FlashServer }); this.app = new Application(); //{ serverConstructor: FlashServer });
this.assetBundle = options.assetBundle;
this.password = options.password; this.password = options.password;
this.systemBoot = new SpaceSystem(
this.globalModules = JSON.parse( options.assetBundle,
this.assetBundle.readTextFileSync(`web/global.plug.json`), options.pagesPath,
options.dbPath,
); );
// Set up the PlugOS System
this.system = new System<SilverBulletHooks>("server");
// Instantiate the event bus hook
this.eventHook = new EventHook();
this.system.addHook(this.eventHook);
// And the page namespace hook
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 FileMetaSpacePrimitives(
new AssetBundlePlugSpacePrimitives(
new EventedSpacePrimitives(
new PlugSpacePrimitives(
new DiskSpacePrimitives(options.pagesPath),
namespaceHook,
"server",
),
this.eventHook,
),
this.assetBundle,
),
this.indexSyscalls,
);
this.space = new Space(this.spacePrimitives);
} catch (e: any) {
if (e instanceof Deno.errors.NotFound) {
console.error("Pages folder", options.pagesPath, "not found");
} else {
console.error(e.message);
}
Deno.exit(1);
}
// The cron hook
this.system.addHook(new DenoCronHook());
// Register syscalls available on the server side
this.system.registerSyscalls(
[],
this.indexSyscalls,
storeSyscalls(this.db, "store"),
fullTextSearchSyscalls(this.db, "fts"),
spaceSyscalls(this.space),
eventSyscalls(this.eventHook),
markdownSyscalls(buildMarkdown([])),
esbuildSyscalls([this.globalModules]),
systemSyscalls(this, this.system),
sandboxSyscalls(this.system),
assetSyscalls(this.system),
// jwtSyscalls(),
);
// Danger zone
this.system.registerSyscalls(["shell"], shellSyscalls(options.pagesPath));
this.system.registerSyscalls(["fs"], fileSystemSyscalls("/"));
// Register the HTTP endpoint hook (with "/_/<plug-name>"" prefix, hardcoded for now)
this.system.addHook(new EndpointHook(this.app, "/_"));
this.system.on({
sandboxInitialized: async (sandbox) => {
for (
const [modName, code] of Object.entries(
this.globalModules.dependencies!,
)
) {
await sandbox.loadDependency(modName, code as string);
}
},
});
// Second, for loading plug JSON files with absolute or relative (from CWD) paths // Second, for loading plug JSON files with absolute or relative (from CWD) paths
this.eventHook.addLocalListener( this.systemBoot.eventHook.addLocalListener(
"get-plug:file", "get-plug:file",
async (plugPath: string): Promise<Manifest> => { async (plugPath: string): Promise<Manifest> => {
const resolvedPath = path.resolve(plugPath); const resolvedPath = path.resolve(plugPath);
@ -175,57 +52,17 @@ export class HttpServer {
// Rescan disk every 5s to detect any out-of-process file changes // Rescan disk every 5s to detect any out-of-process file changes
setInterval(() => { setInterval(() => {
this.space.updatePageList().catch(console.error); this.systemBoot.space.updatePageList().catch(console.error);
}, 5000); }, 5000);
}
rebuildMdExtensions() { // Register the HTTP endpoint hook (with "/_/<plug-name>"" prefix, hardcoded for now)
this.system.registerSyscalls( this.systemBoot.system.addHook(new EndpointHook(this.app, "/_"));
[],
markdownSyscalls(buildMarkdown(loadMarkdownExtensions(this.system))),
);
}
async reloadPlugs() {
await this.space.updatePageList();
const allPlugs = await this.space.listPlugs();
console.log("Loading plugs", allPlugs);
await Promise.all((await this.space.listPlugs()).map(async (plugName) => {
const { data } = await this.space.readAttachment(plugName, "string");
await this.system.load(JSON.parse(data as string), createSandbox);
}));
this.rebuildMdExtensions();
const corePlug = this.system.loadedPlugs.get("core");
if (!corePlug) {
console.error("Something went very wrong, 'core' plug not found");
return;
}
// Do we need to reindex this space?
if (
!(await this.system.localSyscall("core", "store.get", [indexRequiredKey]))
) {
console.log("Now reindexing space...");
await corePlug.invoke("reindexSpace", []);
await this.system.localSyscall("core", "store.set", [
indexRequiredKey,
true,
]);
}
} }
async start() { async start() {
await ensureIndexTable(this.db); await this.systemBoot.start();
await ensureStoreTable(this.db, "store"); await this.systemBoot.ensureSpaceIndex();
await ensureFTSTable(this.db, "fts");
await this.ensureAndLoadSettings(); await this.ensureAndLoadSettings();
// Load plugs
this.reloadPlugs().catch(console.error);
// Serve static files (javascript, css, html) // Serve static files (javascript, css, html)
this.app.use(async ({ request, response }, next) => { this.app.use(async ({ request, response }, next) => {
if (request.url.pathname === "/") { if (request.url.pathname === "/") {
@ -234,7 +71,7 @@ export class HttpServer {
return; return;
} }
response.headers.set("Content-type", "text/html"); response.headers.set("Content-type", "text/html");
response.body = this.assetBundle.readTextFileSync( response.body = this.systemBoot.assetBundle.readTextFileSync(
"web/index.html", "web/index.html",
); );
response.headers.set("Last-Modified", staticLastModified); response.headers.set("Last-Modified", staticLastModified);
@ -243,7 +80,7 @@ export class HttpServer {
try { try {
const assetName = `web${request.url.pathname}`; const assetName = `web${request.url.pathname}`;
if ( if (
this.assetBundle.has(assetName) && this.systemBoot.assetBundle.has(assetName) &&
request.headers.get("If-Modified-Since") === staticLastModified request.headers.get("If-Modified-Since") === staticLastModified
) { ) {
response.status = 304; response.status = 304;
@ -252,9 +89,9 @@ export class HttpServer {
response.status = 200; response.status = 200;
response.headers.set( response.headers.set(
"Content-type", "Content-type",
this.assetBundle.getMimeType(assetName), this.systemBoot.assetBundle.getMimeType(assetName),
); );
const data = this.assetBundle.readFileSync( const data = this.systemBoot.assetBundle.readFileSync(
assetName, assetName,
); );
response.headers.set("Cache-Control", "no-cache"); response.headers.set("Cache-Control", "no-cache");
@ -270,7 +107,7 @@ export class HttpServer {
}); });
// Pages API // Pages API
const fsRouter = this.buildFsRouter(this.spacePrimitives); const fsRouter = this.buildFsRouter(this.systemBoot.spacePrimitives);
this.app.use(fsRouter.routes()); this.app.use(fsRouter.routes());
this.app.use(fsRouter.allowedMethods()); this.app.use(fsRouter.allowedMethods());
@ -282,7 +119,7 @@ export class HttpServer {
// Fallback, serve index.html // Fallback, serve index.html
this.app.use((ctx) => { this.app.use((ctx) => {
ctx.response.headers.set("Content-type", "text/html"); ctx.response.headers.set("Content-type", "text/html");
ctx.response.body = this.assetBundle.readTextFileSync( ctx.response.body = this.systemBoot.assetBundle.readTextFileSync(
"web/index.html", "web/index.html",
); );
}); });
@ -296,7 +133,34 @@ export class HttpServer {
console.log( console.log(
`Silver Bullet is now running: http://localhost:${this.port}`, `Silver Bullet is now running: http://localhost:${this.port}`,
); );
console.log("--------------"); }
async ensureAndLoadSettings() {
const space = this.systemBoot.space;
try {
await space.getPageMeta("SETTINGS");
} catch {
await space.writePage(
"SETTINGS",
this.systemBoot.assetBundle.readTextFileSync("SETTINGS_template.md"),
true,
);
}
const { text: settingsText } = await space.readPage("SETTINGS");
const settings = parseYamlSettings(settingsText);
if (!settings.indexPage) {
settings.indexPage = "index";
}
try {
await space.getPageMeta(settings.indexPage);
} catch {
await space.writePage(
settings.indexPage,
`Welcome to your new space!`,
);
}
} }
private addPasswordAuth(r: Router) { private addPasswordAuth(r: Router) {
@ -413,6 +277,7 @@ export class HttpServer {
private buildPlugRouter(): Router { private buildPlugRouter(): Router {
const plugRouter = new Router(); const plugRouter = new Router();
this.addPasswordAuth(plugRouter); this.addPasswordAuth(plugRouter);
const system = this.systemBoot.system;
plugRouter.post( plugRouter.post(
"/:plug/syscall/:name", "/:plug/syscall/:name",
@ -421,14 +286,14 @@ export class HttpServer {
const plugName = ctx.params.plug; const plugName = ctx.params.plug;
const args = await ctx.request.body().value; const args = await ctx.request.body().value;
console.log("Got args", args, "for", name, "in", plugName); console.log("Got args", args, "for", name, "in", plugName);
const plug = this.system.loadedPlugs.get(plugName); const plug = system.loadedPlugs.get(plugName);
if (!plug) { if (!plug) {
ctx.response.status = 404; ctx.response.status = 404;
ctx.response.body = `Plug ${plugName} not found`; ctx.response.body = `Plug ${plugName} not found`;
return; return;
} }
try { try {
const result = await this.system.syscallWithContext( const result = await system.syscallWithContext(
{ plug }, { plug },
name, name,
args, args,
@ -450,7 +315,7 @@ export class HttpServer {
const name = ctx.params.name; const name = ctx.params.name;
const plugName = ctx.params.plug; const plugName = ctx.params.plug;
const args = await ctx.request.body().value; const args = await ctx.request.body().value;
const plug = this.system.loadedPlugs.get(plugName); const plug = system.loadedPlugs.get(plugName);
if (!plug) { if (!plug) {
ctx.response.status = 404; ctx.response.status = 404;
ctx.response.body = `Plug ${plugName} not found`; ctx.response.body = `Plug ${plugName} not found`;
@ -471,37 +336,11 @@ export class HttpServer {
return new Router().use("/plug", plugRouter.routes()); return new Router().use("/plug", plugRouter.routes());
} }
async ensureAndLoadSettings() {
try {
await this.space.getPageMeta("SETTINGS");
} catch {
await this.space.writePage(
"SETTINGS",
this.assetBundle.readTextFileSync("SETTINGS_template.md"),
true,
);
}
const { text: settingsText } = await this.space.readPage("SETTINGS");
this.settings = parseYamlSettings(settingsText);
if (!this.settings.indexPage) {
this.settings.indexPage = "index";
}
try {
await this.space.getPageMeta(this.settings.indexPage);
} catch {
await this.space.writePage(
this.settings.indexPage,
`Welcome to your new space!`,
);
}
}
async stop() { async stop() {
const system = this.systemBoot.system;
if (this.abortController) { if (this.abortController) {
console.log("Stopping"); console.log("Stopping");
await this.system.unloadAll(); await system.unloadAll();
console.log("Stopped plugs"); console.log("Stopped plugs");
this.abortController.abort(); this.abortController.abort();
console.log("stopped server"); console.log("stopped server");

185
server/space_system.ts Normal file
View File

@ -0,0 +1,185 @@
import { SilverBulletHooks } from "../common/manifest.ts";
import { loadMarkdownExtensions } from "../common/markdown_ext.ts";
import buildMarkdown from "../common/parser.ts";
import { DiskSpacePrimitives } from "../common/spaces/disk_space_primitives.ts";
import { EventedSpacePrimitives } from "../common/spaces/evented_space_primitives.ts";
import { Space } from "../common/spaces/space.ts";
import { SpacePrimitives } from "../common/spaces/space_primitives.ts";
import { markdownSyscalls } from "../common/syscalls/markdown.ts";
import { createSandbox } from "../plugos/environments/deno_sandbox.ts";
import { EventHook } from "../plugos/hooks/event.ts";
import { DenoCronHook } from "../plugos/hooks/cron.deno.ts";
import { esbuildSyscalls } from "../plugos/syscalls/esbuild.ts";
import { eventSyscalls } from "../plugos/syscalls/event.ts";
import fileSystemSyscalls from "../plugos/syscalls/fs.deno.ts";
import {
ensureFTSTable,
fullTextSearchSyscalls,
} from "../plugos/syscalls/fulltext.sqlite.ts";
import sandboxSyscalls from "../plugos/syscalls/sandbox.ts";
import shellSyscalls from "../plugos/syscalls/shell.deno.ts";
import {
ensureTable as ensureStoreTable,
storeSyscalls,
} from "../plugos/syscalls/store.deno.ts";
import { System } from "../plugos/system.ts";
import { PageNamespaceHook } from "./hooks/page_namespace.ts";
import { PlugSpacePrimitives } from "./hooks/plug_space_primitives.ts";
import {
ensureTable as ensureIndexTable,
pageIndexSyscalls,
} from "./syscalls/index.ts";
import spaceSyscalls from "./syscalls/space.ts";
import { systemSyscalls } from "./syscalls/system.ts";
import { AssetBundlePlugSpacePrimitives } from "../common/spaces/asset_bundle_space_primitives.ts";
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 const indexRequiredKey = "$spaceIndexed";
// A composition of a PlugOS system attached to a Space for server-side use
export class SpaceSystem {
public system: System<SilverBulletHooks>;
public space: Space;
public eventHook: EventHook;
public spacePrimitives: SpacePrimitives;
private db: AsyncSQLite;
constructor(
readonly assetBundle: AssetBundle,
pagesPath: string,
databasePath: string,
) {
const globalModules = JSON.parse(
assetBundle.readTextFileSync(`web/global.plug.json`),
);
// Set up the PlugOS System
this.system = new System<SilverBulletHooks>("server");
// Instantiate the event bus hook
this.eventHook = new EventHook();
this.system.addHook(this.eventHook);
// And the page namespace hook
const namespaceHook = new PageNamespaceHook();
this.system.addHook(namespaceHook);
// The database used for persistence (SQLite)
this.db = new AsyncSQLite(databasePath);
this.db.init().catch((e) => {
console.error("Error initializing database", e);
});
const indexSyscalls = pageIndexSyscalls(this.db);
// The space
try {
this.spacePrimitives = new FileMetaSpacePrimitives(
new AssetBundlePlugSpacePrimitives(
new EventedSpacePrimitives(
new PlugSpacePrimitives(
new DiskSpacePrimitives(pagesPath),
namespaceHook,
"server",
),
this.eventHook,
),
assetBundle,
),
indexSyscalls,
);
this.space = new Space(this.spacePrimitives);
} catch (e: any) {
if (e instanceof Deno.errors.NotFound) {
console.error("Pages folder", pagesPath, "not found");
} else {
console.error(e.message);
}
Deno.exit(1);
}
// The cron hook
this.system.addHook(new DenoCronHook());
// Register syscalls available on the server side
this.system.registerSyscalls(
[],
indexSyscalls,
storeSyscalls(this.db, "store"),
fullTextSearchSyscalls(this.db, "fts"),
spaceSyscalls(this.space),
eventSyscalls(this.eventHook),
markdownSyscalls(buildMarkdown([])),
esbuildSyscalls([globalModules]),
systemSyscalls(this.loadPlugsFromSpace.bind(this), this.system),
sandboxSyscalls(this.system),
assetSyscalls(this.system),
);
// Danger zone, these syscalls require requesting specific permissions
this.system.registerSyscalls(["shell"], shellSyscalls(pagesPath));
this.system.registerSyscalls(["fs"], fileSystemSyscalls("/"));
this.system.on({
sandboxInitialized: async (sandbox) => {
for (
const [modName, code] of Object.entries(
globalModules.dependencies!,
)
) {
await sandbox.loadDependency(modName, code as string);
}
},
});
}
// Loads all plugs under "_plug/" in the space
async loadPlugsFromSpace() {
await this.space.updatePageList();
const allPlugs = await this.space.listPlugs();
console.log("Going to load", allPlugs.length, "plugs...");
await Promise.all(allPlugs.map(async (plugName) => {
const { data } = await this.space.readAttachment(plugName, "string");
await this.system.load(JSON.parse(data as string), createSandbox);
}));
// Re-register the markdown syscall with new markdown extensions
this.system.registerSyscalls(
[],
markdownSyscalls(buildMarkdown(loadMarkdownExtensions(this.system))),
);
}
// Checks if the space has been indexed, and if not, does so
async ensureSpaceIndex(forceReindex = false) {
const corePlug = this.system.loadedPlugs.get("core");
if (!corePlug) {
console.error("Something went very wrong, 'core' plug not found");
return;
}
// Do we need to reindex this space?
if (
forceReindex ||
!(await this.system.localSyscall("core", "store.get", [indexRequiredKey]))
) {
console.log("Now reindexing space...");
await corePlug.invoke("reindexSpace", []);
await this.system.localSyscall("core", "store.set", [
indexRequiredKey,
true,
]);
}
}
async start() {
await ensureIndexTable(this.db);
await ensureStoreTable(this.db, "store");
await ensureFTSTable(this.db, "fts");
await this.loadPlugsFromSpace();
}
}

View File

@ -1,9 +1,8 @@
import { Plug } from "../../plugos/plug.ts"; import { Plug } from "../../plugos/plug.ts";
import { SysCallMapping, System } from "../../plugos/system.ts"; import { SysCallMapping, System } from "../../plugos/system.ts";
import type { HttpServer } from "../http_server.ts";
export function systemSyscalls( export function systemSyscalls(
httpServer: HttpServer, plugReloader: () => Promise<void>,
system: System<any>, system: System<any>,
): SysCallMapping { ): SysCallMapping {
return { return {
@ -30,7 +29,7 @@ export function systemSyscalls(
return plug.invoke(name, args); return plug.invoke(name, args);
}, },
"system.reloadPlugs": () => { "system.reloadPlugs": () => {
return httpServer.reloadPlugs(); return plugReloader();
}, },
}; };
} }

View File

@ -8,6 +8,7 @@ import { fixCommand } from "./cmd/fix.ts";
import { serveCommand } from "./cmd/server.ts"; import { serveCommand } from "./cmd/server.ts";
import { plugCompileCommand } from "./cmd/plug_compile.ts"; import { plugCompileCommand } from "./cmd/plug_compile.ts";
import { publishCommand } from "./cmd/publish.ts"; import { publishCommand } from "./cmd/publish.ts";
import { invokeFunction } from "./cmd/invokeFunction.ts";
await new Command() await new Command()
.name("silverbullet") .name("silverbullet")
@ -20,6 +21,9 @@ await new Command()
// Main command // Main command
.arguments("<folder:string>") .arguments("<folder:string>")
.option("-p, --port <port:number>", "Port to listen on") .option("-p, --port <port:number>", "Port to listen on")
.option("--db <dbfile:string>", "Filename for the database", {
default: "data.db",
})
.option("--password <password:string>", "Password for basic authentication") .option("--password <password:string>", "Password for basic authentication")
.action(serveCommand) .action(serveCommand)
// fix // fix
@ -43,6 +47,13 @@ await new Command()
) )
.option("--importmap <path:string>", "Path to import map file to use") .option("--importmap <path:string>", "Path to import map file to use")
.action(plugCompileCommand) .action(plugCompileCommand)
// invokeFunction
.command("invokeFunction", "Invoke a specific plug function from the CLI")
.arguments("<path:string> <function:string> [...arguments:string]")
.option("--db <dbfile:string>", "Filename for the database", {
default: "data.db",
})
.action(invokeFunction)
// publish // publish
.command("publish") .command("publish")
.description("Publish a SilverBullet site") .description("Publish a SilverBullet site")