Merge pull request #76 from cohenerickson/main
This commit is contained in:
commit
9de8d5fa48
28 changed files with 2275 additions and 4518 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -5,3 +5,6 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
.breakpoints
|
.breakpoints
|
||||||
|
|
||||||
|
memory.txt
|
||||||
|
old.app.js
|
||||||
|
|
|
||||||
28
README.md
28
README.md
|
|
@ -11,7 +11,7 @@ NebulaWeb is an official flagship of Nebula Services and Nebula Developer Labs.
|
||||||
- Stunning and highly functional UI with multiple themes
|
- Stunning and highly functional UI with multiple themes
|
||||||
- XOR/b64 encoding all traffic
|
- XOR/b64 encoding all traffic
|
||||||
- Hides your IP from sites
|
- Hides your IP from sites
|
||||||
- [List of officially supported sites](https://github.com/NebulaServices/Nebula/blob/main/docs/officially-supported-sites.md)
|
- [List of officially supported sites](https://github.com/NebulaServices/Nebula/blob/dev/docs/officially-supported-sites.md)
|
||||||
- _limited_ mobile support
|
- _limited_ mobile support
|
||||||
- Stealth Mode (buffed `about:blank` cloaking)
|
- Stealth Mode (buffed `about:blank` cloaking)
|
||||||
- **NEW** Clickoff cloaking
|
- **NEW** Clickoff cloaking
|
||||||
|
|
@ -33,8 +33,6 @@ Table of contents
|
||||||
<br>
|
<br>
|
||||||
[](https://replit.com/github/NebulaServices/Nebula)
|
[](https://replit.com/github/NebulaServices/Nebula)
|
||||||
<br>
|
<br>
|
||||||
[](https://glitch.com/edit/#!/import/github/NebulaServices/Nebula)
|
|
||||||
<br>
|
|
||||||
[](https://cloud.ibm.com/devops/setup/deploy?repository=https://github.com/NebulaServices/Nebula)
|
[](https://cloud.ibm.com/devops/setup/deploy?repository=https://github.com/NebulaServices/Nebula)
|
||||||
<br>
|
<br>
|
||||||
[](https://console.aws.amazon.com/amplify/home#/deploy?repo=https://github.com/NebulaServices/Nebula)
|
[](https://console.aws.amazon.com/amplify/home#/deploy?repo=https://github.com/NebulaServices/Nebula)
|
||||||
|
|
@ -174,21 +172,21 @@ _Note: Server will need to run` cd Nebula && sudo nohup PORT=80 node . &` on reb
|
||||||
|
|
||||||
| **File** | Purpose | |
|
| **File** | Purpose | |
|
||||||
| -------------------------------- | -------------------------------------------------------------------------------------------------------- | --- |
|
| -------------------------------- | -------------------------------------------------------------------------------------------------------- | --- |
|
||||||
| `static/index.html` | The main frontend visuals for NebulaWEB. | |
|
| `src/index.html` | The main frontend visuals for NebulaWEB. | |
|
||||||
| `static/unv.html` | The verification-required frontend/visuals. | |
|
| `src/unv.html` | The verification-required frontend/visuals. | |
|
||||||
| `static/options/` | The frontend for Nebula's options, settings, and preferences. | |
|
| `src/options.html` | The frontend for Nebula's options, settings, and preferences. | |
|
||||||
| `static/resources/v.js` | Client verification system for the OTP system. | |
|
| `public/resources/v.js` | Client verification system for the OTP system. | |
|
||||||
| `static/resources/nebulamain.js` | All of the DOM/client code for NebulaWEB. Includes options, themeSystem, cloak, stealthengine, and more. | |
|
| `public/resources/nebulamain.js` | All of the DOM/client code for NebulaWEB. Includes options, themeSystem, cloak, stealthengine, and more. | |
|
||||||
| `app.js` | The backend server for Nebula. Contains Nodestatic, Bare, HTTP, and more. | |
|
| `app.js` | The backend server for Nebula. Contains Nodestatic, Bare, HTTP, and more. | |
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
- HTML, JS, CSS
|
- HTML, JS, CSS
|
||||||
- Partical.JS (Specifically v4, 5, 6.1 &< only)
|
- Partical.JS (Specifically v4, 5, 6.1 &< only)
|
||||||
- UV Backend Proxy
|
- Ultraviolet (proxy)
|
||||||
- Osana Backend Proxy
|
- Osana (proxy)
|
||||||
- TompHTTP Bare Server
|
- TompHTTP Bare Server Node
|
||||||
- node HTTP (No ExpressJS!)
|
- ExpressJS
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
|
@ -200,9 +198,9 @@ For support, email chloe@nebula.bio or join our discord: discord.gg/unblocker
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
- [UV (one of the back-end proxy we use)](https://github.com/titaniumnetwork-dev/Ultraviolet)
|
- [UV (one of the proxies use)](https://github.com/titaniumnetwork-dev/Ultraviolet)
|
||||||
- [Osana (one of the back-end proxy we use)](https://github.com/NebulaServices/Osana)
|
- [Osana (one of the proxies we use)](https://github.com/NebulaServices/Osana)
|
||||||
- [Bare Server](https://github.com/tomphttp/bare-server-node)
|
- [Bare Server Node](https://github.com/tomphttp/bare-server-node)
|
||||||
- [Partical.JS (v4, 5, 6.1 &< only)](https://github.com/VincentGarreau/particles.js)
|
- [Partical.JS (v4, 5, 6.1 &< only)](https://github.com/VincentGarreau/particles.js)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
|
||||||
308
app.js
308
app.js
|
|
@ -1,24 +1,59 @@
|
||||||
|
import express from "express";
|
||||||
|
import cookieParser from "cookie-parser";
|
||||||
|
import http from "node:http";
|
||||||
import createBareServer from "@tomphttp/bare-server-node";
|
import createBareServer from "@tomphttp/bare-server-node";
|
||||||
import http from "http";
|
import { uvPath } from "@titaniumnetwork-dev/ultraviolet";
|
||||||
import { createRequire } from "module";
|
import path from "node:path";
|
||||||
import { dirname, join } from "path";
|
import config from "./deployment.config.json" assert { type: "json" };
|
||||||
import serveStatic from "serve-static";
|
import sgMail from "@sendgrid/mail";
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
const config = require("./deployment.config.json");
|
|
||||||
import fs from "fs";
|
|
||||||
var base64data;
|
|
||||||
import sgTransport from "nodemailer-sendgrid-transport";
|
|
||||||
import nodemailer from "nodemailer";
|
import nodemailer from "nodemailer";
|
||||||
import fetch from "node-fetch";
|
import * as uuid from "uuid";
|
||||||
const options = {
|
import fs from "node:fs";
|
||||||
auth: {
|
import bcrypt from "bcrypt";
|
||||||
api_key: config.sendgrid_options.api_key
|
|
||||||
}
|
const PORT = process.env.PORT || 3000;
|
||||||
};
|
const __dirname = process.cwd();
|
||||||
const sendgridMailerAgent = nodemailer.createTransport(sgTransport(options));
|
const ACTIVE_CODES = new Set();
|
||||||
const smtpMailerAgent = nodemailer.createTransport(config.smtp_options);
|
let TOKENS = fs
|
||||||
function sendVerificationEmail(UUID, OTP) {
|
.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 = {
|
let email = {
|
||||||
to: "",
|
to: "",
|
||||||
from: "",
|
from: "",
|
||||||
|
|
@ -26,137 +61,149 @@ function sendVerificationEmail(UUID, OTP) {
|
||||||
text: `
|
text: `
|
||||||
####### ACCESS CODE (OTP) ${OTP} #######
|
####### ACCESS CODE (OTP) ${OTP} #######
|
||||||
####### DO NOT SHARE THIS CODE! #######
|
####### DO NOT SHARE THIS CODE! #######
|
||||||
(this message is automated)`,
|
(this message is automated)`
|
||||||
html: `
|
|
||||||
####### ACCESS CODE (OTP) ${OTP} #######
|
|
||||||
####### DO NOT SHARE THIS CODE! #######
|
|
||||||
(this message is automated)
|
|
||||||
`
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (config.sendgrid_verification == true) {
|
if (config.sendgrid_verification == true) {
|
||||||
|
sgMail.setApiKey(config.sendgrid_options.api_key);
|
||||||
|
|
||||||
email.to = config.sendgrid_options.to_email;
|
email.to = config.sendgrid_options.to_email;
|
||||||
email.from = config.sendgrid_options.sendFromEmail;
|
email.from = config.sendgrid_options.sendFromEmail;
|
||||||
sendgridMailerAgent.sendMail(email, (err, res) => {
|
try {
|
||||||
if (err) {
|
await sgMail.send(msg);
|
||||||
console.log(err);
|
} catch {
|
||||||
|
return res.status(504).end();
|
||||||
}
|
}
|
||||||
console.log(res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.smtp_verification == true) {
|
if (config.smtp_verification == true) {
|
||||||
|
const smtpMailerAgent = nodemailer.createTransport(config.smtp_options);
|
||||||
|
|
||||||
email.to = config.smtp_options.to_email;
|
email.to = config.smtp_options.to_email;
|
||||||
email.from = config.smtp_options.sendFromEmail;
|
email.from = config.smtp_options.sendFromEmail;
|
||||||
smtpMailerAgent.sendMail(email, (err, res) => {
|
try {
|
||||||
if (err) {
|
smtpMailerAgent.sendMail(email);
|
||||||
console.log(err);
|
} catch {
|
||||||
|
return res.status(504).end();
|
||||||
}
|
}
|
||||||
console.log(res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.discord_verification == true) {
|
if (config.discord_verification == true) {
|
||||||
fetch(config.webhook_url, {
|
try {
|
||||||
|
await fetch(config.webhook_url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
content: `Your NebulaWEB access code is ${OTP}`
|
content: `Your NebulaWEB access code is \`${OTP}\``
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
} catch {
|
||||||
|
return res.status(500).end();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function getNewCode() {
|
|
||||||
var seq = (Math.floor(Math.random() * 10000) + 10000).toString().substring(1);
|
|
||||||
if (seq == "0") {
|
|
||||||
getNewCode();
|
|
||||||
}
|
}
|
||||||
return seq;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
res.status(200).end();
|
||||||
const bareServer = createBareServer("/bare/", {
|
} else {
|
||||||
logErrors: false,
|
res.status(404).end();
|
||||||
localAddress: undefined
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const serve = serveStatic(
|
function generateCode() {
|
||||||
join(dirname(fileURLToPath(import.meta.url)), "static/"),
|
const code = Math.floor(Math.random() * 1000000);
|
||||||
{
|
return code.toString().padStart(6, "0");
|
||||||
fallthrough: false,
|
}
|
||||||
maxAge: 5 * 60 * 1000
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const server = http.createServer();
|
app.post("/validate-otp", (req, res) => {
|
||||||
|
|
||||||
server.on("request", (request, response) => {
|
|
||||||
try {
|
|
||||||
if (bareServer.shouldRoute(request)) {
|
|
||||||
bareServer.routeRequest(request, response);
|
|
||||||
} else {
|
|
||||||
let base64data;
|
|
||||||
const url = request.url;
|
|
||||||
if (url.startsWith("/sendNewCode")) {
|
|
||||||
const OTP = getNewCode();
|
|
||||||
fs.writeFile("./memory.txt", OTP, function (err) {
|
|
||||||
if (err) return console.log(err);
|
|
||||||
console.log(`Wrote OTP code to temp file`);
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.readFile("./memory.txt", "utf8", (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
sendVerificationEmail("10", data);
|
|
||||||
let buff = new Buffer(data);
|
|
||||||
base64data = buff.toString("base64");
|
|
||||||
console.log("302");
|
|
||||||
response.writeHead(302, {
|
|
||||||
location: "/unv.html?c=" + base64data
|
|
||||||
});
|
|
||||||
response.end();
|
|
||||||
});
|
|
||||||
} else if (url.startsWith("/verification")) {
|
|
||||||
var body;
|
|
||||||
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
|
||||||
) {
|
) {
|
||||||
const body = "true";
|
const OTP = req.body.otp;
|
||||||
response.writeHead(200, {
|
|
||||||
"Content-Length": Buffer.byteLength(body),
|
if (ACTIVE_CODES.has(OTP)) {
|
||||||
"Content-Type": "text/plain"
|
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}`
|
||||||
});
|
});
|
||||||
response.end(body);
|
|
||||||
} else {
|
} else {
|
||||||
const body = "false";
|
res.status(401).json({
|
||||||
response.writeHead(200, {
|
success: false
|
||||||
"Content-Length": Buffer.byteLength(body),
|
|
||||||
"Content-Type": "text/plain"
|
|
||||||
});
|
});
|
||||||
response.end(body);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
serve(request, response, (err) => {
|
res.status(404).end();
|
||||||
response.writeHead(err?.statusCode || 500, null, {
|
|
||||||
"Content-Type": "text/plain"
|
|
||||||
});
|
|
||||||
response.end(err?.stack);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
response.writeHead(500, "Internal Server Error", {
|
|
||||||
"Content-Type": "text/plain"
|
|
||||||
});
|
|
||||||
response.end(e.stack);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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) => {
|
server.on("upgrade", (req, socket, head) => {
|
||||||
if (bareServer.shouldRoute(req)) {
|
if (bareServer.shouldRoute(req)) {
|
||||||
bareServer.routeUpgrade(req, socket, head);
|
bareServer.routeUpgrade(req, socket, head);
|
||||||
|
|
@ -165,14 +212,31 @@ server.on("upgrade", (req, socket, head) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(PORT);
|
server.on("listening", () => {
|
||||||
|
console.log(`Server running at http://localhost:${PORT}/.`);
|
||||||
|
});
|
||||||
|
|
||||||
if (process.env.UNSAFE_CONTINUE)
|
server.listen({
|
||||||
process.on("uncaughtException", (err, origin) => {
|
port: PORT
|
||||||
console.error(`Critical error (${origin}):`);
|
});
|
||||||
console.error(err);
|
|
||||||
console.error("UNSAFELY CONTINUING EXECUTION");
|
|
||||||
console.error();
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Server running at http://localhost:${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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
5003
|
|
||||||
3815
package-lock.json
generated
3815
package-lock.json
generated
File diff suppressed because it is too large
Load diff
13
package.json
13
package.json
|
|
@ -13,11 +13,14 @@
|
||||||
"author": "Nebula Services",
|
"author": "Nebula Services",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tomphttp/bare-server-node": "^1.2.2",
|
"@sendgrid/mail": "^7.7.0",
|
||||||
"node-fetch": "^3.3.0",
|
"@titaniumnetwork-dev/ultraviolet": "^1.0.8-beta",
|
||||||
"nodemailer": "^6.8.0",
|
"@tomphttp/bare-server-node": "^1.2.3",
|
||||||
"nodemailer-sendgrid-transport": "^0.2.0",
|
"bcrypt": "^5.1.0",
|
||||||
"serve-static": "^1.15.0"
|
"cookie-parser": "^1.4.6",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"nodemailer": "^6.9.1",
|
||||||
|
"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");
|
||||||
41
public/resources/v.js
Normal file
41
public/resources/v.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
const generateOTP = document.getElementById("generate-otp");
|
||||||
|
|
||||||
|
generateOTP.onclick = () => {
|
||||||
|
fetch("/generate-otp", { method: "PATCH" });
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateOTP = document.getElementById("validate-otp");
|
||||||
|
|
||||||
|
validateOTP.onclick = () => {
|
||||||
|
fetch("/validate-otp", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
otp: document.getElementById("otp").value
|
||||||
|
})
|
||||||
|
}).then((response) => {
|
||||||
|
return response.json();
|
||||||
|
}).then((data) => {
|
||||||
|
if (data.success) {
|
||||||
|
setCookie("validation", data.validation, 30);
|
||||||
|
location.href = "/";
|
||||||
|
} else {
|
||||||
|
alert("Invalid OTP.");
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
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=/";
|
||||||
|
}
|
||||||
10
public/uv/uv.config.js
Normal file
10
public/uv/uv.config.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
self.__uv$config = {
|
||||||
|
prefix: "/service/go/",
|
||||||
|
bare: "/bare/",
|
||||||
|
encodeUrl: Ultraviolet.codec.xor.encode,
|
||||||
|
decodeUrl: Ultraviolet.codec.xor.decode,
|
||||||
|
handler: "/uv/uv.handler.js",
|
||||||
|
bundle: "/uv/uv.bundle.js",
|
||||||
|
config: "/uv/uv.config.js",
|
||||||
|
sw: "/uv/uv.sw.js"
|
||||||
|
};
|
||||||
306
src/options.html
Normal file
306
src/options.html
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -23,7 +23,7 @@
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
id="send-code"
|
id="generate-otp"
|
||||||
style="
|
style="
|
||||||
background: #eb6f92;
|
background: #eb6f92;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<input
|
<input
|
||||||
id="codeBox"
|
id="otp"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter the code sent to your email"
|
placeholder="Enter the code sent to your email"
|
||||||
style="
|
style="
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<button
|
<button
|
||||||
id="checkCode"
|
id="validate-otp"
|
||||||
style="
|
style="
|
||||||
background: #eb6f92;
|
background: #eb6f92;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,11 +0,0 @@
|
||||||
self.__uv$config = {
|
|
||||||
prefix: '/service/go/',
|
|
||||||
bare: '/bare/',
|
|
||||||
encodeUrl: Ultraviolet.codec.xor.encode,
|
|
||||||
decodeUrl: Ultraviolet.codec.xor.decode,
|
|
||||||
handler: '/uv/uv.handler.js',
|
|
||||||
bundle: '/uv/uv.bundle.js',
|
|
||||||
config: '/uv/uv.config.js',
|
|
||||||
sw: '/uv/uv.sw.js',
|
|
||||||
};
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,789 +0,0 @@
|
||||||
importScripts("/uv/uv.bundle.js");
|
|
||||||
importScripts("/uv/uv.config.js");
|
|
||||||
|
|
||||||
class UVServiceWorker extends EventEmitter {
|
|
||||||
constructor(config = __uv$config) {
|
|
||||||
super();
|
|
||||||
if (!config.bare) config.bare = '/bare/';
|
|
||||||
this.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location));
|
|
||||||
this.headers = {
|
|
||||||
csp: [
|
|
||||||
'cross-origin-embedder-policy',
|
|
||||||
'cross-origin-opener-policy',
|
|
||||||
'cross-origin-resource-policy',
|
|
||||||
'content-security-policy',
|
|
||||||
'content-security-policy-report-only',
|
|
||||||
'expect-ct',
|
|
||||||
'feature-policy',
|
|
||||||
'origin-isolation',
|
|
||||||
'strict-transport-security',
|
|
||||||
'upgrade-insecure-requests',
|
|
||||||
'x-content-type-options',
|
|
||||||
'x-download-options',
|
|
||||||
'x-frame-options',
|
|
||||||
'x-permitted-cross-domain-policies',
|
|
||||||
'x-powered-by',
|
|
||||||
'x-xss-protection',
|
|
||||||
],
|
|
||||||
forward: [
|
|
||||||
'accept-encoding',
|
|
||||||
'connection',
|
|
||||||
'content-length',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
this.method = {
|
|
||||||
empty: [
|
|
||||||
'GET',
|
|
||||||
'HEAD'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
this.statusCode = {
|
|
||||||
empty: [
|
|
||||||
204,
|
|
||||||
304,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
this.config = config;
|
|
||||||
this.browser = Ultraviolet.Bowser.getParser(self.navigator.userAgent).getBrowserName();
|
|
||||||
|
|
||||||
if (this.browser === 'Firefox') {
|
|
||||||
this.headers.forward.push('user-agent');
|
|
||||||
this.headers.forward.push('content-type');
|
|
||||||
};
|
|
||||||
};
|
|
||||||
async fetch({ request }) {
|
|
||||||
if (!request.url.startsWith(location.origin + (this.config.prefix || '/service/'))) {
|
|
||||||
return fetch(request);
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
|
|
||||||
const ultraviolet = new Ultraviolet(this.config);
|
|
||||||
|
|
||||||
if (typeof this.config.construct === 'function') {
|
|
||||||
this.config.construct(ultraviolet, 'service');
|
|
||||||
};
|
|
||||||
|
|
||||||
const db = await ultraviolet.cookie.db();
|
|
||||||
|
|
||||||
ultraviolet.meta.origin = location.origin;
|
|
||||||
ultraviolet.meta.base = ultraviolet.meta.url = new URL(ultraviolet.sourceUrl(request.url));
|
|
||||||
|
|
||||||
const requestCtx = new RequestContext(
|
|
||||||
request,
|
|
||||||
this,
|
|
||||||
ultraviolet,
|
|
||||||
!this.method.empty.includes(request.method.toUpperCase()) ? await request.blob() : null
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ultraviolet.meta.url.protocol === 'blob:') {
|
|
||||||
requestCtx.blob = true;
|
|
||||||
requestCtx.base = requestCtx.url = new URL(requestCtx.url.pathname);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (request.referrer && request.referrer.startsWith(location.origin)) {
|
|
||||||
const referer = new URL(ultraviolet.sourceUrl(request.referrer));
|
|
||||||
|
|
||||||
if (requestCtx.headers.origin || ultraviolet.meta.url.origin !== referer.origin && request.mode === 'cors') {
|
|
||||||
requestCtx.headers.origin = referer.origin;
|
|
||||||
};
|
|
||||||
|
|
||||||
requestCtx.headers.referer = referer.href;
|
|
||||||
};
|
|
||||||
|
|
||||||
const cookies = await ultraviolet.cookie.getCookies(db) || [];
|
|
||||||
const cookieStr = ultraviolet.cookie.serialize(cookies, ultraviolet.meta, false);
|
|
||||||
|
|
||||||
if (this.browser === 'Firefox' && !(request.destination === 'iframe' || request.destination === 'document')) {
|
|
||||||
requestCtx.forward.shift();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (cookieStr) requestCtx.headers.cookie = cookieStr;
|
|
||||||
requestCtx.headers.Host = requestCtx.url.host;
|
|
||||||
|
|
||||||
|
|
||||||
const reqEvent = new HookEvent(requestCtx, null, null);
|
|
||||||
this.emit('request', reqEvent);
|
|
||||||
|
|
||||||
if (reqEvent.intercepted) return reqEvent.returnValue;
|
|
||||||
|
|
||||||
const response = await fetch(requestCtx.send);
|
|
||||||
|
|
||||||
if (response.status === 500) {
|
|
||||||
return Promise.reject('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const responseCtx = new ResponseContext(requestCtx, response, this);
|
|
||||||
const resEvent = new HookEvent(responseCtx, null, null);
|
|
||||||
|
|
||||||
this.emit('beforemod', resEvent);
|
|
||||||
if (resEvent.intercepted) return resEvent.returnValue;
|
|
||||||
|
|
||||||
for (const name of this.headers.csp) {
|
|
||||||
if (responseCtx.headers[name]) delete responseCtx.headers[name];
|
|
||||||
};
|
|
||||||
|
|
||||||
if (responseCtx.headers.location) {
|
|
||||||
responseCtx.headers.location = ultraviolet.rewriteUrl(responseCtx.headers.location);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (responseCtx.headers['set-cookie']) {
|
|
||||||
Promise.resolve(ultraviolet.cookie.setCookies(responseCtx.headers['set-cookie'], db, ultraviolet.meta)).then(() => {
|
|
||||||
self.clients.matchAll().then(function (clients){
|
|
||||||
clients.forEach(function(client){
|
|
||||||
client.postMessage({
|
|
||||||
msg: 'updateCookies',
|
|
||||||
url: ultraviolet.meta.url.href,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
delete responseCtx.headers['set-cookie'];
|
|
||||||
};
|
|
||||||
|
|
||||||
if (responseCtx.body) {
|
|
||||||
switch(request.destination) {
|
|
||||||
case 'script':
|
|
||||||
case 'worker':
|
|
||||||
responseCtx.body = `if (!self.__uv && self.importScripts) importScripts('${__uv$config.bundle}', '${__uv$config.config}', '${__uv$config.handler}');\n`;
|
|
||||||
responseCtx.body += ultraviolet.js.rewrite(
|
|
||||||
await response.text()
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'style':
|
|
||||||
responseCtx.body = ultraviolet.rewriteCSS(
|
|
||||||
await response.text()
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'iframe':
|
|
||||||
case 'document':
|
|
||||||
if (isHtml(ultraviolet.meta.url, (responseCtx.headers['content-type'] || ''))) {
|
|
||||||
responseCtx.body = ultraviolet.rewriteHtml(
|
|
||||||
await response.text(),
|
|
||||||
{
|
|
||||||
document: true ,
|
|
||||||
injectHead: ultraviolet.createHtmlInject(
|
|
||||||
this.config.handler,
|
|
||||||
this.config.bundle,
|
|
||||||
this.config.config,
|
|
||||||
ultraviolet.cookie.serialize(cookies, ultraviolet.meta, true),
|
|
||||||
request.referrer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if (requestCtx.headers.accept === 'text/event-stream') {
|
|
||||||
responseCtx.headers['content-type'] = 'text/event-stream';
|
|
||||||
};
|
|
||||||
|
|
||||||
this.emit('response', resEvent);
|
|
||||||
if (resEvent.intercepted) return resEvent.returnValue;
|
|
||||||
|
|
||||||
return new Response(responseCtx.body, {
|
|
||||||
headers: responseCtx.headers,
|
|
||||||
status: responseCtx.status,
|
|
||||||
statusText: responseCtx.statusText,
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
return new Response(err.toString(), {
|
|
||||||
status: 500,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
getBarerResponse(response) {
|
|
||||||
const headers = {};
|
|
||||||
const raw = JSON.parse(response.headers.get('x-bare-headers'));
|
|
||||||
|
|
||||||
for (const key in raw) {
|
|
||||||
headers[key.toLowerCase()] = raw[key];
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
headers,
|
|
||||||
status: +response.headers.get('x-bare-status'),
|
|
||||||
statusText: response.headers.get('x-bare-status-text'),
|
|
||||||
body: !this.statusCode.empty.includes(+response.headers.get('x-bare-status')) ? response.body : null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
get address() {
|
|
||||||
return this.addresses[Math.floor(Math.random() * this.addresses.length)];
|
|
||||||
};
|
|
||||||
static Ultraviolet = Ultraviolet;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.UVServiceWorker = UVServiceWorker;
|
|
||||||
|
|
||||||
|
|
||||||
class ResponseContext {
|
|
||||||
constructor(request, response, worker) {
|
|
||||||
const { headers, status, statusText, body } = !request.blob ? worker.getBarerResponse(response) : {
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText,
|
|
||||||
headers: Object.fromEntries([...response.headers.entries()]),
|
|
||||||
body: response.body,
|
|
||||||
};
|
|
||||||
this.request = request;
|
|
||||||
this.raw = response;
|
|
||||||
this.ultraviolet = request.ultraviolet;
|
|
||||||
this.headers = headers;
|
|
||||||
this.status = status;
|
|
||||||
this.statusText = statusText;
|
|
||||||
this.body = body;
|
|
||||||
};
|
|
||||||
get url() {
|
|
||||||
return this.request.url;
|
|
||||||
}
|
|
||||||
get base() {
|
|
||||||
return this.request.base;
|
|
||||||
};
|
|
||||||
set base(val) {
|
|
||||||
this.request.base = val;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class RequestContext {
|
|
||||||
constructor(request, worker, ultraviolet, body = null) {
|
|
||||||
this.ultraviolet = ultraviolet;
|
|
||||||
this.request = request;
|
|
||||||
this.headers = Object.fromEntries([...request.headers.entries()]);
|
|
||||||
this.method = request.method;
|
|
||||||
this.forward = [...worker.headers.forward];
|
|
||||||
this.address = worker.address;
|
|
||||||
this.body = body || null;
|
|
||||||
this.redirect = request.redirect;
|
|
||||||
this.credentials = 'omit';
|
|
||||||
this.mode = request.mode === 'cors' ? request.mode : 'same-origin';
|
|
||||||
this.blob = false;
|
|
||||||
};
|
|
||||||
get send() {
|
|
||||||
return new Request((!this.blob ? this.address.href + 'v1/' : 'blob:' + location.origin + this.url.pathname), {
|
|
||||||
method: this.method,
|
|
||||||
headers: {
|
|
||||||
'x-bare-protocol': this.url.protocol,
|
|
||||||
'x-bare-host': this.url.hostname,
|
|
||||||
'x-bare-path': this.url.pathname + this.url.search,
|
|
||||||
'x-bare-port': this.url.port || (this.url.protocol === 'https:' ? '443' : '80'),
|
|
||||||
'x-bare-headers': JSON.stringify(this.headers),
|
|
||||||
'x-bare-forward-headers': JSON.stringify(this.forward),
|
|
||||||
},
|
|
||||||
redirect: this.redirect,
|
|
||||||
credentials: this.credentials,
|
|
||||||
mode: location.origin !== this.address.origin ? 'cors' : this.mode,
|
|
||||||
body: this.body
|
|
||||||
});
|
|
||||||
};
|
|
||||||
get url() {
|
|
||||||
return this.ultraviolet.meta.url;
|
|
||||||
};
|
|
||||||
set url(val) {
|
|
||||||
this.ultraviolet.meta.url = val;
|
|
||||||
};
|
|
||||||
get base() {
|
|
||||||
return this.ultraviolet.meta.base;
|
|
||||||
};
|
|
||||||
set base(val) {
|
|
||||||
this.ultraviolet.meta.base = val;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHtml(url, contentType = '') {
|
|
||||||
return (Ultraviolet.mime.contentType((contentType || url.pathname)) || 'text/html').split(';')[0] === 'text/html';
|
|
||||||
};
|
|
||||||
|
|
||||||
class HookEvent {
|
|
||||||
#intercepted;
|
|
||||||
#returnValue;
|
|
||||||
constructor(data = {}, target = null, that = null) {
|
|
||||||
this.#intercepted = false;
|
|
||||||
this.#returnValue = null;
|
|
||||||
this.data = data;
|
|
||||||
this.target = target;
|
|
||||||
this.that = that;
|
|
||||||
};
|
|
||||||
get intercepted() {
|
|
||||||
return this.#intercepted;
|
|
||||||
};
|
|
||||||
get returnValue() {
|
|
||||||
return this.#returnValue;
|
|
||||||
};
|
|
||||||
respondWith(input) {
|
|
||||||
this.#returnValue = input;
|
|
||||||
this.#intercepted = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var R = typeof Reflect === 'object' ? Reflect : null
|
|
||||||
var ReflectApply = R && typeof R.apply === 'function'
|
|
||||||
? R.apply
|
|
||||||
: function ReflectApply(target, receiver, args) {
|
|
||||||
return Function.prototype.apply.call(target, receiver, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ReflectOwnKeys
|
|
||||||
if (R && typeof R.ownKeys === 'function') {
|
|
||||||
ReflectOwnKeys = R.ownKeys
|
|
||||||
} else if (Object.getOwnPropertySymbols) {
|
|
||||||
ReflectOwnKeys = function ReflectOwnKeys(target) {
|
|
||||||
return Object.getOwnPropertyNames(target)
|
|
||||||
.concat(Object.getOwnPropertySymbols(target));
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
ReflectOwnKeys = function ReflectOwnKeys(target) {
|
|
||||||
return Object.getOwnPropertyNames(target);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function ProcessEmitWarning(warning) {
|
|
||||||
if (console && console.warn) console.warn(warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
|
|
||||||
return value !== value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function EventEmitter() {
|
|
||||||
EventEmitter.init.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backwards-compat with node 0.10.x
|
|
||||||
EventEmitter.EventEmitter = EventEmitter;
|
|
||||||
|
|
||||||
EventEmitter.prototype._events = undefined;
|
|
||||||
EventEmitter.prototype._eventsCount = 0;
|
|
||||||
EventEmitter.prototype._maxListeners = undefined;
|
|
||||||
|
|
||||||
// By default EventEmitters will print a warning if more than 10 listeners are
|
|
||||||
// added to it. This is a useful default which helps finding memory leaks.
|
|
||||||
var defaultMaxListeners = 10;
|
|
||||||
|
|
||||||
function checkListener(listener) {
|
|
||||||
if (typeof listener !== 'function') {
|
|
||||||
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
|
|
||||||
enumerable: true,
|
|
||||||
get: function() {
|
|
||||||
return defaultMaxListeners;
|
|
||||||
},
|
|
||||||
set: function(arg) {
|
|
||||||
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
|
|
||||||
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
|
|
||||||
}
|
|
||||||
defaultMaxListeners = arg;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
EventEmitter.init = function() {
|
|
||||||
|
|
||||||
if (this._events === undefined ||
|
|
||||||
this._events === Object.getPrototypeOf(this)._events) {
|
|
||||||
this._events = Object.create(null);
|
|
||||||
this._eventsCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._maxListeners = this._maxListeners || undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Obviously not all Emitters should be limited to 10. This function allows
|
|
||||||
// that to be increased. Set to zero for unlimited.
|
|
||||||
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
|
|
||||||
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
|
|
||||||
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
|
|
||||||
}
|
|
||||||
this._maxListeners = n;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
function _getMaxListeners(that) {
|
|
||||||
if (that._maxListeners === undefined)
|
|
||||||
return EventEmitter.defaultMaxListeners;
|
|
||||||
return that._maxListeners;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
|
|
||||||
return _getMaxListeners(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.emit = function emit(type) {
|
|
||||||
var args = [];
|
|
||||||
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
|
|
||||||
var doError = (type === 'error');
|
|
||||||
|
|
||||||
var events = this._events;
|
|
||||||
if (events !== undefined)
|
|
||||||
doError = (doError && events.error === undefined);
|
|
||||||
else if (!doError)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If there is no 'error' event listener then throw.
|
|
||||||
if (doError) {
|
|
||||||
var er;
|
|
||||||
if (args.length > 0)
|
|
||||||
er = args[0];
|
|
||||||
if (er instanceof Error) {
|
|
||||||
// Note: The comments on the `throw` lines are intentional, they show
|
|
||||||
// up in Node's output if this results in an unhandled exception.
|
|
||||||
throw er; // Unhandled 'error' event
|
|
||||||
}
|
|
||||||
// At least give some kind of context to the user
|
|
||||||
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
|
|
||||||
err.context = er;
|
|
||||||
throw err; // Unhandled 'error' event
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler = events[type];
|
|
||||||
|
|
||||||
if (handler === undefined)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (typeof handler === 'function') {
|
|
||||||
ReflectApply(handler, this, args);
|
|
||||||
} else {
|
|
||||||
var len = handler.length;
|
|
||||||
var listeners = arrayClone(handler, len);
|
|
||||||
for (var i = 0; i < len; ++i)
|
|
||||||
ReflectApply(listeners[i], this, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
function _addListener(target, type, listener, prepend) {
|
|
||||||
var m;
|
|
||||||
var events;
|
|
||||||
var existing;
|
|
||||||
|
|
||||||
checkListener(listener);
|
|
||||||
|
|
||||||
events = target._events;
|
|
||||||
if (events === undefined) {
|
|
||||||
events = target._events = Object.create(null);
|
|
||||||
target._eventsCount = 0;
|
|
||||||
} else {
|
|
||||||
// To avoid recursion in the case that type === "newListener"! Before
|
|
||||||
// adding it to the listeners, first emit "newListener".
|
|
||||||
if (events.newListener !== undefined) {
|
|
||||||
target.emit('newListener', type,
|
|
||||||
listener.listener ? listener.listener : listener);
|
|
||||||
|
|
||||||
// Re-assign `events` because a newListener handler could have caused the
|
|
||||||
// this._events to be assigned to a new object
|
|
||||||
events = target._events;
|
|
||||||
}
|
|
||||||
existing = events[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existing === undefined) {
|
|
||||||
// Optimize the case of one listener. Don't need the extra array object.
|
|
||||||
existing = events[type] = listener;
|
|
||||||
++target._eventsCount;
|
|
||||||
} else {
|
|
||||||
if (typeof existing === 'function') {
|
|
||||||
// Adding the second element, need to change to array.
|
|
||||||
existing = events[type] =
|
|
||||||
prepend ? [listener, existing] : [existing, listener];
|
|
||||||
// If we've already got an array, just append.
|
|
||||||
} else if (prepend) {
|
|
||||||
existing.unshift(listener);
|
|
||||||
} else {
|
|
||||||
existing.push(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for listener leak
|
|
||||||
m = _getMaxListeners(target);
|
|
||||||
if (m > 0 && existing.length > m && !existing.warned) {
|
|
||||||
existing.warned = true;
|
|
||||||
// No error code for this since it is a Warning
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
var w = new Error('Possible EventEmitter memory leak detected. ' +
|
|
||||||
existing.length + ' ' + String(type) + ' listeners ' +
|
|
||||||
'added. Use emitter.setMaxListeners() to ' +
|
|
||||||
'increase limit');
|
|
||||||
w.name = 'MaxListenersExceededWarning';
|
|
||||||
w.emitter = target;
|
|
||||||
w.type = type;
|
|
||||||
w.count = existing.length;
|
|
||||||
ProcessEmitWarning(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.addListener = function addListener(type, listener) {
|
|
||||||
return _addListener(this, type, listener, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
|
||||||
|
|
||||||
EventEmitter.prototype.prependListener =
|
|
||||||
function prependListener(type, listener) {
|
|
||||||
return _addListener(this, type, listener, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
function onceWrapper() {
|
|
||||||
if (!this.fired) {
|
|
||||||
this.target.removeListener(this.type, this.wrapFn);
|
|
||||||
this.fired = true;
|
|
||||||
if (arguments.length === 0)
|
|
||||||
return this.listener.call(this.target);
|
|
||||||
return this.listener.apply(this.target, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _onceWrap(target, type, listener) {
|
|
||||||
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
|
|
||||||
var wrapped = onceWrapper.bind(state);
|
|
||||||
wrapped.listener = listener;
|
|
||||||
state.wrapFn = wrapped;
|
|
||||||
return wrapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.once = function once(type, listener) {
|
|
||||||
checkListener(listener);
|
|
||||||
this.on(type, _onceWrap(this, type, listener));
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.prependOnceListener =
|
|
||||||
function prependOnceListener(type, listener) {
|
|
||||||
checkListener(listener);
|
|
||||||
this.prependListener(type, _onceWrap(this, type, listener));
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Emits a 'removeListener' event if and only if the listener was removed.
|
|
||||||
EventEmitter.prototype.removeListener =
|
|
||||||
function removeListener(type, listener) {
|
|
||||||
var list, events, position, i, originalListener;
|
|
||||||
|
|
||||||
checkListener(listener);
|
|
||||||
|
|
||||||
events = this._events;
|
|
||||||
if (events === undefined)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
list = events[type];
|
|
||||||
if (list === undefined)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
if (list === listener || list.listener === listener) {
|
|
||||||
if (--this._eventsCount === 0)
|
|
||||||
this._events = Object.create(null);
|
|
||||||
else {
|
|
||||||
delete events[type];
|
|
||||||
if (events.removeListener)
|
|
||||||
this.emit('removeListener', type, list.listener || listener);
|
|
||||||
}
|
|
||||||
} else if (typeof list !== 'function') {
|
|
||||||
position = -1;
|
|
||||||
|
|
||||||
for (i = list.length - 1; i >= 0; i--) {
|
|
||||||
if (list[i] === listener || list[i].listener === listener) {
|
|
||||||
originalListener = list[i].listener;
|
|
||||||
position = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position < 0)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
if (position === 0)
|
|
||||||
list.shift();
|
|
||||||
else {
|
|
||||||
spliceOne(list, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.length === 1)
|
|
||||||
events[type] = list[0];
|
|
||||||
|
|
||||||
if (events.removeListener !== undefined)
|
|
||||||
this.emit('removeListener', type, originalListener || listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
|
||||||
|
|
||||||
EventEmitter.prototype.removeAllListeners =
|
|
||||||
function removeAllListeners(type) {
|
|
||||||
var listeners, events, i;
|
|
||||||
|
|
||||||
events = this._events;
|
|
||||||
if (events === undefined)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
// not listening for removeListener, no need to emit
|
|
||||||
if (events.removeListener === undefined) {
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
this._events = Object.create(null);
|
|
||||||
this._eventsCount = 0;
|
|
||||||
} else if (events[type] !== undefined) {
|
|
||||||
if (--this._eventsCount === 0)
|
|
||||||
this._events = Object.create(null);
|
|
||||||
else
|
|
||||||
delete events[type];
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// emit removeListener for all listeners on all events
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
var keys = Object.keys(events);
|
|
||||||
var key;
|
|
||||||
for (i = 0; i < keys.length; ++i) {
|
|
||||||
key = keys[i];
|
|
||||||
if (key === 'removeListener') continue;
|
|
||||||
this.removeAllListeners(key);
|
|
||||||
}
|
|
||||||
this.removeAllListeners('removeListener');
|
|
||||||
this._events = Object.create(null);
|
|
||||||
this._eventsCount = 0;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners = events[type];
|
|
||||||
|
|
||||||
if (typeof listeners === 'function') {
|
|
||||||
this.removeListener(type, listeners);
|
|
||||||
} else if (listeners !== undefined) {
|
|
||||||
// LIFO order
|
|
||||||
for (i = listeners.length - 1; i >= 0; i--) {
|
|
||||||
this.removeListener(type, listeners[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
function _listeners(target, type, unwrap) {
|
|
||||||
var events = target._events;
|
|
||||||
|
|
||||||
if (events === undefined)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
var evlistener = events[type];
|
|
||||||
if (evlistener === undefined)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
if (typeof evlistener === 'function')
|
|
||||||
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
|
|
||||||
|
|
||||||
return unwrap ?
|
|
||||||
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.listeners = function listeners(type) {
|
|
||||||
return _listeners(this, type, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.rawListeners = function rawListeners(type) {
|
|
||||||
return _listeners(this, type, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.listenerCount = function(emitter, type) {
|
|
||||||
if (typeof emitter.listenerCount === 'function') {
|
|
||||||
return emitter.listenerCount(type);
|
|
||||||
} else {
|
|
||||||
return listenerCount.call(emitter, type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
EventEmitter.prototype.listenerCount = listenerCount;
|
|
||||||
function listenerCount(type) {
|
|
||||||
var events = this._events;
|
|
||||||
|
|
||||||
if (events !== undefined) {
|
|
||||||
var evlistener = events[type];
|
|
||||||
|
|
||||||
if (typeof evlistener === 'function') {
|
|
||||||
return 1;
|
|
||||||
} else if (evlistener !== undefined) {
|
|
||||||
return evlistener.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventEmitter.prototype.eventNames = function eventNames() {
|
|
||||||
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
|
|
||||||
};
|
|
||||||
|
|
||||||
function arrayClone(arr, n) {
|
|
||||||
var copy = new Array(n);
|
|
||||||
for (var i = 0; i < n; ++i)
|
|
||||||
copy[i] = arr[i];
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
function spliceOne(list, index) {
|
|
||||||
for (; index + 1 < list.length; index++)
|
|
||||||
list[index] = list[index + 1];
|
|
||||||
list.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
function unwrapListeners(arr) {
|
|
||||||
var ret = new Array(arr.length);
|
|
||||||
for (var i = 0; i < ret.length; ++i) {
|
|
||||||
ret[i] = arr[i].listener || arr[i];
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function once(emitter, name) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
function errorListener(err) {
|
|
||||||
emitter.removeListener(name, resolver);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolver() {
|
|
||||||
if (typeof emitter.removeListener === 'function') {
|
|
||||||
emitter.removeListener('error', errorListener);
|
|
||||||
}
|
|
||||||
resolve([].slice.call(arguments));
|
|
||||||
};
|
|
||||||
|
|
||||||
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
|
|
||||||
if (name !== 'error') {
|
|
||||||
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
|
|
||||||
if (typeof emitter.on === 'function') {
|
|
||||||
eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
|
|
||||||
if (typeof emitter.on === 'function') {
|
|
||||||
if (flags.once) {
|
|
||||||
emitter.once(name, listener);
|
|
||||||
} else {
|
|
||||||
emitter.on(name, listener);
|
|
||||||
}
|
|
||||||
} else if (typeof emitter.addEventListener === 'function') {
|
|
||||||
// EventTarget does not have `error` event semantics like Node
|
|
||||||
// EventEmitters, we do not listen for `error` events here.
|
|
||||||
emitter.addEventListener(name, function wrapListener(arg) {
|
|
||||||
// IE does not have builtin `{ once: true }` support so we
|
|
||||||
// have to do it manually.
|
|
||||||
if (flags.once) {
|
|
||||||
emitter.removeEventListener(name, wrapListener);
|
|
||||||
}
|
|
||||||
listener(arg);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue