From 9ece56849d15093501023b11a56c36533ca17c75 Mon Sep 17 00:00:00 2001 From: bingus_violet Date: Sat, 13 Jul 2024 05:53:26 -0500 Subject: [PATCH] Functional --- .gitignore | 1 + expressHandler.js | 13 ++- fileHandler.js | 69 +++++++++++++ index.js | 4 +- package-lock.json | 187 ++++++++++++++++++++++++++++++++++ package.json | 3 +- resources/html/postUrl.html | 22 ++++ resources/html/redirWarn.html | 22 ++++ resources/json/config.json | 4 + shortener.js | 88 ++++++++++++++++ static/index.html | 2 +- static/style.css | 4 + 12 files changed, 411 insertions(+), 8 deletions(-) create mode 100644 fileHandler.js create mode 100644 resources/html/postUrl.html create mode 100644 resources/html/redirWarn.html create mode 100644 resources/json/config.json create mode 100644 shortener.js diff --git a/.gitignore b/.gitignore index ceaea36..500c2c9 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,4 @@ dist .yarn/install-state.gz .pnp.* +/data \ No newline at end of file diff --git a/expressHandler.js b/expressHandler.js index 4602335..d4ecc2d 100644 --- a/expressHandler.js +++ b/expressHandler.js @@ -1,14 +1,17 @@ const express = require("express"), -path = require("path") +bodyParser = require("body-parser"), +paths = require("./fileHandler.js").paths const PORT = process.env.PORT || 8080 const app = express() -const staticPath = path.join(__dirname, "static") - -app.use(express.static(staticPath)) +app.use(express.static(paths.static)) app.listen(PORT, () => { console.log("QuickURL now listening for PORT: " + PORT) -}) \ No newline at end of file +}) + +module.exports = { + app: app +} \ No newline at end of file diff --git a/fileHandler.js b/fileHandler.js new file mode 100644 index 0000000..aced82b --- /dev/null +++ b/fileHandler.js @@ -0,0 +1,69 @@ +const path = require("path"), +chokidar = require("chokidar"), +fs = require("fs") + +var paths = { + static: path.join(__dirname, "static"), + postUrl: path.join(__dirname, "resources/html/postUrl.html"), + redirWarn: path.join(__dirname, "resources/html/redirWarn.html"), + data: path.join(__dirname, "data"), + config: path.join(__dirname, "data/config.json"), + defaultConfig: path.join(__dirname, "resources/json/config.json"), + urls: path.join(__dirname, "data/urls.json") +} + +module.exports = { + paths, + config: undefined, + urls: [], + writeUrls: function (props) { + var urls = module.exports.urls + props.shortCode = 0 + + for (var i in urls) { + props.shortCode = urls[i].shortCode + 1 + } + + if (props.shortCode > 100000) { + props.shortCode = 0 + } + + urls.push(props) + + var expirePoint = 0 + + for (var i in urls) { + var url = urls[i] + if (url.expire < Date.now()) { + expirePoint = i + continue + } + } + + urls.splice(0, expirePoint) + + fs.writeFileSync(paths.urls, JSON.stringify(module.exports.urls)) + return props + } +} + +var currentConfig = JSON.parse(fs.readFileSync(paths.config)) +var defaultConfig = JSON.parse(fs.readFileSync(paths.defaultConfig)) +currentConfig = Object.assign({}, defaultConfig, currentConfig) +fs.writeFileSync(paths.config, JSON.stringify(currentConfig)) + +var watcher = chokidar.watch(paths.data) + +function dataUpdate() { + module.exports.config = JSON.parse(fs.readFileSync(paths.config)) +} + +watcher +.on("change", dataUpdate) +.on("add", dataUpdate) + +if (!fs.existsSync(paths.urls)) { + fs.writeFileSync(paths.urls, "[]") +} + +module.exports.urls = JSON.parse(fs.readFileSync(paths.urls)) \ No newline at end of file diff --git a/index.js b/index.js index f39fcc1..cc380ec 100644 --- a/index.js +++ b/index.js @@ -1 +1,3 @@ -require("./expressHandler.js") \ No newline at end of file +require("./fileHandler.js") +require("./expressHandler.js") +require("./shortener.js") \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 714ceea..722dda9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "Unlicense", "dependencies": { + "chokidar": "^3.6.0", "express": "^4.19.2" } }, @@ -25,12 +26,37 @@ "node": ">= 0.6" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -55,6 +81,18 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -83,6 +121,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -257,6 +319,18 @@ "node": ">= 0.10.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -293,6 +367,20 @@ "node": ">= 0.6" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -321,6 +409,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -424,6 +524,48 @@ "node": ">= 0.10" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -496,6 +638,15 @@ "node": ">= 0.6" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", @@ -535,6 +686,18 @@ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "license": "MIT" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -587,6 +750,18 @@ "node": ">= 0.8" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -708,6 +883,18 @@ "node": ">= 0.8" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/package.json b/package.json index f49a712..ac49ee3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "quickurl", - "version": "0.0.1", + "version": "1.0.0", "description": "An extremely generic privacy & security focused URL shortener.", "main": "index.js", "scripts": { @@ -14,6 +14,7 @@ "author": "Violet", "license": "Unlicense", "dependencies": { + "chokidar": "^3.6.0", "express": "^4.19.2" } } diff --git a/resources/html/postUrl.html b/resources/html/postUrl.html new file mode 100644 index 0000000..f82d300 --- /dev/null +++ b/resources/html/postUrl.html @@ -0,0 +1,22 @@ + + + + + + + + + + + QuickURL + + + +
+

