Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add custom error page and improve IP filtering with error handling #12

Merged
merged 1 commit into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions public/error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WOLITE| ERROR</title>
<link rel="icon" type="image/png" href="icons/favicon.png" />
<link rel="stylesheet" href="style.css" />
</head>
<body class="bg-primary text-secondary font-inter">
<div class="flex flex-col h-screen justify-center items-center lg:space-y-1">
<div class="text-2xl font-light">ERROR | {{errorCode}}</div>
<div class="text-2xl font-light">{{errorMessage}}</div>

<button
class="hover:bg-secondary hover:text-primary text-secondary p-2 rounded mt-4 transition duration-1000"
>
<a href="/">Go Back</a>
</button>
</div>
</body>
</html>
195 changes: 169 additions & 26 deletions public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
--text-3xl--line-height: calc(2.25 / 1.875);
--text-4xl: 2.25rem;
--text-4xl--line-height: calc(2.5 / 2.25);
--text-5xl: 3rem;
--text-5xl--line-height: 1;
--text-7xl: 4.5rem;
--text-7xl--line-height: 1;
--text-9xl: 8rem;
--text-9xl--line-height: 1;
--font-weight-extralight: 200;
--font-weight-light: 300;
--font-weight-bold: 700;
--default-transition-duration: 150ms;
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-sans);
--default-font-feature-settings: var(--font-sans--font-feature-settings);
--default-font-variation-settings: var(
Expand Down Expand Up @@ -249,6 +251,9 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
.mx-auto {
margin-inline: auto;
}
.mt-4 {
margin-top: calc(var(--spacing) * 4);
}
.block {
display: block;
}
Expand Down Expand Up @@ -282,6 +287,9 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
.border-collapse {
border-collapse: collapse;
}
.transform {
transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y);
}
.cursor-pointer {
cursor: pointer;
}
Expand Down Expand Up @@ -327,6 +335,9 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
.bg-secondary {
background-color: var(--color-secondary);
}
.p-1 {
padding: calc(var(--spacing) * 1);
}
.p-2 {
padding: calc(var(--spacing) * 2);
}
Expand Down Expand Up @@ -360,14 +371,18 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
.font-inter {
font-family: var(--font-inter);
}
.text-2xl {
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
}
.text-3xl {
font-size: var(--text-3xl);
line-height: var(--tw-leading, var(--text-3xl--line-height));
}
.text-4xl {
font-size: var(--text-4xl);
line-height: var(--tw-leading, var(--text-4xl--line-height));
}
.text-5xl {
font-size: var(--text-5xl);
line-height: var(--tw-leading, var(--text-5xl--line-height));
}
.text-7xl {
font-size: var(--text-7xl);
line-height: var(--tw-leading, var(--text-7xl--line-height));
Expand All @@ -384,12 +399,17 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
--tw-font-weight: var(--font-weight-bold);
font-weight: var(--font-weight-bold);
}
.font-extralight {
--tw-font-weight: var(--font-weight-extralight);
font-weight: var(--font-weight-extralight);
}
.font-light {
--tw-font-weight: var(--font-weight-light);
font-weight: var(--font-weight-light);
}
.text-wrap {
text-wrap: wrap;
}
.text-\[\#5c5c5c\] {
color: #5c5c5c;
}
.text-muted {
color: var(--color-muted);
}
Expand All @@ -412,13 +432,52 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
.filter {
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
}
.backdrop-filter {
-webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
}
.transition {
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.duration-1 {
--tw-duration: 1ms;
transition-duration: 1ms;
}
.duration-75 {
--tw-duration: 75ms;
transition-duration: 75ms;
}
.duration-500 {
--tw-duration: 500ms;
transition-duration: 500ms;
}
.duration-1000 {
--tw-duration: 1000ms;
transition-duration: 1000ms;
}
.hover\:bg-secondary {
&:hover {
@media (hover: hover) {
background-color: var(--color-secondary);
}
}
}
.hover\:bg-white {
&:hover {
@media (hover: hover) {
background-color: var(--color-white);
}
}
}
.hover\:text-primary {
&:hover {
@media (hover: hover) {
color: var(--color-primary);
}
}
}
.hover\:text-secondary {
&:hover {
@media (hover: hover) {
Expand All @@ -440,23 +499,12 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
}
}
}
.sm\:text-justify {
@media (width >= 40rem) {
text-align: justify;
}
}
.sm\:text-2xl {
@media (width >= 40rem) {
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
}
}
.sm\:text-5xl {
@media (width >= 40rem) {
font-size: var(--text-5xl);
line-height: var(--tw-leading, var(--text-5xl--line-height));
}
}
.sm\:text-7xl {
@media (width >= 40rem) {
font-size: var(--text-7xl);
Expand All @@ -473,6 +521,42 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
width: calc(1/2 * 100%);
}
}
.lg\:space-y-0 {
@media (width >= 64rem) {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)));
}
}
}
.lg\:space-y-0\.5 {
@media (width >= 64rem) {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 0.5) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 0.5) * calc(1 - var(--tw-space-y-reverse)));
}
}
}
.lg\:space-y-1 {
@media (width >= 64rem) {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)));
}
}
}
.lg\:space-y-2 {
@media (width >= 64rem) {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));
}
}
}
.lg\:space-y-8 {
@media (width >= 64rem) {
:where(& > :not(:last-child)) {
Expand All @@ -493,19 +577,38 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
line-height: var(--tw-leading, var(--text-3xl--line-height));
}
}
.xl\:text-7xl {
@media (width >= 80rem) {
font-size: var(--text-7xl);
line-height: var(--tw-leading, var(--text-7xl--line-height));
}
}
.xl\:text-9xl {
@media (width >= 80rem) {
font-size: var(--text-9xl);
line-height: var(--tw-leading, var(--text-9xl--line-height));
}
}
}
@property --tw-rotate-x {
syntax: "*";
inherits: false;
initial-value: rotateX(0);
}
@property --tw-rotate-y {
syntax: "*";
inherits: false;
initial-value: rotateY(0);
}
@property --tw-rotate-z {
syntax: "*";
inherits: false;
initial-value: rotateZ(0);
}
@property --tw-skew-x {
syntax: "*";
inherits: false;
initial-value: skewX(0);
}
@property --tw-skew-y {
syntax: "*";
inherits: false;
initial-value: skewY(0);
}
@property --tw-space-y-reverse {
syntax: "*";
inherits: false;
Expand Down Expand Up @@ -565,3 +668,43 @@ https://youtu.be/K6XO8FTiGIs?si=sH0Fx1ThPbmmWemT [how to add color]
syntax: "*";
inherits: false;
}
@property --tw-backdrop-blur {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-brightness {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-contrast {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-grayscale {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-hue-rotate {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-invert {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-opacity {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-saturate {
syntax: "*";
inherits: false;
}
@property --tw-backdrop-sepia {
syntax: "*";
inherits: false;
}
@property --tw-duration {
syntax: "*";
inherits: false;
}
3 changes: 2 additions & 1 deletion server/middleware/basicIpFilter.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const GetErrorPage = require("../utils/error");
const LogConsole = require("../utils/logging");
require("dotenv").config();

Expand All @@ -21,7 +22,7 @@ function basicIpAuth(req, res, next) {
// Check if the client's IP is in the allowedIPs list
if (!allowedOrigins.includes(clientIp)) {
LogConsole("warn", "Access denied. IP not allowed.", clientIp);
return res.status(403).send("Access denied.");
return res.status(403).send(GetErrorPage(403, "Access denied. IP not allowed."));
}
LogConsole("info", "IP allowed.", clientIp);
next();
Expand Down
3 changes: 3 additions & 0 deletions server/routes/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const bcrypt = require("bcrypt");
require("dotenv").config();
const createOTP = require("../utils/otp");
const LogConsole = require("../utils/logging");
const basicIpFilter = require("../middleware/basicIpFilter");

const router = express.Router();

Expand All @@ -12,6 +13,8 @@ router.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "..", "..", "public", "login.html"));
});

router.use(basicIpFilter);

// POST /auth - process the login form submission
router.post("/", (req, res) => {
const { username, password, totp } = req.body;
Expand Down
13 changes: 13 additions & 0 deletions server/utils/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const fs = require("fs");
const path = require("path");

function GetErrorPage(code, message) {
const errorFilePath = path.join(__dirname, "..", "..", "public", "error.html");
let errorHtml = fs.readFileSync(errorFilePath, "utf8");
errorHtml = errorHtml.replace("{{errorCode}}", code);
errorHtml = errorHtml.replace("{{errorMessage}}", message);

return errorHtml;
}

module.exports = GetErrorPage;