Compare commits
16 Commits
f7971fef0f
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| da1b0ab58c | |||
| ff8b626b8a | |||
| e7b770b177 | |||
| 5d203d2ca7 | |||
| 4e99509d96 | |||
| 16afa0abc5 | |||
| 794b84a152 | |||
| c09993d9bc | |||
| 2c0d3ef78e | |||
| 2448d69f1f | |||
| 7f57c0ed23 | |||
| 292ff9297d | |||
| 9897bcd577 | |||
| 847950c726 | |||
| 6da1f4d556 | |||
| a4e44fe884 |
+3
-1
@@ -6,4 +6,6 @@
|
||||
!/package.json
|
||||
!/package-lock.json
|
||||
!/README.md
|
||||
!.gitkeep
|
||||
!/files/www/qonq.js
|
||||
!/favicon.ico
|
||||
!/antiscrape.js
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
Clone repository and install dependencies with `npm ci`.
|
||||
@@ -17,12 +17,13 @@ The following environment variables are optional.
|
||||
|
||||
| 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 |
|
||||
| `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) |
|
||||
| `PORT` | TCP port to listen on | 8568 |
|
||||
| `ADDRESS` | Address to bind to | 0.0.0.0 (all) |
|
||||
| `FILES_DIR` | Directory to store the files in | files (relative of working directory) |
|
||||
| `TMP_DIR` | Directory to store pending uploads in | os.tmpdir() |
|
||||
| `BASE_HOSTNAME` | Overrides the hostname for generating URLs. | host header on the upload request |
|
||||
| `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` |
|
||||
| `ADDRESS` | Address to bind to | `'0.0.0.0'` (all) |
|
||||
| `FILES_DIR` | Directory to store the files in | `'files'` (relative of working directory) |
|
||||
| `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.
|
||||
|
||||
@@ -33,12 +34,12 @@ Run with `node qonq.js` or your favorite init system.
|
||||
"Version": "12.4.1",
|
||||
"DestinationType": "ImageUploader, TextUploader, FileUploader",
|
||||
"RequestMethod": "POST",
|
||||
"RequestURL": "http://localhost:8568/upload",
|
||||
"RequestURL": "https://qonq.gq/",
|
||||
"Headers": {
|
||||
"authentication": "paste value of AUTH_TOKEN here"
|
||||
"authentication": "paste value of AUTH_TOKEN here",
|
||||
"filename": "$filename$"
|
||||
},
|
||||
"Body": "MultipartFormData",
|
||||
"FileFormName": "file"
|
||||
"Body": "Binary"
|
||||
}
|
||||
```
|
||||
You can import this custom uploader config and modify the Request URL and authentication fields as necessary.
|
||||
|
||||
@@ -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
Binary file not shown.
|
After Width: | Height: | Size: 734 B |
@@ -1 +0,0 @@
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../qonq.js
|
||||
Generated
+50
-14
@@ -8,7 +8,7 @@
|
||||
"colors": "^1.4.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"express": "^4.17.1",
|
||||
"formidable": "^1.2.2",
|
||||
"serve-favicon": "^2.5.0",
|
||||
"serve-index": "^1.9.1"
|
||||
}
|
||||
},
|
||||
@@ -214,14 +214,6 @@
|
||||
"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": {
|
||||
"version": "0.1.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
|
||||
@@ -717,11 +734,6 @@
|
||||
"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": {
|
||||
"version": "0.1.2",
|
||||
"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": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"colors": "^1.4.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"express": "^4.17.1",
|
||||
"formidable": "^1.2.2",
|
||||
"serve-favicon": "^2.5.0",
|
||||
"serve-index": "^1.9.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
require("dotenv").config();
|
||||
var colors = require("colors");
|
||||
var express = require("express");
|
||||
var formidable = require("formidable");
|
||||
var serveIndex = require("serve-index");
|
||||
var serveFavicon = require("serve-favicon");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
try { var customGen = require("./customGen"); } catch(e) {}
|
||||
|
||||
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();
|
||||
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.use((req,res,next) => { res.header("Access-Control-Allow-Origin", '*'); next(); });
|
||||
|
||||
app.use((req, res, next)=>{
|
||||
var d = new Date;
|
||||
@@ -27,20 +24,17 @@ app.use((req, res, next)=>{
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(serveFavicon("favicon.ico"));
|
||||
|
||||
|
||||
app.post("*", (req, res, next) => {
|
||||
if (req.headers.authentication != process.env.AUTH_TOKEN) return void res.status(403).send("Unauthorized");
|
||||
var form = new formidable.IncomingForm({
|
||||
maxFileSize: 2**30, // 1 GiB
|
||||
maxFields: 1,
|
||||
uploadDir: process.env.TMP_DIR
|
||||
});
|
||||
form.parse(req, function(err, fields, files) {
|
||||
if (err) return void next(err);
|
||||
var file = files.file;
|
||||
if (!file) return void res.sendStatus(400);
|
||||
(function trymkdir() {
|
||||
var filecode = Math.random().toString(36).slice(2).substring(0,4);
|
||||
if (customGen) {
|
||||
var g = customGen(req);
|
||||
var filecode = g.next().value;
|
||||
}
|
||||
filecode ||= Math.random().toString(36).slice(2).substring(0,4);
|
||||
var webroot = path.join(FILES_DIR, filecode);
|
||||
fs.mkdir(webroot, function (error) {
|
||||
if (error) if (error.code == "EEXIST") {
|
||||
@@ -48,26 +42,30 @@ app.post("*", (req, res, next) => {
|
||||
trymkdir();
|
||||
} else next(error);
|
||||
else {
|
||||
fs.rename(file.path, path.join(webroot, file.name), function (error) {
|
||||
if (error) return void next(error);
|
||||
let url = `${req.protocol}://${filecode}.${randomHostname?.() || req.hostname}`;
|
||||
res.send(url);
|
||||
var filename = req.headers.filename || "file";
|
||||
var filepath = path.join(webroot, filename);
|
||||
req.pipe(fs.createWriteStream(filepath));
|
||||
req.on("close", () => {
|
||||
var url = g?.next().value || `${req.protocol}://${filecode}.${process.env.BASE_HOSTNAME || req.hostname}`;
|
||||
res.type('text').send(url);
|
||||
require("./discord-preloader.js")(url);
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
});
|
||||
});
|
||||
|
||||
app.use(require("./antiscrape"));
|
||||
|
||||
app.get("*", function(req, res, next){
|
||||
var filecode = req.hostname.split('.')[0]; // for webroot of main domain i.e. qonq.gq, mkdir files/qonq
|
||||
var webroot = path.join(FILES_DIR, filecode);
|
||||
app.get(['/', '/:code/', '/:code/*'], function(req, res, next){
|
||||
var subdomain = req.subdomains.at(-1);
|
||||
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) {
|
||||
if (error) return void next(error.code == "ENOENT" ? "route" : error);
|
||||
if (webrootdirlist.length > 1) {
|
||||
req.url = path.join(filecode, req.url);
|
||||
if (subdomain) req.url = path.join(req.filecode, req.url);
|
||||
next();
|
||||
} else if (webrootdirlist.length == 1) {
|
||||
res.sendFile(webrootdirlist[0], {
|
||||
@@ -79,4 +77,3 @@ app.get("*", function(req, res, next){
|
||||
}
|
||||
});
|
||||
}, express.static(FILES_DIR), serveIndex(FILES_DIR, {icons: true}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user