Updates games, and add ssr games page.
This commit is contained in:
parent
4579865ee4
commit
c3e17ea81a
20 changed files with 2011 additions and 1946 deletions
|
|
@ -11,6 +11,7 @@ module.exports = {
|
|||
},
|
||||
rules: {
|
||||
"no-unused-vars": "error",
|
||||
"no-undef": "off",
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,4 +20,11 @@ export default defineConfig({
|
|||
adapter: node({
|
||||
mode: "middleware",
|
||||
}),
|
||||
vite: {
|
||||
server: {
|
||||
watch: {
|
||||
usePolling: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
3
index.js
3
index.js
|
|
@ -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
3715
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1 +1 @@
|
|||
Subproject commit 8793db1e2fe64cae8936c00bcd4f333f158f6ebd
|
||||
Subproject commit 3feceedb2e0ff4266df7a593c2e24945c1c46d65
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"nav": {
|
||||
"brand": "Alu",
|
||||
"games": "Games",
|
||||
"settings": "Settings"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"brand": "アルー",
|
||||
"games": "ゲーム",
|
||||
"settings": "設定"
|
||||
}
|
||||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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!);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
69
src/pages/[lang]/game/[game].astro
Normal file
69
src/pages/[lang]/game/[game].astro
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}>
|
||||
|
|
|
|||
|
|
@ -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
50
src/types.d.ts
vendored
Normal 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;
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue