diff --git a/.example.env b/.example.env index 6e9c4d3b1..7e2b57a07 100644 --- a/.example.env +++ b/.example.env @@ -7,6 +7,12 @@ SITE_NAME=Kutt # Optional - The domain that this website is on DEFAULT_DOMAIN=localhost:3000 +# Optional - The path the service will run on (ex: localhost:3000/url-shortener) +BASE_PATH= + +# Optional - Whether the shortened links will use the base path (default false) +SHORT_URLS_INCLUDE_PATH=false + # Required - A passphrase to encrypt JWT. Use a random long string JWT_SECRET= diff --git a/README.md b/README.md index 6ad07f955..16aed769a 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,8 @@ You can use files for each of the variables by appending `_FILE` to the name of | `PORT` | The port to start the app on | `3000` | `8888` | | `SITE_NAME` | Name of the website | `Kutt` | `Your Site` | | `DEFAULT_DOMAIN` | The domain address that this app runs on | `localhost:3000` | `yoursite.com` | +| `BASE_PATH` | The path the service will run on | `/` | `/url-shortener` | +| `SHORT_URLS_INCLUDE_PATH` | Whether the shortened links will use the base path. If this value is false and BASE_PATH is specified, your proxy will need to route to the BASE_PATH of the service. | `false` | `true` | | `LINK_LENGTH` | The length of of shortened address | `6` | `5` | | `LINK_CUSTOM_ALPHABET` | Alphabet used to generate custom addresses. Default value omits o, O, 0, i, I, l, 1, and j to avoid confusion when reading the URL. | (abcd..789) | `abcABC^&*()@` | | `DISALLOW_REGISTRATION` | Disable registration. Note that if `MAIL_ENABLED` is set to false, then the registration would still be disabled since it relies on emails to sign up users. | `true` | `false` | diff --git a/package-lock.json b/package-lock.json index 00c1f0bed..2ffc07694 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "cookie-parser": "1.4.7", "cors": "2.8.5", "date-fns": "2.30.0", - "dotenv": "^16.4.7", + "dotenv": "16.4.7", "envalid": "8.0.0", "express": "4.21.2", "express-rate-limit": "7.5.0", diff --git a/server/env.js b/server/env.js index 87f7bc373..fb2e96e84 100644 --- a/server/env.js +++ b/server/env.js @@ -65,6 +65,8 @@ const spec = { REPORT_EMAIL: str({ default: "" }), CONTACT_EMAIL: str({ default: "" }), NODE_APP_INSTANCE: num({ default: 0 }), + BASE_PATH: str({ default: "" }), + SHORT_URLS_INCLUDE_PATH: bool({ default: false }), }; for (const key in spec) { diff --git a/server/handlers/auth.handler.js b/server/handlers/auth.handler.js index cb120b5b4..b8df15fe5 100644 --- a/server/handlers/auth.handler.js +++ b/server/handlers/auth.handler.js @@ -28,11 +28,11 @@ function authenticate(type, error, isStrict, redirect) { (user && user.banned)) ) { if (redirect === "page") { - res.redirect("/logout"); + res.redirect(utils.getPath('/logout')); return; } if (redirect === "header") { - res.setHeader("HX-Redirect", "/logout"); + res.setHeader("HX-Redirect", utils.getPath('/logout')); res.send("NOT_AUTHENTICATED"); return; } @@ -357,7 +357,7 @@ function featureAccess(features, redirect) { for (let i = 0; i < features.length; ++i) { if (!features[i]) { if (redirect) { - return res.redirect("/"); + return res.redirect(utils.getPath("/")); } else { throw new CustomError("Request is not allowed.", 400); } diff --git a/server/handlers/helpers.handler.js b/server/handlers/helpers.handler.js index ff53454b3..bf63ba7ef 100644 --- a/server/handlers/helpers.handler.js +++ b/server/handlers/helpers.handler.js @@ -6,6 +6,7 @@ const { CustomError } = require("../utils"); const query = require("../queries"); const redis = require("../redis"); const env = require("../env"); +const utils = require("../utils"); function error(error, req, res, _next) { if (!(error instanceof CustomError)) { @@ -131,7 +132,7 @@ async function adminSetup(req, res, next) { return; } - res.redirect("/create-admin"); + res.redirect(utils.getPath('/create-admin')); } module.exports = { diff --git a/server/handlers/links.handler.js b/server/handlers/links.handler.js index 93a0a6489..d2c2764da 100644 --- a/server/handlers/links.handler.js +++ b/server/handlers/links.handler.js @@ -458,7 +458,7 @@ async function ban(req, res) { async function redirect(req, res, next) { const isPreservedUrl = utils.preservedURLs.some( - item => item === req.path.replace("/", "") + item => item === req.path.replace(env.BASE_PATH, "") ); if (isPreservedUrl) return next(); @@ -480,12 +480,12 @@ async function redirect(req, res, next) { // 3. When no link, if has domain redirect to domain's homepage // otherwise redirect to 404 if (!link) { - return res.redirect(domain?.homepage || "/404"); + return res.redirect(domain?.homepage || utils.getPath('/404')); } // 4. If link is banned, redirect to banned page. if (link.banned) { - return res.redirect("/banned"); + return res.redirect(utils.getPath("/banned")); } // 5. If wants to see link info, then redirect @@ -594,7 +594,7 @@ async function redirectCustomDomainHomepage(req, res, next) { const path = req.path; const pathName = path.replace("/", "").split("/")[0]; if ( - path === "/" || + path === env.BASE_PATH || utils.preservedURLs.includes(pathName) ) { const domain = await query.domain.find({ address: host }); @@ -618,7 +618,7 @@ async function stats(req, res) { if (!link) { if (req.isHTML) { - res.setHeader("HX-Redirect", "/404"); + res.setHeader("HX-Redirect", utils.getPath("/404")); res.status(200).send(""); return; } diff --git a/server/handlers/locals.handler.js b/server/handlers/locals.handler.js index 7b1b4ab25..f39a9c1e3 100644 --- a/server/handlers/locals.handler.js +++ b/server/handlers/locals.handler.js @@ -30,6 +30,7 @@ function config(req, res, next) { res.locals.mail_enabled = env.MAIL_ENABLED; res.locals.report_email = env.REPORT_EMAIL; res.locals.custom_styles = utils.getCustomCSSFileNames(); + res.locals.base_path = env.BASE_PATH; next(); } diff --git a/server/handlers/renders.handler.js b/server/handlers/renders.handler.js index 79058fdb4..1f0ce7f8e 100644 --- a/server/handlers/renders.handler.js +++ b/server/handlers/renders.handler.js @@ -10,7 +10,7 @@ const env = require("../env"); async function homepage(req, res) { if (env.DISALLOW_ANONYMOUS_LINKS && !req.user) { - res.redirect("/login"); + res.redirect(utils.getPath("/login")); return; } res.render("homepage", { @@ -20,7 +20,7 @@ async function homepage(req, res) { async function login(req, res) { if (req.user) { - res.redirect("/"); + res.redirect(utils.getPath("/")); return; } @@ -39,7 +39,7 @@ function logout(req, res) { async function createAdmin(req, res) { const isThereAUser = await query.user.findAny(); if (isThereAUser) { - res.redirect("/login"); + res.redirect(utils.getPath("/login")); return; } res.render("create_admin", { @@ -79,7 +79,7 @@ async function banned(req, res) { async function report(req, res) { if (!env.REPORT_EMAIL) { - res.redirect("/"); + res.redirect(utils.getPath("/")); return; } res.render("report", { diff --git a/server/mail/mail.js b/server/mail/mail.js index 93e5304d7..d4f231322 100644 --- a/server/mail/mail.js +++ b/server/mail/mail.js @@ -35,14 +35,17 @@ if (env.MAIL_ENABLED) { resetEmailTemplate = fs .readFileSync(resetEmailTemplatePath, { encoding: "utf-8" }) .replace(/{{domain}}/gm, env.DEFAULT_DOMAIN) + .replace(/{{base_path}}/gm, env.BASE_PATH) .replace(/{{site_name}}/gm, env.SITE_NAME); verifyEmailTemplate = fs .readFileSync(verifyEmailTemplatePath, { encoding: "utf-8" }) .replace(/{{domain}}/gm, env.DEFAULT_DOMAIN) + .replace(/{{base_path}}/gm, env.BASE_PATH) .replace(/{{site_name}}/gm, env.SITE_NAME); changeEmailTemplate = fs .readFileSync(changeEmailTemplatePath, { encoding: "utf-8" }) .replace(/{{domain}}/gm, env.DEFAULT_DOMAIN) + .replace(/{{base_path}}/gm, env.BASE_PATH) .replace(/{{site_name}}/gm, env.SITE_NAME); } diff --git a/server/mail/template-reset.html b/server/mail/template-reset.html index dc247beb7..ae1eab8e4 100644 --- a/server/mail/template-reset.html +++ b/server/mail/template-reset.html @@ -457,7 +457,7 @@ > @@ -478,7 +478,7 @@ 404 | Link could not be found. - + ← Back to homepage diff --git a/server/views/banned.hbs b/server/views/banned.hbs index a2434f312..f134c73cd 100644 --- a/server/views/banned.hbs +++ b/server/views/banned.hbs @@ -6,7 +6,7 @@

