Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
FROM node:19-bullseye
FROM node:20-bullseye
ENV NODE_ENV=production
WORKDIR /app
COPY ["package.json", "package-lock.json*", "./"]
Expand Down
52 changes: 32 additions & 20 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import express from 'express';
import http from 'node:http';
import path from 'node:path';
import { epoxyPath } from "@mercuryworkshop/epoxy-transport";
import { baremuxPath } from "@mercuryworkshop/bare-mux/node";
import { createBareServer } from "@tomphttp/bare-server-node";
import { uvPath } from "@titaniumnetwork-dev/ultraviolet";
import { server as wisp } from "@mercuryworkshop/wisp-js/server";
import request from '@cypress/request';
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import { epoxyPath } from '@mercuryworkshop/epoxy-transport';
import { baremuxPath } from '@mercuryworkshop/bare-mux/node';
import { createBareServer } from '@tomphttp/bare-server-node';
Comment on lines +4 to +8
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cypress/request is no longer imported anywhere in the codebase, but it remains in package.json dependencies. Consider removing it to reduce install size and avoid carrying an unused dependency.

Copilot uses AI. Check for mistakes.
import { uvPath } from '@titaniumnetwork-dev/ultraviolet';
import { server as wisp } from '@mercuryworkshop/wisp-js/server';
import chalk from 'chalk';
import packageJson from './package.json' with { type: 'json' };

const __dirname = path.resolve();
const require = createRequire(import.meta.url);
const packageJson = require('./package.json');
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const server = http.createServer();
const bareServer = createBareServer('/seal/');
const app = express(server);
const app = express();
const version = packageJson.version;
const discord = 'https://discord.gg/unblocking';

const routes = [
{ route: '/mastery', file: './static/loader.html' },
{ route: '/apps', file: './static/apps.html' },
{ route: '/gms', file: './static/gms.html' },
{ route: '/lessons', file: './static/agloader.html' },
{ route: '/lessons', file: './static/loader.html' },
{ route: '/info', file: './static/info.html' },
{ route: '/mycourses', file: './static/loading.html' }
{ route: '/mycourses', file: './static/loading.html' },
];

app.use(express.json());
Expand All @@ -34,9 +36,9 @@ app.use(
);

app.use(express.static(path.join(__dirname, 'static')));
app.use("/uv/", express.static(uvPath));
app.use("/epoxy/", express.static(epoxyPath));
app.use("/baremux/", express.static(baremuxPath));
app.use('/uv/', express.static(uvPath));
app.use('/epoxy/', express.static(epoxyPath));
app.use('/baremux/', express.static(baremuxPath));

