var express = require("express"); var multer = require("multer"); var sanitizeFilename = require("sanitize-filename"); var serveIndex = require("serve-index"); var colors = require("colors"); var {WebSocketServer} = require("ws"); var qs = require("qs"); var proxyaddr = require("proxy-addr"); var fs = require("fs"); var app = express(); app.set("env", "production"); app.set("trust proxy", "loopback"); app.set("view engine", "pug"); var server = app.listen(process.env.PORT || 80); var upload = multer({storage: multer.diskStorage({ destination: "uploads", filename: (req, file, cb) => cb(null, safeFilename(file.filename)) })}); var wss = new WebSocketServer({server, maxPayload: 0}); wss.on("connection", (ws, req) => { req.query = qs.parse(req.url.substring(req.url.indexOf('?')+1)); req.ip = proxyaddr(req, app.get("trust proxy")); if (!req.query.name) return ws.close(); var d = new Date; var filename = safeFilename(req.query.name); var tmpname = Math.random().toString(36).substring(2); try { var fd = fs.openSync(`tmp/${tmpname}`, 'w'); } catch(error) { ws.send(error.message); return; } var complete = false; ws.on("message", (data, isBinary) => { if (isBinary) { fs.write(fd, data, (error) => ws.send(error?.message)); } else { if (data == "end") { complete = true; fs.closeSync(fd); fs.renameSync(`tmp/${tmpname}`, `uploads/${filename}`); ws.close(); } } }); ws.on("close", () => { console.log(`${`[${d.toISOString()}]`.magenta} ${req.ip.cyan} ${"WebSocket".bold.yellow} ${req.headers.host}${req.url} ${`"${req.headers["user-agent"]}"`.gray} ${Date.now()-d}ms`); if (!complete) { fs.closeSync(fd); fs.unlinkSync(`tmp/${tmpname}`); } }); }); app.use((req, res, next)=>{ var d = new Date; res.on("finish", () => { var sc = res.statusCode.toString(), sc = sc.startsWith('2') ? sc.green : sc.startsWith('3') ? sc.cyan : sc.startsWith('4') ? sc.red : sc.startsWith('5') ? sc.yellow.bgRed : sc; console.log(`${`[${d.toISOString()}]`.magenta} ${req.ip.cyan} ${req.method.bold.yellow} ${req.hostname}${req.url} ${sc} ${`"${req.headers["user-agent"]}"`.gray} ${Date.now()-d}ms`); }); next(); }); app.get("/upload", (req, res) => { var files = fs.readdirSync("uploads").map(name => Object.assign({name}, fs.statSync(`uploads/${name}`))).sort((a,b) => b.mtime - a.mtime); res.render("upload", {files}); }); app.post("/upload", upload.array("file"), (req, res) => { res.header("Refresh", 3); res.type("json").send(require('util').inspect(req.files)); }); app.use("/assets/", express.static("assets")); //app.use((req, res) => serveHandler(req, res, {public: "public"})); app.use(express.static("public"), serveIndex("public", {icons: true})); function safeFilename(f) { f = sanitizeFilename(f); while (fs.existsSync(`uploads/${f}`)) { var x = f.lastIndexOf('.'); if (x != -1) { var fn = f.substring(0, x); var fe = f.substring(x); } else var fn = f, fe = ''; var m = fn.match(/(.*)(?<= )(\d+)$/); if (m) fn = m[1] + ++m[2]; else fn += " 2"; f = fn + fe; } return f; }