mirror of
https://github.com/NebulaServices/Nebula.git
synced 2025-05-17 13:30:00 -04:00
242 lines
5.5 KiB
JavaScript
242 lines
5.5 KiB
JavaScript
import express from "express";
|
|
import cookieParser from "cookie-parser";
|
|
import http from "node:http";
|
|
import createBareServer from "@tomphttp/bare-server-node";
|
|
import { uvPath } from "@titaniumnetwork-dev/ultraviolet";
|
|
import path from "node:path";
|
|
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 __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 app = express(server);
|
|
const bareServer = createBareServer("/bare/");
|
|
|
|
// Middleware
|
|
app.use(cookieParser());
|
|
app.use(express.json());
|
|
app.use(
|
|
express.urlencoded({
|
|
extended: true
|
|
})
|
|
);
|
|
|
|
// Verification
|
|
app.patch("/generate-otp", async (req, res) => {
|
|
if (
|
|
config.sendgrid_verification == true ||
|
|
config.discord_verification == true ||
|
|
config.smtp_verificaton == true
|
|
) {
|
|
const OTP = generateCode();
|
|
ACTIVE_CODES.add(OTP);
|
|
|
|
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(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
|
|
server.on("request", (req, res) => {
|
|
if (bareServer.shouldRoute(req)) {
|
|
bareServer.routeRequest(req, res);
|
|
} else {
|
|
app(req, res);
|
|
}
|
|
});
|
|
|
|
server.on("upgrade", (req, socket, head) => {
|
|
if (bareServer.shouldRoute(req)) {
|
|
bareServer.routeUpgrade(req, socket, head);
|
|
} else {
|
|
socket.end();
|
|
}
|
|
});
|
|
|
|
server.on("listening", () => {
|
|
console.log(`Server running at http://localhost:${PORT}/.`);
|
|
});
|
|
|
|
server.listen({
|
|
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);
|
|
}
|