Updates games, and add ssr games page.

This commit is contained in:
wearrrrr 2024-07-18 02:58:35 -05:00
parent 4579865ee4
commit c3e17ea81a
20 changed files with 2011 additions and 1946 deletions

View file

@ -11,6 +11,7 @@ module.exports = {
},
rules: {
"no-unused-vars": "error",
"no-undef": "off",
},
overrides: [
{

View file

@ -20,4 +20,11 @@ export default defineConfig({
adapter: node({
mode: "middleware",
}),
vite: {
server: {
watch: {
usePolling: true
}
}
}
});

View file

@ -16,6 +16,8 @@ import dotenv from "dotenv";
import cookieParser from "cookie-parser";
import wisp from "wisp-server-node";
import { masqrCheck } from "./masqr.js";
import { handler as ssrHandler } from './dist/server/entry.mjs';
dotenv.config();
const whiteListedDomains = ["aluu.xyz", "localhost:3000"];
@ -52,6 +54,7 @@ const rammerheadScopes = [
];
const rammerheadSession = /^\/[a-z0-9]{32}/;
const app = express();
app.use(ssrHandler);
app.use(compression({ threshold: 0, filter: () => true }));
app.use(cookieParser());

3715
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

@ -1 +1 @@
Subproject commit 8793db1e2fe64cae8936c00bcd4f333f158f6ebd
Subproject commit 3feceedb2e0ff4266df7a593c2e24945c1c46d65

View file

@ -1,7 +0,0 @@
{
"nav": {
"brand": "Alu",
"games": "Games",
"settings": "Settings"
}
}

View file

@ -1,5 +0,0 @@
{
"brand": "アルー",
"games": "ゲーム",
"settings": "設定"
}

View file

@ -1,6 +1,5 @@
importScripts("/uv/uv.bundle.js")
importScripts("/uv.config.js")
importScripts( __uv$config.sw, "/workerware/workerware.js");
importScripts("/uv/uv.bundle.js", "/uv.config.js", "/workerware/workerware.js");
importScripts( __uv$config.sw);
const ww = new WorkerWare({
debug: true,
@ -17,6 +16,8 @@ function loadExtensionScripts() {
let extensions = request.result;
extensions.forEach((extension) => {
if (extension.type != "serviceWorker") return;
// Loads the function to be added as a middleware into global scope.
// The function defined should NOT immediately execute any function.
eval(atob(extension.scriptCopy));
ww.use({
function: self[extension.entryNamespace][extension.entryFunc],

View file

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

View file

@ -17,7 +17,7 @@
document.addEventListener("astro:after-swap", switchTheme);
import IDBManager, { type ExtensionMetadata } from "@components/ts/IDBManager";
import IDBManager from "@components/ts/IDBManager";
const idb = IDBManager.loadIDB("AluDB", 1);
idb.onsuccess = () => {
@ -28,7 +28,7 @@
store.getAll().onsuccess = (event) => {
const result = (event.target as IDBRequest).result;
if (result) {
result.forEach((extension: ExtensionMetadata) => {
result.forEach((extension: IExtensionMetadata) => {
if (extension.type === "theme" && extension.themeName) {
// Load theme CSS
window.loadedThemeAtob = atob(extension.scriptCopy!);

View file

@ -1,18 +1,3 @@
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) {

View file

@ -1,10 +1,4 @@
type Extension = {
name: string;
script: string;
serviceWorkerExtension: boolean;
};
export async function retrieveExtensions() {
export async function retrieveExtensions(type: ExtType) {
const extensionsArr: Array<Extension> = [];
const db = await new Promise<IDBDatabase>((resolve, reject) => {
const request = indexedDB.open("AluDB", 1);
@ -12,7 +6,7 @@ export async function retrieveExtensions() {
request.onerror = reject;
});
const transaction = (await db).transaction("InstalledExtensions", "readwrite");
const transaction = db.transaction("InstalledExtensions", "readwrite");
const objectStore = transaction.objectStore("InstalledExtensions");
const extensions: Array<Extension> = await new Promise((resolve, reject) => {
const request = objectStore.getAll();
@ -20,10 +14,6 @@ export async function retrieveExtensions() {
request.onerror = reject;
});
extensions.forEach(async (extension: Extension) => {
if (extension.serviceWorkerExtension) {
extensionsArr.push(extension);
}
});
extensions.forEach(async (extension: Extension) => {});
return extensionsArr;
}

View file

@ -2,7 +2,9 @@ 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";
import IDBManager from "./IDBManager";
const extManifest = marketplaceManifest as ExtensionMetadataJSON;
// This just makes it shorter to type
interface HTMLButton extends HTMLButtonElement {}
@ -13,12 +15,6 @@ enum EXT_RETURN {
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;
@ -81,13 +77,13 @@ Array.from(installButtons).forEach((btn) => {
});
});
async function getMarketplaceObj(slug: string): Promise<ExtensionMetadata> {
const manifest = (marketplaceManifest as unknown as { [key: string]: ExtensionMetadata })[slug];
async function getMarketplaceObj(slug: string): Promise<IExtensionMetadata> {
const manifest = extManifest[slug];
manifest.scriptCopy = btoa(await fetch(manifest.script).then((res) => res.text()));
return manifest;
}
async function installExtension(ext: ExtensionMetadata, slug: string): Promise<InstallReturn> {
async function installExtension(ext: IExtensionMetadata, slug: string): Promise<InstallReturn> {
return new Promise<InstallReturn>((resolve, reject) => {
const request = IDBManager.GetIDB();
const transaction = request.transaction("InstalledExtensions", "readwrite");
@ -152,8 +148,6 @@ document.addEventListener("astro:after-swap", () => {
addUninstallEventListeners();
});
async function uninstallExtension(slug: string): Promise<InstallReturn> {
return new Promise<InstallReturn>((resolve, reject) => {
if (!slug || slug == null) {

View file

@ -2,7 +2,7 @@ import { ui, defaultLang } from "./ui";
export const STATIC_PATHS = [{ params: { lang: "en" } }, { params: { lang: "jp" } }];
function getLangFromUrl(url: URL) {
export function getLangFromUrl(url: URL) {
// comma lol
const [, lang] = url.pathname.split("/");
if (lang in ui) return lang as keyof typeof ui;

View file

@ -1,6 +1,7 @@
{
"2048": {
"name": "2048",
"description": "Join the numbers and get to the 2048 tile!",
"image": "/games/2048/logo.png",
"slug": "2048"
},
@ -124,11 +125,6 @@
"image": "/games/game-inside/logo.png",
"slug": "game-inside"
},
"google-snake": {
"name": "Google Snake",
"image": "/games/google-snake/logo.png",
"slug": "google-snake"
},
"grindcraft": {
"name": "Grindcraft",
"image": "/games/grindcraft/logo.png",
@ -184,11 +180,6 @@
"image": "/games/moto-x3m-winter/logo.png",
"slug": "moto-x3m-winter"
},
"osu": {
"name": "osu!",
"image": "/games/osu/logo.png",
"slug": "osu"
},
"retro-bowl": {
"name": "Retro Bowl",
"image": "/games/retro-bowl/logo.png",

View file

@ -0,0 +1,69 @@
---
export const prerender = false;
import Layout from "src/layouts/Layout.astro";
import games from "../../../json/games.json";
let gamesList = games as GameList;
// get the current game based on the information in the url
let game = Astro.params.game;
if (!game) {
Astro.redirect("/en/games/");
return;
}
function isValidGameKey(key: string) {
return key in gamesList;
}
let gameData = isValidGameKey(game) ? gamesList[game] : null;
if (!gameData) {
return Astro.redirect("/en/games/");
}
---
<Layout title="Game">
<div id="main-content">
<h1 class="title-text">{gameData.name}</h1>
<p class="title-desc">{gameData?.description || "Play the game!"}</p>
<div class="game-container">
<iframe scrolling="no" src=`/games/${gameData.slug}` title={gameData.name} id="game-frame"/>
</div>
</div>
</Layout>
<style>
.game-container {
display: flex;
align-items: center;
flex-direction: column;
overflow: hidden;
margin-top: 4rem;
margin-bottom: 4rem;
}
#game-frame {
width: 80%;
height: 80vh;
border: 0;
}
</style>
<script>
let iframe = document.getElementById("game-frame") as HTMLIFrameElement;
iframe.contentWindow?.focus();
iframe?.addEventListener("load", () => {
iframe.contentWindow?.focus();
});
iframe.addEventListener("click", () => {
iframe.contentWindow?.focus();
})
document.addEventListener("astro:after-swap", () => {
let iframe = document.getElementById("game-frame") as HTMLIFrameElement;
iframe?.contentWindow?.focus();
})
</script>

View file

@ -3,9 +3,10 @@ import Layout from "../../layouts/Layout.astro";
import games from "../../json/games.json";
import GameItem from "@components/GameItem.astro";
import { STATIC_PATHS, i18n } from "@i18n/utils";
import { STATIC_PATHS, i18n, getLangFromUrl } from "@i18n/utils";
import Input from "@components/UI/Input.astro";
const t = i18n.inferLangUseTranslations(Astro.url);
const lang = getLangFromUrl(Astro.url);
export function getStaticPaths() {
return STATIC_PATHS;
@ -22,7 +23,7 @@ export function getStaticPaths() {
{
Object.keys(games).map((key) => {
const game = (games as any)[key];
return <GameItem name={game.name} image={game.image} slugName={game.slug} />;
return <GameItem lang={lang} name={game.name} image={game.image} slugName={game.slug} />;
})
}
</main>

View file

@ -6,18 +6,6 @@ import marketplace from "../../json/marketplace.json";
export const getStaticPaths = () => {
return STATIC_PATHS;
};
type MarketplaceItem = {
title: string;
description: string;
version: string;
image: string;
script: string;
type: string;
entryNamespace?: string;
entryFunc?: string;
themeName?: string;
};
---
<Layout title="Marketplace | Alu">
@ -28,7 +16,7 @@ type MarketplaceItem = {
<div class="marketplace-ext-grid">
{
Object.keys(marketplace).map((mp_item) => {
const item = (marketplace as { [key: string]: MarketplaceItem })[mp_item];
const item = (marketplace as { [key: string]: IExtensionMetadata })[mp_item];
const slug = mp_item;
return (
<div class="marketplace-item" data-slug={slug}>

View file

@ -56,7 +56,7 @@ export function getStaticPaths() {
</div>
</main>
<script>
import IDBManager, { type ExtensionMetadata } from "@components/ts/IDBManager";
import IDBManager from "@components/ts/IDBManager";
let load = IDBManager.loadIDB("AluDB", 1);
load.onsuccess = () => {
window.idb = IDBManager.GetIDB();

50
src/types.d.ts vendored Normal file
View file

@ -0,0 +1,50 @@
type ExtType = "serviceWorker" | "theme" | "page";
type Extension = {
name: string;
script: string;
type: ExtType;
};
/*
- title: The title of the extension
- description: A description of the extension
- version: The version of the extension (semver)
- script: The script URL to be downloaded and saved into scriptCopy
- entryNamespace: The namespace of the entry function for serviceWorker extensions
- entryFunc: The name of the entry function for serviceWorker extensions
- scriptCopy: The script contents of the extension btoa'd (null if not downloaded)
- type: The type of extension, see ExtType.
- themeName: The name of the theme that goes in the data attribute
*/
interface IExtensionMetadata {
title: string;
description: string;
version: string;
image: string;
script: string;
entryNamespace?: string;
entryFunc?: string;
scriptCopy?: string;
type: ExtType;
themeName?: string;
}
type ExtensionMetadataJSON = Record<string, IExtensionMetadata>;
type InstallReturn = {
code: EXT_RETURN;
slug: string;
title?: string;
};
type GameMetadata = {
name: string,
description?: string,
image: string,
slug: string,
}
type GameList = {
[key: string]: GameMetadata;
};