Compare commits

..

16 Commits

Author SHA1 Message Date
lamp da1b0ab58c no 405 2023-03-03 02:39:46 -06:00
lamp ff8b626b8a rename env var 2022-01-18 15:38:30 -06:00
lamp e7b770b177 tweak 2022-01-18 15:29:15 -06:00
lamp 5d203d2ca7 tweak 2022-01-18 15:24:22 -06:00
lamp 4e99509d96 Merge branch 'master' of gitea.moe:lamp/qonq 2021-12-05 02:40:36 -06:00
lamp 16afa0abc5 add antiscrape 2021-12-05 02:40:33 -06:00
lamp 794b84a152 Update 'README.md' 2021-12-05 01:18:50 -06:00
lamp c09993d9bc simple binary upload instead of form 2021-12-05 01:17:35 -06:00
lamp 2c0d3ef78e rename HOSTNAME env because conflict 2021-12-05 00:44:10 -06:00
lamp 2448d69f1f hmm 2021-12-03 16:51:58 -06:00
lamp 7f57c0ed23 Merge branch 'master' of gitea.moe:lamp/qonq 2021-10-22 17:43:22 -05:00
lamp 292ff9297d custom url gen 2021-10-22 17:42:17 -05:00
lamp 9897bcd577 Update 'README.md' 2021-10-22 16:51:20 -05:00
lamp 847950c726 favicon 2021-10-22 16:49:31 -05:00
lamp 6da1f4d556 404 handler 2021-10-22 16:30:40 -05:00
lamp a4e44fe884 support filecode in url 2021-10-22 16:22:16 -05:00
9 changed files with 119 additions and 68 deletions
+3 -1
View File
@@ -6,4 +6,6 @@
!/package.json !/package.json
!/package-lock.json !/package-lock.json
!/README.md !/README.md
!.gitkeep !/files/www/qonq.js
!/favicon.ico
!/antiscrape.js
+12 -11
View File
@@ -2,7 +2,7 @@
Simple file host that puts the file code in the hostname. The leftmost part of the Host (i.e. `1234` of `1234.qonq.gq`) is used to serve a corresponding folder (i.e. `files/1234/`); if the folder contains one file then it is served directly, but if it contains multiple files then it is served as a webroot with a directory index. Designed for use as a ShareX Custom Uploader, but you can drop any lowercase alphanumeric folder into the files directory to serve it instantly. Simple file host that puts the file code in the hostname. The leftmost part of the Host (i.e. `1234` of `1234.qonq.gq`) is used to serve a corresponding folder (i.e. `files/1234/`); if the folder contains one file then it is served directly, but if it contains multiple files then it is served as a webroot with a directory index. Designed for use as a ShareX Custom Uploader, but you can drop any lowercase alphanumeric folder into the files directory to serve it instantly.
Note: Please refrain from using your own file server if you cannot commit to maintaining it indefinitely, because all links you post will depend on your server and dead servers cause link rot. You can also put the file code in the URL of the main domain. For example, https://1234.qonq.gq/ and https://qonq.gq/1234/ are the same. If there is no code in either the URL or subdomain, then the default code is `www`, which means https://www.qonq.gq/ and https://qonq.gq/ are the same, and you can put content in `files/www` for it (`index.html` etc).
## Usage ## Usage
Clone repository and install dependencies with `npm ci`. Clone repository and install dependencies with `npm ci`.
@@ -17,12 +17,13 @@ The following environment variables are optional.
| Variable | Description | Default | | Variable | Description | Default |
|----------|-------------|---------| |----------|-------------|---------|
| `HOSTNAMES` | Comma-separated list of hostnames that will be chosen at random for the URL sent back after an upload. | request hostname | | `BASE_HOSTNAME` | Overrides the hostname for generating URLs. | host header on the upload request |
| `DISCORD_WEBHOOK` | Discord webhook url that uploads will be sent to to make them pre-load the embed, which theoretically makes it faster. | undefined (disabled) | | `DISCORD_WEBHOOK` | Discord webhook url that newly-uploaded URLs will be sent to to pre-load their embed, which theoretically makes them embed again faster. | `undefined` (disabled) |
| `PORT` | TCP port to listen on | 8568 | | `PORT` | TCP port to listen on | `8568` |
| `ADDRESS` | Address to bind to | 0.0.0.0 (all) | | `ADDRESS` | Address to bind to | `'0.0.0.0'` (all) |
| `FILES_DIR` | Directory to store the files in | files (relative of working directory) | | `FILES_DIR` | Directory to store the files in | `'files'` (relative of working directory) |
| `TMP_DIR` | Directory to store pending uploads in | os.tmpdir() | | `TRUST_PROXY` | Value for express's [`'trust proxy'`](https://expressjs.com/en/5x/api.html#trust.proxy.options.table) setting | `'loopback'` |
Run with `node qonq.js` or your favorite init system. Run with `node qonq.js` or your favorite init system.
@@ -33,12 +34,12 @@ Run with `node qonq.js` or your favorite init system.
"Version": "12.4.1", "Version": "12.4.1",
"DestinationType": "ImageUploader, TextUploader, FileUploader", "DestinationType": "ImageUploader, TextUploader, FileUploader",
"RequestMethod": "POST", "RequestMethod": "POST",
"RequestURL": "http://localhost:8568/upload", "RequestURL": "https://qonq.gq/",
"Headers": { "Headers": {
"authentication": "paste value of AUTH_TOKEN here" "authentication": "paste value of AUTH_TOKEN here",
"filename": "$filename$"
}, },
"Body": "MultipartFormData", "Body": "Binary"
"FileFormName": "file"
} }
``` ```
You can import this custom uploader config and modify the Request URL and authentication fields as necessary. You can import this custom uploader config and modify the Request URL and authentication fields as necessary.
+15
View File
@@ -0,0 +1,15 @@
// someone could iterate over all ~1.6 million possible file codes to download all files.
// prevent this by banning IP addresses that request too many non-existant files.
var ip404 = {};
module.exports = (req, res, next) => {
if (ip404[req.ip]?.size > 10)
return res.status(403).send("Banned");
res.on("finish", () => {
if (res.statusCode == 404 && req.filecode) {
if (!ip404[req.ip]) ip404[req.ip] = new Set();
ip404[req.ip].add(req.filecode);
}
});
next();
};
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 734 B

