modularize
This commit is contained in:
parent
50a3df6da7
commit
6adaaa7c3e
18 changed files with 83 additions and 171 deletions
BIN
data.db
BIN
data.db
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
asdasdasdas
|
|
@ -1 +0,0 @@
|
|||
asdasdasda
|
|
@ -1 +0,0 @@
|
|||
asdasdasd
|
|
@ -1 +0,0 @@
|
|||
asdasdasd
|
|
@ -1,4 +0,0 @@
|
|||
<h1>hello</h1>
|
||||
asdasdasd
|
||||
hi
|
||||
nice
|
|
@ -1 +0,0 @@
|
|||
asdasd
|
|
@ -1 +0,0 @@
|
|||
asdasdasd
|
|
@ -1 +0,0 @@
|
|||
asdasdasd
|
|
@ -1 +0,0 @@
|
|||
asdasdasdasd
|
38
index.js
38
index.js
|
@ -1,2 +1,38 @@
|
|||
const server = require("./server/routes.js");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const renderMarkdown = require("./server/markdown.js");
|
||||
const files = require("./server/files.js");
|
||||
const server = require("./server/main.js");
|
||||
const base = fs.readFileSync("public/main.html", "utf8").split("$");
|
||||
|
||||
function compose(req, res) {
|
||||
const id = path.parse(req.url).name;
|
||||
const file = files.grab(id);
|
||||
if (!file) return res.writeHead(404).end("can't find that");
|
||||
res.writeHead(200, { "Content-Type": "text/html" });
|
||||
res.write(base[0]);
|
||||
res.write("hello");
|
||||
res.write(base[1]);
|
||||
file.pipe(res, { end: false });
|
||||
file.on("end", () => res.end(base[2]));
|
||||
}
|
||||
|
||||
function upload(req, res) {
|
||||
let body = "";
|
||||
req.on("data", (c) => (body += c.toString()));
|
||||
req.on("end", () => {
|
||||
const cont = new URLSearchParams(body).get("content");
|
||||
if (!cont) return res.writeHead(400).end("empty content");
|
||||
const id = files.create(renderMarkdown(cont));
|
||||
res.writeHead(301, { Location: "/" + id }).end(`success!\nid: ${id}`);
|
||||
});
|
||||
}
|
||||
|
||||
server.host("public/index.html", "");
|
||||
server.host("public/style.css", "style.css", true);
|
||||
server.host("public/input.css", "input.css", true);
|
||||
server.host("public/script.js", "script.js");
|
||||
server.on("GET", compose);
|
||||
server.on("POST", upload);
|
||||
|
||||
server.listen(process.env.PORT || 3000, () => console.log("ready"));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "md",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "simple and efficient web server",
|
||||
"author": "ZestyLemonade",
|
||||
"main": "index.js",
|
||||
|
|
|
@ -6,5 +6,7 @@
|
|||
<meta content="utf-8" http-equiv="encoding" />
|
||||
<link href="/style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>$</body>
|
||||
<body>
|
||||
$
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -6,11 +6,15 @@ body {
|
|||
max-width: 550px;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
code, pre, key {
|
||||
code,
|
||||
pre,
|
||||
key {
|
||||
font-family: Monaco, "Courier New", monospace;
|
||||
border-radius: 3px;
|
||||
background: #eee;
|
||||
|
@ -36,10 +40,18 @@ blockquote {
|
|||
margin-left: 3px;
|
||||
}
|
||||
|
||||
hr { margin: 1em 0; }
|
||||
a { color: #2082ea; }
|
||||
a:visited { color: #741ac9; }
|
||||
li { line-height: 1.5; }
|
||||
hr {
|
||||
margin: 1em 0;
|
||||
}
|
||||
a {
|
||||
color: #2082ea;
|
||||
}
|
||||
a:visited {
|
||||
color: #741ac9;
|
||||
}
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
|
@ -47,11 +59,19 @@ li { line-height: 1.5; }
|
|||
color: #eee;
|
||||
}
|
||||
|
||||
code, pre, key {
|
||||
code,
|
||||
pre,
|
||||
key {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
key { box-shadow: 0 3px 0 #333; }
|
||||
a { color: #66b0f0; }
|
||||
a:visited { color: #a65fea; }
|
||||
key {
|
||||
box-shadow: 0 3px 0 #333;
|
||||
}
|
||||
a {
|
||||
color: #66b0f0;
|
||||
}
|
||||
a:visited {
|
||||
color: #a65fea;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
const fs = require("fs");
|
||||
const blockSize = 4 + 8 + 3 + 16 + 16 + 64;
|
||||
|
||||
const defaults = {
|
||||
author: "anon",
|
||||
title: "untitled",
|
||||
hash: Buffer.alloc(16),
|
||||
};
|
||||
|
||||
function prep(str) {
|
||||
const buf = Buffer.from(str);
|
||||
buf.length = Math.min(buf.length, 255);
|
||||
return buf;
|
||||
}
|
||||
|
||||
class Data {
|
||||
constructor(file) {
|
||||
if (!fs.existsSync(file)) fs.writeFileSync(file, "");
|
||||
this.index = fs.openSync(file, "r+");
|
||||
this.cache = new Map();
|
||||
}
|
||||
|
||||
find(id) {
|
||||
let index = 0;
|
||||
while (true) {
|
||||
const got = Buffer.alloc(blockSize);
|
||||
if (fs.readSync(this.index, got, 0, 4, index) === 0) break;
|
||||
if (got.readUInt32BE() === id) return index;
|
||||
index += blockSize;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
write(id, data) {
|
||||
data = { ...defaults, ...data };
|
||||
|
||||
const buf = Buffer.alloc(blockSize);
|
||||
const [author, title] = [prep(data.author), prep(data.title)];
|
||||
|
||||
buf.writeUInt32BE(id, 0);
|
||||
buf.writeBigInt64BE(BigInt(Date.now()), 4);
|
||||
|
||||
buf.writeUInt8(author.length, 12);
|
||||
buf.writeUInt8(title.length, 13);
|
||||
author.copy(buf, 14);
|
||||
data.hash.copy(buf, 30);
|
||||
title.copy(buf, 46);
|
||||
|
||||
const index = this.find(id);
|
||||
if (index === -1) {
|
||||
fs.writeSync(this.index, buf);
|
||||
} else {
|
||||
fs.writeSync(this.index, buf, 0, blockSize, index);
|
||||
}
|
||||
}
|
||||
|
||||
read(id) {
|
||||
if (this.cache.has(id)) return this.cache.get(id);
|
||||
const index = this.find(id);
|
||||
if (index === -1) return null;
|
||||
|
||||
const buf = Buffer.alloc(blockSize);
|
||||
const data = {};
|
||||
fs.readSync(this.index, buf, 0, blockSize, index);
|
||||
data.id = buf.readUInt32BE(0);
|
||||
data.date = new Date(Number(buf.readBigInt64BE(4)));
|
||||
|
||||
const alen = buf.readUInt8(12);
|
||||
const tlen = buf.readUInt8(13);
|
||||
data.author = buf.slice(14, 14 + alen).toString("utf8");
|
||||
data.hash = buf.slice(30, 30 + hlen);
|
||||
data.title = buf.slice(46, 46 + tlen).toString("utf8");
|
||||
|
||||
this.cache.set(id, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
delete(id) {
|
||||
const index = this.find(id);
|
||||
if (index === -1) return null;
|
||||
|
||||
const zero = Buffer.alloc(4).fill(0);
|
||||
fs.writeSync(this.index, zero, 0, 4, index);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Data("data.db");
|
|
@ -15,7 +15,10 @@ class Server {
|
|||
if (method === "GET" && this.map.has(file)) {
|
||||
const fromMap = this.map.get(file);
|
||||
return res
|
||||
.writeHead(200, { "Content-Type": fromMap.type })
|
||||
.writeHead(200, {
|
||||
"Content-Type": fromMap.type,
|
||||
"Cache-Control": "max-age=600",
|
||||
})
|
||||
.end(fromMap.fmt);
|
||||
}
|
||||
if (this.handlers.has(method)) {
|
||||
|
|
|
@ -10,11 +10,16 @@ function list(str, kind) {
|
|||
);
|
||||
}
|
||||
|
||||
function renderMarkdown(text) {
|
||||
return list(list(text.replace(/\r\n/g, "\n"), "ul"), "ol")
|
||||
function clean(str) {
|
||||
return str
|
||||
.replace(/\r\n/g, "\n")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/>/g, ">");
|
||||
}
|
||||
|
||||
function renderMarkdown(text) {
|
||||
return list(list(clean(text), "ul"), "ol")
|
||||
.replace(/###### (.+)/gim, "<h6>$1</h6>")
|
||||
.replace(/##### (.+)/gim, "<h5>$1</h5>")
|
||||
.replace(/#### (.+)/gim, "<h4>$1</h4>")
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const renderMarkdown = require("./markdown.js");
|
||||
const files = require("./files.js");
|
||||
const server = require("./main.js");
|
||||
const data = require("./data.js");
|
||||
const base = fs.readFileSync("public/main.html", "utf8").split("$");
|
||||
const hash = (data) => crypto.createHash("sha256").update(data).digest();
|
||||
|
||||
function compose(id, stream) {
|
||||
const file = files.grab(id);
|
||||
if (!file) return res.writeHead(404).end("can't find that");
|
||||
const params = data.read(id);
|
||||
stream.writeHead(200, { "Content-Type": "text/html" })
|
||||
stream.write(base[0])
|
||||
stream.write(params?.title || "hello!")
|
||||
stream.write(base[1]);
|
||||
file.pipe(stream, { end: false })
|
||||
file.on("end", () => stream.end(base[2]));
|
||||
}
|
||||
|
||||
function getbody(req, call) {
|
||||
const params = {};
|
||||
let body = "";
|
||||
req.on("data", (c) => (body += c.toString()));
|
||||
return new Promise((res) =>
|
||||
req.on("end", () => {
|
||||
for (let [key, value] of new URLSearchParams(body)) {
|
||||
params[key] = value;
|
||||
}
|
||||
res(params);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function upload(req, res) {
|
||||
const body = await getbody(req);
|
||||
if (!body.content) return res.end(400, "empty content");
|
||||
const id = files.create(renderMarkdown(body.content));
|
||||
res.writeHead(301, { Location: "/" + id }).end(`success!\nid: ${id}`);
|
||||
}
|
||||
|
||||
function getPage(req, res) {
|
||||
const id = path.parse(req.url).name;
|
||||
return compose(id, res);
|
||||
}
|
||||
|
||||
server.host("public/index.html", "");
|
||||
server.host("public/style.css", "style.css", true);
|
||||
server.host("public/input.css", "input.css", true);
|
||||
server.host("public/script.js", "script.js");
|
||||
server.on("GET", getPage);
|
||||
server.on("POST", upload);
|
||||
|
||||
module.exports = server;
|
Loading…
Reference in a new issue