aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2025-11-07 23:02:37 -0800
committerPinapelz <yukais@pinapelz.com>2025-11-07 23:05:19 -0800
commita3b1a3bfdf8714469e35d431f28d559619326c73 (patch)
treeda80bd2330d343c38d21f8bdec8d7a07b313957d
parent91c737c907f174b5252877876126a8b81e6cb831 (diff)
add instance info to landing page
-rw-r--r--README.md3
-rw-r--r--backend/.env.example1
-rw-r--r--frontend/src/pages/Landing.tsx78
3 files changed, 81 insertions, 1 deletions
diff --git a/README.md b/README.md
index bd180b1..310b324 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Mirage
-**Mirage** is a "rhythm" game score tracker that doesn’t rely on predefined seeds or chart metadata. It preseves your scores across games — even niche ones.
+**Mirage** is a self-hostable "rhythm" game score tracker that doesn’t rely on predefined seeds or chart metadata. It preseves your scores across games, even niche ones.
![https://github.com/user-attachments/assets/e1a03b39-3c6f-4d31-8a4e-7ae9abaa93a2](https://github.com/user-attachments/assets/e1a03b39-3c6f-4d31-8a4e-7ae9abaa93a2)
![https://github.com/user-attachments/assets/63341f3b-991c-4147-8014-30f27020dc05](https://github.com/user-attachments/assets/63341f3b-991c-4147-8014-30f27020dc05)
@@ -18,6 +18,7 @@
- MUSIC DIVER
- Nostalgia
- REFLEC BEAT
+- Taiko no Tatsujin Arcade
> [!NOTE]
> Basically stuff that isn't supported by [Tachi](https://github.com/zkrising/Tachi) (*yet?*), you should use it for all the other games it does support
diff --git a/backend/.env.example b/backend/.env.example
index f570912..e6356a9 100644
--- a/backend/.env.example
+++ b/backend/.env.example
@@ -2,3 +2,4 @@ DATABASE_URL="postgresql://postgres@localhost:5432/mirage"
SESSION_SECRET="your-very-secure-secret-key-change-this-in-production-make-it-long-and-random"
PORT=5000
NODE_ENV=development
+REQUIRE_INVITE=true # needs an invite to join
diff --git a/frontend/src/pages/Landing.tsx b/frontend/src/pages/Landing.tsx
index e3b7898..20eb887 100644
--- a/frontend/src/pages/Landing.tsx
+++ b/frontend/src/pages/Landing.tsx
@@ -1,7 +1,38 @@
import { Link } from "react-router";
+import { useEffect, useState } from "react";
+import type { SupportedGame } from "../types/game";
import sakuraMirageImage from "../assets/games/mirage.webp";
const Landing = () => {
+ const [requireInvite, setRequireInvite] = useState(false);
+ const [numUsers, setNumUsers] = useState(0);
+ const [supportedGames, setSupportedGames] = useState<SupportedGame[] | null>(null);
+ useEffect(() => {
+ const fetchServerInfo = async () => {
+ try {
+ const response = await fetch(import.meta.env.VITE_API_URL + "/info");
+ const data = await response.json();
+ setRequireInvite(Boolean(data.requireInvite));
+ setNumUsers(Number(data.userCount));
+ } catch (error) {
+ console.error('Error fetching server info:', error);
+ }
+ };
+
+ const fetchSupportedGames = async () => {
+ try {
+ const response = await fetch(import.meta.env.VITE_API_URL + "/supportedGames");
+ const data = await response.json();
+ setSupportedGames(data);
+ } catch (error) {
+ console.error('Error fetching supported games:', error);
+ }
+ };
+ fetchServerInfo();
+ fetchSupportedGames();
+ }, []);
+
+
return (
<div className="min-h-screen bg-slate-950 text-slate-100">
{/* Navigation */}
@@ -64,6 +95,53 @@ const Landing = () => {
</div>
</section>
+ {/* Server Stats Section */}
+ <section className="py-6 sm:py-8 border-t border-slate-800 bg-slate-900/50">
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+ <div className="max-w-4xl">
+ <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white mb-4 sm:mb-6">
+ Server <span className="text-violet-400">Status</span>
+ </h2>
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
+ <div className="bg-slate-800/50 rounded-lg p-4 border border-slate-700">
+ <h3 className="text-lg font-semibold text-violet-300 mb-2">Users</h3>
+ <p className="text-2xl font-bold text-white">{numUsers}</p>
+ <p className="text-sm text-slate-400">registered users</p>
+ </div>
+ <div className="bg-slate-800/50 rounded-lg p-4 border border-slate-700">
+ <h3 className="text-lg font-semibold text-violet-300 mb-2">Registration</h3>
+ <p className="text-2xl font-bold text-white">
+ {requireInvite ? "Invite Only" : "Open"}
+ </p>
+ <p className="text-sm text-slate-400">
+ {requireInvite ? "ask an admin" : "anyone can join"}
+ </p>
+ </div>
+ <div className="bg-slate-800/50 rounded-lg p-4 border border-slate-700">
+ <h3 className="text-lg font-semibold text-violet-300 mb-2">Games</h3>
+ <p className="text-2xl font-bold text-white">
+ {supportedGames ? supportedGames.length : "Loading..."}
+ </p>
+ <p className="text-sm text-slate-400">supported games</p>
+ </div>
+ </div>
+ {supportedGames && supportedGames.length > 0 && (
+ <div>
+ <h3 className="text-lg font-semibold text-white mb-3">Supported Games</h3>
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
+ {supportedGames.map((game) => (
+ <div key={game.internalName} className="bg-slate-800/30 rounded-md p-3 border border-slate-700/50">
+ <h4 className="text-violet-300 font-medium">{game.formattedName}</h4>
+ <p className="text-sm text-slate-400 mt-1">{game.description}</p>
+ </div>
+ ))}
+ </div>
+ </div>
+ )}
+ </div>
+ </div>
+ </section>
+
{/* Introduction Section */}
<section className="py-6 sm:py-8 border-t border-slate-800">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage