Created
October 2, 2022 07:55
-
-
Save papnkukn/2bc439126336fd5b01819dfcd692b54e to your computer and use it in GitHub Desktop.
A very simple pastebin clone. Pure Node.js with no external dependencies.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** A very simple pastebin clone ***/ | |
const fs = require('fs'); | |
const path = require('path'); | |
const http = require('http'); | |
const host = process.env.NODE_HOST || "0.0.0.0"; | |
const port = process.env.NODE_PORT || 3000; | |
const datadir = process.env.NODE_DATA_DIR || "data"; | |
if (!fs.existsSync(datadir)) { | |
console.error("Data directory does not exist: " + datadir); | |
process.exit(1); | |
} | |
function htmlencode(c) { | |
c = c.replace(/&/g, '&'); | |
c = c.replace(/</g, '<'); | |
c = c.replace(/>/g, '>'); | |
return c; | |
} | |
function regex(url) { | |
let m = /^\/?([A-Za-z0-9\-]+)$/g.exec(url); | |
return m ? m[1] : null; | |
} | |
function render(model) { | |
let content = htmlencode(model.content); | |
let html = `<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous"> | |
<title>Pastebin</title> | |
</head> | |
<body style="background: #eee;"> | |
<form action="/${model.id}" method="post"> | |
<div class="container-fluid mt-3"> | |
<div class="row"> | |
<div class="col-12 d-flex"> | |
<h3 class="text-monospace">Pastebin <a href="/${model.id}" class="text-primary text-monospace">${model.id}</a></h3> | |
<div class="ml-auto"> | |
<a href="/" class="btn btn-sm btn-outline-primary" accesskey="q">New</a> | |
<button class="btn btn-sm btn-success" type="submit" accesskey="s">Save</button> | |
</div> | |
</div> | |
<div class="col-12"> | |
<div class="form-group"> | |
<textarea autofocus name="content" placeholder="Paste some text..." rows="35" class="form-control text-monospace w-100 h-100">${content}</textarea> | |
</div> | |
</div> | |
</div> | |
</div> | |
</form> | |
</body> | |
</html>`; | |
return html; | |
} | |
const server = http.createServer(function(request, response) { | |
//Debug: console.log(request.method + " " + request.url); | |
//Index page: GET / | |
if (request.method == "GET" && request.url == "/") { | |
let id = Math.random().toString(36).substr(2, 9); | |
let model = { id, mode: "new", content: "" }; | |
response.writeHead(200, { "Content-Type": "text/html" }); | |
return response.end(render(model)); | |
} | |
//View content: GET /:id where :id can be A-Z, a-z, 0-9, and - | |
if (request.method == "GET" && regex(request.url)) { | |
let id = regex(request.url); | |
let file = path.join(datadir, id) + ".txt"; | |
let content = fs.existsSync(file) ? fs.readFileSync(file, "utf-8") : ""; | |
let model = { id, mode: "edit", content }; | |
response.writeHead(200, { "Content-Type": "text/html" }); | |
return response.end(render(model)); | |
} | |
//Save content: POST /:id where :id can be A-Z, a-z, 0-9, and - | |
if (request.method == "POST" && regex(request.url)) { | |
let id = regex(request.url); | |
let body = ""; | |
request.on('data', function(data) { | |
body += data; | |
}); | |
request.on('end', function() { | |
let value = body.substring("content=".length); | |
let content = decodeURIComponent(value).replace(/\+/g, " "); | |
let file = path.join(datadir, id) + ".txt"; | |
fs.writeFileSync(file, content); | |
response.writeHead(302, { "Location": "/" + id }); | |
return response.end(); | |
}); | |
return; | |
} | |
//Delete content: DELETE /:id where :id can be A-Z, a-z, 0-9, and - | |
if (request.method == "DELETE" && regex(request.url)) { | |
let id = regex(request.url); | |
let file = path.join(datadir, id) + ".txt"; | |
fs.existsSync(file) ? fs.unlinkSync(file) : null; | |
response.writeHead(302, { "Location": "/" }); | |
return response.end(); | |
} | |
//404 Not Found | |
response.writeHead(404, { "Content-Type": "text/plain" }); | |
response.end("404 Not Found"); | |
}); | |
server.listen(port, host); | |
console.log(`Listening at http://${host}:${port}`); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Getting started
Download
server.js
and install Node.jsCreate the content storage directory
data
mkdir data && chmod +w data
Start HTTP server on port 3000
Open in a web browser
Advanced options
Environmental variables
To run the process with environmental variables
Clean up
Create a shell script and run it periodically to delete content older than 7 days: