mirror of
https://github.com/QuiteAFancyEmerald/Holy-Unblocker.git
synced 2025-05-12 11:30:01 -04:00
Formatting and workflow organization changes
This commit is contained in:
parent
461c89212f
commit
8c9f79d3e7
13 changed files with 1010 additions and 979 deletions
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Start server
|
- name: Start server
|
||||||
run: npm run manual-start
|
run: npm run workflow-test
|
||||||
|
|
||||||
- name: Test server response
|
- name: Test server response
|
||||||
run: npm test
|
run: npm test
|
||||||
|
@ -53,7 +53,7 @@ jobs:
|
||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Start server
|
- name: Start server
|
||||||
run: npm run manual-start
|
run: npm run workflow-test
|
||||||
|
|
||||||
- name: Test server response
|
- name: Test server response
|
||||||
run: npm test
|
run: npm test
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
/lib
|
/lib
|
||||||
|
/views/assets/js/*.js
|
|
@ -12,7 +12,8 @@
|
||||||
"manual-start": "node run-command.mjs start",
|
"manual-start": "node run-command.mjs start",
|
||||||
"kill": "node run-command.mjs stop kill",
|
"kill": "node run-command.mjs stop kill",
|
||||||
"build": "node run-command.mjs build && cd lib/rammerhead && npm install && npm run build",
|
"build": "node run-command.mjs build && cd lib/rammerhead && npm install && npm run build",
|
||||||
"proxy-validator": "node proxyServiceValidator.js"
|
"proxy-validator": "node proxyServiceValidator.js",
|
||||||
|
"workflow-test": "node run-command.mjs workflow"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"proxy",
|
"proxy",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// This file is solely used for the automatically run GitHub job, which checks to
|
// This file is solely used for the automatically run GitHub job, which checks to
|
||||||
// see if all HU LTS code is working properly (at least on an Ubuntu machine).
|
// see if all HU LTS code is working properly (at least on an Ubuntu machine).
|
||||||
|
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const puppeteer = require('puppeteer');
|
const puppeteer = require('puppeteer');
|
||||||
|
@ -122,21 +122,21 @@ const testCommonJSOnPage = async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Locate the omnibox element on the Rammerhead page.
|
// Locate the omnibox element on the Rammerhead page.
|
||||||
let omnibox = document.getElementById('pr-rh');
|
let omnibox = document.getElementById('pr-rh');
|
||||||
omnibox = omnibox && omnibox.querySelector('input[type=text]');
|
omnibox = omnibox && omnibox.querySelector('input[type=text]');
|
||||||
|
|
||||||
if (omnibox) {
|
if (omnibox) {
|
||||||
try {
|
try {
|
||||||
// Send an artificial input to the omnibox. The omnibox will create
|
// Send an artificial input to the omnibox. The omnibox will create
|
||||||
// a proxy URL and leave it as the input value in response.
|
// a proxy URL and leave it as the input value in response.
|
||||||
const urlPath = 'example.com';
|
const urlPath = 'example.com';
|
||||||
omnibox.value = urlPath;
|
omnibox.value = urlPath;
|
||||||
await omnibox.dispatchEvent(
|
await omnibox.dispatchEvent(
|
||||||
new KeyboardEvent('keydown', { code: 'Validator Test' })
|
new KeyboardEvent('keydown', { code: 'Validator Test' })
|
||||||
);
|
);
|
||||||
|
|
||||||
// Wait up to 5 seconds for the omnibox to finish updating.
|
// Wait up to 5 seconds for the omnibox to finish updating.
|
||||||
const loadUrl = new Promise((resolve) => {
|
const loadUrl = new Promise((resolve) => {
|
||||||
if (omnibox.value !== urlPath) resolve(omnibox.value);
|
if (omnibox.value !== urlPath) resolve(omnibox.value);
|
||||||
else
|
else
|
||||||
|
@ -147,7 +147,7 @@ const testCommonJSOnPage = async () => {
|
||||||
timeout = new Promise((resolve) => {
|
timeout = new Promise((resolve) => {
|
||||||
setTimeout(() => resolve(omnibox.value), 40000);
|
setTimeout(() => resolve(omnibox.value), 40000);
|
||||||
}),
|
}),
|
||||||
// Record the proxy URL that the omnibox left here.
|
// Record the proxy URL that the omnibox left here.
|
||||||
rammerheadUrl = await Promise.race([loadUrl, timeout]);
|
rammerheadUrl = await Promise.race([loadUrl, timeout]);
|
||||||
console.log('Generated Rammerhead URL:', rammerheadUrl);
|
console.log('Generated Rammerhead URL:', rammerheadUrl);
|
||||||
results.rammerhead = rammerheadUrl ? rammerheadUrl : 'failure';
|
results.rammerhead = rammerheadUrl ? rammerheadUrl : 'failure';
|
||||||
|
@ -225,8 +225,8 @@ xx xx
|
||||||
? resolve()
|
? resolve()
|
||||||
: window.addEventListener('load', resolve);
|
: window.addEventListener('load', resolve);
|
||||||
|
|
||||||
// Wait until a service worker is registered before continuing.
|
// Wait until a service worker is registered before continuing.
|
||||||
// Also make sure the document is loaded.
|
// Also make sure the document is loaded.
|
||||||
const waitForWorker = async () =>
|
const waitForWorker = async () =>
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
(await navigator.serviceWorker.getRegistrations()).length >= 1
|
(await navigator.serviceWorker.getRegistrations()).length >= 1
|
||||||
|
@ -237,32 +237,32 @@ xx xx
|
||||||
waitForWorker();
|
waitForWorker();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Locate the omnibox element on the Ultraviolet page.
|
// Locate the omnibox element on the Ultraviolet page.
|
||||||
let omnibox = document.getElementById('pr-uv');
|
let omnibox = document.getElementById('pr-uv');
|
||||||
omnibox = omnibox && omnibox.querySelector('input[type=text]');
|
omnibox = omnibox && omnibox.querySelector('input[type=text]');
|
||||||
|
|
||||||
if (omnibox) {
|
if (omnibox) {
|
||||||
// For the hacky URL test, use the URL page's EXACT title.
|
// For the hacky URL test, use the URL page's EXACT title.
|
||||||
const website = {
|
const website = {
|
||||||
path: 'example.com',
|
path: 'example.com',
|
||||||
title: 'Example Domain',
|
title: 'Example Domain',
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Send an artificial input to the omnibox. The omnibox will create
|
// Send an artificial input to the omnibox. The omnibox will create
|
||||||
// a proxy URL and leave it as the input value in response.
|
// a proxy URL and leave it as the input value in response.
|
||||||
omnibox.value = website.path;
|
omnibox.value = website.path;
|
||||||
await omnibox.dispatchEvent(
|
await omnibox.dispatchEvent(
|
||||||
new KeyboardEvent('keydown', { code: 'Validator Test' })
|
new KeyboardEvent('keydown', { code: 'Validator Test' })
|
||||||
);
|
);
|
||||||
|
|
||||||
// Record the proxy URL that the omnibox left here.
|
// Record the proxy URL that the omnibox left here.
|
||||||
const generatedUrl = omnibox.value;
|
const generatedUrl = omnibox.value;
|
||||||
console.log('Generated Ultraviolet URL:', generatedUrl);
|
console.log('Generated Ultraviolet URL:', generatedUrl);
|
||||||
results[0].ultraviolet = generatedUrl ? generatedUrl : 'failure';
|
results[0].ultraviolet = generatedUrl ? generatedUrl : 'failure';
|
||||||
|
|
||||||
// Test to see if the document title for example.com has loaded,
|
// Test to see if the document title for example.com has loaded,
|
||||||
// by appending an IFrame to the document and grabbing its content.
|
// by appending an IFrame to the document and grabbing its content.
|
||||||
const testGeneratedUrlHacky = async (url) => {
|
const testGeneratedUrlHacky = async (url) => {
|
||||||
let result = false;
|
let result = false;
|
||||||
const exampleIFrame = document.createElement('iframe');
|
const exampleIFrame = document.createElement('iframe');
|
||||||
|
@ -311,7 +311,7 @@ xx xx
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Run tests for Rammerhead and Ultraviolet
|
// Run tests for Rammerhead and Ultraviolet.
|
||||||
const rammerheadPassed = await testRammerhead();
|
const rammerheadPassed = await testRammerhead();
|
||||||
const ultravioletPassed = await testUltraviolet();
|
const ultravioletPassed = await testUltraviolet();
|
||||||
|
|
||||||
|
|
126
run-command.mjs
126
run-command.mjs
|
@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url';
|
||||||
import { build } from 'esbuild';
|
import { build } from 'esbuild';
|
||||||
import ecosystem from './ecosystem.config.js';
|
import ecosystem from './ecosystem.config.js';
|
||||||
|
|
||||||
// Some necessary constants are copied over from /src/server.mjs.
|
// Some necessary constants are copied over from /src/server.mjs.
|
||||||
|
|
||||||
const config = Object.freeze(
|
const config = Object.freeze(
|
||||||
JSON.parse(await readFile(new URL('./src/config.json', import.meta.url)))
|
JSON.parse(await readFile(new URL('./src/config.json', import.meta.url)))
|
||||||
|
@ -27,12 +27,12 @@ const serverUrl = ((base) => {
|
||||||
|
|
||||||
const shutdown = fileURLToPath(new URL('./src/.shutdown', import.meta.url));
|
const shutdown = fileURLToPath(new URL('./src/.shutdown', import.meta.url));
|
||||||
|
|
||||||
// Run each command line argument passed after node run-command.mjs.
|
// Run each command line argument passed after node run-command.mjs.
|
||||||
// Commands are defined in the switch case statement below.
|
// Commands are defined in the switch case statement below.
|
||||||
commands: for (let i = 2; i < process.argv.length; i++)
|
commands: for (let i = 2; i < process.argv.length; i++)
|
||||||
switch (process.argv[i]) {
|
switch (process.argv[i]) {
|
||||||
// Commmand to boot up the server. Use PM2 to run if production is true in the
|
// Commmand to boot up the server. Use PM2 to run if production is true in the
|
||||||
// config file.
|
// config file.
|
||||||
case 'start':
|
case 'start':
|
||||||
if (config.production)
|
if (config.production)
|
||||||
exec(
|
exec(
|
||||||
|
@ -42,8 +42,8 @@ commands: for (let i = 2; i < process.argv.length; i++)
|
||||||
console.log(stdout);
|
console.log(stdout);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Handle setup on Windows differently from platforms with POSIX-compliant shells.
|
// Handle setup on Windows differently from platforms with POSIX-compliant
|
||||||
// This should run the server as a background process.
|
// shells. This should run the server as a background process.
|
||||||
else if (process.platform === 'win32')
|
else if (process.platform === 'win32')
|
||||||
exec('START /MIN "" node backend.js', (error, stdout) => {
|
exec('START /MIN "" node backend.js', (error, stdout) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -52,48 +52,29 @@ commands: for (let i = 2; i < process.argv.length; i++)
|
||||||
}
|
}
|
||||||
console.log(stdout);
|
console.log(stdout);
|
||||||
});
|
});
|
||||||
// The following approach (and similar approaches) will not work on Windows,
|
// The following approach (and similar approaches) will not work on Windows,
|
||||||
// because exiting this program will also terminate backend.js on Windows.
|
// because exiting this program will also terminate backend.js on Windows.
|
||||||
else {
|
else {
|
||||||
const server = fork(
|
const server = fork(
|
||||||
fileURLToPath(new URL('./backend.js', import.meta.url)),
|
fileURLToPath(new URL('./backend.js', import.meta.url)),
|
||||||
{
|
{ cwd: process.cwd(), detached: true }
|
||||||
cwd: process.cwd(),
|
|
||||||
stdio: ['inherit', 'pipe', 'pipe', 'ipc'],
|
|
||||||
detached: true
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
server.stderr.on("data", stderr => {
|
|
||||||
console.error(stderr.toString());
|
|
||||||
if (stderr.toString().indexOf("DeprecationWarning") + 1)
|
|
||||||
return;
|
|
||||||
server.stderr.destroy();
|
|
||||||
process.exitCode = 1;
|
|
||||||
});
|
|
||||||
server.stdout.on("data", () => {
|
|
||||||
server.kill();
|
|
||||||
const server2 = fork(
|
|
||||||
fileURLToPath(new URL('./backend.js', import.meta.url)),
|
|
||||||
{cwd: process.cwd(), detached: true}
|
|
||||||
);
|
|
||||||
server2.unref();
|
|
||||||
server2.disconnect();
|
|
||||||
});
|
|
||||||
server.unref();
|
server.unref();
|
||||||
server.disconnect();
|
server.disconnect();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Stop the server. Make a temporary file that the server will check for if told
|
// Stop the server. Make a temporary file that the server will check for if told
|
||||||
// to shut down. This is done by sending a GET request to the server.
|
// to shut down. This is done by sending a GET request to the server.
|
||||||
case 'stop': {
|
case 'stop': {
|
||||||
await writeFile(shutdown, '');
|
await writeFile(shutdown, '');
|
||||||
let timeoutId,
|
let timeoutId,
|
||||||
hasErrored = false;
|
hasErrored = false;
|
||||||
try {
|
try {
|
||||||
// Give the server 5 seconds to respond, otherwise cancel this and throw an
|
/* Give the server 5 seconds to respond, otherwise cancel this and throw an
|
||||||
// error to the console. The fetch request will also throw an error immediately
|
* error to the console. The fetch request will also throw an error
|
||||||
// if checking the server on localhost and the port is unused.
|
* immediately if checking the server on localhost and the port is unused.
|
||||||
|
*/
|
||||||
const response = await Promise.race([
|
const response = await Promise.race([
|
||||||
fetch(new URL('/test-shutdown', serverUrl)),
|
fetch(new URL('/test-shutdown', serverUrl)),
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
|
@ -105,18 +86,18 @@ commands: for (let i = 2; i < process.argv.length; i++)
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
if (response === 'Error') throw new Error('Server is unresponsive.');
|
if (response === 'Error') throw new Error('Server is unresponsive.');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Remove the temporary shutdown file since the server didn't remove it.
|
// Remove the temporary shutdown file since the server didn't remove it.
|
||||||
await unlink(shutdown);
|
await unlink(shutdown);
|
||||||
// Check if this is the error thrown by the fetch request for an unused port.
|
// Check if this is the error thrown by the fetch request for an unused port.
|
||||||
// Don't print the unused port error, since nothing has actually broken.
|
// Don't print the unused port error, since nothing has actually broken.
|
||||||
if (e instanceof TypeError) clearTimeout(timeoutId);
|
if (e instanceof TypeError) clearTimeout(timeoutId);
|
||||||
else {
|
else {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
// Stop here unless Node will be killed later.
|
// Stop here unless Node will be killed later.
|
||||||
if (!process.argv.slice(i + 1).includes('kill')) hasErrored = true;
|
if (!process.argv.slice(i + 1).includes('kill')) hasErrored = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Do not run this if Node will be killed later in this script. It will fail.
|
// Do not run this if Node will be killed later in this script. It will fail.
|
||||||
if (config.production && !process.argv.slice(i + 1).includes('kill'))
|
if (config.production && !process.argv.slice(i + 1).includes('kill'))
|
||||||
exec('npx pm2 stop ecosystem.config.js', (error, stdout) => {
|
exec('npx pm2 stop ecosystem.config.js', (error, stdout) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -125,8 +106,8 @@ commands: for (let i = 2; i < process.argv.length; i++)
|
||||||
}
|
}
|
||||||
console.log(stdout);
|
console.log(stdout);
|
||||||
});
|
});
|
||||||
// Do not continue executing commands since the server was unable to be stopped.
|
// Do not continue executing commands since the server was unable to be stopped.
|
||||||
// Mostly implemented to prevent duplicating Node instances with npm restart.
|
// Mostly implemented to prevent duplicating Node instances with npm restart.
|
||||||
if (hasErrored) {
|
if (hasErrored) {
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
break commands;
|
break commands;
|
||||||
|
@ -154,9 +135,10 @@ commands: for (let i = 2; i < process.argv.length; i++)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kill all node processes and fully reset PM2. To be used for debugging.
|
/* Kill all node processes and fully reset PM2. To be used for debugging.
|
||||||
// Using npx pm2 monit, or npx pm2 list in the terminal will also bring up
|
* Using npx pm2 monit, or npx pm2 list in the terminal will also bring up
|
||||||
// more PM2 debugging tools.
|
* more PM2 debugging tools.
|
||||||
|
*/
|
||||||
case 'kill':
|
case 'kill':
|
||||||
if (process.platform === 'win32')
|
if (process.platform === 'win32')
|
||||||
exec(
|
exec(
|
||||||
|
@ -174,7 +156,57 @@ commands: for (let i = 2; i < process.argv.length; i++)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// No default case.
|
case 'workflow':
|
||||||
|
if (config.production)
|
||||||
|
exec(
|
||||||
|
'npx pm2 start ecosystem.config.js --env production',
|
||||||
|
(error, stdout) => {
|
||||||
|
if (error) throw error;
|
||||||
|
console.log(stdout);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Handle setup on Windows differently from platforms with POSIX-compliant
|
||||||
|
// shells. This should run the server as a background process.
|
||||||
|
else if (process.platform === 'win32')
|
||||||
|
exec('START /MIN "" node backend.js', (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
|
console.log(stdout);
|
||||||
|
});
|
||||||
|
// The following approach (and similar approaches) will not work on Windows,
|
||||||
|
// because exiting this program will also terminate backend.js on Windows.
|
||||||
|
else {
|
||||||
|
const server = fork(
|
||||||
|
fileURLToPath(new URL('./backend.js', import.meta.url)),
|
||||||
|
{
|
||||||
|
cwd: process.cwd(),
|
||||||
|
stdio: ['inherit', 'pipe', 'pipe', 'ipc'],
|
||||||
|
detached: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
server.stderr.on('data', (stderr) => {
|
||||||
|
console.error(stderr.toString());
|
||||||
|
if (stderr.toString().indexOf('DeprecationWarning') + 1) return;
|
||||||
|
server.stderr.destroy();
|
||||||
|
process.exitCode = 1;
|
||||||
|
});
|
||||||
|
server.stdout.on('data', () => {
|
||||||
|
server.kill();
|
||||||
|
const server2 = fork(
|
||||||
|
fileURLToPath(new URL('./backend.js', import.meta.url)),
|
||||||
|
{ cwd: process.cwd(), detached: true }
|
||||||
|
);
|
||||||
|
server2.unref();
|
||||||
|
server2.disconnect();
|
||||||
|
});
|
||||||
|
server.unref();
|
||||||
|
server.disconnect();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// No default case.
|
||||||
}
|
}
|
||||||
|
|
||||||
process.exitCode = process.exitCode || 0;
|
process.exitCode = process.exitCode || 0;
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { libcurlPath } from '@mercuryworkshop/libcurl-transport';
|
||||||
import { bareModulePath } from '@mercuryworkshop/bare-as-module3';
|
import { bareModulePath } from '@mercuryworkshop/bare-as-module3';
|
||||||
import { baremuxPath } from '@mercuryworkshop/bare-mux/node';
|
import { baremuxPath } from '@mercuryworkshop/bare-mux/node';
|
||||||
import { uvPath } from '@titaniumnetwork-dev/ultraviolet';
|
import { uvPath } from '@titaniumnetwork-dev/ultraviolet';
|
||||||
// import { createBareServer } from "@tomphttp/bare-server-node";
|
// import { createBareServer } from "@tomphttp/bare-server-node";
|
||||||
|
|
||||||
const config = JSON.parse(
|
const config = JSON.parse(
|
||||||
await readFile(new URL('./config.json', import.meta.url))
|
await readFile(new URL('./config.json', import.meta.url))
|
||||||
|
@ -23,7 +23,7 @@ const config = JSON.parse(
|
||||||
port = process.env.PORT || config.port,
|
port = process.env.PORT || config.port,
|
||||||
app = express(),
|
app = express(),
|
||||||
router = express.Router(),
|
router = express.Router(),
|
||||||
// bare = createBareServer("/bare/"),
|
// bare = createBareServer("/bare/"),
|
||||||
rh = createRammerhead();
|
rh = createRammerhead();
|
||||||
|
|
||||||
const rammerheadScopes = [
|
const rammerheadScopes = [
|
||||||
|
@ -60,10 +60,10 @@ const rammerheadSession = /^\/[a-z0-9]{32}/,
|
||||||
},
|
},
|
||||||
server = http.createServer((req, res) => {
|
server = http.createServer((req, res) => {
|
||||||
/*
|
/*
|
||||||
if (bare.shouldRoute(req)) {
|
if (bare.shouldRoute(req)) {
|
||||||
bare.routeRequest(req, res);
|
bare.routeRequest(req, res);
|
||||||
} else
|
} else
|
||||||
*/
|
*/
|
||||||
if (shouldRouteRh(req)) {
|
if (shouldRouteRh(req)) {
|
||||||
routeRhRequest(req, res);
|
routeRhRequest(req, res);
|
||||||
} else {
|
} else {
|
||||||
|
@ -76,7 +76,7 @@ server.on('upgrade', (req, socket, head) => {
|
||||||
if (bare.shouldRoute(req)) {
|
if (bare.shouldRoute(req)) {
|
||||||
bare.routeUpgrade(req, socket, head);
|
bare.routeUpgrade(req, socket, head);
|
||||||
} else
|
} else
|
||||||
*/
|
*/
|
||||||
if (shouldRouteRh(req)) {
|
if (shouldRouteRh(req)) {
|
||||||
routeRhUpgrade(req, socket, head);
|
routeRhUpgrade(req, socket, head);
|
||||||
} else if (req.url.endsWith('/wisp/')) {
|
} else if (req.url.endsWith('/wisp/')) {
|
||||||
|
@ -84,17 +84,18 @@ server.on('upgrade', (req, socket, head) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply Helmet middleware for security
|
// Apply Helmet middleware for security.
|
||||||
app.use(
|
app.use(
|
||||||
helmet({
|
helmet({
|
||||||
contentSecurityPolicy: false, // Disable CSP
|
contentSecurityPolicy: false, // Disable CSP
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// All website files are stored in the /views directory.
|
/* All website files are stored in the /views directory.
|
||||||
// This takes one of those files and displays it for a site visitor.
|
* This takes one of those files and displays it for a site visitor.
|
||||||
// Query strings like /?j are converted into paths like /views/hidden.html
|
* Query strings like /?j are converted into paths like /views/hidden.html
|
||||||
// back here. Which query string converts to what is defined in routes.mjs.
|
* back here. Which query string converts to what is defined in routes.mjs.
|
||||||
|
*/
|
||||||
router.get('/', async (req, res) =>
|
router.get('/', async (req, res) =>
|
||||||
res.send(
|
res.send(
|
||||||
paintSource(
|
paintSource(
|
||||||
|
@ -103,8 +104,8 @@ router.get('/', async (req, res) =>
|
||||||
path.join(
|
path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
'views',
|
'views',
|
||||||
// Return the error page if the query is not found in
|
// Return the error page if the query is not found in
|
||||||
// routes.mjs. Also set index as the default page.
|
// routes.mjs. Also set index as the default page.
|
||||||
'/?'.indexOf(req.url)
|
'/?'.indexOf(req.url)
|
||||||
? pages[Object.keys(req.query)[0]] || 'error.html'
|
? pages[Object.keys(req.query)[0]] || 'error.html'
|
||||||
: pages.index
|
: pages.index
|
||||||
|
@ -125,7 +126,7 @@ app.use('/baremux/', express.static(baremuxPath));
|
||||||
|
|
||||||
app.disable('x-powered-by');
|
app.disable('x-powered-by');
|
||||||
|
|
||||||
// Redundant code since 404 is handled elsewhere; left here as insurance.
|
// Redundant code since 404 is handled elsewhere; left here as insurance.
|
||||||
app.use((req, res) => {
|
app.use((req, res) => {
|
||||||
res.status(404).send(paintSource(loadTemplates(text404)));
|
res.status(404).send(paintSource(loadTemplates(text404)));
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,9 +11,10 @@ const {
|
||||||
text404,
|
text404,
|
||||||
} = pkg;
|
} = pkg;
|
||||||
|
|
||||||
// Below are lots of function definitions used to obfuscate the website.
|
/* Below are lots of function definitions used to obfuscate the website.
|
||||||
// This makes the website harder to properly categorize, as its source code
|
* This makes the website harder to properly categorize, as its source code
|
||||||
// changes with each time it is loaded.
|
* changes with each time it is loaded.
|
||||||
|
*/
|
||||||
const randomListItem = (lis) => () => lis[(Math.random() * lis.length) | 0],
|
const randomListItem = (lis) => () => lis[(Math.random() * lis.length) | 0],
|
||||||
charset = /­|​|­|<wbr>/gi,
|
charset = /­|​|­|<wbr>/gi,
|
||||||
getRandomChar = randomListItem(charRandom),
|
getRandomChar = randomListItem(charRandom),
|
||||||
|
@ -28,26 +29,25 @@ const randomListItem = (lis) => () => lis[(Math.random() * lis.length) | 0],
|
||||||
'<!-- IMPORTANT-HUTAOCOOKINGINSERT-DONOTDELETE -->',
|
'<!-- IMPORTANT-HUTAOCOOKINGINSERT-DONOTDELETE -->',
|
||||||
getCookingText
|
getCookingText
|
||||||
),
|
),
|
||||||
// This one isn't for obfuscation; it's just for dealing with cache issues.
|
// This one isn't for obfuscation; it's just for dealing with cache issues.
|
||||||
cacheBusting = (str) => {
|
cacheBusting = (str) => {
|
||||||
for (let item of Object.entries(cacheBustList))
|
for (let item of Object.entries(cacheBustList))
|
||||||
str = str.replaceAll(item[0], item[1]);
|
str = str.replaceAll(item[0], item[1]);
|
||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
// Apply the final obfuscation changes to an entire file.
|
// Apply the final obfuscation changes to an entire file.
|
||||||
paintSource = (str) =>
|
paintSource = (str) =>
|
||||||
insertCharset(hutaoInsert(versionInsert(insertCooking(cacheBusting(str))))),
|
insertCharset(hutaoInsert(versionInsert(insertCooking(cacheBusting(str))))),
|
||||||
// Use this instead of text404 for a preloaded error page.
|
// Use this instead of text404 for a preloaded error page.
|
||||||
preloaded404 = paintSource(text404),
|
preloaded404 = paintSource(text404),
|
||||||
// Grab the text content of a file. Ensure the file is a string.
|
// Grab the text content of a file. Ensure the file is a string.
|
||||||
tryReadFile = (file) =>
|
tryReadFile = (file) =>
|
||||||
existsSync(file + '') ? readFileSync(file + '', 'utf8') : preloaded404;
|
existsSync(file + '') ? readFileSync(file + '', 'utf8') : preloaded404;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// All of this is now old code.
|
|
||||||
// The newer versions of these functions are directly above.
|
All of this is now old code.
|
||||||
*/
|
The newer versions of these functions are directly above.
|
||||||
/*
|
|
||||||
|
|
||||||
function randomListItem(lis) {
|
function randomListItem(lis) {
|
||||||
return lis[Math.floor(Math.random() * lis.length)];
|
return lis[Math.floor(Math.random() * lis.length)];
|
||||||
|
|
|
@ -27,9 +27,10 @@ const config = Object.freeze(
|
||||||
{ pages, externalPages } = pageRoutes,
|
{ pages, externalPages } = pageRoutes,
|
||||||
__dirname = path.resolve();
|
__dirname = path.resolve();
|
||||||
|
|
||||||
// Record the server's location as a URL object, including its host and port.
|
/* Record the server's location as a URL object, including its host and port.
|
||||||
// The host can be modified at /src/config.json, whereas the ports can be modified
|
* The host can be modified at /src/config.json, whereas the ports can be modified
|
||||||
// at /ecosystem.config.js.
|
* at /ecosystem.config.js.
|
||||||
|
*/
|
||||||
const serverUrl = ((base) => {
|
const serverUrl = ((base) => {
|
||||||
try {
|
try {
|
||||||
base = new URL(config.host);
|
base = new URL(config.host);
|
||||||
|
@ -43,8 +44,8 @@ const serverUrl = ((base) => {
|
||||||
})();
|
})();
|
||||||
console.log(serverUrl);
|
console.log(serverUrl);
|
||||||
|
|
||||||
// The server will check for the existence of this file when a shutdown is requested.
|
// The server will check for the existence of this file when a shutdown is requested.
|
||||||
// The shutdown script in run-command.js will temporarily produce this file.
|
// The shutdown script in run-command.js will temporarily produce this file.
|
||||||
const shutdown = fileURLToPath(new URL('./.shutdown', import.meta.url));
|
const shutdown = fileURLToPath(new URL('./.shutdown', import.meta.url));
|
||||||
|
|
||||||
const rh = createRammerhead();
|
const rh = createRammerhead();
|
||||||
|
@ -85,7 +86,7 @@ const rammerheadSession = /^\/[a-z0-9]{32}/,
|
||||||
rh.emit('upgrade', req, socket, head);
|
rh.emit('upgrade', req, socket, head);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a server factory for RH, and wisp (and bare if you please).
|
// Create a server factory for RH, and wisp (and bare if you please).
|
||||||
const serverFactory = (handler) => {
|
const serverFactory = (handler) => {
|
||||||
return createServer()
|
return createServer()
|
||||||
.on('request', (req, res) => {
|
.on('request', (req, res) => {
|
||||||
|
@ -98,7 +99,7 @@ const serverFactory = (handler) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set logger to true for logs
|
// Set logger to true for logs.
|
||||||
const app = Fastify({
|
const app = Fastify({
|
||||||
ignoreDuplicateSlashes: true,
|
ignoreDuplicateSlashes: true,
|
||||||
ignoreTrailingSlash: true,
|
ignoreTrailingSlash: true,
|
||||||
|
@ -106,13 +107,13 @@ const app = Fastify({
|
||||||
serverFactory: serverFactory,
|
serverFactory: serverFactory,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply Helmet middleware for security
|
// Apply Helmet middleware for security.
|
||||||
app.register(fastifyHelmet, {
|
app.register(fastifyHelmet, {
|
||||||
contentSecurityPolicy: false, // Disable CSP
|
contentSecurityPolicy: false, // Disable CSP
|
||||||
xPoweredBy: false,
|
xPoweredBy: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assign server file paths to different paths, for serving content on the website.
|
// Assign server file paths to different paths, for serving content on the website.
|
||||||
app.register(fastifyStatic, {
|
app.register(fastifyStatic, {
|
||||||
root: fileURLToPath(new URL('../views/pages', import.meta.url)),
|
root: fileURLToPath(new URL('../views/pages', import.meta.url)),
|
||||||
decorateReply: false,
|
decorateReply: false,
|
||||||
|
@ -133,7 +134,7 @@ app.register(fastifyStatic, {
|
||||||
app.register(fastifyStatic, {
|
app.register(fastifyStatic, {
|
||||||
root: fileURLToPath(
|
root: fileURLToPath(
|
||||||
new URL(
|
new URL(
|
||||||
// Use the pre-compiled, minified scripts instead, if enabled in config.
|
// Use the pre-compiled, minified scripts instead, if enabled in config.
|
||||||
config.minifyScripts ? '../views/dist/assets/js' : '../views/assets/js',
|
config.minifyScripts ? '../views/dist/assets/js' : '../views/assets/js',
|
||||||
import.meta.url
|
import.meta.url
|
||||||
)
|
)
|
||||||
|
@ -145,7 +146,7 @@ app.register(fastifyStatic, {
|
||||||
app.register(fastifyStatic, {
|
app.register(fastifyStatic, {
|
||||||
root: fileURLToPath(
|
root: fileURLToPath(
|
||||||
new URL(
|
new URL(
|
||||||
// Use the pre-compiled, minified stylesheets instead, if enabled in config.
|
// Use the pre-compiled, minified stylesheets instead, if enabled in config.
|
||||||
config.minifyScripts ? '../views/dist/assets/css' : '../views/assets/css',
|
config.minifyScripts ? '../views/dist/assets/css' : '../views/assets/css',
|
||||||
import.meta.url
|
import.meta.url
|
||||||
)
|
)
|
||||||
|
@ -154,13 +155,13 @@ app.register(fastifyStatic, {
|
||||||
decorateReply: false,
|
decorateReply: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// This combines scripts from the official UV repository with local UV scripts into
|
// This combines scripts from the official UV repository with local UV scripts into
|
||||||
// one directory path. Local versions of files override the official versions.
|
// one directory path. Local versions of files override the official versions.
|
||||||
app.register(fastifyStatic, {
|
app.register(fastifyStatic, {
|
||||||
root: [
|
root: [
|
||||||
fileURLToPath(
|
fileURLToPath(
|
||||||
new URL(
|
new URL(
|
||||||
// Use the pre-compiled, minified scripts instead, if enabled in config.
|
// Use the pre-compiled, minified scripts instead, if enabled in config.
|
||||||
config.minifyScripts ? '../views/dist/uv' : '../views/uv',
|
config.minifyScripts ? '../views/dist/uv' : '../views/uv',
|
||||||
import.meta.url
|
import.meta.url
|
||||||
)
|
)
|
||||||
|
@ -171,7 +172,7 @@ app.register(fastifyStatic, {
|
||||||
decorateReply: false,
|
decorateReply: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register proxy paths to the website.
|
// Register proxy paths to the website.
|
||||||
app.register(fastifyStatic, {
|
app.register(fastifyStatic, {
|
||||||
root: epoxyPath,
|
root: epoxyPath,
|
||||||
prefix: '/epoxy/',
|
prefix: '/epoxy/',
|
||||||
|
@ -196,12 +197,13 @@ app.register(fastifyStatic, {
|
||||||
decorateReply: false,
|
decorateReply: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// All website files are stored in the /views directory.
|
/* All website files are stored in the /views directory.
|
||||||
// This takes one of those files and displays it for a site visitor.
|
* This takes one of those files and displays it for a site visitor.
|
||||||
// Paths like /browsing are converted into paths like /views/pages/surf.html
|
* Paths like /browsing are converted into paths like /views/pages/surf.html
|
||||||
// back here. Which path converts to what is defined in routes.mjs.
|
* back here. Which path converts to what is defined in routes.mjs.
|
||||||
|
*/
|
||||||
app.get('/:path', (req, reply) => {
|
app.get('/:path', (req, reply) => {
|
||||||
// Testing for future features that need cookies to deliver alternate source files.
|
// Testing for future features that need cookies to deliver alternate source files.
|
||||||
if (req.raw.rawHeaders.includes('Cookie'))
|
if (req.raw.rawHeaders.includes('Cookie'))
|
||||||
console.log(req.raw.rawHeaders[req.raw.rawHeaders.indexOf('Cookie') + 1]);
|
console.log(req.raw.rawHeaders[req.raw.rawHeaders.indexOf('Cookie') + 1]);
|
||||||
|
|
||||||
|
@ -214,8 +216,8 @@ app.get('/:path', (req, reply) => {
|
||||||
return reply.redirect(externalRoute);
|
return reply.redirect(externalRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a GET request is sent to /test-shutdown and a script-generated shutdown file
|
// If a GET request is sent to /test-shutdown and a script-generated shutdown file
|
||||||
// is present, gracefully shut the server down.
|
// is present, gracefully shut the server down.
|
||||||
if (reqPath === 'test-shutdown' && existsSync(shutdown)) {
|
if (reqPath === 'test-shutdown' && existsSync(shutdown)) {
|
||||||
console.log('Holy Unblocker is shutting down.');
|
console.log('Holy Unblocker is shutting down.');
|
||||||
app.close();
|
app.close();
|
||||||
|
@ -223,7 +225,7 @@ app.get('/:path', (req, reply) => {
|
||||||
process.exitCode = 0;
|
process.exitCode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the error page if the query is not found in routes.mjs.
|
// Return the error page if the query is not found in routes.mjs.
|
||||||
if (reqPath && !(reqPath in pages))
|
if (reqPath && !(reqPath in pages))
|
||||||
return reply.code(404).type('text/html').send(preloaded404);
|
return reply.code(404).type('text/html').send(preloaded404);
|
||||||
|
|
||||||
|
@ -234,7 +236,7 @@ app.get('/:path', (req, reply) => {
|
||||||
path.join(
|
path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
'views',
|
'views',
|
||||||
// Set the index the as the default page.
|
// Set the index the as the default page.
|
||||||
reqPath ? pages[reqPath] : pages.index
|
reqPath ? pages[reqPath] : pages.index
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -259,7 +261,7 @@ app.get("/assets/js/uv/uv.config.js", (req, reply) => {
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Set an error page for invalid paths outside the query string system.
|
// Set an error page for invalid paths outside the query string system.
|
||||||
app.setNotFoundHandler((req, reply) => {
|
app.setNotFoundHandler((req, reply) => {
|
||||||
reply.code(404).type('text/html').send(preloaded404);
|
reply.code(404).type('text/html').send(preloaded404);
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,11 +15,11 @@ const header = tryReadFile(path.normalize(__dirname + '/header.html')),
|
||||||
.replace('<!--HEADER-->', header)
|
.replace('<!--HEADER-->', header)
|
||||||
.replace('<!--FOOTER-->', footer)
|
.replace('<!--FOOTER-->', footer)
|
||||||
|
|
||||||
// Used only on docs.html
|
// Used only on docs.html
|
||||||
.replace('<!--DOCS-->', documentation)
|
.replace('<!--DOCS-->', documentation)
|
||||||
// Used only on faq.html
|
// Used only on faq.html
|
||||||
.replace('<!--FAQ-->', faq)
|
.replace('<!--FAQ-->', faq)
|
||||||
// Used only on terms.html
|
// Used only on terms.html
|
||||||
.replace('<!--TOS-->', terms)
|
.replace('<!--TOS-->', terms)
|
||||||
// Used only on header.html
|
// Used only on header.html
|
||||||
.replace('<!--SETTINGS-->', settings);
|
.replace('<!--SETTINGS-->', settings);
|
||||||
|
|
|
@ -4,34 +4,35 @@
|
||||||
/* Card Shimmer Mouse Follow Script
|
/* Card Shimmer Mouse Follow Script
|
||||||
/* ----------------------------------------------- */
|
/* ----------------------------------------------- */
|
||||||
|
|
||||||
// Encase everything in a new scope so that variables are not accidentally
|
// Encase everything in a new scope so that variables are not accidentally
|
||||||
// attached to the global scope.
|
// attached to the global scope.
|
||||||
(() => {
|
(() => {
|
||||||
// Track the cursor position with respect to the top left of the card.
|
// Track the cursor position with respect to the top left of the card.
|
||||||
// The "this" keyword gets the element that invoked the event listener.
|
// The "this" keyword gets the element that invoked the event listener.
|
||||||
const handleMouseMove = (element) => {
|
const handleMouseMove = (element) => {
|
||||||
element.addEventListener('mousemove', (e) => {
|
element.addEventListener('mousemove', (e) => {
|
||||||
const rect = element.getBoundingClientRect();
|
const rect = element.getBoundingClientRect();
|
||||||
const x = e.clientX - rect.left;
|
const x = e.clientX - rect.left;
|
||||||
const y = e.clientY - rect.top;
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
element.style.setProperty('--mouse-x', `${x}px`);
|
element.style.setProperty('--mouse-x', `${x}px`);
|
||||||
element.style.setProperty('--mouse-y', `${y}px`);
|
element.style.setProperty('--mouse-y', `${y}px`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Reset the cursor tracking variables when the cursor leaves the card.
|
// Reset the cursor tracking variables when the cursor leaves the card.
|
||||||
handleMouseLeave = (element) => {
|
handleMouseLeave = (element) => {
|
||||||
element.addEventListener('mouseleave', () => {
|
element.addEventListener('mouseleave', () => {
|
||||||
element.style.setProperty('--mouse-x', `50%`);
|
element.style.setProperty('--mouse-x', `50%`);
|
||||||
element.style.setProperty('--mouse-y', `50%`);
|
element.style.setProperty('--mouse-y', `50%`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Get the box card elements and add the event listeners to them.
|
// Get the box card elements and add the event listeners to them.
|
||||||
shimmerEffects = document.querySelectorAll('.box-card, .box-hero');
|
shimmerEffects = document.querySelectorAll('.box-card, .box-hero');
|
||||||
|
|
||||||
// Attach CSS variables, mouse-x and mouse-y, to elements that will be
|
/* Attach CSS variables, mouse-x and mouse-y, to elements that will be
|
||||||
// given shimmer effects, by adding or modifying the style attribute.
|
* given shimmer effects, by adding or modifying the style attribute.
|
||||||
// CSS calculates and renders the actual shimmer effect from there.
|
* CSS calculates and renders the actual shimmer effect from there.
|
||||||
shimmerEffects.forEach(handleMouseMove);
|
*/
|
||||||
shimmerEffects.forEach(handleMouseLeave);
|
shimmerEffects.forEach(handleMouseMove);
|
||||||
|
shimmerEffects.forEach(handleMouseLeave);
|
||||||
})();
|
})();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,173 +5,168 @@
|
||||||
/* Settings Menu
|
/* Settings Menu
|
||||||
/* ----------------------------------------------- */
|
/* ----------------------------------------------- */
|
||||||
|
|
||||||
// Encase everything in a new scope so that variables are not accidentally
|
// Encase everything in a new scope so that variables are not accidentally
|
||||||
// attached to the global scope.
|
// attached to the global scope.
|
||||||
(() => {
|
(() => {
|
||||||
// Determine the expiration date of a new cookie.
|
// Determine the expiration date of a new cookie.
|
||||||
let date = new Date();
|
let date = new Date();
|
||||||
date.setFullYear(date.getFullYear() + 100);
|
date.setFullYear(date.getFullYear() + 100);
|
||||||
date = date.toUTCString();
|
date = date.toUTCString();
|
||||||
|
|
||||||
// All cookies should be secure and are intended to work in iframes.
|
// All cookies should be secure and are intended to work in iframes.
|
||||||
const setCookie = (name, value) => {
|
const setCookie = (name, value) => {
|
||||||
document.cookie =
|
document.cookie =
|
||||||
name +
|
name +
|
||||||
`=${encodeURIComponent(value)}; expires=${date}; SameSite=None; Secure;`;
|
`=${encodeURIComponent(value)}; expires=${date}; SameSite=None; Secure;`;
|
||||||
},
|
},
|
||||||
removeCookie = (name) => {
|
removeCookie = (name) => {
|
||||||
document.cookie =
|
document.cookie =
|
||||||
name +
|
name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=None; Secure;';
|
||||||
'=; expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=None; Secure;';
|
},
|
||||||
},
|
readCookie = async (name) => {
|
||||||
readCookie = async (name) => {
|
// Get the first cookie that has the same name.
|
||||||
// Get the first cookie that has the same name.
|
for (let cookie of document.cookie.split('; '))
|
||||||
for (let cookie of document.cookie.split('; '))
|
if (!cookie.indexOf(name + '='))
|
||||||
if (!cookie.indexOf(name + '='))
|
// Return the cookie's stored content.
|
||||||
// Return the cookie's stored content.
|
return decodeURIComponent(cookie.slice(name.length + 1));
|
||||||
return decodeURIComponent(cookie.slice(name.length + 1));
|
},
|
||||||
},
|
// Customize the page's title.
|
||||||
// Customize the page's title.
|
pageTitle = (value) => {
|
||||||
pageTitle = (value) => {
|
let tag =
|
||||||
let tag =
|
document.getElementsByTagName('title')[0] ||
|
||||||
document.getElementsByTagName('title')[0] ||
|
document.createElement('title');
|
||||||
document.createElement('title');
|
tag.innerHTML = value;
|
||||||
tag.innerHTML = value;
|
document.head.appendChild(tag);
|
||||||
document.head.appendChild(tag);
|
},
|
||||||
},
|
// Set the page's favicon to a new URL.
|
||||||
// Set the page's favicon to a new URL.
|
pageIcon = (value) => {
|
||||||
pageIcon = (value) => {
|
let tag =
|
||||||
let tag =
|
document.querySelector("link[rel*='icon']") ||
|
||||||
document.querySelector("link[rel*='icon']") ||
|
document.createElement('link');
|
||||||
document.createElement('link');
|
tag.rel = 'icon';
|
||||||
tag.rel = 'icon';
|
tag.href = value;
|
||||||
tag.href = value;
|
document.head.appendChild(tag);
|
||||||
document.head.appendChild(tag);
|
},
|
||||||
},
|
// Make a small stylesheet to override a setting from the main stylesheet.
|
||||||
// Make a small stylesheet to override a setting from the main stylesheet.
|
pageShowAds = () => {
|
||||||
pageShowAds = () => {
|
let advertising = document.createElement('style');
|
||||||
let advertising = document.createElement('style');
|
advertising.id = 'advertising';
|
||||||
advertising.id = 'advertising';
|
advertising.innerText = '.ad { display:block; }';
|
||||||
advertising.innerText = '.ad { display:block; }';
|
(
|
||||||
|
document.head ||
|
||||||
|
document.body ||
|
||||||
|
document.documentElement ||
|
||||||
|
document
|
||||||
|
).appendChild(advertising);
|
||||||
|
},
|
||||||
|
// Remove the stylesheet made by the function above, if it exists.
|
||||||
|
pageHideAds = () => {
|
||||||
|
(document.getElementById('advertising') || new Text()).remove();
|
||||||
|
},
|
||||||
|
// These titles and icons are used as autofill templates by settings.html.
|
||||||
|
// The icon URLs and tab titles may need to be updated over time.
|
||||||
|
presetIcons = Object.freeze({
|
||||||
|
'': ' \n ',
|
||||||
|
Google: 'Google \n https://www.google.com/favicon.ico',
|
||||||
|
Bing: 'Bing \n https://www.bing.com/sa/simg/favicon-trans-bg-blue-mg-28.ico',
|
||||||
|
'Google Drive':
|
||||||
|
'Home - Google Drive \n https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png',
|
||||||
|
Gmail:
|
||||||
|
'Inbox - Gmail \n https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico',
|
||||||
|
}),
|
||||||
|
// Choose the default transport mode, for proxying, based on the browser.
|
||||||
|
// Firefox is not supported by epoxy yet, which is why this is implemented.
|
||||||
|
defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent)
|
||||||
|
? 'epoxy'
|
||||||
|
: 'libcurl';
|
||||||
|
|
||||||
|
// Load a custom page title and favicon if it was previously stored.
|
||||||
|
readCookie('HBTitle').then((s) => {
|
||||||
|
s != undefined && pageTitle(s);
|
||||||
|
});
|
||||||
|
readCookie('HBIcon').then((s) => {
|
||||||
|
s != undefined && pageIcon(s);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load the UV transport mode that was last used, or use the default.
|
||||||
|
readCookie('HBTransport').then((s) => {
|
||||||
|
let transportMode = document.querySelector(
|
||||||
|
`#uv-transport-list input[value="${s || defaultMode}"]`
|
||||||
|
);
|
||||||
|
if (transportMode) transportMode.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ads are disabled by default. Load ads if ads were enabled previously.
|
||||||
|
// Change !== to === here if ads should be enabled by default.
|
||||||
|
readCookie('HBHideAds').then((s) => {
|
||||||
|
s !== 'false'
|
||||||
|
? pageHideAds()
|
||||||
|
: pageShowAds(((document.getElementById('hideads') || {}).checked = 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tor is disabled by default. Enable Tor if it was enabled previously.
|
||||||
|
readCookie('HBUseOnion').then((s) => {
|
||||||
|
if (s === 'true') {
|
||||||
|
let torCheck = document.getElementById('useonion') || {
|
||||||
|
dispatchEvent: () => {},
|
||||||
|
};
|
||||||
|
torCheck.checked = 1;
|
||||||
|
torCheck.dispatchEvent(new Event('change'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// All code below is used by the Settings UI in the navigation bar.
|
||||||
|
if (document.getElementById('csel')) {
|
||||||
|
const attachEventListener = (selector, ...args) =>
|
||||||
(
|
(
|
||||||
document.head ||
|
document.getElementById(selector) || document.querySelector(selector)
|
||||||
document.body ||
|
).addEventListener(...args),
|
||||||
document.documentElement ||
|
focusElement = document
|
||||||
document
|
.getElementsByClassName('dropdown-settings')[0]
|
||||||
).appendChild(advertising);
|
.parentElement.querySelector("a[href='#']");
|
||||||
},
|
|
||||||
// Remove the stylesheet made by the function above, if it exists.
|
|
||||||
pageHideAds = () => {
|
|
||||||
(document.getElementById('advertising') || new Text()).remove();
|
|
||||||
},
|
|
||||||
// These titles and icons are used as autofill templates by settings.html.
|
|
||||||
// The icon URLs and tab titles may need to be updated over time.
|
|
||||||
presetIcons = Object.freeze({
|
|
||||||
'': ' \n ',
|
|
||||||
Google: 'Google \n https://www.google.com/favicon.ico',
|
|
||||||
Bing: 'Bing \n https://www.bing.com/sa/simg/favicon-trans-bg-blue-mg-28.ico',
|
|
||||||
'Google Drive':
|
|
||||||
'Home - Google Drive \n https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png',
|
|
||||||
Gmail:
|
|
||||||
'Inbox - Gmail \n https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico',
|
|
||||||
}),
|
|
||||||
// Choose the default transport mode, for proxying, based on the browser.
|
|
||||||
// Firefox is not supported by epoxy yet, which is why this is implemented.
|
|
||||||
defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent)
|
|
||||||
? 'epoxy'
|
|
||||||
: 'libcurl';
|
|
||||||
|
|
||||||
// Load a custom page title and favicon if it was previously stored.
|
attachEventListener('.dropdown-settings .close-settings-btn', 'click', () => {
|
||||||
readCookie('HBTitle').then((s) => {
|
document.activeElement.blur();
|
||||||
s != undefined && pageTitle(s);
|
|
||||||
});
|
|
||||||
readCookie('HBIcon').then((s) => {
|
|
||||||
s != undefined && pageIcon(s);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load the UV transport mode that was last used, or use the default.
|
// Allow users to set a custom title with the UI.
|
||||||
readCookie('HBTransport').then((s) => {
|
attachEventListener('titleform', 'submit', (e) => {
|
||||||
let transportMode = document.querySelector(
|
e.preventDefault();
|
||||||
`#uv-transport-list input[value="${s || defaultMode}"]`
|
e = e.target.firstElementChild;
|
||||||
);
|
if (e.value) {
|
||||||
if (transportMode) transportMode.click();
|
pageTitle(e.value);
|
||||||
});
|
setCookie('HBTitle', e.value);
|
||||||
|
e.value = '';
|
||||||
// Ads are disabled by default. Load ads if ads were enabled previously.
|
} else if (confirm('Reset the title to default?')) {
|
||||||
// Change !== to === here if ads should be enabled by default.
|
// Allow users to reset the title to default if nothing is entered.
|
||||||
readCookie('HBHideAds').then((s) => {
|
focusElement.focus();
|
||||||
s !== 'false'
|
removeCookie('HBTitle');
|
||||||
? pageHideAds()
|
pageTitle('Holy Unblocker LTS');
|
||||||
: pageShowAds(((document.getElementById('hideads') || {}).checked = 0));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tor is disabled by default. Enable Tor if it was enabled previously.
|
|
||||||
readCookie('HBUseOnion').then((s) => {
|
|
||||||
if (s === 'true') {
|
|
||||||
let torCheck = document.getElementById('useonion') || {
|
|
||||||
dispatchEvent: () => {},
|
|
||||||
};
|
|
||||||
torCheck.checked = 1;
|
|
||||||
torCheck.dispatchEvent(new Event('change'));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// All code below is used by the Settings UI in the navigation bar.
|
// Allow users to set a custom favicon with the UI.
|
||||||
if (document.getElementById('csel')) {
|
attachEventListener('iconform', 'submit', (e) => {
|
||||||
const attachEventListener = (selector, ...args) =>
|
e.preventDefault();
|
||||||
(
|
e = e.target.firstElementChild;
|
||||||
document.getElementById(selector) || document.querySelector(selector)
|
if (e.value) {
|
||||||
).addEventListener(...args),
|
pageIcon(e.value);
|
||||||
focusElement = document
|
setCookie('HBIcon', e.value);
|
||||||
.getElementsByClassName('dropdown-settings')[0]
|
e.value = '';
|
||||||
.parentElement.querySelector("a[href='#']");
|
} else if (confirm('Reset the icon to default?')) {
|
||||||
|
// Allow users to reset the favicon to default if nothing is entered.
|
||||||
|
focusElement.focus();
|
||||||
|
removeCookie('HBIcon');
|
||||||
|
pageIcon('assets/img/icon.png');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
attachEventListener(
|
/*
|
||||||
'.dropdown-settings .close-settings-btn',
|
|
||||||
'click',
|
|
||||||
() => {
|
|
||||||
document.activeElement.blur();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Allow users to set a custom title with the UI.
|
|
||||||
attachEventListener('titleform', 'submit', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e = e.target.firstElementChild;
|
|
||||||
if (e.value) {
|
|
||||||
pageTitle(e.value);
|
|
||||||
setCookie('HBTitle', e.value);
|
|
||||||
e.value = '';
|
|
||||||
} else if (confirm('Reset the title to default?')) {
|
|
||||||
// Allow users to reset the title to default if nothing is entered.
|
|
||||||
focusElement.focus();
|
|
||||||
removeCookie('HBTitle');
|
|
||||||
pageTitle('Holy Unblocker LTS');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Allow users to set a custom favicon with the UI.
|
|
||||||
attachEventListener('iconform', 'submit', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e = e.target.firstElementChild;
|
|
||||||
if (e.value) {
|
|
||||||
pageIcon(e.value);
|
|
||||||
setCookie('HBIcon', e.value);
|
|
||||||
e.value = '';
|
|
||||||
} else if (confirm('Reset the icon to default?')) {
|
|
||||||
// Allow users to reset the favicon to default if nothing is entered.
|
|
||||||
focusElement.focus();
|
|
||||||
removeCookie('HBIcon');
|
|
||||||
pageIcon('assets/img/icon.png');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
This is unused in the current settings menu.
|
This is unused in the current settings menu.
|
||||||
|
|
||||||
// Allow users to make a new about:blank tab and view the site from there.
|
// Allow users to make a new about:blank tab and view the site from there.
|
||||||
// An iframe of the current page is inserted into the new tab.
|
// An iframe of the current page is inserted into the new tab.
|
||||||
attachEventListener("cselab", "click", () => {
|
attachEventListener("cselab", "click", () => {
|
||||||
let win = window.open();
|
let win = window.open();
|
||||||
let iframe = win.document.createElement("iframe");
|
let iframe = win.document.createElement("iframe");
|
||||||
|
@ -179,70 +174,71 @@
|
||||||
iframe.src = location.href;
|
iframe.src = location.href;
|
||||||
win.document.body.appendChild(iframe);
|
win.document.body.appendChild(iframe);
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Provides users with a handy set of title and icon autofill options.
|
// Provides users with a handy set of title and icon autofill options.
|
||||||
attachEventListener('icon-list', 'change', (e) => {
|
attachEventListener('icon-list', 'change', (e) => {
|
||||||
let titleform = document.getElementById('titleform'),
|
let titleform = document.getElementById('titleform'),
|
||||||
iconform = document.getElementById('iconform');
|
iconform = document.getElementById('iconform');
|
||||||
[titleform.firstElementChild.value, iconform.firstElementChild.value] = (
|
[titleform.firstElementChild.value, iconform.firstElementChild.value] = (
|
||||||
presetIcons[e.target.value] || ' \n '
|
presetIcons[e.target.value] || ' \n '
|
||||||
).split(' \n ');
|
).split(' \n ');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Allow users to change the UV transport mode, for proxying, with the UI.
|
||||||
|
const uvTransportList = document.getElementById('uv-transport-list');
|
||||||
|
uvTransportList.querySelectorAll('input').forEach((element) => {
|
||||||
|
element.addEventListener('change', (e) => {
|
||||||
|
!uvTransportList.querySelector('input:checked') ||
|
||||||
|
e.target.value === defaultMode
|
||||||
|
? removeCookie('HBTransport')
|
||||||
|
: setCookie('HBTransport', e.target.value);
|
||||||
|
|
||||||
|
// Only the libcurl transport mode supports Tor at the moment.
|
||||||
|
let torCheck = document.getElementById('useonion');
|
||||||
|
if (e.target.value !== 'libcurl' && torCheck.checked) torCheck.click();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Allow users to change the UV transport mode, for proxying, with the UI.
|
// Allow users to toggle ads with the UI.
|
||||||
const uvTransportList = document.getElementById('uv-transport-list');
|
attachEventListener('hideads', 'change', (e) => {
|
||||||
uvTransportList.querySelectorAll('input').forEach((element) => {
|
if (e.target.checked) {
|
||||||
element.addEventListener('change', (e) => {
|
pageHideAds();
|
||||||
!uvTransportList.querySelector('input:checked') ||
|
setCookie('HBHideAds', 'true');
|
||||||
e.target.value === defaultMode
|
} else {
|
||||||
? removeCookie('HBTransport')
|
pageShowAds();
|
||||||
: setCookie('HBTransport', e.target.value);
|
setCookie('HBHideAds', 'false');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Only the libcurl transport mode supports Tor at the moment.
|
/* Allow users to toggle onion routing in Ultraviolet with the UI. Only
|
||||||
let torCheck = document.getElementById('useonion');
|
* the libcurl transport mode supports Tor at the moment, so ensure that
|
||||||
if (e.target.value !== 'libcurl' && torCheck.checked) torCheck.click();
|
* users are aware that they cannot use Tor with other modes.
|
||||||
});
|
*/
|
||||||
});
|
attachEventListener('useonion', 'change', (e) => {
|
||||||
|
let unselectedModes = document.querySelectorAll(
|
||||||
// Allow users to toggle ads with the UI.
|
'#uv-transport-list input:not([value=libcurl])'
|
||||||
attachEventListener('hideads', 'change', (e) => {
|
);
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
pageHideAds();
|
let selectedMode = document.querySelector(
|
||||||
setCookie('HBHideAds', 'true');
|
'#uv-transport-list input[value=libcurl]'
|
||||||
} else {
|
|
||||||
pageShowAds();
|
|
||||||
setCookie('HBHideAds', 'false');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Allow users to toggle onion routing in Ultraviolet with the UI. Only
|
|
||||||
// the libcurl transport mode supports Tor at the moment, so ensure that
|
|
||||||
// users are aware that they cannot use Tor with other modes.
|
|
||||||
attachEventListener('useonion', 'change', (e) => {
|
|
||||||
let unselectedModes = document.querySelectorAll(
|
|
||||||
'#uv-transport-list input:not([value=libcurl])'
|
|
||||||
);
|
);
|
||||||
if (e.target.checked) {
|
unselectedModes.forEach((e) => {
|
||||||
let selectedMode = document.querySelector(
|
e.setAttribute('disabled', 'true');
|
||||||
'#uv-transport-list input[value=libcurl]'
|
});
|
||||||
);
|
selectedMode.click();
|
||||||
unselectedModes.forEach((e) => {
|
setCookie('HBUseOnion', 'true');
|
||||||
e.setAttribute('disabled', 'true');
|
} else {
|
||||||
});
|
unselectedModes.forEach((e) => {
|
||||||
selectedMode.click();
|
e.removeAttribute('disabled');
|
||||||
setCookie('HBUseOnion', 'true');
|
});
|
||||||
} else {
|
|
||||||
unselectedModes.forEach((e) => {
|
|
||||||
e.removeAttribute('disabled');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tor will likely never be enabled by default, so removing the cookie
|
// Tor will likely never be enabled by default, so removing the cookie
|
||||||
// here may be better than setting it to false.
|
// here may be better than setting it to false.
|
||||||
removeCookie('HBUseOnion');
|
removeCookie('HBUseOnion');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/* -----------------------------------------------
|
/* -----------------------------------------------
|
||||||
|
|
|
@ -1,83 +1,84 @@
|
||||||
// Encase everything in a new scope so that variables are not accidentally
|
// Encase everything in a new scope so that variables are not accidentally
|
||||||
// attached to the global scope.
|
// attached to the global scope.
|
||||||
(() => {
|
(() => {
|
||||||
const stockSW = '/uv/sw.js',
|
const stockSW = '/uv/sw.js',
|
||||||
blacklistSW = '/uv/sw-blacklist.js',
|
blacklistSW = '/uv/sw-blacklist.js',
|
||||||
swAllowedHostnames = ['localhost', '127.0.0.1'],
|
swAllowedHostnames = ['localhost', '127.0.0.1'],
|
||||||
connection = new BareMux.BareMuxConnection('/baremux/worker.js'),
|
connection = new BareMux.BareMuxConnection('/baremux/worker.js'),
|
||||||
wispUrl =
|
wispUrl =
|
||||||
(location.protocol === 'https:' ? 'wss' : 'ws') +
|
(location.protocol === 'https:' ? 'wss' : 'ws') +
|
||||||
'://' +
|
'://' +
|
||||||
location.host +
|
location.host +
|
||||||
'/wisp/',
|
'/wisp/',
|
||||||
// Proxy configuration
|
// Proxy configuration
|
||||||
proxyUrl = 'socks5h://localhost:9050', // Replace with your proxy URL
|
proxyUrl = 'socks5h://localhost:9050', // Replace with your proxy URL
|
||||||
transports = {
|
transports = {
|
||||||
epoxy: '/epoxy/index.mjs',
|
epoxy: '/epoxy/index.mjs',
|
||||||
libcurl: '/libcurl/index.mjs',
|
libcurl: '/libcurl/index.mjs',
|
||||||
bare: '/baremux/index.mjs',
|
bare: '/baremux/index.mjs',
|
||||||
},
|
},
|
||||||
// The following two variables are copied and pasted here from csel.js.
|
// The following two variables are copied and pasted here from csel.js.
|
||||||
readCookie = async (name) => {
|
readCookie = async (name) => {
|
||||||
// Get the first cookie that has the same name.
|
// Get the first cookie that has the same name.
|
||||||
for (let cookie of document.cookie.split('; '))
|
for (let cookie of document.cookie.split('; '))
|
||||||
if (!cookie.indexOf(name + '='))
|
if (!cookie.indexOf(name + '='))
|
||||||
// Return the cookie's stored content.
|
// Return the cookie's stored content.
|
||||||
return decodeURIComponent(cookie.slice(name.length + 1));
|
return decodeURIComponent(cookie.slice(name.length + 1));
|
||||||
},
|
},
|
||||||
// Sets the default transport mode based on the browser. Firefox is not
|
// Sets the default transport mode based on the browser. Firefox is not
|
||||||
// supported by epoxy yet, which is why this is implemented.
|
// supported by epoxy yet, which is why this is implemented.
|
||||||
defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent)
|
defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent)
|
||||||
? 'epoxy'
|
? 'epoxy'
|
||||||
: 'libcurl';
|
: 'libcurl';
|
||||||
|
|
||||||
transports.default = transports[defaultMode];
|
transports.default = transports[defaultMode];
|
||||||
|
|
||||||
// Prevent the transports object from accidentally being edited.
|
// Prevent the transports object from accidentally being edited.
|
||||||
Object.freeze(transports);
|
Object.freeze(transports);
|
||||||
|
|
||||||
const registerSW = async () => {
|
const registerSW = async () => {
|
||||||
if (!navigator.serviceWorker) {
|
if (!navigator.serviceWorker) {
|
||||||
if (
|
if (
|
||||||
location.protocol !== 'https:' &&
|
location.protocol !== 'https:' &&
|
||||||
!swAllowedHostnames.includes(location.hostname)
|
!swAllowedHostnames.includes(location.hostname)
|
||||||
)
|
)
|
||||||
throw new Error('Service workers cannot be registered without https.');
|
throw new Error('Service workers cannot be registered without https.');
|
||||||
|
|
||||||
throw new Error("Your browser doesn't support service workers.");
|
throw new Error("Your browser doesn't support service workers.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user has changed the transport mode, use that over the default.
|
// If the user has changed the transport mode, use that over the default.
|
||||||
const transportMode =
|
const transportMode =
|
||||||
transports[await readCookie('HBTransport')] || transports.default;
|
transports[await readCookie('HBTransport')] || transports.default;
|
||||||
let transportOptions = { wisp: wispUrl };
|
let transportOptions = { wisp: wispUrl };
|
||||||
|
|
||||||
// Only use Tor with the proxy if the user has enabled it in settings.
|
// Only use Tor with the proxy if the user has enabled it in settings.
|
||||||
if ((await readCookie('HBUseOnion')) === 'true')
|
if ((await readCookie('HBUseOnion')) === 'true')
|
||||||
transportOptions.proxy = proxyUrl;
|
transportOptions.proxy = proxyUrl;
|
||||||
|
|
||||||
await connection.setTransport(transportMode, [transportOptions]);
|
await connection.setTransport(transportMode, [transportOptions]);
|
||||||
|
|
||||||
// Choose a service worker to register based on whether or not the user
|
/* Choose a service worker to register based on whether or not the user
|
||||||
// has ads enabled. If the user changes this setting, this script needs
|
* has ads enabled. If the user changes this setting, this script needs
|
||||||
// to be reloaded for this to update, such as by refreshing the page.
|
* to be reloaded for this to update, such as by refreshing the page.
|
||||||
const registrations = await navigator.serviceWorker.getRegistrations(),
|
*/
|
||||||
usedSW =
|
const registrations = await navigator.serviceWorker.getRegistrations(),
|
||||||
(await readCookie('HBHideAds')) !== 'false' ? blacklistSW : stockSW;
|
usedSW =
|
||||||
|
(await readCookie('HBHideAds')) !== 'false' ? blacklistSW : stockSW;
|
||||||
|
|
||||||
// Unregister a service worker if it isn't the one being used.
|
// Unregister a service worker if it isn't the one being used.
|
||||||
for (const registration of registrations)
|
for (const registration of registrations)
|
||||||
if (
|
if (
|
||||||
registration.active &&
|
registration.active &&
|
||||||
new URL(registration.active.scriptURL).pathname !==
|
new URL(registration.active.scriptURL).pathname !==
|
||||||
new URL(usedSW, location.origin).pathname
|
new URL(usedSW, location.origin).pathname
|
||||||
)
|
)
|
||||||
await registration.unregister();
|
await registration.unregister();
|
||||||
|
|
||||||
await navigator.serviceWorker.register(usedSW);
|
await navigator.serviceWorker.register(usedSW);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Commented out upon discovering that a duplicate BareMux connection may be
|
Commented out upon discovering that a duplicate BareMux connection may be
|
||||||
unnecessary; previously thought to have prevented issues with refreshing.
|
unnecessary; previously thought to have prevented issues with refreshing.
|
||||||
|
@ -89,9 +90,9 @@ async function setupTransportOnLoad() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run transport setup on page load.
|
// Run transport setup on page load.
|
||||||
setupTransportOnLoad();
|
setupTransportOnLoad();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
registerSW();
|
registerSW();
|
||||||
})();
|
})();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue