Massive cleanup and plugbox cleanup
This commit is contained in:
+3
-3
@@ -1,4 +1,4 @@
|
||||
import { test, expect, beforeAll, afterAll, describe } from "@jest/globals";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "@jest/globals";
|
||||
|
||||
import { createServer } from "http";
|
||||
import { io as Client } from "socket.io-client";
|
||||
@@ -7,7 +7,7 @@ import { SocketServer } from "./api_server";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import { System } from "../plugbox/runtime";
|
||||
import { System } from "../plugbox/system";
|
||||
|
||||
describe("Server test", () => {
|
||||
let io: Server,
|
||||
@@ -43,7 +43,7 @@ describe("Server test", () => {
|
||||
socketServer = new SocketServer(
|
||||
tmpDir,
|
||||
io,
|
||||
new System<SilverBulletHooks>()
|
||||
new System<SilverBulletHooks>("server")
|
||||
);
|
||||
clientSocket.on("connect", done);
|
||||
await socketServer.init();
|
||||
|
||||
+14
-5
@@ -3,17 +3,20 @@ import { Page } from "./types";
|
||||
import * as path from "path";
|
||||
import { IndexApi } from "./index_api";
|
||||
import { PageApi } from "./page_api";
|
||||
import { System } from "../plugbox/runtime";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import pageIndexSyscalls from "./syscalls/page_index";
|
||||
import { safeRun } from "./util";
|
||||
import { System } from "../plugbox/system";
|
||||
|
||||
export class ClientConnection {
|
||||
openPages = new Set<string>();
|
||||
|
||||
constructor(readonly sock: Socket) {}
|
||||
}
|
||||
|
||||
export interface ApiProvider {
|
||||
init(): Promise<void>;
|
||||
|
||||
api(): Object;
|
||||
}
|
||||
|
||||
@@ -62,13 +65,19 @@ export class SocketServer {
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
console.log("Disconnected", socket.id);
|
||||
clientConn.openPages.forEach(disconnectPageSocket);
|
||||
clientConn.openPages.forEach((pageName) => {
|
||||
safeRun(async () => {
|
||||
await disconnectPageSocket(pageName);
|
||||
});
|
||||
});
|
||||
this.connectedSockets.delete(socket);
|
||||
});
|
||||
|
||||
socket.on("page.closePage", (pageName: string) => {
|
||||
console.log("Client closed page", pageName);
|
||||
disconnectPageSocket(pageName);
|
||||
safeRun(async () => {
|
||||
await disconnectPageSocket(pageName);
|
||||
});
|
||||
clientConn.openPages.delete(pageName);
|
||||
});
|
||||
|
||||
@@ -87,12 +96,12 @@ export class SocketServer {
|
||||
});
|
||||
};
|
||||
|
||||
const disconnectPageSocket = (pageName: string) => {
|
||||
const disconnectPageSocket = async (pageName: string) => {
|
||||
let page = this.openPages.get(pageName);
|
||||
if (page) {
|
||||
for (let client of page.clientStates) {
|
||||
if (client.socket === socket) {
|
||||
(this.apis.get("page")! as PageApi).disconnectClient(
|
||||
await (this.apis.get("page")! as PageApi).disconnectClient(
|
||||
client,
|
||||
page
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { readdir, readFile, stat, unlink, writeFile } from "fs/promises";
|
||||
import { mkdir, readdir, readFile, stat, unlink, writeFile } from "fs/promises";
|
||||
import * as path from "path";
|
||||
import { PageMeta } from "./types";
|
||||
|
||||
@@ -48,7 +48,7 @@ export class DiskStorage {
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
// console.error("Error while writing page", pageName, e);
|
||||
// console.error("Error while reading page", pageName, e);
|
||||
throw Error(`Could not read page ${pageName}`);
|
||||
}
|
||||
}
|
||||
@@ -56,9 +56,13 @@ export class DiskStorage {
|
||||
async writePage(pageName: string, text: string): Promise<PageMeta> {
|
||||
let localPath = path.join(this.rootPath, pageName + ".md");
|
||||
try {
|
||||
// Ensure parent folder exists
|
||||
await mkdir(path.dirname(localPath), { recursive: true });
|
||||
|
||||
// Actually write the file
|
||||
await writeFile(localPath, text);
|
||||
|
||||
// console.log(`Wrote to ${localPath}`);
|
||||
// Fetch new metadata
|
||||
const s = await stat(localPath);
|
||||
return {
|
||||
name: pageName,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Express } from "express";
|
||||
import { System } from "../plugbox/runtime";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import { exposeSystem } from "../plugbox/endpoints";
|
||||
import { EndpointFeature } from "../plugbox/feature/endpoint";
|
||||
import { readFile } from "fs/promises";
|
||||
import { System } from "../plugbox/system";
|
||||
|
||||
export class ExpressServer {
|
||||
app: Express;
|
||||
@@ -19,7 +19,7 @@ export class ExpressServer {
|
||||
this.rootPath = rootPath;
|
||||
this.system = system;
|
||||
|
||||
app.use(exposeSystem(this.system));
|
||||
system.addFeature(new EndpointFeature(app));
|
||||
|
||||
// Fallback, serve index.html
|
||||
let cachedIndex: string | undefined = undefined;
|
||||
|
||||
+30
-25
@@ -9,8 +9,8 @@ import fs from "fs";
|
||||
import path from "path";
|
||||
import { stat } from "fs/promises";
|
||||
import { Cursor, cursorEffect } from "../webapp/cursorEffect";
|
||||
import { System } from "../plugbox/runtime";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import { System } from "../plugbox/system";
|
||||
|
||||
export class PageApi implements ApiProvider {
|
||||
openPages: Map<string, Page>;
|
||||
@@ -34,17 +34,18 @@ export class PageApi implements ApiProvider {
|
||||
|
||||
async init(): Promise<void> {
|
||||
this.fileWatcher();
|
||||
// TODO: Move this elsewhere, this doesn't belong here
|
||||
this.system.on({
|
||||
plugUpdated: (plugName, plugDef) => {
|
||||
plugLoaded: (plugName, plugDef) => {
|
||||
console.log("Plug updated on disk, broadcasting to all clients");
|
||||
this.connectedSockets.forEach((socket) => {
|
||||
socket.emit("plugUpdated", plugName, plugDef);
|
||||
socket.emit("plugLoaded", plugName, plugDef.manifest);
|
||||
});
|
||||
},
|
||||
plugRemoved: (plugName) => {
|
||||
plugUnloaded: (plugName) => {
|
||||
console.log("Plug removed on disk, broadcasting to all clients");
|
||||
this.connectedSockets.forEach((socket) => {
|
||||
socket.emit("plugRemoved", plugName);
|
||||
socket.emit("plugUnloaded", plugName);
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -53,27 +54,25 @@ export class PageApi implements ApiProvider {
|
||||
broadcastCursors(page: Page) {
|
||||
page.clientStates.forEach((client) => {
|
||||
client.socket.emit(
|
||||
"cursorSnapshot",
|
||||
page.name,
|
||||
Object.fromEntries(page.cursors.entries())
|
||||
"cursorSnapshot",
|
||||
page.name,
|
||||
Object.fromEntries(page.cursors.entries())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
flushPageToDisk(name: string, page: Page) {
|
||||
safeRun(async () => {
|
||||
let meta = await this.pageStore.writePage(name, page.text.sliceString(0));
|
||||
console.log(`Wrote page ${name} to disk`);
|
||||
page.meta = meta;
|
||||
});
|
||||
async flushPageToDisk(name: string, page: Page) {
|
||||
let meta = await this.pageStore.writePage(name, page.text.sliceString(0));
|
||||
console.log(`Wrote page ${name} to disk`);
|
||||
page.meta = meta;
|
||||
}
|
||||
|
||||
disconnectClient(client: ClientPageState, page: Page) {
|
||||
async disconnectClient(client: ClientPageState, page: Page) {
|
||||
console.log("Disconnecting client");
|
||||
page.clientStates.delete(client);
|
||||
if (page.clientStates.size === 0) {
|
||||
console.log("No more clients for", page.name, "flushing");
|
||||
this.flushPageToDisk(page.name, page);
|
||||
await this.flushPageToDisk(page.name, page);
|
||||
this.openPages.delete(page.name);
|
||||
} else {
|
||||
page.cursors.delete(client.socket.id);
|
||||
@@ -214,16 +213,22 @@ export class PageApi implements ApiProvider {
|
||||
// Throttle
|
||||
if (!page.saveTimer) {
|
||||
page.saveTimer = setTimeout(() => {
|
||||
if (page) {
|
||||
console.log("Indexing", pageName);
|
||||
safeRun(async () => {
|
||||
if (page) {
|
||||
console.log(
|
||||
"Persisting",
|
||||
pageName,
|
||||
" to disk and indexing."
|
||||
);
|
||||
await this.flushPageToDisk(pageName, page);
|
||||
|
||||
this.system.dispatchEvent("page:index", {
|
||||
name: pageName,
|
||||
text: page.text.sliceString(0),
|
||||
});
|
||||
this.flushPageToDisk(pageName, page);
|
||||
page.saveTimer = undefined;
|
||||
}
|
||||
await this.system.dispatchEvent("page:index", {
|
||||
name: pageName,
|
||||
text: page.text.sliceString(0),
|
||||
});
|
||||
page.saveTimer = undefined;
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -4,10 +4,12 @@ import { Server } from "socket.io";
|
||||
import { SocketServer } from "./api_server";
|
||||
import yargs from "yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import { System } from "../plugbox/runtime";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import { ExpressServer } from "./express_server";
|
||||
import { DiskPlugLoader } from "../plugbox/plug_loader";
|
||||
import { NodeCronFeature } from "../plugbox/feature/node_cron";
|
||||
import shellSyscalls from "./syscalls/shell";
|
||||
import { System } from "../plugbox/system";
|
||||
|
||||
let args = yargs(hideBin(process.argv))
|
||||
.option("debug", {
|
||||
@@ -23,7 +25,7 @@ const pagesPath = args._[0] as string;
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
const system = new System<SilverBulletHooks>();
|
||||
const system = new System<SilverBulletHooks>("server");
|
||||
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
@@ -52,6 +54,8 @@ expressServer
|
||||
);
|
||||
await plugLoader.loadPlugs();
|
||||
plugLoader.watcher();
|
||||
system.registerSyscalls(shellSyscalls(pagesPath));
|
||||
system.addFeature(new NodeCronFeature());
|
||||
server.listen(port, () => {
|
||||
console.log(`Server listening on port ${port}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user