QuickURL

+ {CONTENT} + Go Back +
+ + + \ No newline at end of file diff --git a/resources/html/redirWarn.html b/resources/html/redirWarn.html new file mode 100644 index 0000000..6ee6b2a --- /dev/null +++ b/resources/html/redirWarn.html @@ -0,0 +1,22 @@ + + + + + + + + + + + QuickURL + + + +
+

QuickURL

+

This link redirects you to:
{REDIRECT_URL}
Are you sure you want to continue?

+ Continue +
+ + + \ No newline at end of file diff --git a/resources/json/config.json b/resources/json/config.json new file mode 100644 index 0000000..b5d0e2f --- /dev/null +++ b/resources/json/config.json @@ -0,0 +1,4 @@ +{ + "trust": [], + "blacklist": [] +} \ No newline at end of file diff --git a/shortener.js b/shortener.js new file mode 100644 index 0000000..bbb0203 --- /dev/null +++ b/shortener.js @@ -0,0 +1,88 @@ +const expressHandler = require("./expressHandler"), +fileHandler = require("./fileHandler.js") +paths = fileHandler.paths, +fs = require("fs") + +const app = expressHandler.app + +app.get("/makeURL", (req, res) => { + function writep(inp) { + return res.write("

" + inp + "

") + } + + function finishHtml() { + res.write(html.substring(html.indexOf("{CONTENT}") + 9), () => { + res.end() + }) + } + + res.setHeader("X-Accel-Buffering", "no") + + var html = fs.readFileSync(paths.postUrl).toString() + res.write(html.substring(0, html.indexOf("{CONTENT}"))) + var config = fileHandler.config + writep("HTML Loaded.") + + var url = req.query.url.toLowerCase() + + if (!url.includes("http://") && !url.includes("https://")) { + writep("Error: URL is missing protocol! Please include http:// or https:// in the URL for it to be valid.") + finishHtml() + return + } + + var domain = url.substring(url.indexOf("://") + 3) + if (domain.charAt(-1) != "/") { + domain += "/" + } + domain = domain.substring(0, domain.indexOf("/")) + + domain = domain.substring(domain.lastIndexOf(".", domain.lastIndexOf(".") - 1) + 1) + + + writep("Input URL: " + url) + + writep("Domain: " + domain) + + var trust = config.trust.includes(domain) + var blacklisted = config.blacklist.includes(domain) + + writep("Domain on blacklist: " + blacklisted) + writep("Domain on trust list: " + trust) + + if (blacklisted) { + writep("Error: Domain found on the blacklist! This URL is not allowed.") + finishHtml() + return + } + + var shortURL = fileHandler.writeUrls({ + "trusted": trust, + "expire": Date.now() + (7 * 24 * 60 * 60 * 1000), + "url": url + }) + + writep(`Done! Find your URL at: ${req.hostname + "/url/" + shortURL.shortCode}`) + + finishHtml() +}) + +app.get("/url/:shortCode*", (req, res) => { + var shortCode = req.params.shortCode + var urls = fileHandler.urls + + for (var i in urls) { + var data = urls[i] + if (data.shortCode == shortCode) { + if (data.trusted) { + res.redirect(data.url) + } else { + var html = fs.readFileSync(paths.redirWarn).toString() + html = html.replaceAll("{REDIRECT_URL}", data.url) + + res.send(html) + } + continue + } + } +}) \ No newline at end of file diff --git a/static/index.html b/static/index.html index 493a2f0..f4e98df 100644 --- a/static/index.html +++ b/static/index.html @@ -19,7 +19,7 @@

QuickURL

-
+

diff --git a/static/style.css b/static/style.css index 9e12066..e7aac95 100644 --- a/static/style.css +++ b/static/style.css @@ -89,4 +89,8 @@ input[type="url"] { select:hover, input:hover { border-color: var(--text-color) +} + +a { + color: rgb(125, 125, 255) } \ No newline at end of file