If you noticed a malware/scam link shortened by {{default_domain}}, - + send us a report .

diff --git a/server/views/error.hbs b/server/views/error.hbs index 9b6d95224..bc37f8a7a 100644 --- a/server/views/error.hbs +++ b/server/views/error.hbs @@ -4,7 +4,7 @@ Error!

{{message}}

- + ← Back to homepage diff --git a/server/views/layout.hbs b/server/views/layout.hbs index 814339cc6..4bedcf5b2 100644 --- a/server/views/layout.hbs +++ b/server/views/layout.hbs @@ -3,29 +3,29 @@ - - - - - - + + + + + + - + - + - + - + {{site_name}} | {{title}} - + {{#each custom_styles}} - + {{/each}} {{{block "stylesheets"}}} @@ -35,8 +35,8 @@ {{{block "scripts"}}} - - - + + + \ No newline at end of file diff --git a/server/views/logout.hbs b/server/views/logout.hbs index 49ea74b2e..291488d20 100644 --- a/server/views/logout.hbs +++ b/server/views/logout.hbs @@ -1,5 +1,5 @@ {{> header}} -
+

Logged out. Redirecting to homepage...

diff --git a/server/views/partials/admin/dialog/add_domain.hbs b/server/views/partials/admin/dialog/add_domain.hbs index 2b5b5c14c..5d163030d 100644 --- a/server/views/partials/admin/dialog/add_domain.hbs +++ b/server/views/partials/admin/dialog/add_domain.hbs @@ -2,7 +2,7 @@

Add domain

Create user diff --git a/server/views/partials/admin/domains/tr.hbs b/server/views/partials/admin/domains/tr.hbs index cf86f5406..1458209eb 100644 --- a/server/views/partials/admin/domains/tr.hbs +++ b/server/views/partials/admin/domains/tr.hbs @@ -10,7 +10,7 @@ @@ -28,7 +28,7 @@ class="action edit" hx-trigger="click queue:none" hx-ext="path-params" - hx-get="/admin/link/edit/{id}" + hx-get="./admin/link/edit/{id}" hx-vals='{"id":"{{id}}"}' hx-swap="beforeend" hx-target="next tr.edit" @@ -50,7 +50,7 @@ diff --git a/server/views/partials/header.hbs b/server/views/partials/header.hbs index e032a42bd..4fa4f6f5a 100644 --- a/server/views/partials/header.hbs +++ b/server/views/partials/header.hbs @@ -1,7 +1,7 @@
-