Some document stuff in progress
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
export const serverEvents = {
|
||||
openPage: "openPage",
|
||||
closePage: "closePage",
|
||||
pageText: "pageText",
|
||||
};
|
||||
+156
-41
@@ -4,10 +4,23 @@ import fs from "fs";
|
||||
import { readdir, readFile, stat, unlink } from "fs/promises";
|
||||
import path from "path";
|
||||
import stream from "stream";
|
||||
import {} from "stream/promises";
|
||||
import { promisify } from "util";
|
||||
|
||||
import { ChangeSet, Text } from "@codemirror/state";
|
||||
import { Update } from "@codemirror/collab";
|
||||
import http from "http";
|
||||
import { Server } from "socket.io";
|
||||
import { serverEvents } from "./events";
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
methods: "GET,HEAD,PUT,OPTIONS,POST,DELETE",
|
||||
preflightContinue: true,
|
||||
},
|
||||
});
|
||||
|
||||
const port = 3000;
|
||||
const pipeline = promisify(stream.pipeline);
|
||||
const pagesPath = "../pages";
|
||||
@@ -18,34 +31,88 @@ type PageMeta = {
|
||||
lastModified: number;
|
||||
};
|
||||
|
||||
app.use("/", express.static(distDir));
|
||||
class DiskFS {
|
||||
rootPath: string;
|
||||
|
||||
let fsRouter = express.Router();
|
||||
constructor(rootPath: string) {
|
||||
this.rootPath = rootPath;
|
||||
}
|
||||
|
||||
// Page list
|
||||
fsRouter.route("/").get(async (req, res) => {
|
||||
const localPath = pagesPath;
|
||||
let fileNames: PageMeta[] = [];
|
||||
async listPages(): Promise<PageMeta[]> {
|
||||
let fileNames: PageMeta[] = [];
|
||||
|
||||
async function walkPath(dir: string) {
|
||||
let files = await readdir(dir);
|
||||
for (let file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
let s = await stat(fullPath);
|
||||
if (s.isDirectory()) {
|
||||
await walkPath(fullPath);
|
||||
} else {
|
||||
if (path.extname(file) === ".md") {
|
||||
fileNames.push({
|
||||
name: fullPath.substring(pagesPath.length + 1, fullPath.length - 3),
|
||||
lastModified: s.mtime.getTime(),
|
||||
});
|
||||
let _this = this;
|
||||
|
||||
async function walkPath(dir: string) {
|
||||
let files = await readdir(dir);
|
||||
for (let file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
let s = await stat(fullPath);
|
||||
if (s.isDirectory()) {
|
||||
await walkPath(fullPath);
|
||||
} else {
|
||||
if (path.extname(file) === ".md") {
|
||||
fileNames.push({
|
||||
name: fullPath.substring(
|
||||
_this.rootPath.length + 1,
|
||||
fullPath.length - 3
|
||||
),
|
||||
lastModified: s.mtime.getTime(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await walkPath(this.rootPath);
|
||||
return fileNames;
|
||||
}
|
||||
await walkPath(pagesPath);
|
||||
res.json(fileNames);
|
||||
|
||||
async readPage(pageName: string): Promise<{ text: string; meta: PageMeta }> {
|
||||
const localPath = path.join(pagesPath, pageName + ".md");
|
||||
const s = await stat(localPath);
|
||||
return {
|
||||
text: await readFile(localPath, "utf8"),
|
||||
meta: {
|
||||
name: pageName,
|
||||
lastModified: s.mtime.getTime(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async writePage(pageName: string, body: any): Promise<PageMeta> {
|
||||
let localPath = path.join(pagesPath, pageName + ".md");
|
||||
await pipeline(body, fs.createWriteStream(localPath));
|
||||
console.log(`Wrote to ${localPath}`);
|
||||
const s = await stat(localPath);
|
||||
return {
|
||||
name: pageName,
|
||||
lastModified: s.mtime.getTime(),
|
||||
};
|
||||
}
|
||||
|
||||
async getPageMeta(pageName: string): Promise<PageMeta> {
|
||||
let localPath = path.join(pagesPath, pageName + ".md");
|
||||
const s = await stat(localPath);
|
||||
return {
|
||||
name: pageName,
|
||||
lastModified: s.mtime.getTime(),
|
||||
};
|
||||
}
|
||||
|
||||
async deletePage(pageName: string) {
|
||||
let localPath = path.join(pagesPath, pageName + ".md");
|
||||
await unlink(localPath);
|
||||
}
|
||||
}
|
||||
|
||||
app.use("/", express.static(distDir));
|
||||
|
||||
let fsRouter = express.Router();
|
||||
let diskFS = new DiskFS(pagesPath);
|
||||
|
||||
// Page list
|
||||
fsRouter.route("/").get(async (req, res) => {
|
||||
res.json(await diskFS.listPages());
|
||||
});
|
||||
|
||||
fsRouter
|
||||
@@ -54,13 +121,11 @@ fsRouter
|
||||
let reqPath = req.params[0];
|
||||
console.log("Getting", reqPath);
|
||||
try {
|
||||
const localPath = path.join(pagesPath, reqPath + ".md");
|
||||
const s = await stat(localPath);
|
||||
let content = await readFile(localPath, "utf8");
|
||||
let { text, meta } = await diskFS.readPage(reqPath);
|
||||
res.status(200);
|
||||
res.header("Last-Modified", "" + s.mtime.getTime());
|
||||
res.header("Last-Modified", "" + meta.lastModified);
|
||||
res.header("Content-Type", "text/markdown");
|
||||
res.send(content);
|
||||
res.send(text);
|
||||
} catch (e) {
|
||||
res.status(200);
|
||||
res.send("");
|
||||
@@ -69,14 +134,10 @@ fsRouter
|
||||
.put(async (req, res) => {
|
||||
let reqPath = req.params[0];
|
||||
|
||||
let localPath = path.join(pagesPath, reqPath + ".md");
|
||||
|
||||
try {
|
||||
await pipeline(req, fs.createWriteStream(localPath));
|
||||
console.log(`Wrote to ${localPath}`);
|
||||
const s = await stat(localPath);
|
||||
let meta = await diskFS.writePage(reqPath, req);
|
||||
res.status(200);
|
||||
res.header("Last-Modified", "" + s.mtime.getTime());
|
||||
res.header("Last-Modified", "" + meta.lastModified);
|
||||
res.send("OK");
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
@@ -87,11 +148,9 @@ fsRouter
|
||||
.options(async (req, res) => {
|
||||
let reqPath = req.params[0];
|
||||
try {
|
||||
const localPath = path.join(pagesPath, reqPath + ".md");
|
||||
const s = await stat(localPath);
|
||||
const meta = await diskFS.getPageMeta(reqPath);
|
||||
res.status(200);
|
||||
res.header("Last-Modified", "" + s.mtime.getTime());
|
||||
res.header("Content-length", "" + s.size);
|
||||
res.header("Last-Modified", "" + meta.lastModified);
|
||||
res.header("Content-Type", "text/markdown");
|
||||
res.send("");
|
||||
} catch (e) {
|
||||
@@ -101,13 +160,12 @@ fsRouter
|
||||
})
|
||||
.delete(async (req, res) => {
|
||||
let reqPath = req.params[0];
|
||||
const localPath = path.join(pagesPath, reqPath + ".md");
|
||||
try {
|
||||
await unlink(localPath);
|
||||
await diskFS.deletePage(reqPath);
|
||||
res.status(200);
|
||||
res.send("OK");
|
||||
} catch (e) {
|
||||
console.error("Error deleting file", localPath, e);
|
||||
console.error("Error deleting file", reqPath, e);
|
||||
res.status(500);
|
||||
res.send("OK");
|
||||
}
|
||||
@@ -131,6 +189,63 @@ app.get("/*", async (req, res) => {
|
||||
res.status(200).header("Content-Type", "text/html").send(cachedIndex);
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
import { Socket } from "socket.io";
|
||||
|
||||
class Page {
|
||||
text: Text;
|
||||
updates: Update[];
|
||||
sockets: Map<string, Socket>;
|
||||
|
||||
constructor(text: string) {
|
||||
this.updates = [];
|
||||
this.text = Text.of([text]);
|
||||
this.sockets = new Map<string, Socket>();
|
||||
}
|
||||
}
|
||||
|
||||
let openPages = new Map<string, Page>();
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
function removeSocket(pageName: string) {
|
||||
let page = openPages.get(pageName);
|
||||
if (page) {
|
||||
page.sockets.delete(socket.id);
|
||||
if (page.sockets.size === 0) {
|
||||
console.log("No more sockets for", pageName, "flushing");
|
||||
openPages.delete(pageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Connected", socket.id);
|
||||
let socketOpenPages = new Set<string>();
|
||||
socket.on(serverEvents.openPage, async (pageName: string) => {
|
||||
let page = openPages.get(pageName);
|
||||
if (!page) {
|
||||
let { text } = await diskFS.readPage(pageName);
|
||||
page = new Page(text);
|
||||
openPages.set(pageName, page);
|
||||
}
|
||||
page.sockets.set(socket.id, socket);
|
||||
socketOpenPages.add(pageName);
|
||||
console.log("Sending document text");
|
||||
socket.emit(
|
||||
serverEvents.pageText,
|
||||
pageName,
|
||||
openPages.get(pageName).text.toJSON()
|
||||
);
|
||||
});
|
||||
socket.on(serverEvents.closePage, (pageName: string) => {
|
||||
console.log("Closing page", pageName);
|
||||
removeSocket(pageName);
|
||||
socketOpenPages.delete(pageName);
|
||||
});
|
||||
socket.on("disconnect", () => {
|
||||
console.log("Disconnected", socket.id);
|
||||
socketOpenPages.forEach(removeSocket);
|
||||
});
|
||||
});
|
||||
//sup
|
||||
server.listen(port, () => {
|
||||
console.log(`Server istening on port ${port}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user