-1
View File
@@ -1 +0,0 @@
+1
View File
@@ -0,0 +1 @@
../../qonq.js
+50 -14
View File
@@ -8,7 +8,7 @@
"colors": "^1.4.0", "colors": "^1.4.0",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"express": "^4.17.1", "express": "^4.17.1",
"formidable": "^1.2.2", "serve-favicon": "^2.5.0",
"serve-index": "^1.9.1" "serve-index": "^1.9.1"
} }
}, },
@@ -214,14 +214,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/formidable": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz",
"integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==",
"funding": {
"url": "https://ko-fi.com/tunnckoCore/commissions"
}
},
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -445,6 +437,31 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}, },
"node_modules/serve-favicon": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz",
"integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=",
"dependencies": {
"etag": "~1.8.1",
"fresh": "0.5.2",
"ms": "2.1.1",
"parseurl": "~1.3.2",
"safe-buffer": "5.1.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/serve-favicon/node_modules/ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
},
"node_modules/serve-favicon/node_modules/safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"node_modules/serve-index": { "node_modules/serve-index": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
@@ -717,11 +734,6 @@
"unpipe": "~1.0.0" "unpipe": "~1.0.0"
} }
}, },
"formidable": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz",
"integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q=="
},
"forwarded": { "forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -890,6 +902,30 @@
} }
} }
}, },
"serve-favicon": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz",
"integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=",
"requires": {
"etag": "~1.8.1",
"fresh": "0.5.2",
"ms": "2.1.1",
"parseurl": "~1.3.2",
"safe-buffer": "5.1.1"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
}
}
},
"serve-index": { "serve-index": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+1 -1
View File
@@ -3,7 +3,7 @@
"colors": "^1.4.0", "colors": "^1.4.0",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"express": "^4.17.1", "express": "^4.17.1",
"formidable": "^1.2.2", "serve-favicon": "^2.5.0",
"serve-index": "^1.9.1" "serve-index": "^1.9.1"
} }
} }
+37 -40
View File
@@ -1,22 +1,19 @@
require("dotenv").config(); require("dotenv").config();
var colors = require("colors"); var colors = require("colors");
var express = require("express"); var express = require("express");
var formidable = require("formidable");
var serveIndex = require("serve-index"); var serveIndex = require("serve-index");
var serveFavicon = require("serve-favicon");
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
try { var customGen = require("./customGen"); } catch(e) {}
var FILES_DIR = process.env.FILES_DIR || "files"; var FILES_DIR = process.env.FILES_DIR || "files";
if (process.env.HOSTNAMES) {
var HOSTNAMES = process.env.HOSTNAMES.split(',');
var randomHostname = () => HOSTNAMES[Math.floor(Math.random() * HOSTNAMES.length)];
}
var app = express(); var app = express();
app.set("env", "production"); app.set("env", "production");
app.set('trust proxy', '127.0.0.1'); app.set('trust proxy', process.env.TRUST_PROXY || "loopback");
app.listen(process.env.PORT || 8568, process.env.ADDRESS); app.listen(process.env.PORT || 8568, process.env.ADDRESS);
app.use((req,res,next) => { res.header("Access-Control-Allow-Origin", '*'); next(); });
app.use((req, res, next)=>{ app.use((req, res, next)=>{
var d = new Date; var d = new Date;
@@ -27,47 +24,48 @@ app.use((req, res, next)=>{
next(); next();
}); });
app.use(serveFavicon("favicon.ico"));
app.post("*", (req, res, next) => { app.post("*", (req, res, next) => {
if (req.headers.authentication != process.env.AUTH_TOKEN) return void res.status(403).send("Unauthorized"); if (req.headers.authentication != process.env.AUTH_TOKEN) return void res.status(403).send("Unauthorized");
var form = new formidable.IncomingForm({ (function trymkdir() {
maxFileSize: 2**30, // 1 GiB if (customGen) {
maxFields: 1, var g = customGen(req);
uploadDir: process.env.TMP_DIR var filecode = g.next().value;
}); }
form.parse(req, function(err, fields, files) { filecode ||= Math.random().toString(36).slice(2).substring(0,4);
if (err) return void next(err); var webroot = path.join(FILES_DIR, filecode);
var file = files.file; fs.mkdir(webroot, function (error) {
if (!file) return void res.sendStatus(400); if (error) if (error.code == "EEXIST") {
(function trymkdir() { console.log("oof");
var filecode = Math.random().toString(36).slice(2).substring(0,4); trymkdir();
var webroot = path.join(FILES_DIR, filecode); } else next(error);
fs.mkdir(webroot, function (error) { else {
if (error) if (error.code == "EEXIST") { var filename = req.headers.filename || "file";
console.log("oof"); var filepath = path.join(webroot, filename);
trymkdir(); req.pipe(fs.createWriteStream(filepath));
} else next(error); req.on("close", () => {
else { var url = g?.next().value || `${req.protocol}://${filecode}.${process.env.BASE_HOSTNAME || req.hostname}`;
fs.rename(file.path, path.join(webroot, file.name), function (error) { res.type('text').send(url);
if (error) return void next(error); require("./discord-preloader.js")(url);
let url = `${req.protocol}://${filecode}.${randomHostname?.() || req.hostname}`; });
res.send(url); }
require("./discord-preloader.js")(url); });
}); })();
}
});
})();
});
}); });
app.use(require("./antiscrape"));
app.get("*", function(req, res, next){ app.get(['/', '/:code/', '/:code/*'], function(req, res, next){
var filecode = req.hostname.split('.')[0]; // for webroot of main domain i.e. qonq.gq, mkdir files/qonq var subdomain = req.subdomains.at(-1);
var webroot = path.join(FILES_DIR, filecode); req.filecode = subdomain || req.params.code;
if (!req.filecode) req.filecode = "www";
var webroot = path.join(FILES_DIR, req.filecode);
fs.readdir(webroot, function(error, webrootdirlist) { fs.readdir(webroot, function(error, webrootdirlist) {
if (error) return void next(error.code == "ENOENT" ? "route" : error); if (error) return void next(error.code == "ENOENT" ? "route" : error);
if (webrootdirlist.length > 1) { if (webrootdirlist.length > 1) {
req.url = path.join(filecode, req.url); if (subdomain) req.url = path.join(req.filecode, req.url);
next(); next();
} else if (webrootdirlist.length == 1) { } else if (webrootdirlist.length == 1) {
res.sendFile(webrootdirlist[0], { res.sendFile(webrootdirlist[0], {
@@ -79,4 +77,3 @@ app.get("*", function(req, res, next){
} }
}); });
}, express.static(FILES_DIR), serveIndex(FILES_DIR, {icons: true})); }, express.static(FILES_DIR), serveIndex(FILES_DIR, {icons: true}));