routes.forEach(({ route, file }) => {
app.get(route, (req, res) => {
Expand All @@ -45,7 +47,15 @@ routes.forEach(({ route, file }) => {
});

app.get('/student', (req, res) => {
res.redirect('/portal');
res.redirect('/mastery');
});

app.get('/login.html', (req, res) => {
res.sendFile(path.join(__dirname, './login.html'));
});

app.get('/google0f9bc6787d77dc96.html', (req, res) => {
res.sendFile(path.join(__dirname, './google0f9bc6787d77dc96.html'));
});

// FIXED: Removed broken worker.js mirror route
Expand All @@ -62,16 +72,16 @@ app.use((req, res) => {
res.sendFile(path.join(__dirname, './static/404.html'));
});

server.on("request", (req, res) => {
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)) {
bareServer.routeUpgrade(req, socket, head);
} else if (req.url.endsWith("/wisp/") || req.url.endsWith("/wisp")) {
} else if (req.url.endsWith('/wisp/') || req.url.endsWith('/wisp')) {
wisp.routeRequest(req, socket, head);
} else {
socket.end();
Expand All @@ -86,7 +96,9 @@ server.on('listening', () => {
console.log(chalk.green(' 🕒 Time: ') + chalk.bold(new Date().toLocaleTimeString()));
console.log(chalk.cyan('-----------------------------------------------'));
console.log(chalk.magenta('📦 Version: ') + chalk.bold(version));
console.log(chalk.magenta('🔗 URL: ') + chalk.underline('http://localhost:' + server.address().port));
console.log(
chalk.magenta('🔗 URL: ') + chalk.underline('http://localhost:' + server.address().port)
);
console.log(chalk.cyan('-----------------------------------------------'));
console.log(chalk.blue('💬 Discord: ') + chalk.underline(discord));
console.log(chalk.cyan('-----------------------------------------------'));
Expand All @@ -99,7 +111,7 @@ function shutdown(signal) {
console.log(chalk.yellow(' 🕒 Time: ') + chalk.bold(new Date().toLocaleTimeString()));
console.log(chalk.red('-----------------------------------------------'));
console.log(chalk.blue(' Exiting immediately...'));
process.exit(1);
process.exit(0);
}

process.on('SIGTERM', () => shutdown('SIGTERM'));
Expand Down
2 changes: 1 addition & 1 deletion start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ print_bold_with_outline() {
done

echo -e "\e[1m\e[44m$border\e[0m"
echo -e "\e[1m\e[44m# $msg #\e[0m
echo -e "\e[1m\e[44m# $msg #\e[0m"
echo -e "\e[1m\e[44m$border\e[0m"
}

Expand Down
67 changes: 37 additions & 30 deletions static/assets/js/main.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,62 @@
// Preconnect to CDN
var preconnect = document.createElement("link");
preconnect.rel = "preconnect";
preconnect.href = "https://cdn.jsdelivr.net";
var preconnect = document.createElement('link');
preconnect.rel = 'preconnect';
preconnect.href = 'https://cdn.jsdelivr.net';
document.head.appendChild(preconnect);

// Preload Bootstrap icons CSS
var preload = document.createElement("link");
preload.rel = "preload";
preload.as = "style";
preload.href = "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css";
var preload = document.createElement('link');
preload.rel = 'preload';
preload.as = 'style';
preload.href = 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css';
document.head.appendChild(preload);

function loadScript(src, callback) {
var script = document.createElement("script");
script.type = "text/javascript";
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
script.defer = true;
if (callback) script.onload = callback;
document.head.appendChild(script);
}

function loadCSS(href, callback) {
var link = document.createElement("link");
link.rel = "stylesheet";
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
var supportsOnLoad = "onload" in link;
var supportsOnLoad = 'onload' in link;
if (supportsOnLoad) {
link.onload = callback;
} else {
setTimeout(function() {
setTimeout(function () {
callback();
}, 1000);
}
document.head.appendChild(link);
}

loadCSS('https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css', function() {});
loadCSS(
'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css',
function () {}
);

if (window.location.pathname === "/index.html" || window.location.pathname === "/") {
const options = ["Right-Click to access more features", "Set a custom background in settings.", "Tab Cloaking is highly recommended", "About:Blank Cloak is highly recommended", "This site was originally created for fun and educational purposes."];
if (window.location.pathname === '/index.html' || window.location.pathname === '/') {
const options = [
'Right-Click to access more features',
'Set a custom background in settings.',
'Tab Cloaking is highly recommended',
'About:Blank Cloak is highly recommended',
'This site was originally created for fun and educational purposes.',
];

function getRandomOption() {
const randomNumber = Math.floor(Math.random() * options.length);
return options[randomNumber];
}

const placeholder = document.getElementById("placeholder");
const bar = document.querySelector(".browse-input");
const search = document.getElementById("search");
const placeholder = document.getElementById('placeholder');
const bar = document.querySelector('.browse-input');
const search = document.getElementById('search');

function setRandomPlaceholder() {
if (!placeholder) return;
Expand All @@ -57,26 +66,24 @@ if (window.location.pathname === "/index.html" || window.location.pathname === "
setRandomPlaceholder();

if (bar && search) {
bar.addEventListener("focus", () => {
search.style.marginLeft = "-367px";
bar.addEventListener('focus', () => {
search.style.marginLeft = '-367px';
});

bar.addEventListener("blur", () => {
search.style.marginLeft = "-150px";
bar.addEventListener('blur', () => {
search.style.marginLeft = '-150px';
});
}
}

window.addEventListener("load", function() {
window.addEventListener('load', function () {
// Register service worker for asset caching
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}

loadScript("/worker.js");

if (window.location.pathname === "/index.html" || window.location.pathname === "/") {
location.href = "/math.html";
if (window.location.pathname === '/index.html' || window.location.pathname === '/') {
location.href = '/math.html';
}

Comment on lines +85 to 88
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The client-side redirect from '/' or '/index.html' to '/math.html' makes the index page effectively unreachable (and runs index-only UI setup work that users never see). If the intent is to change the landing page, consider doing this redirect server-side (or removing it) and ensure the service worker pre-cache includes '/math.html' so the first navigation still works offline.

Suggested change
if (window.location.pathname === '/index.html' || window.location.pathname === '/') {
location.href = '/math.html';
}

Copilot uses AI. Check for mistakes.
if (window.location.pathname === '/loading.html') {
Expand Down
71 changes: 70 additions & 1 deletion static/sw.js
Original file line number Diff line number Diff line change
@@ -1 +1,70 @@
importScripts("/uv/uv.bundle.js"),importScripts("/uv/uv.config.js"),importScripts("/uv/uv.sw.js");const CACHE_NAME="arctic-v1.0",urlsToCache=["/static/","/static/index.html","/static/settings.html","/static/assets/css/app.css","/static/assets/css/menu.css","/static/assets/js/particles.js","/static/assets/js/themes.js","/static/assets/js/index.js","/static/assets/js/anym.js","/static/assets/js/main.js","/static/assets/img/salyte.jpg","/static/android-chrome-192x192.png","/static/android-chrome-512x512.png","/static/wk/wk2.js","/static/wk/wk3.js"],uv=new UVServiceWorker;self.addEventListener("install",event=>{event.waitUntil(caches.open(CACHE_NAME).then(cache=>cache.addAll(urlsToCache.map(url=>new Request(url,{cache:"reload"}))))),self.skipWaiting()}),self.addEventListener("activate",event=>(event.waitUntil(caches.keys().then(cacheNames=>Promise.all(cacheNames.map(cacheName=>{if(cacheName!==CACHE_NAME)return caches.delete(cacheName)})))),self.clients.claim()));async function handleRequest(event){if(uv.route(event))return await uv.fetch(event);const cachedResponse=await caches.match(event.request);if(cachedResponse)return cachedResponse;try{const response=await fetch(event.request);if(response&&response.status===200&&response.type==="basic"){const responseToCache=response.clone();caches.open(CACHE_NAME).then(cache=>{cache.put(event.request,responseToCache)})}return response}catch{return await fetch(event.request)}}self.addEventListener("fetch",event=>{event.respondWith(handleRequest(event))})
importScripts('/uv/uv.bundle.js');
importScripts('/uv/uv.config.js');
importScripts('/uv/uv.sw.js');

const CACHE_NAME = 'arctic-v1.0';
const urlsToCache = [
'/',
'/index.html',
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pre-cache list doesn't include '/math.html', but the app now redirects users there from '/' and '/index.html'. This can cause a broken/offline experience (and even an extra network round-trip online) since the redirected-to document isn't guaranteed to be cached.

Suggested change
'/index.html',
'/index.html',
'/math.html',

Copilot uses AI. Check for mistakes.
'/settings.html',
'/assets/css/app.css',
'/assets/css/menu.css',
'/assets/js/particles.js',
'/assets/js/themes.js',
'/assets/js/index.js',
Comment on lines +10 to +14
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HTML requests versioned assets (e.g. /assets/css/app.css?v=3), but the pre-cache list uses unversioned URLs. Since caches.match(event.request) matches on the full URL including query params, these pre-cached entries won't satisfy the versioned requests. Consider caching the exact versioned URLs or adjusting the matching strategy (e.g. ignoreSearch) if appropriate.

Copilot uses AI. Check for mistakes.
'/assets/js/anym.js',
'/assets/js/main.js',
Comment on lines +11 to +16
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The service worker pre-caches '/settings.html' but not its required assets (notably '/assets/css/settings.css?v=3' and '/assets/js/settings.js?v=3'), so the settings page won't work offline even though the document is cached. Consider adding those dependencies to urlsToCache (matching the versioned URLs used in the HTML).

Suggested change
'/assets/css/menu.css',
'/assets/js/particles.js',
'/assets/js/themes.js',
'/assets/js/index.js',
'/assets/js/anym.js',
'/assets/js/main.js',
'/assets/css/menu.css',
'/assets/css/settings.css?v=3',
'/assets/js/particles.js',
'/assets/js/themes.js',
'/assets/js/index.js',
'/assets/js/anym.js',
'/assets/js/main.js',
'/assets/js/settings.js?v=3',

Copilot uses AI. Check for mistakes.
'/assets/img/salyte.jpg',
'/wk/wk2.js',
'/wk/wk3.js',
];

const uv = new UVServiceWorker();

self.addEventListener('install', (event) => {
event.waitUntil(
caches
.open(CACHE_NAME)
.then((cache) =>
cache.addAll(urlsToCache.map((url) => new Request(url, { cache: 'reload' })))
)
);
self.skipWaiting();
});

self.addEventListener('activate', (event) => {
event.waitUntil(
caches
.keys()
.then((cacheNames) =>
Promise.all(
cacheNames.map((cacheName) =>
cacheName !== CACHE_NAME ? caches.delete(cacheName) : undefined
)
)
)
);
self.clients.claim();
});

async function handleRequest(event) {
if (uv.route(event)) return await uv.fetch(event);

const cachedResponse = await caches.match(event.request);
if (cachedResponse) return cachedResponse;

try {
const response = await fetch(event.request);
if (response && response.status === 200 && response.type === 'basic') {
const responseToCache = response.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, responseToCache));
}
return response;
} catch {
return Response.error();
}
}

self.addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event));
});