modularize

This commit is contained in:
sample-text-here 2021-04-07 15:56:04 -07:00
parent 50a3df6da7
commit 6adaaa7c3e
18 changed files with 83 additions and 171 deletions

BIN
data.db

Binary file not shown.

View file

@ -1 +0,0 @@
asdasdasdas

View file

@ -1 +0,0 @@
asdasdasda

View file

@ -1 +0,0 @@
asdasdasd

View file

@ -1 +0,0 @@
asdasdasd

View file

@ -1,4 +0,0 @@
<h1>hello</h1>
asdasdasd
hi
nice

View file

@ -1 +0,0 @@
asdasd

View file

@ -1 +0,0 @@
asdasdasd

View file

@ -1 +0,0 @@
asdasdasd

View file

@ -1 +0,0 @@
asdasdasdasd

View file

@ -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"));

View file

@ -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",

View file

@ -6,5 +6,7 @@
<meta content="utf-8" http-equiv="encoding" />
<link href="/style.css" rel="stylesheet" />
</head>
<body>$</body>
<body>
$
</body>
</html>

View file

@ -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;
}
}

View file

@ -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");

View file

@ -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)) {

View file

@ -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, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/>/g, "&gt;");
}
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>")

View file

@ -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;