mirror of
https://github.com/NebulaServices/Nebula.git
synced 2025-05-17 13:30:00 -04:00
Refactor & implement verification
This commit is contained in:
parent
ec2807105a
commit
326d59bff2
20 changed files with 1348 additions and 69 deletions
208
app.js
208
app.js
|
@ -5,42 +5,195 @@ import createBareServer from "@tomphttp/bare-server-node";
|
||||||
import { uvPath } from "@titaniumnetwork-dev/ultraviolet";
|
import { uvPath } from "@titaniumnetwork-dev/ultraviolet";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import config from "./deployment.config.json" assert { type: "json" };
|
import config from "./deployment.config.json" assert { type: "json" };
|
||||||
|
import sgMail from "@sendgrid/mail";
|
||||||
|
import nodemailer from "nodemailer";
|
||||||
|
import * as uuid from "uuid";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import bcrypt from "bcrypt";
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
const __dirname = process.cwd();
|
const __dirname = process.cwd();
|
||||||
|
const ACTIVE_CODES = new Set();
|
||||||
|
let TOKENS = fs
|
||||||
|
.readFileSync("./memory.txt", "utf-8")
|
||||||
|
.trim()
|
||||||
|
.split("\n")
|
||||||
|
.map((token) => {
|
||||||
|
const parts = token.split(":");
|
||||||
|
return {
|
||||||
|
id: parts[0],
|
||||||
|
token: parts[1],
|
||||||
|
expiration: parts[2]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const server = http.createServer();
|
const server = http.createServer();
|
||||||
const app = express(server);
|
const app = express(server);
|
||||||
const bareServer = createBareServer("/bare/");
|
const bareServer = createBareServer("/bare/");
|
||||||
|
|
||||||
// Static files
|
// Middleware
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(
|
||||||
|
express.urlencoded({
|
||||||
|
extended: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
app.patch("/generate-otp", (req, res) => {
|
// Verification
|
||||||
|
app.patch("/generate-otp", async (req, res) => {
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/validate-otp", (req, res) => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
app.all("/", (req, res, next) => {
|
|
||||||
console.log(req.url);
|
|
||||||
// validate verification
|
|
||||||
if (
|
if (
|
||||||
config.sendgrid_verification == true ||
|
config.sendgrid_verification == true ||
|
||||||
config.discord_verification == true ||
|
config.discord_verification == true ||
|
||||||
config.smtp_verificaton == true
|
config.smtp_verificaton == true
|
||||||
) {
|
) {
|
||||||
if (!req.cookies["verification"]) {
|
const OTP = generateCode();
|
||||||
res.redirect("/unv.html");
|
ACTIVE_CODES.add(OTP);
|
||||||
} else {
|
|
||||||
next();
|
setTimeout(() => {
|
||||||
|
ACTIVE_CODES.delete(OTP);
|
||||||
|
}, 1000 * 60 * 5);
|
||||||
|
|
||||||
|
let email = {
|
||||||
|
to: "",
|
||||||
|
from: "",
|
||||||
|
subject: `NebulaWEB personal access code ${OTP}`,
|
||||||
|
text: `
|
||||||
|
####### ACCESS CODE (OTP) ${OTP} #######
|
||||||
|
####### DO NOT SHARE THIS CODE! #######
|
||||||
|
(this message is automated)`
|
||||||
|
};
|
||||||
|
|
||||||
|
if (config.sendgrid_verification == true) {
|
||||||
|
sgMail.setApiKey(config.sendgrid_options.api_key);
|
||||||
|
|
||||||
|
email.to = config.sendgrid_options.to_email;
|
||||||
|
email.from = config.sendgrid_options.sendFromEmail;
|
||||||
|
try {
|
||||||
|
await sgMail.send(msg);
|
||||||
|
} catch {
|
||||||
|
return res.status(504).end();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.smtp_verification == true) {
|
||||||
|
const smtpMailerAgent = nodemailer.createTransport(config.smtp_options);
|
||||||
|
|
||||||
|
email.to = config.smtp_options.to_email;
|
||||||
|
email.from = config.smtp_options.sendFromEmail;
|
||||||
|
try {
|
||||||
|
smtpMailerAgent.sendMail(email);
|
||||||
|
} catch {
|
||||||
|
return res.status(504).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.discord_verification == true) {
|
||||||
|
try {
|
||||||
|
await fetch(config.webhook_url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
content: `Your NebulaWEB access code is \`${OTP}\``
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return res.status(500).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).end();
|
||||||
|
} else {
|
||||||
|
res.status(404).end();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function generateCode() {
|
||||||
|
const code = Math.floor(Math.random() * 1000000);
|
||||||
|
return code.toString().padStart(6, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
app.post("/validate-otp", (req, res) => {
|
||||||
|
if (
|
||||||
|
config.sendgrid_verification == true ||
|
||||||
|
config.discord_verification == true ||
|
||||||
|
config.smtp_verificaton == true
|
||||||
|
) {
|
||||||
|
const OTP = req.body.otp;
|
||||||
|
|
||||||
|
if (ACTIVE_CODES.has(OTP)) {
|
||||||
|
ACTIVE_CODES.delete(OTP);
|
||||||
|
|
||||||
|
const token = uuid.v4();
|
||||||
|
|
||||||
|
TOKENS.push({
|
||||||
|
id: OTP,
|
||||||
|
token: hash(token),
|
||||||
|
expiration: Date.now() + 1000 * 60 * 60 * 24 * 30
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
"./memory.txt",
|
||||||
|
TOKENS.map((token) => {
|
||||||
|
return `${token.id}:${token.token}:${token.expiration}`;
|
||||||
|
}).join("\n"),
|
||||||
|
"utf-8"
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
validation: `${OTP}:${token}`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(401).json({
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(404).end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Static files
|
||||||
app.use("/uv/", express.static(uvPath));
|
app.use("/uv/", express.static(uvPath));
|
||||||
app.use(express.static(path.join(__dirname, "static")));
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
|
||||||
|
// Login route
|
||||||
|
app.get("/login", (req, res) => {
|
||||||
|
if (
|
||||||
|
config.sendgrid_verification == true ||
|
||||||
|
config.discord_verification == true ||
|
||||||
|
config.smtp_verificaton == true
|
||||||
|
) {
|
||||||
|
res.sendFile(path.join(__dirname, "src", "unv.html"));
|
||||||
|
} else {
|
||||||
|
res.redirect("/");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// General Routes
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
const verification = req.cookies["validation"];
|
||||||
|
if (!verification || !validateToken(verification)) {
|
||||||
|
res.redirect("/login");
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/", (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname, "src", "index.html"));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/options", (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname, "src", "options.html"));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/privacy", (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname, "src", "privacy.html"));
|
||||||
|
});
|
||||||
|
|
||||||
// Bare Server
|
// Bare Server
|
||||||
server.on("request", (req, res) => {
|
server.on("request", (req, res) => {
|
||||||
|
@ -66,3 +219,24 @@ server.on("listening", () => {
|
||||||
server.listen({
|
server.listen({
|
||||||
port: PORT
|
port: PORT
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function hash(token) {
|
||||||
|
const salt = bcrypt.genSaltSync(10);
|
||||||
|
return bcrypt.hashSync(token, salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateToken(verification) {
|
||||||
|
console.log(verification);
|
||||||
|
const [id, token] = verification.split(":");
|
||||||
|
const tokenData = TOKENS.find((token) => token.id == id);
|
||||||
|
|
||||||
|
if (!tokenData) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenData.expiration < Date.now()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bcrypt.compareSync(token, tokenData.token);
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"discord_verification": true,
|
"discord_verification": true,
|
||||||
"webhook_url": "YOUR DISCORD WEBHOOK URL",
|
"webhook_url": "https://discord.com/api/webhooks/1072301556461998172/rsMxTsD5AvGFqLoVRt_mmMcVFWJ9xQDLuiy_FsbWapHoYTamL9tsURZO-4j7P1OT4n0Z",
|
||||||
|
|
||||||
"smtp_verification": false,
|
"smtp_verification": false,
|
||||||
"smtp_options": {
|
"smtp_options": {
|
||||||
|
|
1152
package-lock.json
generated
1152
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -13,14 +13,17 @@
|
||||||
"author": "Nebula Services",
|
"author": "Nebula Services",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@sendgrid/mail": "^7.7.0",
|
||||||
"@titaniumnetwork-dev/ultraviolet": "^1.0.8-beta",
|
"@titaniumnetwork-dev/ultraviolet": "^1.0.8-beta",
|
||||||
"@tomphttp/bare-server-node": "^1.2.2",
|
"@tomphttp/bare-server-node": "^1.2.2",
|
||||||
|
"bcrypt": "^5.1.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"node-fetch": "^3.3.0",
|
"node-fetch": "^3.3.0",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
"nodemailer-sendgrid-transport": "^0.2.0",
|
"nodemailer-sendgrid-transport": "^0.2.0",
|
||||||
"serve-static": "^1.15.0"
|
"serve-static": "^1.15.0",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
@ -310,42 +310,6 @@ if (storedSetTheme == null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
function setCookie(cname, cvalue, exdays) {
|
|
||||||
const d = new Date();
|
|
||||||
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
|
|
||||||
let expires = "expires=" + d.toUTCString();
|
|
||||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCookie(cname) {
|
|
||||||
let name = cname + "=";
|
|
||||||
let decodedCookie = decodeURIComponent(document.cookie);
|
|
||||||
let ca = decodedCookie.split(";");
|
|
||||||
for (let i = 0; i < ca.length; i++) {
|
|
||||||
let c = ca[i];
|
|
||||||
while (c.charAt(0) == " ") {
|
|
||||||
c = c.substring(1);
|
|
||||||
}
|
|
||||||
if (c.indexOf(name) == 0) {
|
|
||||||
return c.substring(name.length, c.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
function httpGet(theUrl) {
|
|
||||||
var xmlHttp = new XMLHttpRequest();
|
|
||||||
xmlHttp.open("GET", theUrl, false); // false for synchronous request
|
|
||||||
xmlHttp.send(null);
|
|
||||||
return xmlHttp.responseText;
|
|
||||||
}
|
|
||||||
if (httpGet("/verification") == "true") {
|
|
||||||
if (getCookie("verifiedAccess") == "") {
|
|
||||||
console.log("COOKIE NOT FOUND - ENTRY NOT PERMITTED");
|
|
||||||
window.location = "/unv.html";
|
|
||||||
} else {
|
|
||||||
console.log("COOKIE RECOGNIZED - ENTRY PERMITTED ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let background = localStorage.getItem("--background-primary");
|
let background = localStorage.getItem("--background-primary");
|
||||||
let navbar = localStorage.getItem("--navbar-color");
|
let navbar = localStorage.getItem("--navbar-color");
|
||||||
let navbarHeight = localStorage.getItem("--navbar-height");
|
let navbarHeight = localStorage.getItem("--navbar-height");
|
|
@ -19,7 +19,8 @@ validateOTP.onclick = () => {
|
||||||
return response.json();
|
return response.json();
|
||||||
}).then((data) => {
|
}).then((data) => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
|
setCookie("validation", data.validation, 30);
|
||||||
|
location.href = "/";
|
||||||
} else {
|
} else {
|
||||||
alert("Invalid OTP.");
|
alert("Invalid OTP.");
|
||||||
}
|
}
|
||||||
|
@ -27,3 +28,14 @@ validateOTP.onclick = () => {
|
||||||
alert("An error occurred while validating your OTP.")
|
alert("An error occurred while validating your OTP.")
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function setCookie(name, value, days) {
|
||||||
|
var expires = "";
|
||||||
|
if (days) {
|
||||||
|
var date = new Date();
|
||||||
|
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
|
||||||
|
expires = "; expires=" + date.toUTCString();
|
||||||
|
}
|
||||||
|
document.cookie = name + "=" + (value || "") + expires + "; path=/";
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue