Merge branch 'workerware' into upstream-main

This commit is contained in:
Percs 2024-07-11 16:46:46 -05:00 committed by GitHub
commit 10d1a84d9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 5187 additions and 6412 deletions

View file

@ -1,2 +1,3 @@
dist dist
node_modules node_modules
public

View file

@ -16,8 +16,7 @@
<body> <body>
<h1>Welcome to nginx!</h1> <h1>Welcome to nginx!</h1>
<p> <p>
If you see this page, the nginx web server is successfully installed and working. Further If you see this page, the nginx web server is successfully installed and working. Further configuration is required. If you are expecting another page, please check your network or
configuration is required. If you are expecting another page, please check your network or
<a href="/" id="rcheck"><b>Refresh this page</b></a> <a href="/" id="rcheck"><b>Refresh this page</b></a>
</p> </p>

View file

@ -1,9 +1,7 @@
import { defineConfig } from "astro/config"; import { defineConfig } from "astro/config";
import node from "@astrojs/node"; import node from "@astrojs/node";
import sitemap from "@astrojs/sitemap"; import sitemap from "@astrojs/sitemap";
// https://astro.build/config
export default defineConfig({ export default defineConfig({
site: "https://aluu.xyz", site: "https://aluu.xyz",
integrations: [ integrations: [

View file

@ -15,7 +15,6 @@ import { existsSync } from "fs";
import dotenv from "dotenv"; import dotenv from "dotenv";
import cookieParser from "cookie-parser"; import cookieParser from "cookie-parser";
import wisp from "wisp-server-node"; import wisp from "wisp-server-node";
import fetch from "node-fetch";
import { masqrCheck } from "./masqr.js"; import { masqrCheck } from "./masqr.js";
dotenv.config(); dotenv.config();
@ -90,8 +89,22 @@ app.use("/custom-favicon", async (req, res) => {
const buffer = new Buffer.from(await response.arrayBuffer()); const buffer = new Buffer.from(await response.arrayBuffer());
res.set("Content-Type", "image/png"); res.set("Content-Type", "image/png");
res.send(buffer); res.send(buffer);
} catch {} } catch {
res.sendStatus(500);
}
}); });
app.use("/blocklist", async (req, res) => {
try {
const { url } = req.query;
const response = await fetch(url).then((r) => r.text());
res.set("Content-Type", "text/plain");
res.send(response);
} catch {
res.sendStatus(500);
}
});
app.use("/", express.static("dist/client/")); app.use("/", express.static("dist/client/"));
app.get("/favicon.ico", (req, res) => { app.get("/favicon.ico", (req, res) => {
res.sendFile(path.join(process.cwd(), "dist/client/favicon.svg")); res.sendFile(path.join(process.cwd(), "dist/client/favicon.svg"));
@ -107,9 +120,7 @@ app.get("/search", async (req, res) => {
try { try {
const { query } = req.query; const { query } = req.query;
const response = await fetch(`http://api.duckduckgo.com/ac?q=${query}&format=json`).then( const response = await fetch(`http://api.duckduckgo.com/ac?q=${query}&format=json`).then((apiRes) => apiRes.json());
(apiRes) => apiRes.json()
);
res.send(response); res.send(response);
} catch (err) { } catch (err) {

View file

@ -1,4 +1,5 @@
import path from "path"; import path from "path";
import fs from "fs";
const failureFile = fs.readFileSync("Checkfailed.html", "utf8"); const failureFile = fs.readFileSync("Checkfailed.html", "utf8");
@ -30,12 +31,7 @@ export async function masqrCheck(config) {
const auth = Buffer.from(authheader.split(" ")[1], "base64").toString().split(":"); const auth = Buffer.from(authheader.split(" ")[1], "base64").toString().split(":");
const pass = auth[1]; const pass = auth[1];
const licenseCheck = ( const licenseCheck = (await (await fetch(config.licenseServer + pass + "&host=" + req.headers.host)).json())["status"];
await (await fetch(config.licenseServer + pass + "&host=" + req.headers.host)).json()
)["status"];
console.log(
config.licenseServer + pass + "&host=" + req.headers.host + " returned " + licenseCheck
);
if (licenseCheck == "License valid") { if (licenseCheck == "License valid") {
res.cookie("authcheck", "true", { res.cookie("authcheck", "true", {
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000),

View file

@ -12,6 +12,7 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/node": "^8.2.5", "@astrojs/node": "^8.2.5",
"@astrojs/partytown": "^2.1.1",
"@astrojs/sitemap": "^3.1.4", "@astrojs/sitemap": "^3.1.4",
"@mercuryworkshop/bare-mux": "^2.0.1", "@mercuryworkshop/bare-mux": "^2.0.1",
"@mercuryworkshop/epoxy-transport": "^2.1.3", "@mercuryworkshop/epoxy-transport": "^2.1.3",
@ -25,7 +26,6 @@
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"node-fetch": "^3.3.2",
"notyf": "^3.10.0", "notyf": "^3.10.0",
"rammerhead": "https://github.com/NebulaServices/rammerhead/releases/download/rammerhead-1.2.41-nebula.8/rammerhead-1.2.41-nebula.7.tgz", "rammerhead": "https://github.com/NebulaServices/rammerhead/releases/download/rammerhead-1.2.41-nebula.8/rammerhead-1.2.41-nebula.7.tgz",
"wisp-server-node": "^1.1.0" "wisp-server-node": "^1.1.0"

9710
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@ const config = {
bracketSameLine: true, bracketSameLine: true,
arrowParens: "always", arrowParens: "always",
plugins: ["prettier-plugin-astro"], plugins: ["prettier-plugin-astro"],
printWidth: 100, printWidth: 200,
}; };
export default config; export default config;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View file

@ -1 +0,0 @@
console.log("Hello World!");

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,9 @@
[data-theme="oled"] {
--background-color: #000;
--background-highlight: #111;
--accent-color: rgb(30 0 79);
--accent-color-brighter: rgb(60 0 155);
--text-color: #fff;
--text-color-accent: #dddddd;
--dropdown-background-color: #1a1a1a;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -1,12 +1,44 @@
importScripts("/uv/uv.bundle.js"); importScripts("/uv/uv.bundle.js", "/uv.config.js", __uv$config.sw, "/workerware/workerware.js");
importScripts("/uv.config.js");
importScripts(__uv$config.sw); const ww = new WorkerWare({
debug: true,
});
function loadExtensionScripts() {
try {
let db = indexedDB.open("AluDB", 1);
db.onsuccess = () => {
let transaction = db.result.transaction("InstalledExtensions", "readonly");
let store = transaction.objectStore("InstalledExtensions");
let request = store.getAll();
request.onsuccess = () => {
let extensions = request.result;
extensions.forEach((extension) => {
if (extension.type != "serviceWorker") return;
eval(atob(extension.scriptCopy));
ww.use({
function: self[extension.entryNamespace][extension.entryFunc],
name: extension.title,
events: ["fetch"],
});
});
};
};
} catch (err) {
console.error(`Failed load extension scripts: ${err}`);
}
}
loadExtensionScripts();
const uv = new UVServiceWorker(); const uv = new UVServiceWorker();
self.addEventListener("fetch", (event) => { self.addEventListener("fetch", async (event) => {
event.respondWith( event.respondWith(
(async () => { (async () => {
let mwResponse = await ww.run(event)();
if (mwResponse.includes(null)) {
return;
}
if (event.request.url.startsWith(location.origin + __uv$config.prefix)) { if (event.request.url.startsWith(location.origin + __uv$config.prefix)) {
return await uv.fetch(event); return await uv.fetch(event);
} }

View file

@ -0,0 +1,6 @@
class WWError extends Error {
constructor(message) {
super(message);
this.name = "[WorkerWare Exception]";
}
}

View file

@ -0,0 +1,143 @@
importScripts("/workerware/WWError.js");
const dbg = console.log.bind(console, "[WorkerWare]");
const time = console.time.bind(console, "[WorkerWare]");
const timeEnd = console.timeEnd.bind(console, "[WorkerWare]");
/*
OPTS:
debug - Enables debug logging.
randomNames - Generate random names for middlewares.
timing - Logs timing for each middleware.
*/
const defaultOpt = {
debug: false,
randomNames: false,
timing: false,
};
const validEvents = [
"abortpayment",
"activate",
"backgroundfetchabort",
"backgroundfetchclick",
"backgroundfetchfail",
"backgroundfetchsuccess",
"canmakepayment",
"contentdelete",
"cookiechange",
"fetch",
"install",
"message",
"messageerror",
"notificationclick",
"notificationclose",
"paymentrequest",
"periodicsync",
"push",
"pushsubscriptionchange",
"sync",
];
class WorkerWare {
constructor(opt) {
this._opt = Object.assign({}, defaultOpt, opt);
this._middlewares = [];
}
info() {
return {
version: "0.1.0",
middlewares: this._middlewares,
options: this._opt,
};
}
use(middleware) {
let validateMW = this.validateMiddleware(middleware);
if (validateMW.error) throw new WWError(validateMW.error);
// This means the middleware is an anonymous function, or the user is silly and named their function "function"
if (middleware.function.name == "function") middleware.name = crypto.randomUUID();
if (!middleware.name) middleware.name = middleware.function.name;
if (this._opt.randomNames) middleware.name = crypto.randomUUID();
if (this._opt.debug) dbg("Adding middleware:", middleware.name);
this._middlewares.push(middleware);
}
// Run all middlewares for the event type passed in.
run(event) {
const middlewares = this._middlewares;
const returnList = [];
let fn = async () => {
for (let i = 0; i < middlewares.length; i++) {
if (middlewares[i].events.includes(event.type)) {
if (this._opt.timing) console.time(middlewares[i].name);
// Add the configuration to the event object.
event.workerware = {
config: middlewares[i].configuration || {},
};
let res = await middlewares[i].function(event);
if (this._opt.timing) console.timeEnd(middlewares[i].name);
returnList.push(res);
}
}
return returnList;
};
return fn;
}
deleteByName(middlewareID) {
if (this._opt.debug) dbg("Deleting middleware:", middlewareID);
this._middlewares = this._middlewares.filter((mw) => mw.name !== middlewareID);
}
deleteByEvent(middlewareEvent) {
if (this._opt.debug) dbg("Deleting middleware by event:", middlewareEvent);
this._middlewares = this._middlewares.filter((mw) => !mw.events.includes(middlewareEvent));
}
get() {
return this._middlewares;
}
/*
Run a single middleware by ID.
This assumes that the user knows what they're doing, and is running the middleware on an event that it's supposed to run on.
*/
runMW(id, event) {
const middlewares = this._middlewares;
if (this._opt.debug) dbg("Running middleware:", id);
if (middlewares.includes(id)) {
return middlewares[id](event);
}
}
// type middlewareManifest = {
// function: Function,
// name?: string,
// events: string[], // Should be a union of validEvents.
// configuration?: Object // Optional configuration for the middleware.
// }
validateMiddleware(middleware) {
if (!middleware.function)
return {
error: "middleware.function is required",
};
if (typeof middleware.function !== "function")
return {
error: "middleware.function must be typeof function",
};
if (typeof middleware.configuration !== "object" && middleware.configuration !== undefined) {
return {
error: "middleware.configuration must be typeof object",
};
}
if (!middleware.events)
return {
error: "middleware.events is required",
};
if (!Array.isArray(middleware.events))
return {
error: "middleware.events must be an array",
};
if (middleware.events.some((ev) => !validEvents.includes(ev)))
return {
error: "Invalid event type! Must be one of the following: " + validEvents.join(", "),
};
return {
error: undefined,
};
}
}

View file

@ -2,7 +2,7 @@
const { name, image, slugName } = Astro.props; const { name, image, slugName } = Astro.props;
--- ---
<div class="game"> <div class="game" data-name={name}>
<a href=`/games/${slugName || name}`> <a href=`/games/${slugName || name}`>
<img class="game-img" src={image} alt="" /> <img class="game-img" src={image} alt="" />
</a> </a>

View file

@ -62,10 +62,7 @@
else if (!(url.startsWith("https://") || url.startsWith("http://"))) url = "http://" + url; else if (!(url.startsWith("https://") || url.startsWith("http://"))) url = "http://" + url;
if (openWith) { if (openWith) {
let openWithParsed = JSON.parse(openWith); let openWithParsed = JSON.parse(openWith);
if ( if (openWithParsed.value === "newTab" || (currentProxy && JSON.parse(currentProxy).value === "rammerhead")) {
openWithParsed.value === "newTab" ||
(currentProxy && JSON.parse(currentProxy).value === "rammerhead")
) {
window.open(await getProxyURL(), "_blank"); window.open(await getProxyURL(), "_blank");
return; return;
} }
@ -118,15 +115,7 @@
iframe.classList.add("proxy-frame"); iframe.classList.add("proxy-frame");
document.body.appendChild(iframe); document.body.appendChild(iframe);
setTimeout(() => { setTimeout(() => {
iframeLoad( iframeLoad();
iframe,
loadingContent,
topbar,
closeButton,
shareButton,
forwardsButton,
backwardsButton
);
}, 500); }, 500);
function setActive() { function setActive() {
@ -157,15 +146,7 @@
// } // }
// } // }
function iframeLoad( function iframeLoad() {
iframe: HTMLIFrameElement,
loadingContent: HTMLElement,
topbar: HTMLDivElement,
closeButton: HTMLButtonElement,
shareButton: HTMLImageElement,
forwardsButton: HTMLImageElement,
backwardsButton: HTMLImageElement
) {
loadingContent.style.opacity = "0"; loadingContent.style.opacity = "0";
iframe.style.opacity = "1"; iframe.style.opacity = "1";
topbar.style.opacity = "1"; topbar.style.opacity = "1";
@ -199,13 +180,7 @@
let currentProxy = localStorage.getItem("alu__selectedProxy"); let currentProxy = localStorage.getItem("alu__selectedProxy");
let proxyFrame = document.getElementById("proxy-frame") as HTMLIFrameElement; let proxyFrame = document.getElementById("proxy-frame") as HTMLIFrameElement;
if (currentProxy && JSON.parse(currentProxy).value === "rammerhead") { if (currentProxy && JSON.parse(currentProxy).value === "rammerhead") {
navigator.clipboard.writeText( navigator.clipboard.writeText(window.location.origin + "/" + getCookie("rammerhead-session") + "/" + input!.value.trim());
window.location.origin +
"/" +
getCookie("rammerhead-session") +
"/" +
input!.value.trim()
);
} else { } else {
navigator.clipboard.writeText(getUVProxyURL(proxyFrame)); navigator.clipboard.writeText(getUVProxyURL(proxyFrame));
} }
@ -293,14 +268,12 @@
} }
} }
function updateProxiedFavicon(iframe: HTMLIFrameElement, hasErrored = false) { function updateProxiedFavicon(iframe: HTMLIFrameElement) {
if (!iframe) return; if (!iframe) return;
let proxiedFavicon = document.getElementById("proxied-favicon") as HTMLImageElement; let proxiedFavicon = document.getElementById("proxied-favicon") as HTMLImageElement;
if (iframe) { if (iframe) {
if (iframe.contentDocument) { if (iframe.contentDocument) {
let favicon = let favicon = (iframe.contentDocument.querySelector("link[rel='icon']") as HTMLLinkElement) || (iframe.contentDocument.querySelector("link[rel*='icon']") as HTMLLinkElement);
(iframe.contentDocument.querySelector("link[rel='icon']") as HTMLLinkElement) ||
(iframe.contentDocument.querySelector("link[rel*='icon']") as HTMLLinkElement);
if (favicon && favicon.href.includes("data:image")) { if (favicon && favicon.href.includes("data:image")) {
proxiedFavicon.src = favicon.href; proxiedFavicon.src = favicon.href;
return; return;

View file

@ -45,10 +45,7 @@ const presetCloaks = [
{ {
presetCloaks.map((cloak: any) => { presetCloaks.map((cloak: any) => {
return ( return (
<div <div class="cloak-item" data-cloak-name={cloak.cloakTitle} data-cloak-icon={cloak.favicon}>
class="cloak-item"
data-cloak-name={cloak.cloakTitle}
data-cloak-icon={cloak.favicon}>
<img class="cloak-image" src={cloak.favicon} alt={cloak.cloakTitle} /> <img class="cloak-image" src={cloak.favicon} alt={cloak.cloakTitle} />
</div> </div>
); );

View file

@ -6,32 +6,16 @@ const t = i18n.inferLangUseTranslations(Astro.url);
<div class="settings-container"> <div class="settings-container">
<div class="credits-container"> <div class="credits-container">
<p class="credit-item"> <p class="credit-item">
{t("ultraviolet")} - <a {t("ultraviolet")} - <a target="_blank" rel="noreferrer noopener" href="https://titaniumnetwork.org/">Titanium Network</a>
target="_blank"
rel="noreferrer noopener"
href="https://titaniumnetwork.org/">Titanium Network</a
>
</p> </p>
<p class="credit-item"> <p class="credit-item">
{t("settings.credits.japaneseTranslations")} - <a {t("settings.credits.japaneseTranslations")} - <a target="_blank" rel="noreferrer noopener" href="https://wearr.dev">wearr</a>
target="_blank"
rel="noreferrer noopener"
href="https://wearr.dev">wearr</a
>
</p> </p>
<p class="credit-item"> <p class="credit-item">
{t("settings.credits.mochaandmacchiatothemes")} - <a {t("settings.credits.mochaandmacchiatothemes")} - <a target="_blank" rel="noreferrer noopener" href="https://github.com/catppuccin/catppuccin">Catppuccin</a>
target="_blank"
rel="noreferrer noopener"
href="https://github.com/catppuccin/catppuccin">Catppuccin</a
>
</p> </p>
<p class="credit-item"> <p class="credit-item">
Rosé Pine Theme - <a Rosé Pine Theme - <a target="_blank" rel="noreferrer noopener" href="https://rosepinetheme.com/">Rosé Pine</a>
target="_blank"
rel="noreferrer noopener"
href="https://rosepinetheme.com/">Rosé Pine</a
>
</p> </p>
</div> </div>
</div> </div>

View file

@ -23,12 +23,60 @@ const languageList = [
<Dropdown buttonNameDefault="Alu" dropdownList={themeList} id="dropdown__selected-theme" /> <Dropdown buttonNameDefault="Alu" dropdownList={themeList} id="dropdown__selected-theme" />
</div> </div>
<div class="setting__language"> <div class="setting__language">
<label aria-label="Language" class="setting-label">{t("settings.customization.language")}</label <label aria-label="Language" class="setting-label">{t("settings.customization.language")}</label>
> <Dropdown buttonNameDefault="English" dropdownList={languageList} id="dropdown__selected-language" />
<Dropdown
buttonNameDefault="English"
dropdownList={languageList}
id="dropdown__selected-language"
/>
</div> </div>
</div> </div>
<script>
// import IDBManager, { type ExtensionMetadata } from "@components/ts/IDBManager";
// let idb = IDBManager.GetIDB();
// if (!(idb instanceof IDBDatabase)) {
// idb = await loadIndexedDB();
// }
// let store = IDBManager.GetStore("InstalledExtensions", "readonly");
// store.getAll().onsuccess = (event) => {
// const result = (event.target as IDBRequest).result;
// if (result) {
// result.forEach((extension: ExtensionMetadata) => {
// if (extension.type === "theme" && extension.themeName) {
// // Create a dropdown item for the theme
// addThemeToDropdown(extension);
// }
// });
// }
// };
// async function loadIndexedDB(): Promise<IDBDatabase> {
// return new Promise((resolve, reject) => {
// const load = IDBManager.loadIDB("AluDB", 1);
// load.onsuccess = () => {
// const db = load.result;
// IDBManager.SetIDB(db);
// resolve(idb);
// };
// });
// }
// function addThemeToDropdown(extension: ExtensionMetadata) {
// let dropdown = document.getElementById("dropdown__selected-theme-menu");
// if (dropdown) {
// let themeItem = document.createElement("li");
// themeItem.classList.add("dropdown-item");
// themeItem.dataset.setting = extension.themeName;
// themeItem.textContent = extension.title!;
// dropdown.appendChild(themeItem);
// themeItem.addEventListener("click", () => {
// localStorage.setItem("alu__selectedTheme", JSON.stringify({ name: extension.title, value: extension.themeName }));
// });
// } else {
// setTimeout(() => {
// addThemeToDropdown(extension);
// }, 1000);
// }
// }
</script>

View file

@ -33,63 +33,31 @@ const transportsList = [
<div class="settings-container"> <div class="settings-container">
<div class="setting__selected-proxy"> <div class="setting__selected-proxy">
<label aria-label="Selected Proxy" class="setting-label" <label aria-label="Selected Proxy" class="setting-label">{t("settings.proxy.selectedProxy")}</label>
>{t("settings.proxy.selectedProxy")}</label <Dropdown buttonNameDefault="Ultraviolet" dropdownList={proxyList} localStorageKey="alu__selectedProxy" id="dropdown__selected-proxy" />
>
<Dropdown
buttonNameDefault="Ultraviolet"
dropdownList={proxyList}
localStorageKey="alu__selectedProxy"
id="dropdown__selected-proxy"
/>
</div> </div>
<div class="setting__search-engine"> <div class="setting__search-engine">
<label aria-label="Search Engine" class="setting-label" <label aria-label="Search Engine" class="setting-label">{t("settings.proxy.searchEngine")}</label>
>{t("settings.proxy.searchEngine")}</label <Dropdown buttonNameDefault="Google" dropdownList={searchEngineList} localStorageKey="alu__searchEngine" id="dropdown__search-engine" />
>
<Dropdown
buttonNameDefault="Google"
dropdownList={searchEngineList}
localStorageKey="alu__searchEngine"
id="dropdown__search-engine"
/>
</div> </div>
<div class="setting__open_with"> <div class="setting__open_with">
<label aria-label="Open Page With" class="setting-label" <label aria-label="Open Page With" class="setting-label">{t("settings.proxy.openPageWith")}</label>
>{t("settings.proxy.openPageWith")}</label <Dropdown buttonNameDefault={t("settings.proxy.openPageWith.embed")} dropdownList={openPageWith} localStorageKey="alu__selectedOpenWith" id="dropdown__open-with" />
>
<Dropdown
buttonNameDefault={t("settings.proxy.openPageWith.embed")}
dropdownList={openPageWith}
localStorageKey="alu__selectedOpenWith"
id="dropdown__open-with"
/>
</div> </div>
<div class="setting__wisp_url"> <div class="setting__wisp_url">
<label aria-label="Wisp URL" for="wisp-url-input" class="setting-label" <label aria-label="Wisp URL" for="wisp-url-input" class="setting-label">{t("settings.proxy.wispURL")}</label>
>{t("settings.proxy.wispURL")}</label
>
<Input height="50px" inputName="wisp-url" /> <Input height="50px" inputName="wisp-url" />
</div> </div>
<div class="setting__bare_url"> <div class="setting__bare_url">
<label aria-label="Bare Server URL" for="bare-url-input" class="setting-label" <label aria-label="Bare Server URL" for="bare-url-input" class="setting-label">{t("settings.proxy.bareURL")}</label>
>{t("settings.proxy.bareURL")}</label
>
<Input height="50px" inputName="bare-url" /> <Input height="50px" inputName="bare-url" />
</div> </div>
<div class="setting__transport"> <div class="setting__transport">
<label aria-label="Wisp Transport" class="setting-label">{t("settings.proxy.transport")}</label> <label aria-label="Wisp Transport" class="setting-label">{t("settings.proxy.transport")}</label>
<Dropdown <Dropdown buttonNameDefault="Epoxy" dropdownList={transportsList} localStorageKey="alu__selectedTransport" id="dropdown__transport" />
buttonNameDefault="Epoxy"
dropdownList={transportsList}
localStorageKey="alu__selectedTransport"
id="dropdown__transport"
/>
</div> </div>
</div> </div>
<div class="setting__searxng-url"> <div class="setting__searxng-url">
<label aria-label="SearXNG URL" for="searxng-url-input" class="setting-label" <label aria-label="SearXNG URL" for="searxng-url-input" class="setting-label">{t("settings.proxy.searxngURL")}</label>
>{t("settings.proxy.searxngURL")}</label
>
<Input height="50px" inputName="searxng-url" defaultTextContent="https://searxng.site/" /> <Input height="50px" inputName="searxng-url" defaultTextContent="https://searxng.site/" />
</div> </div>

View file

@ -1,19 +1,51 @@
<script> <script>
function switchTheme() { function switchTheme() {
let currentTheme = localStorage.getItem("alu__selectedTheme"); let currentTheme = localStorage.getItem("alu__selectedTheme");
if (currentTheme) { if (currentTheme) {
document.documentElement.setAttribute( document.documentElement.setAttribute("data-theme", JSON.parse(currentTheme).value.toLowerCase());
"data-theme",
JSON.parse(currentTheme).value.toLowerCase()
);
let footer = document.getElementById("footer"); let footer = document.getElementById("footer");
if (footer) { if (footer) {
footer.dataset.theme = JSON.parse(currentTheme).value.toLowerCase(); footer.dataset.theme = JSON.parse(currentTheme).value.toLowerCase();
} }
} else {
localStorage.setItem("alu__selectedTheme", JSON.stringify({ name: "Alu", value: "alu" }));
switchTheme();
} }
} }
switchTheme(); switchTheme();
document.addEventListener("astro:after-swap", switchTheme); document.addEventListener("astro:after-swap", switchTheme);
import IDBManager, { type ExtensionMetadata } from "@components/ts/IDBManager";
const idb = IDBManager.loadIDB("AluDB", 1);
idb.onsuccess = () => {
const db = idb.result;
IDBManager.SetIDB(db);
let store = IDBManager.GetStore("InstalledExtensions", "readonly");
store.getAll().onsuccess = (event) => {
const result = (event.target as IDBRequest).result;
if (result) {
result.forEach((extension: ExtensionMetadata) => {
if (extension.type === "theme" && extension.themeName) {
// Load theme CSS
window.loadedThemeAtob = atob(extension.scriptCopy!);
loadStyleFromAtob();
document.addEventListener("astro:after-swap", () => {
loadStyleFromAtob();
});
}
});
}
};
}
function loadStyleFromAtob() {
const style = document.createElement("style");
style.textContent = window.loadedThemeAtob;
document.head.appendChild(style);
}
</script> </script>

View file

@ -3,12 +3,7 @@ const { buttonNameDefault, dropdownList, id, localStorageKey } = Astro.props;
--- ---
<div class="dropdown"> <div class="dropdown">
<button <button data-local-storage-key={localStorageKey} id={id} class="dropdown-toggle" type="button" data-toggle="dropdown">
data-local-storage-key={localStorageKey}
id={id}
class="dropdown-toggle"
type="button"
data-toggle="dropdown">
{buttonNameDefault} {buttonNameDefault}
<span class="caret"></span></button <span class="caret"></span></button
> >
@ -24,7 +19,7 @@ const { buttonNameDefault, dropdownList, id, localStorageKey } = Astro.props;
} }
</ul> </ul>
</div> </div>
<style> <style is:global>
.dropdown { .dropdown {
box-shadow: 4px 6px 15px 0px var(--background-color); box-shadow: 4px 6px 15px 0px var(--background-color);
border-bottom-left-radius: 10px; border-bottom-left-radius: 10px;

View file

@ -10,6 +10,7 @@ const t = i18n.useTranslations(lang);
<a href={`/${lang}/`} class="header-item">{t("nav.brand")}</a> <a href={`/${lang}/`} class="header-item">{t("nav.brand")}</a>
</div> </div>
<div class="right"> <div class="right">
<a href={`/${lang}/marketplace/`} class="header-item">Marketplace</a>
<a href={`/${lang}/games/`} class="header-item">{t("nav.games")}</a> <a href={`/${lang}/games/`} class="header-item">{t("nav.games")}</a>
<a href={`/${lang}/settings/`} class="header-item">{t("nav.settings")}</a> <a href={`/${lang}/settings/`} class="header-item">{t("nav.settings")}</a>
</div> </div>

View file

@ -1,13 +1,5 @@
--- ---
const { const { inputName, defaultTextContent, height, placeholder, className, defaultStyles = true, autocomplete = "on" } = Astro.props;
inputName,
defaultTextContent,
height,
placeholder,
className,
defaultStyles = true,
autocomplete = "on",
} = Astro.props;
const styleList = className ? className.split(" ") : []; const styleList = className ? className.split(" ") : [];

View file

@ -1,11 +1,4 @@
<svg <svg width="100%" height="100%" preserveAspectRatio="none" id="svg" viewBox="0 0 1440 390" xmlns="http://www.w3.org/2000/svg" class="transition duration-300 ease-in-out delay-150">
width="100%"
height="100%"
preserveAspectRatio="none"
id="svg"
viewBox="0 0 1440 390"
xmlns="http://www.w3.org/2000/svg"
class="transition duration-300 ease-in-out delay-150">
<style> <style>
.path-0 { .path-0 {
animation: pathAnim-0 15s; animation: pathAnim-0 15s;

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -1,35 +1,14 @@
<script> <script>
let primaryColor = "#8c25fa"; let primaryColor = "#8c25fa";
let secondaryColor = "#601aab"; let secondaryColor = "#601aab";
console.log( console.log("%cWelcome to Alu", `color: ${primaryColor}; font-size: 2rem; font-weight: bold; text-shadow: 2px 2px 0 ${secondaryColor};`);
"%cWelcome to Alu",
`color: ${primaryColor}; font-size: 2rem; font-weight: bold; text-shadow: 2px 2px 0 ${secondaryColor};`
);
console.log( console.log("%cSystem Information: ", `color: ${primaryColor}; font-size: 1rem; font-weight: bold;`);
"%cSystem Information: ", console.log("%cOS: " + navigator.platform, `color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`);
`color: ${primaryColor}; font-size: 1rem; font-weight: bold;` console.log("%cBrowser: " + navigator.userAgent, `color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`);
); console.log("%cCPU Cores: " + navigator.hardwareConcurrency, `color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`);
console.log(
"%cOS: " + navigator.platform,
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
console.log(
"%cBrowser: " + navigator.userAgent,
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
console.log(
"%cCPU Cores: " + navigator.hardwareConcurrency,
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
// Cmon firefox, do we really not support this?? Basic stuff here from the "indie browser". // Cmon firefox, do we really not support this?? Basic stuff here from the "indie browser".
console.log( console.log("%cMemory: " + (navigator as any).deviceMemory + "GB", `color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`);
"%cMemory: " + (navigator as any).deviceMemory + "GB",
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
console.log( console.log("%cPlease include this information in a bug report!", `color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`);
"%cPlease include this information in a bug report!",
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
</script> </script>

View file

@ -0,0 +1,44 @@
type VALID_EXT_TYPES = "serviceWorker" | "theme" | "page";
export interface ExtensionMetadata {
title: string;
description?: string;
// Versions should follow semantic versioning
version: string;
script: string;
entryNamespace?: string;
entryFunc?: string;
scriptCopy: string | null;
type: VALID_EXT_TYPES;
themeName?: string;
}
export let CurrentIDB: IDBDatabase;
export function loadIDB(name: string, version: number) {
const request = window.indexedDB.open(name, version);
return request;
}
export function GetIDB() {
return CurrentIDB;
}
export function SetIDB(idb: IDBDatabase) {
CurrentIDB = idb;
}
export function GetStore(name: string, mode: IDBTransactionMode) {
if (CurrentIDB == null) {
throw new Error("IDB not loaded!");
}
return CurrentIDB.transaction(name, mode).objectStore(name);
}
export default {
loadIDB,
SetIDB,
GetIDB,
GetStore,
};

View file

@ -11,6 +11,8 @@ declare global {
}; };
loadFormContent: Function | null; loadFormContent: Function | null;
loadSelectedTransport: Function | null; loadSelectedTransport: Function | null;
loadedThemeAtob: string;
idb: IDBDatabase;
} }
} }
@ -21,8 +23,7 @@ type transportConfig =
} }
| string; | string;
export const wispURLDefault = export const wispURLDefault = (location.protocol === "https:" ? "wss://" : "ws://") + location.host + "/wisp/";
(location.protocol === "https:" ? "wss://" : "ws://") + location.host + "/wisp/";
export default class TransportManager { export default class TransportManager {
connection: BareMuxConnection; connection: BareMuxConnection;
private transport = "/epoxy/index.mjs"; private transport = "/epoxy/index.mjs";
@ -68,19 +69,23 @@ export default class TransportManager {
export const TransportMgr = new TransportManager(); export const TransportMgr = new TransportManager();
export async function registerSW() { export async function registerAndUpdateSW() {
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
await navigator.serviceWorker.register("/sw.js").then((registration) => { navigator.serviceWorker
registration.update().then(() => { .register("/sw.js", {
console.log("Registered SW!"); updateViaCache: "none",
})
.then(async (reg) => {
console.log("Service worker registered!");
reg.update();
resolve(null); resolve(null);
}); });
}); });
});
} }
export async function initTransport() { export async function initTransport() {
TransportMgr.setTransport( await TransportMgr.setTransport(
TransportMgr.getTransport(), TransportMgr.getTransport(),
localStorage.getItem("alu__wispUrl") || wispURLDefault localStorage.getItem("alu__wispUrl") || wispURLDefault
); );

View file

@ -0,0 +1,252 @@
import "notyf/notyf.min.css";
import { Notyf } from "notyf";
import marketplaceManifest from "../../json/marketplace.json";
const installButtons = document.getElementsByClassName("btn-install");
import IDBManager, { type ExtensionMetadata } from "./IDBManager";
// This just makes it shorter to type
interface HTMLButton extends HTMLButtonElement {}
enum EXT_RETURN {
ACTION_SUCCESS = 0,
INSTALL_FAILED = -1,
ALREADY_INSTALLED = 1,
}
type InstallReturn = {
code: EXT_RETURN;
slug: string;
title?: string;
};
Array.from(installButtons).forEach((btn) => {
btn.addEventListener("click", async (event) => {
const ele = event.target as HTMLButton;
const title = ele.dataset.title;
let notification = new Notyf({
duration: 999999,
position: { x: "right", y: "bottom" },
dismissible: true,
ripple: true,
});
let installNotif = notification.success(`Installing ${title}...`);
if (ele.dataset.slug) {
let obj = await getMarketplaceObj(ele.dataset.slug);
installExtension(obj, ele.dataset.slug)
.then((ret) => {
let notifMessage: string;
let timeout = 2000;
switch (ret.code) {
case EXT_RETURN.ACTION_SUCCESS:
notifMessage = `Installed ${title} Successfully!`;
// Unregister the service worker if it's a service worker
if (obj.type === "serviceWorker") {
navigator.serviceWorker.getRegistration().then((reg) => {
if (reg) {
reg.unregister().then(() => {
console.log("Service worker unregistered!");
});
}
});
};
break;
case EXT_RETURN.ALREADY_INSTALLED:
notifMessage = `${title} is already installed!`;
timeout = 0;
break;
case EXT_RETURN.INSTALL_FAILED:
// We should NEVER get here, but just in case.
notifMessage = `Failed to install ${title}!`;
break;
}
setTimeout(() => {
notification.dismiss(installNotif);
notification.options.duration = 2000;
notification.success(notifMessage);
setTimeout(() => {
window.location.reload();
}, 1000)
notification.options.duration = 999999;
let btn = document.querySelector(`button[data-slug="${ret.slug}"]`) as HTMLButton;
setInstallBtnText(btn);
}, timeout);
})
.catch(() => {
notification.dismiss(installNotif);
notification.options.duration = 2000;
notification.error(`Failed to install ${title}!`);
notification.options.duration = 999999;
});
}
});
});
async function getMarketplaceObj(slug: string): Promise<ExtensionMetadata> {
const manifest = (marketplaceManifest as unknown as { [key: string]: ExtensionMetadata })[slug];
manifest.scriptCopy = btoa(await fetch(manifest.script).then((res) => res.text()));
return manifest;
}
async function installExtension(ext: ExtensionMetadata, slug: string): Promise<InstallReturn> {
return new Promise<InstallReturn>((resolve, reject) => {
const request = IDBManager.GetIDB();
const transaction = request.transaction("InstalledExtensions", "readwrite");
const store = transaction.objectStore("InstalledExtensions");
const extensionObject = {
slug: slug,
...ext,
};
let slugCheck = store.get(slug);
slugCheck.onsuccess = async () => {
if (slugCheck.result != null) {
resolve({ code: EXT_RETURN.ALREADY_INSTALLED, slug: slug });
} else {
const addRequest = store.add(extensionObject);
addRequest.onerror = () => {
console.error(`Error installing ${slug}!`);
reject({ code: EXT_RETURN.INSTALL_FAILED, slug: slug });
};
addRequest.onsuccess = () => {
resolve({ code: EXT_RETURN.ACTION_SUCCESS, slug: slug });
};
}
};
});
}
document.querySelectorAll("button[data-uninstall-slug]").forEach((btn) => {
btn.addEventListener("click", async (event) => {
if (!confirm("Are you sure you want to uninstall this extension?")) {
return;
}
let uninst = await uninstallExtension((event.target as HTMLButton).dataset.uninstallSlug!)
let notification = new Notyf({
duration: 999999,
position: { x: "right", y: "bottom" },
dismissible: true,
ripple: true,
});
switch (uninst.code) {
case EXT_RETURN.ACTION_SUCCESS:
notification.success(`Uninstalled ${uninst.title}!`);
let btn = document.querySelector(`button[data-slug="${uninst.slug}"]`) as HTMLButton;
btn.disabled = false;
btn.textContent = "Install";
btn.classList.remove("installed");
(event.target as HTMLButton).classList.add("btn-hidden");
break;
case EXT_RETURN.INSTALL_FAILED:
notification.error(`Failed to uninstall ${uninst.title}!`);
break;
}
setTimeout(() => {
window.location.reload();
}, 2000);
})
})
async function uninstallExtension(slug: string): Promise<InstallReturn> {
return new Promise<InstallReturn>((resolve, reject) => {
if (!slug || slug == null) {
reject({ code: EXT_RETURN.INSTALL_FAILED, slug: slug });
}
const request = IDBManager.GetIDB();
const transaction = request.transaction("InstalledExtensions", "readwrite");
const store = transaction.objectStore("InstalledExtensions");
let ext = store.get(slug);
ext.onsuccess = async () => {
if (ext.result == null) {
reject({ code: EXT_RETURN.INSTALL_FAILED, slug: slug });
}
if (ext.result.type === "theme") {
let currTheme = localStorage.getItem("alu__selectedTheme");
if (currTheme) {
if (JSON.parse(currTheme!).value == ext.result.themeName) {
console.log("Reverting theme to default!")
localStorage.setItem("alu__selectedTheme", JSON.stringify({ name: "Alu", value: "alu" }));
}
}
}
const deleteRequest = store.delete(slug);
deleteRequest.onerror = () => {
console.error(`Error uninstalling ${slug}!`);
reject({ code: EXT_RETURN.INSTALL_FAILED, slug: slug, title: slug });
};
deleteRequest.onsuccess = () => {
navigator.serviceWorker.getRegistration().then((reg) => {
if (reg) {
reg.unregister().then(() => {
console.log("Service worker unregistered!");
});
}
});
resolve({ code: EXT_RETURN.ACTION_SUCCESS, slug: slug, title: ext.result.title });
};
};
});
};
function setInstallBtnText(btn: HTMLButton) {
btn.disabled = true;
btn.textContent = "Installed";
btn.classList.add("installed");
}
function getInstallStatus() {
let installBtns = document.querySelectorAll("button[data-slug]") as NodeListOf<HTMLButton>;
let transaction = IDBManager.GetStore("InstalledExtensions", "readonly").transaction;
let store = transaction.objectStore("InstalledExtensions");
let cursor = store.openCursor();
cursor.onsuccess = () => {
let res = cursor.result;
if (res) {
let slug = res.value.slug;
installBtns.forEach((btn) => {
if (btn.dataset.slug == slug) {
setInstallBtnText(btn);
document.querySelector(`button[data-uninstall-slug="${slug}"]`)!.classList.remove("btn-hidden");
}
});
res.continue();
}
};
}
async function InitIDB() {
if (!window.indexedDB) {
console.error("This browser doesn't support IndexedDB");
document.getElementById("support-warning")!.innerText = "Your browser doesn't support IndexedDB. Please use a different browser!";
return;
}
if (IDBManager.GetIDB() != null) {
getInstallStatus();
return;
}
const request = IDBManager.loadIDB("AluDB", 1);
request.onerror = (event: Event) => {
console.error("Database error: " + (event.target as any).errorCode);
};
request.onsuccess = () => {
console.log("Database opened successfully");
IDBManager.SetIDB(request.result);
getInstallStatus();
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains("InstalledExtensions")) {
db.createObjectStore("InstalledExtensions", { keyPath: "slug" });
console.log("Database setup complete");
}
};
}
InitIDB();
document.addEventListener("astro:after-swap", () => {
InitIDB();
});

22
src/env.d.ts vendored
View file

@ -55,14 +55,9 @@ declare module "astro-i18n" {
/** Typed astro-i18n config definition. */ /** Typed astro-i18n config definition. */
export function defineAstroI18nConfig(config: Partial<AstroI18nConfig>): Partial<AstroI18nConfig>; export function defineAstroI18nConfig(config: Partial<AstroI18nConfig>): Partial<AstroI18nConfig>;
/** The `astro-i18n` middleware. */ /** The `astro-i18n` middleware. */
export function useAstroI18n( export function useAstroI18n(config?: Partial<AstroI18nConfig> | string, formatters?: TranslationFormatters): (...args: any[]) => any;
config?: Partial<AstroI18nConfig> | string,
formatters?: TranslationFormatters
): (...args: any[]) => any;
/** Workaround function to make astroI18n work inside getStaticPaths. This is because Astro's getStaticPaths runs before everything which doesn't allows astroI18n to update its state automatically. */ /** Workaround function to make astroI18n work inside getStaticPaths. This is because Astro's getStaticPaths runs before everything which doesn't allows astroI18n to update its state automatically. */
function createGetStaticPaths( function createGetStaticPaths(callback: (props: GetStaticPathsProps) => GetStaticPathsItem[] | Promise<GetStaticPathsItem[]>): (
callback: (props: GetStaticPathsProps) => GetStaticPathsItem[] | Promise<GetStaticPathsItem[]>
): (
props: GetStaticPathsProps & { props: GetStaticPathsProps & {
astroI18n?: { astroI18n?: {
locale: string; locale: string;
@ -78,9 +73,7 @@ declare module "astro-i18n" {
key: T | (string & {}), key: T | (string & {}),
...args: undefined extends TranslationVariables[T] ...args: undefined extends TranslationVariables[T]
? [ ? [
properties?: keyof TranslationVariables extends T properties?: keyof TranslationVariables extends T ? Record<string, unknown> : TranslationVariables[T],
? Record<string, unknown>
: TranslationVariables[T],
options?: { options?: {
route?: Route | (string & {}); route?: Route | (string & {});
locale?: Locale | (string & {}); locale?: Locale | (string & {});
@ -163,9 +156,7 @@ declare module "astro-i18n" {
key: T | (string & {}), key: T | (string & {}),
...args: undefined extends TranslationVariables[T] ...args: undefined extends TranslationVariables[T]
? [ ? [
properties?: keyof TranslationVariables extends T properties?: keyof TranslationVariables extends T ? Record<string, unknown> : TranslationVariables[T],
? Record<string, unknown>
: TranslationVariables[T],
options?: { options?: {
route?: Route | (string & {}); route?: Route | (string & {});
locale?: Locale | (string & {}); locale?: Locale | (string & {});
@ -229,10 +220,7 @@ declare module "astro-i18n" {
/** Tries to parse one of the configured locales out of the given route. If no configured locale is found it will return `null`. */ /** Tries to parse one of the configured locales out of the given route. If no configured locale is found it will return `null`. */
extractRouteLocale(route: string): string | null; extractRouteLocale(route: string): string | null;
/** Initializes astro-i18n on the server-side. */ /** Initializes astro-i18n on the server-side. */
initialize( initialize(config?: Partial<AstroI18nConfig> | string, formatters?: TranslationFormatters = {}): Promise<void>;
config?: Partial<AstroI18nConfig> | string,
formatters?: TranslationFormatters = {}
): Promise<void>;
/** Redirects the user to the given destination. */ /** Redirects the user to the given destination. */
redirect(destination: string | URL, status = 301); redirect(destination: string | URL, status = 301);
} }

View file

@ -33,6 +33,7 @@
"footer.aluProject": "Alu Project", "footer.aluProject": "Alu Project",
"games.title": "Games", "games.title": "Games",
"games.search": "Search...",
"settings.title": "Settings", "settings.title": "Settings",
"settings.proxy": "Proxy", "settings.proxy": "Proxy",

View file

@ -33,6 +33,7 @@
"footer.aluProject": "Alu Project", "footer.aluProject": "Alu Project",
"games.title": "ゲーム", "games.title": "ゲーム",
"games.search": "検索...",
"settings.title": "設定", "settings.title": "設定",
"settings.proxy": "プロキシ", "settings.proxy": "プロキシ",

View file

@ -1,14 +1,66 @@
{ {
"dev.wearr.adblock": { "dev.wearr.adblock": {
"title": "Adblocker", "title": "Alu Adblocker",
"description": "Alu Adblocker is the best adblocker for web proxy services. It blocks up to 97% of all trackers and ads.",
"version": "0.0.1", "version": "0.0.1",
"image": "/marketplace/adblock/adblock.png", "image": "/marketplace/adblock/adblock.png",
"script": "/marketplace/adblock/adblocker.js" "script": "/marketplace/adblock/index.js",
"type": "serviceWorker",
"entryNamespace": "adblockExt",
"entryFunc": "filterRequest"
}, },
"dev.wearr.adblock2": { "dev.wearr.oled-theme": {
"title": "Adblocker2", "title": "OLED Theme",
"version": "0.0.2", "description": "A beautiful OLED theme for Alu.",
"image": "/marketplace/adblock/adblock.png", "version": "0.0.1",
"script": "/marketplace/adblock/adblocker.js" "image": "/marketplace/oled-theme/theme.png",
"script": "/marketplace/oled-theme/theme.css",
"type": "theme",
"themeName": "oled"
},
"dev.wearr.oled-theme2": {
"title": "OLED Theme",
"description": "A beautiful OLED theme for Alu.",
"version": "0.0.1",
"image": "/marketplace/oled-theme/theme.png",
"script": "/marketplace/oled-theme/theme.css",
"type": "theme",
"themeName": "oled"
},
"dev.wearr.oled-theme3": {
"title": "OLED Theme",
"description": "A beautiful OLED theme for Alu.",
"version": "0.0.1",
"image": "/marketplace/oled-theme/theme.png",
"script": "/marketplace/oled-theme/theme.css",
"type": "theme",
"themeName": "oled"
},
"dev.wearr.oled-theme4": {
"title": "OLED Theme",
"description": "A beautiful OLED theme for Alu.",
"version": "0.0.1",
"image": "/marketplace/oled-theme/theme.png",
"script": "/marketplace/oled-theme/theme.css",
"type": "theme",
"themeName": "oled"
},
"dev.wearr.oled-theme5": {
"title": "OLED Theme",
"description": "A beautiful OLED theme for Alu.",
"version": "0.0.1",
"image": "/marketplace/oled-theme/theme.png",
"script": "/marketplace/oled-theme/theme.css",
"type": "theme",
"themeName": "oled"
},
"dev.wearr.oled-theme6": {
"title": "OLED Theme",
"description": "A beautiful OLED theme for Alu.",
"version": "0.0.1",
"image": "/marketplace/oled-theme/theme.png",
"script": "/marketplace/oled-theme/theme.css",
"type": "theme",
"themeName": "oled"
} }
} }

View file

@ -27,25 +27,16 @@ const { title, optionalPreloads } = Astro.props;
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<meta name="title" content="Alu" /> <meta name="title" content="Alu" />
<meta <meta name="description" content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization." />
name="description"
content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization."
/>
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="https://aluu.xyz" /> <meta property="og:url" content="https://aluu.xyz" />
<meta property="og:title" content="Alu" /> <meta property="og:title" content="Alu" />
<meta <meta property="og:description" content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization." />
property="og:description"
content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization."
/>
<meta property="og:image" content="/logo.png" /> <meta property="og:image" content="/logo.png" />
<meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://aluu.xyz" /> <meta property="twitter:url" content="https://aluu.xyz" />
<meta property="twitter:title" content="Alu" /> <meta property="twitter:title" content="Alu" />
<meta <meta property="twitter:description" content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization." />
property="twitter:description"
content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization."
/>
<meta property="twitter:image" content="/logo.png" /> <meta property="twitter:image" content="/logo.png" />
<link rel="sitemap" href="/sitemap-index.xml" /> <link rel="sitemap" href="/sitemap-index.xml" />
<link href="/varela-round.css" rel="stylesheet" as="style" /> <link href="/varela-round.css" rel="stylesheet" as="style" />
@ -54,11 +45,11 @@ const { title, optionalPreloads } = Astro.props;
return <link rel="preload" href={item.href} as={item.as} />; return <link rel="preload" href={item.href} as={item.as} />;
}) })
} }
<script src="https://www.googletagmanager.com/gtag/js?id=G-P1JX4G9KSF" is:inline></script> <script src="https://www.googletagmanager.com/gtag/js?id=G-P1JX4G9KSF" type="text/partytown" is:inline></script>
<script is:inline> <script type="text/partytown" is:inline>
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag() { function gtag() {
dataLayer.push(arguments); window.dataLayer.push(arguments);
} }
gtag("js", new Date()); gtag("js", new Date());
@ -78,6 +69,9 @@ const { title, optionalPreloads } = Astro.props;
<Footer /> <Footer />
<style is:global> <style is:global>
:root { :root {
--background-color: black;
}
[data-theme="alu"] {
--background-color: #211d29; --background-color: #211d29;
--background-highlight: #35314a; --background-highlight: #35314a;
--accent-color: #624978; --accent-color: #624978;
@ -172,7 +166,7 @@ const { title, optionalPreloads } = Astro.props;
color: var(--text-color); color: var(--text-color);
text-align: center; text-align: center;
font-weight: 400; font-weight: 400;
font-size: 40px; font-size: 2.5rem;
} }
.title-desc { .title-desc {
color: var(--text-color); color: var(--text-color);

View file

@ -4,6 +4,7 @@ import games from "../../json/games.json";
import GameItem from "@components/GameItem.astro"; import GameItem from "@components/GameItem.astro";
import { STATIC_PATHS, i18n } from "@i18n/utils"; import { STATIC_PATHS, i18n } from "@i18n/utils";
import Input from "@components/UI/Input.astro";
const t = i18n.inferLangUseTranslations(Astro.url); const t = i18n.inferLangUseTranslations(Astro.url);
export function getStaticPaths() { export function getStaticPaths() {
@ -13,6 +14,10 @@ export function getStaticPaths() {
<Layout title={t("pages.games")}> <Layout title={t("pages.games")}>
<h1 class="title-text">{t("games.title")}</h1> <h1 class="title-text">{t("games.title")}</h1>
<div class="search">
<Input inputName="games-search" placeholder={t("games.search")} height="30px" />
</div>
<div class="no-results"></div>
<main id="main-content" class="grid"> <main id="main-content" class="grid">
{ {
Object.keys(games).map((key) => { Object.keys(games).map((key) => {
@ -23,31 +28,69 @@ export function getStaticPaths() {
</main> </main>
</Layout> </Layout>
<script>
let search = document.getElementById('games-search-input') as HTMLInputElement;
let mainContent = document.getElementById('main-content') as HTMLDivElement;
search.addEventListener('input', () => {
let filter = search.value.toUpperCase();
let games = mainContent.children;
let results = 0;
for (let i = 0; i < games.length; i++) {
let game = games[i] as HTMLDivElement;
let name = game.getAttribute('data-name')!;
if (name.toUpperCase().indexOf(filter) > -1) {
game.style.display = '';
results++;
} else {
game.style.display = 'none';
}
}
console.log(results)
if (results === 0) {
let noResults = document.querySelector('.no-results') as HTMLDivElement;
if (noResults) {
noResults.style.display = 'block';
noResults.innerHTML = 'No results found';
}
} else {
let noResults = document.querySelector('.no-results') as HTMLDivElement;
if (noResults) {
noResults.style.display = 'none';
}
}
});
</script>
<style> <style>
.title-text {
margin-bottom: 5px;
}
.search {
display: flex;
justify-content: center;
margin-bottom: 20px;
width: 50%;
margin: 0 auto;
}
.grid { .grid {
color: var(--text-color); color: var(--text-color);
margin-top: -10px;
height: max-content; height: max-content;
width: calc(100% - 60px); display: grid;
display: flex; grid-template-columns: repeat(auto-fit, minmax(175px, 1fr));
flex-direction: row;
flex-wrap: wrap;
text-align: center; text-align: center;
justify-content: center; justify-content: center;
gap: 1rem; gap: 1rem;
margin-left: 20px; width: 90%;
margin: 0 auto;
margin-top: 20px;
} }
@media only screen and (max-width: 973px) { .no-results {
.grid { text-align: center;
grid-template-columns: repeat(3, 1fr); height: 15rem;
padding: 0; width: 100%;
} font-size: 2rem;
} margin-top: 10px;
display: none;
@media only screen and (max-width: 467px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
} }
</style> </style>

View file

@ -18,15 +18,7 @@ export function getStaticPaths() {
<section id="proxy-input"> <section id="proxy-input">
<div class="form-wrapper"> <div class="form-wrapper">
<form class="url-input-form" id="url-input-form"> <form class="url-input-form" id="url-input-form">
<Input <Input className="url-input" inputName="url" height="50px" placeholder={t("menu.search")} defaultStyles={false} transition:persist autocomplete="off" />
className="url-input"
inputName="url"
height="50px"
placeholder={t("menu.search")}
defaultStyles={false}
transition:persist
autocomplete="off"
/>
<div id="search-suggestions"></div> <div id="search-suggestions"></div>
<div id="loading-content">Loading...</div> <div id="loading-content">Loading...</div>
</form> </form>
@ -58,11 +50,7 @@ export function getStaticPaths() {
<h2>{t("faq.contributeToAlu")}</h2> <h2>{t("faq.contributeToAlu")}</h2>
<p> <p>
{t("faq.contributeToAlu.answer.segment1")} {t("faq.contributeToAlu.answer.segment1")}
<Link <Link href="https://www.patreon.com/wearr" newTab content={t("faq.contributeToAlu.answer.patreonLinkText")} />
href="https://www.patreon.com/wearr"
newTab
content={t("faq.contributeToAlu.answer.patreonLinkText")}
/>
{t("faq.contributeToAlu.answer.segment2")} {t("faq.contributeToAlu.answer.segment2")}
</p> </p>
</div> </div>
@ -75,11 +63,11 @@ export function getStaticPaths() {
</main> </main>
<ProxyRegistrar /> <ProxyRegistrar />
<script> <script>
import { initTransport, registerSW } from "@components/ts/TransportManager"; import { initTransport, registerAndUpdateSW } from "@components/ts/TransportManager";
type Suggestion = { type Suggestion = {
phrase: string; phrase: string;
}; };
await registerSW(); await registerAndUpdateSW();
async function sendAPIRequest(urlInput: HTMLInputElement, searchSuggestions: HTMLDivElement) { async function sendAPIRequest(urlInput: HTMLInputElement, searchSuggestions: HTMLDivElement) {
if (!urlInput) throw new Error("urlInput is null"); if (!urlInput) throw new Error("urlInput is null");
@ -160,10 +148,6 @@ export function getStaticPaths() {
height: 56px; height: 56px;
} }
.title-text {
font-size: 38px;
}
.url-input-form { .url-input-form {
border: none; border: none;
padding: 0; padding: 0;

View file

@ -9,16 +9,22 @@ export const getStaticPaths = () => {
type MarketplaceItem = { type MarketplaceItem = {
title: string; title: string;
version: string | number; description: string;
version: string;
image: string; image: string;
script: string; script: string;
type: string;
entryNamespace?: string;
entryFunc?: string;
themeName?: string;
}; };
--- ---
<Layout title="Marketplace | Alu"> <Layout title="Marketplace | Alu">
<div id="main-content"> <div id="main-content">
<h1 class="title-text">Marketplace</h1> <h1 class="title-text">Marketplace</h1>
<p class="title-desc">Install custom userscripts and themes for Alu.</p> <p class="title-desc">(WIP) Install custom extensions for Alu!</p>
<div id="support-warning"></div>
<div class="marketplace-ext-grid"> <div class="marketplace-ext-grid">
{ {
Object.keys(marketplace).map((mp_item) => { Object.keys(marketplace).map((mp_item) => {
@ -28,174 +34,34 @@ type MarketplaceItem = {
<div class="marketplace-item" data-slug={slug}> <div class="marketplace-item" data-slug={slug}>
<img class="marketplace-item-image" src={item.image} alt={`${item.title} Logo`} /> <img class="marketplace-item-image" src={item.image} alt={`${item.title} Logo`} />
<div class="marketplace-item-title">{item.title}</div> <div class="marketplace-item-title">{item.title}</div>
<button class="marketplace-install-btn" data-slug={slug} data-title={item.title}> <div class="marketplace-item-desc">{item.description}</div>
<button class="marketplace-btn btn-install" data-slug={slug} data-title={item.title}>
Install Install
</button> </button>
<button class="marketplace-btn btn-hidden" data-uninstall-slug={slug}>
Uninstall
</button>
</div> </div>
); );
}) })
} }
</div> </div>
</div> </div>
<script src="@components/ts/marketplace.ts"></script>
</Layout> </Layout>
<script>
import "notyf/notyf.min.css";
import { Notyf } from "notyf";
import marketplaceManifest from "../../json/marketplace.json";
const installButtons = document.getElementsByClassName(
"marketplace-install-btn"
) as HTMLCollectionOf<HTMLButtonElement>;
Array.from(installButtons).forEach((btn) => {
btn.addEventListener("click", async (event) => {
const ele = event.target as HTMLButtonElement;
const title = ele.dataset.title;
let notification = new Notyf({
duration: 999999,
position: { x: "right", y: "bottom" },
dismissible: true,
ripple: true,
});
let installNotif = notification.success(`Installing ${title}...`);
if (ele.dataset.slug) {
let obj = await getMarketplaceObj(ele.dataset.slug);
installExtension(obj, ele.dataset.slug)
.then((code) => {
let notifMessage = "";
let timeout = 2000;
switch (code) {
case 0:
notifMessage = `Installed ${title} Successfully!`;
break;
case 1:
notifMessage = `${title} is already installed!`;
timeout = 0;
break;
case -1:
// We should NEVER get here, but just in case.
notifMessage = `Failed to install ${title}!`;
break;
}
setTimeout(() => {
notification.dismiss(installNotif);
notification.options.duration = 2000;
notification.success(notifMessage);
notification.options.duration = 999999;
}, timeout);
})
.catch(() => {
notification.dismiss(installNotif);
notification.options.duration = 2000;
notification.error(`Failed to install ${title}!`);
notification.options.duration = 999999;
});
}
});
});
type MarketplaceItem = {
title: string;
version: string | number;
image: string;
script: string;
scriptBtoa: string | null;
};
async function getMarketplaceObj(slug: string): Promise<ExtensionMetadata> {
const manifest = (marketplaceManifest as unknown as { [key: string]: MarketplaceItem })[slug];
manifest.scriptBtoa = btoa(await fetch(manifest.script).then((res) => res.text()));
return manifest;
}
interface ExtensionMetadata {
title: string;
version: string | number;
script: string;
scriptBtoa: string | null;
}
enum EXT_RETURN {
INSTALL_SUCCESS = 0,
INSTALL_FAILED = -1,
ALREADY_INSTALLED = 1,
}
async function installExtension(ext: ExtensionMetadata, slug: string) {
return new Promise(async (resolve, reject) => {
const request = window.indexedDB.open("AluDB", 1);
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains("InstalledExtensions")) {
db.createObjectStore("InstalledExtensions", { keyPath: "slug" });
}
};
request.onsuccess = async (event) => {
const db = (event.target as IDBOpenDBRequest).result;
const transaction = db.transaction("InstalledExtensions", "readwrite");
const store = transaction.objectStore("InstalledExtensions");
const extensionObject = {
slug: slug,
title: ext.title,
version: ext.version,
script: ext.scriptBtoa,
};
// Check if the key already exists in the IDB
let slugCheck = store.get(slug);
slugCheck.onsuccess = async () => {
if (slugCheck.result != undefined) {
resolve(EXT_RETURN.ALREADY_INSTALLED);
} else {
const addRequest = store.add(extensionObject);
addRequest.onerror = () => {
console.error(`Error installing ${slug}!`);
reject(EXT_RETURN.INSTALL_FAILED);
};
addRequest.onsuccess = () => {
resolve(EXT_RETURN.INSTALL_SUCCESS);
};
}
};
slugCheck.onerror = () => {
console.error("Error checking install status!");
reject(EXT_RETURN.INSTALL_FAILED);
};
};
});
}
function InitIDB() {
if (!window.indexedDB) {
console.error("This browser doesn't support IndexedDB");
return;
}
const request = window.indexedDB.open("AluDB", 1);
request.onerror = (event: Event) => {
console.error("Database error: " + (event.target as any).errorCode);
};
request.onsuccess = () => {
console.log("Database opened successfully");
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains("InstalledExtensions")) {
db.createObjectStore("InstalledExtensions", { keyPath: "slug" });
console.log("Database setup complete");
}
};
}
InitIDB();
</script>
<style> <style>
.title-desc {
font-size: 1.2rem;
}
.marketplace-ext-grid { .marketplace-ext-grid {
display: grid; display: grid;
width: 90%; width: 90%;
margin: 0 auto; margin: 0 auto;
color: var(--text-color); color: var(--text-color);
display: grid; display: grid;
display: flex; grid-template-columns: repeat(auto-fill, minmax(45%, 1fr));
flex-direction: row; align-items: center;
flex-wrap: wrap;
text-align: center; text-align: center;
justify-content: center; justify-content: center;
gap: 20px; gap: 20px;
@ -204,13 +70,17 @@ type MarketplaceItem = {
border: 3px solid var(--accent-color); border: 3px solid var(--accent-color);
padding: 20px; padding: 20px;
border-radius: 15px; border-radius: 15px;
width: 180px; width: 100%;
} }
.marketplace-item-image { .marketplace-item-image {
width: 64px; width: 64px;
height: 64px; height: 64px;
} }
.marketplace-install-btn { .marketplace-item-desc {
width: 80%;
margin: 0 auto;
}
.marketplace-btn {
width: 100%; width: 100%;
background-color: var(--accent-color); background-color: var(--accent-color);
color: var(--text-color); color: var(--text-color);
@ -219,5 +89,23 @@ type MarketplaceItem = {
padding: 3px; padding: 3px;
cursor: pointer; cursor: pointer;
font-family: "Varela Round", sans-serif; font-family: "Varela Round", sans-serif;
border-radius: 15px;
margin-top: 10px;
transition: 250ms ease-in-out;
}
.marketplace-btn.installed {
filter: brightness(0.8);
cursor: auto;
}
#support-warning {
padding-top: 10px;
padding-bottom: 50px;
height: 20px;
color: #ff6923;
text-align: center;
}
.btn-hidden {
opacity: 0;
pointer-events: none;
} }
</style> </style>

View file

@ -55,8 +55,15 @@ export function getStaticPaths() {
<div id="current-content"></div> <div id="current-content"></div>
</div> </div>
</main> </main>
<script>
import IDBManager, { type ExtensionMetadata } from "@components/ts/IDBManager";
let load = IDBManager.loadIDB("AluDB", 1);
load.onsuccess = () => {
window.idb = IDBManager.GetIDB();
};
</script>
<script is:inline> <script is:inline>
window.loadedContentStorage = {}; loadedContentStorage = {};
window.currentlySelectedTab; window.currentlySelectedTab;
function settingsLoad() { function settingsLoad() {
document.addEventListener("astro:before-swap", () => { document.addEventListener("astro:before-swap", () => {
@ -67,7 +74,7 @@ export function getStaticPaths() {
Array.from(document.getElementsByClassName("setting-tab")).forEach((tab) => { Array.from(document.getElementsByClassName("setting-tab")).forEach((tab) => {
let contentToLoad = document.getElementById("content-" + tab.id); let contentToLoad = document.getElementById("content-" + tab.id);
if (contentToLoad) { if (contentToLoad) {
window.loadedContentStorage[tab.id] = contentToLoad.innerHTML; loadedContentStorage[tab.id] = contentToLoad.innerHTML;
contentToLoad.remove(); contentToLoad.remove();
} }
@ -83,7 +90,7 @@ export function getStaticPaths() {
if (currentContent) { if (currentContent) {
currentContent.style.opacity = "0"; currentContent.style.opacity = "0";
setTimeout(() => { setTimeout(() => {
currentContent.innerHTML = window.loadedContentStorage[tabID]; currentContent.innerHTML = loadedContentStorage[tabID];
currentContent.style.opacity = "1"; currentContent.style.opacity = "1";
document.dispatchEvent(new CustomEvent("setting-tabChange", { detail: tabID })); document.dispatchEvent(new CustomEvent("setting-tabChange", { detail: tabID }));
document.dispatchEvent(new CustomEvent("setting-tabLoad", { detail: tabID })); document.dispatchEvent(new CustomEvent("setting-tabLoad", { detail: tabID }));
@ -193,27 +200,44 @@ export function getStaticPaths() {
}); });
} }
function addThemeToDropdown(extension) {
let dropdown = document.getElementById("dropdown__selected-theme-menu");
if (dropdown) {
let themeItem = document.createElement("li");
themeItem.classList.add("dropdown-item");
themeItem.dataset.setting = extension.themeName;
themeItem.textContent = extension.title;
dropdown.appendChild(themeItem);
} else {
setTimeout(() => {
addThemeToDropdown(extension);
}, 1000);
}
}
document.addEventListener("setting-tabChange", determineListener); document.addEventListener("setting-tabChange", determineListener);
loadContent("setting-tab-proxy"); loadContent("setting-tab-proxy");
function setupCustomizationSettings() { function setupCustomizationSettings() {
let store = window.idb.transaction("InstalledExtensions", "readonly").objectStore("InstalledExtensions");
store.getAll().onsuccess = (event) => {
const result = event.target.result;
if (result) {
result.forEach((extension) => {
if (extension.type === "theme" && extension.themeName) {
// Create a dropdown item for the theme
addThemeToDropdown(extension);
}
});
}
applySavedLocalStorage("alu__selectedTheme", "dropdown__selected-theme"); applySavedLocalStorage("alu__selectedTheme", "dropdown__selected-theme");
applySavedLocalStorage("alu__selectedLanguage", "dropdown__selected-language"); applySavedLocalStorage("alu__selectedLanguage", "dropdown__selected-language");
let themeDropdown = document.getElementById("dropdown__selected-theme-menu"); let themeDropdown = document.getElementById("dropdown__selected-theme-menu");
let languageDropdown = document.getElementById("dropdown__selected-language-menu"); let languageDropdown = document.getElementById("dropdown__selected-language-menu");
applyDropdownEventListeners( applyDropdownEventListeners(themeDropdown, "dropdown__selected-theme", "alu__selectedTheme", changeTheme);
themeDropdown, applyDropdownEventListeners(languageDropdown, "dropdown__selected-language", "alu__selectedLanguage", navigateToNewLangaugePage);
"dropdown__selected-theme", };
"alu__selectedTheme",
changeTheme
);
applyDropdownEventListeners(
languageDropdown,
"dropdown__selected-language",
"alu__selectedLanguage",
navigateToNewLangaugePage
);
} }
function setupCloakingSettings() { function setupCloakingSettings() {
@ -313,18 +337,15 @@ export function getStaticPaths() {
let bareURLInput = document.getElementById("bare-url-input"); let bareURLInput = document.getElementById("bare-url-input");
let savedSearxngUrl = localStorage.getItem("alu__searxngUrl"); let savedSearxngUrl = localStorage.getItem("alu__searxngUrl");
if (savedSearxngUrl != null) { if (savedSearxngUrl != null) {
if (savedSearxngUrl == "") if (savedSearxngUrl == "") localStorage.setItem("alu__searxngUrl", "https://searxng.site/");
localStorage.setItem("alu__searxngUrl", "https://searxng.site/");
searxngUrlInput.value = localStorage.getItem("alu__searxngUrl"); searxngUrlInput.value = localStorage.getItem("alu__searxngUrl");
} }
const useWss = location.protocol == "https:"; const useWss = location.protocol == "https:";
const webSocketProtocol = useWss ? "wss://" : "ws://"; const webSocketProtocol = useWss ? "wss://" : "ws://";
let savedWispUrl = localStorage.getItem("alu__wispUrl"); let savedWispUrl = localStorage.getItem("alu__wispUrl");
if (savedWispUrl == null || savedWispUrl == "") if (savedWispUrl == null || savedWispUrl == "") localStorage.setItem("alu__wispUrl", webSocketProtocol + location.host + "/wisp/");
localStorage.setItem("alu__wispUrl", webSocketProtocol + location.host + "/wisp/");
let savedBareUrl = localStorage.getItem("alu__bareUrl"); let savedBareUrl = localStorage.getItem("alu__bareUrl");
if (savedBareUrl == null || savedBareUrl == "") if (savedBareUrl == null || savedBareUrl == "") localStorage.setItem("alu__bareUrl", location.origin + "/bare/");
localStorage.setItem("alu__bareUrl", location.origin + "/bare/");
wispURLInput.value = localStorage.getItem("alu__wispUrl"); wispURLInput.value = localStorage.getItem("alu__wispUrl");
bareURLInput.value = localStorage.getItem("alu__bareUrl"); bareURLInput.value = localStorage.getItem("alu__bareUrl");
// Proxy settings // Proxy settings
@ -332,22 +353,11 @@ export function getStaticPaths() {
applyInputListeners(wispURLInput, "alu__wispUrl"); applyInputListeners(wispURLInput, "alu__wispUrl");
applyInputListeners(bareURLInput, "alu__bareUrl"); applyInputListeners(bareURLInput, "alu__bareUrl");
[selectedProxyDropdown, openWithDropdown, currentTransportDropdown].forEach( [selectedProxyDropdown, openWithDropdown, currentTransportDropdown].forEach((dropdown) => {
(dropdown) => {
let dropdownButton = document.getElementById(dropdown.id.replace("-menu", "")); let dropdownButton = document.getElementById(dropdown.id.replace("-menu", ""));
applyDropdownEventListeners( applyDropdownEventListeners(dropdown, dropdownButton.id, dropdownButton.dataset.localStorageKey);
dropdown, });
dropdownButton.id, applyDropdownEventListeners(searchEngineDropdown, "dropdown__search-engine", "alu__search_engine", checkSearxng);
dropdownButton.dataset.localStorageKey
);
}
);
applyDropdownEventListeners(
searchEngineDropdown,
"dropdown__search-engine",
"alu__search_engine",
checkSearxng
);
checkSearxng(); checkSearxng();
} else if (event.detail == "setting-tab-customization") { } else if (event.detail == "setting-tab-customization") {
setupCustomizationSettings(); setupCustomizationSettings();

View file

@ -8,25 +8,16 @@ import { ViewTransitions } from "astro:transitions";
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<meta name="title" content="Alu" /> <meta name="title" content="Alu" />
<meta <meta name="description" content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization." />
name="description"
content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization."
/>
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="https://aluu.xyz" /> <meta property="og:url" content="https://aluu.xyz" />
<meta property="og:title" content="Alu" /> <meta property="og:title" content="Alu" />
<meta <meta property="og:description" content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization." />
property="og:description"
content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization."
/>
<meta property="og:image" content="/logo.png" /> <meta property="og:image" content="/logo.png" />
<meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://aluu.xyz" /> <meta property="twitter:url" content="https://aluu.xyz" />
<meta property="twitter:title" content="Alu" /> <meta property="twitter:title" content="Alu" />
<meta <meta property="twitter:description" content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization." />
property="twitter:description"
content="Alu is a sleek web proxy supporting multiple standards of communication, and wide levels of customization."
/>
<meta property="twitter:image" content="/logo.png" /> <meta property="twitter:image" content="/logo.png" />
<ViewTransitions /> <ViewTransitions />
<script> <script>

View file

@ -8,30 +8,21 @@ import Layout from "src/layouts/Layout.astro";
<h1 class="title-text">Privacy Policy</h1> <h1 class="title-text">Privacy Policy</h1>
<p> <p>
Alu Project operates the <Link href="https://aluu.xyz" newTab content="https://aluu.xyz" /> website, Alu Project operates the <Link href="https://aluu.xyz" newTab content="https://aluu.xyz" /> website, which provides this Web Proxy service.
which provides this Web Proxy service. </p>
<p>This page is used to inform website visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service, the Alu website.</p>
<p>
If you choose to use our Service, then you agree to the collection and use of information in relation with this policy. The Personal Information that we collect are used for providing and
improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.
</p> </p>
<p> <p>
This page is used to inform website visitors regarding our policies with the collection, use, The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at <Link href="https://aluu.xyz" newTab content="https://aluu.xyz" />, unless
and disclosure of Personal Information if anyone decided to use our Service, the Alu website. otherwise defined in this Privacy Policy.
</p>
<p>
If you choose to use our Service, then you agree to the collection and use of information in
relation with this policy. The Personal Information that we collect are used for providing and
improving the Service. We will not use or share your information with anyone except as
described in this Privacy Policy.
</p>
<p>
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions,
which is accessible at <Link href="https://aluu.xyz" newTab content="https://aluu.xyz" />,
unless otherwise defined in this Privacy Policy.
</p> </p>
<h2><strong>Log Data</strong></h2> <h2><strong>Log Data</strong></h2>
<p> <p>
Whenever you visit our Service, we potentially collect information that your browser sends to Whenever you visit our Service, we potentially collect information that your browser sends to us which we call Log Data. This Log Data may include information such as your computer's IP address,
us which we call Log Data. This Log Data may include information such as your computer's IP User Agent, pages of our Service that you visit, the time and date of your visit, and other statistics.
address, User Agent, pages of our Service that you visit, the time and date of your visit, and
other statistics.
</p> </p>
<h2><strong>Service Providers</strong></h2> <h2><strong>Service Providers</strong></h2>
<p>We may employ third-party companies and individuals due to the following reasons:</p> <p>We may employ third-party companies and individuals due to the following reasons:</p>
@ -42,45 +33,33 @@ import Layout from "src/layouts/Layout.astro";
<li>To assist us in analyzing how our Service is used.</li> <li>To assist us in analyzing how our Service is used.</li>
</ul> </ul>
<p> <p>
We want to inform our Service users that these third parties may have access to your Personal We want to inform our Service users that these third parties may have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are
Information. The reason is to perform the tasks assigned to them on our behalf. However, they obligated not to disclose or use the information for any other purpose.
are obligated not to disclose or use the information for any other purpose.
</p> </p>
<h2><strong>Security</strong></h2> <h2><strong>Security</strong></h2>
<p> <p>
We value your trust in providing us your Personal Information, thus we are striving to use We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the
commercially acceptable means of protecting it. But remember that no method of transmission internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.
over the internet, or method of electronic storage is 100% secure and reliable, and we cannot
guarantee its absolute security.
</p> </p>
<h2><strong>Links to Other Sites</strong></h2> <h2><strong>Links to Other Sites</strong></h2>
<p> <p>
Our Service may contain links to other sites. If you click on a third-party link, you will be Our Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly
directed to that site. Note that these external sites are not operated by us. Therefore, we advise you to review the Privacy Policy of these websites. We have no control over, and assume no responsibility for the content, privacy policies, or practices of any third-party sites or
strongly advise you to review the Privacy Policy of these websites. We have no control over, services.
and assume no responsibility for the content, privacy policies, or practices of any
third-party sites or services.
</p> </p>
<h2><strong>Children's Privacy</strong></h2> <h2><strong>Children's Privacy</strong></h2>
<p> <p>
Our Services do not address anyone under the age of 13. We do not knowingly collect personal Our Services do not address anyone under the age of 13. We do not knowingly collect personal identifiable information from children under 13. In the case we discover that a child under 13 has
identifiable information from children under 13. In the case we discover that a child under 13 provided us with personal information, we immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information,
has provided us with personal information, we immediately delete this from our servers. If you please contact us so that we will be able to do necessary actions.
are a parent or guardian and you are aware that your child has provided us with personal
information, please contact us so that we will be able to do necessary actions.
</p> </p>
<h2><strong>Changes to This Privacy Policy</strong></h2> <h2><strong>Changes to This Privacy Policy</strong></h2>
<p> <p>
We may update our Privacy Policy from time to time. Thus, we advise you to review this page We may update our Privacy Policy from time to time. Thus, we advise you to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on
periodically for any changes. We will notify you of any changes by posting the new Privacy this page. These changes are effective immediately, after they are posted on this page.
Policy on this page. These changes are effective immediately, after they are posted on this
page.
</p> </p>
<h2><strong>Contact Us</strong></h2> <h2><strong>Contact Us</strong></h2>
<p> <p>If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.</p>
If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact
us.
</p>
</div> </div>
</Layout> </Layout>

View file

@ -8,50 +8,32 @@ import Layout from "src/layouts/Layout.astro";
<p>Welcome to Alu!</p> <p>Welcome to Alu!</p>
<p> <p>These terms and conditions outline the rules and regulations for the use of Alu Project's Website, located at https://aluu.xyz.</p>
These terms and conditions outline the rules and regulations for the use of Alu Project's
Website, located at https://aluu.xyz. <p>By accessing this website we assume you accept these terms and conditions. Do not continue to use Alu if you do not agree to take all of the terms and conditions stated on this page.</p>
</p>
<p> <p>
By accessing this website we assume you accept these terms and conditions. Do not continue to The following terminology applies to these Terms and Conditions, Privacy Statement and Disclaimer Notice and all Agreements: "Client", "You" and "Your" refers to you, the person log on this
use Alu if you do not agree to take all of the terms and conditions stated on this page. website and compliant to the Company's terms and conditions. "The Company", "Ourselves", "We", "Our" and "Us", refers to our Company. "Party", "Parties", or "Us", refers to both the Client and
</p> ourselves. All terms refer to the offer, acceptance and consideration of payment necessary to undertake the process of our assistance to the Client in the most appropriate manner for the express
purpose of meeting the Client's needs in respect of provision of the Company's stated services, in accordance with and subject to, prevailing law of us. Any use of the above terminology or other
<p> words in the singular, plural, capitalization and/or he/she or they, are taken as interchangeable and therefore as referring to same.
The following terminology applies to these Terms and Conditions, Privacy Statement and
Disclaimer Notice and all Agreements: "Client", "You" and "Your" refers to you, the person log
on this website and compliant to the Company's terms and conditions. "The Company",
"Ourselves", "We", "Our" and "Us", refers to our Company. "Party", "Parties", or "Us", refers
to both the Client and ourselves. All terms refer to the offer, acceptance and consideration
of payment necessary to undertake the process of our assistance to the Client in the most
appropriate manner for the express purpose of meeting the Client's needs in respect of
provision of the Company's stated services, in accordance with and subject to, prevailing law
of us. Any use of the above terminology or other words in the singular, plural, capitalization
and/or he/she or they, are taken as interchangeable and therefore as referring to same.
</p> </p>
<h3><strong>Cookies</strong></h3> <h3><strong>Cookies</strong></h3>
<p> <p>We employ the use of cookies. By accessing Alu, you agreed to use cookies in agreement with the Alu Project's Privacy Policy.</p>
We employ the use of cookies. By accessing Alu, you agreed to use cookies in agreement with
the Alu Project's Privacy Policy.
</p>
<p> <p>
Most interactive websites use cookies to let us retrieve the user's details for each visit. Most interactive websites use cookies to let us retrieve the user's details for each visit. Cookies are used by our website to enable the functionality of certain areas to make it easier for
Cookies are used by our website to enable the functionality of certain areas to make it easier people visiting our website. Some of our affiliate/advertising partners may also use cookies.
for people visiting our website. Some of our affiliate/advertising partners may also use
cookies.
</p> </p>
<h3><strong>License</strong></h3> <h3><strong>License</strong></h3>
<p> <p>
Unless otherwise stated, Alu Project and/or its licensors own the intellectual property rights Unless otherwise stated, Alu Project and/or its licensors own the intellectual property rights for all material on Alu. All intellectual property rights are reserved. You may access this from
for all material on Alu. All intellectual property rights are reserved. You may access this Alu for your own personal use subjected to restrictions set in these terms and conditions.
from Alu for your own personal use subjected to restrictions set in these terms and
conditions.
</p> </p>
<p>You must not:</p> <p>You must not:</p>
@ -72,26 +54,16 @@ import Layout from "src/layouts/Layout.astro";
<li>Government agencies;</li> <li>Government agencies;</li>
<li>Search engines;</li> <li>Search engines;</li>
<li>News organizations;</li> <li>News organizations;</li>
<li> <li>Online directory distributors may link to our Website in the same manner as they hyperlink to the Websites of other listed businesses; and</li>
Online directory distributors may link to our Website in the same manner as they hyperlink <li>System wide Accredited Businesses except soliciting non-profit organizations, charity shopping malls, and charity fundraising groups which may not hyperlink to our Web site.</li>
to the Websites of other listed businesses; and
</li>
<li>
System wide Accredited Businesses except soliciting non-profit organizations, charity
shopping malls, and charity fundraising groups which may not hyperlink to our Web site.
</li>
</ul> </ul>
<p> <p>
These organizations may link to our home page, to publications or to other Website information These organizations may link to our home page, to publications or to other Website information so long as the link: (a) is not in any way deceptive; (b) does not falsely imply sponsorship,
so long as the link: (a) is not in any way deceptive; (b) does not falsely imply sponsorship, endorsement or approval of the linking party and its products and/or services; and (c) fits within the context of the linking party's site.
endorsement or approval of the linking party and its products and/or services; and (c) fits
within the context of the linking party's site.
</p> </p>
<p> <p>We may consider and approve other link requests from the following types of organizations:</p>
We may consider and approve other link requests from the following types of organizations:
</p>
<ul> <ul>
<li>commonly-known consumer and/or business information sources;</li> <li>commonly-known consumer and/or business information sources;</li>
@ -104,26 +76,20 @@ import Layout from "src/layouts/Layout.astro";
</ul> </ul>
<p> <p>
We will approve link requests from these organizations if we decide that: (a) the link would We will approve link requests from these organizations if we decide that: (a) the link would not make us look unfavorably to ourselves or to our accredited businesses; (b) the organization does
not make us look unfavorably to ourselves or to our accredited businesses; (b) the not have any negative records with us; (c) the benefit to us from the visibility of the hyperlink compensates the absence of Alu Project; and (d) the link is in the context of general resource
organization does not have any negative records with us; (c) the benefit to us from the information.
visibility of the hyperlink compensates the absence of Alu Project; and (d) the link is in the
context of general resource information.
</p> </p>
<p> <p>
These organizations may link to our home page so long as the link: (a) is not in any way These organizations may link to our home page so long as the link: (a) is not in any way deceptive; (b) does not falsely imply sponsorship, endorsement or approval of the linking party and its
deceptive; (b) does not falsely imply sponsorship, endorsement or approval of the linking products or services; and (c) fits within the context of the linking party's site.
party and its products or services; and (c) fits within the context of the linking party's
site.
</p> </p>
<p> <p>
If you are one of the organizations listed in paragraph 2 above and are interested in linking If you are one of the organizations listed in paragraph 2 above and are interested in linking to our website, you must inform us by sending an e-mail to Alu Project. Please include your name,
to our website, you must inform us by sending an e-mail to Alu Project. Please include your your organization name, contact information as well as the URL of your site, a list of any URLs from which you intend to link to our Website, and a list of the URLs on our site to which you
name, your organization name, contact information as well as the URL of your site, a list of would like to link. Wait 2-3 weeks for a response.
any URLs from which you intend to link to our Website, and a list of the URLs on our site to
which you would like to link. Wait 2-3 weeks for a response.
</p> </p>
<p>Approved organizations may hyperlink to our Website as follows:</p> <p>Approved organizations may hyperlink to our Website as follows:</p>
@ -131,87 +97,60 @@ import Layout from "src/layouts/Layout.astro";
<ul> <ul>
<li>By use of our corporate name; or</li> <li>By use of our corporate name; or</li>
<li>By use of the uniform resource locator being linked to; or</li> <li>By use of the uniform resource locator being linked to; or</li>
<li> <li>By use of any other description of our Website being linked to that makes sense within the context and format of content on the linking party's site.</li>
By use of any other description of our Website being linked to that makes sense within the
context and format of content on the linking party's site.
</li>
</ul> </ul>
<p> <p>No use of Alu Project's logo or other artwork will be allowed for linking absent a trademark license agreement.</p>
No use of Alu Project's logo or other artwork will be allowed for linking absent a trademark
license agreement.
</p>
<h3><strong>iFrames</strong></h3> <h3><strong>iFrames</strong></h3>
<p> <p>Without prior approval and written permission, you may not create frames around our Webpages that alter in any way the visual presentation or appearance of our Website.</p>
Without prior approval and written permission, you may not create frames around our Webpages
that alter in any way the visual presentation or appearance of our Website.
</p>
<h3><strong>Content Liability</strong></h3> <h3><strong>Content Liability</strong></h3>
<p> <p>
We shall not be hold responsible for any content that appears on your Website. You agree to We shall not be hold responsible for any content that appears on your Website. You agree to protect and defend us against all claims that is rising on your Website. No link(s) should appear on
protect and defend us against all claims that is rising on your Website. No link(s) should any Website that may be interpreted as libelous, obscene or criminal, or which infringes, otherwise violates, or advocates the infringement or other violation of, any third party rights.
appear on any Website that may be interpreted as libelous, obscene or criminal, or which
infringes, otherwise violates, or advocates the infringement or other violation of, any third
party rights.
</p> </p>
<h3><strong>Reservation of Rights</strong></h3> <h3><strong>Reservation of Rights</strong></h3>
<p> <p>
We reserve the right to request that you remove all links or any particular link to our We reserve the right to request that you remove all links or any particular link to our Website. You approve to immediately remove all links to our Website upon request. We also reserve the
Website. You approve to immediately remove all links to our Website upon request. We also right to amen these terms and conditions and it's linking policy at any time. By continuously linking to our Website, you agree to be bound to and follow these linking terms and conditions.
reserve the right to amen these terms and conditions and it's linking policy at any time. By
continuously linking to our Website, you agree to be bound to and follow these linking terms
and conditions.
</p> </p>
<h3><strong>Removal of links from our website</strong></h3> <h3><strong>Removal of links from our website</strong></h3>
<p> <p>
If you find any link on our Website that is offensive for any reason, you are free to contact If you find any link on our Website that is offensive for any reason, you are free to contact and inform us any moment. We will consider requests to remove links but we are not obligated to or
and inform us any moment. We will consider requests to remove links but we are not obligated so or to respond to you directly.
to or so or to respond to you directly.
</p> </p>
<p> <p>
We do not ensure that the information on this website is correct, we do not warrant its We do not ensure that the information on this website is correct, we do not warrant its completeness or accuracy; nor do we promise to ensure that the website remains available or that the
completeness or accuracy; nor do we promise to ensure that the website remains available or material on the website is kept up to date.
that the material on the website is kept up to date.
</p> </p>
<h3><strong>Disclaimer</strong></h3> <h3><strong>Disclaimer</strong></h3>
<p> <p>
To the maximum extent permitted by applicable law, we exclude all representations, warranties To the maximum extent permitted by applicable law, we exclude all representations, warranties and conditions relating to our website and the use of this website. Nothing in this disclaimer will:
and conditions relating to our website and the use of this website. Nothing in this disclaimer
will:
</p> </p>
<ul> <ul>
<li>limit or exclude our or your liability for death or personal injury;</li> <li>limit or exclude our or your liability for death or personal injury;</li>
<li>limit or exclude our or your liability for fraud or fraudulent misrepresentation;</li> <li>limit or exclude our or your liability for fraud or fraudulent misrepresentation;</li>
<li> <li>limit any of our or your liabilities in any way that is not permitted under applicable law; or</li>
limit any of our or your liabilities in any way that is not permitted under applicable law;
or
</li>
<li>exclude any of our or your liabilities that may not be excluded under applicable law.</li> <li>exclude any of our or your liabilities that may not be excluded under applicable law.</li>
</ul> </ul>
<p> <p>
The limitations and prohibitions of liability set in this Section and elsewhere in this The limitations and prohibitions of liability set in this Section and elsewhere in this disclaimer: (a) are subject to the preceding paragraph; and (b) govern all liabilities arising under the
disclaimer: (a) are subject to the preceding paragraph; and (b) govern all liabilities arising disclaimer, including liabilities arising in contract, in tort and for breach of statutory duty.
under the disclaimer, including liabilities arising in contract, in tort and for breach of
statutory duty.
</p> </p>
<p> <p>As long as the website and the information and services on the website are provided free of charge, we will not be liable for any loss or damage of any nature.</p>
As long as the website and the information and services on the website are provided free of
charge, we will not be liable for any loss or damage of any nature.
</p>
</div> </div>
</Layout> </Layout>