Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
Achetez et vendez des cryptomonnaies au prix réel du marché avec 10 000 $ fictifs, suivez vos plus-values et comparez-vous aux autres joueurs. Aucun argent réel. Je voulais m'entraîner sur Next.js et m'amuser sur un sujet qui m'intéresse : la finance. Un simulateur de trading réunit les deux, avec des données en temps réel et des calculs où l'exactitude compte, même quand l'argent est fictif.查看完整 README ↗
Achetez et vendez des cryptomonnaies au prix réel du marché avec 10 000 $ fictifs, suivez vos plus-values et comparez-vous aux autres joueurs. Aucun argent réel.
Démo : https://papertrade-five-peach.vercel.app
Je voulais m'entraîner sur Next.js et m'amuser sur un sujet qui m'intéresse : la finance. Un simulateur de trading réunit les deux, avec des données en temps réel et des calculs où l'exactitude compte, même quand l'argent est fictif.
BTC puis Entrée, ⌘K pour y accéder) et
touches de fonction F1 à F3 pour changer d'écran| Domaine | Outils |
|---|---|
| Framework | Next.js 16 (App Router, Server Components, Server Actions), React 19, TypeScript strict |
| Interface | Tailwind CSS 4, Lightweight Charts, IBM Plex Mono |
| Données | PostgreSQL, Drizzle ORM, Zod |
| Auth | Better Auth (compte invité, OAuth GitHub) |
| Qualité | Vitest (tests unitaires et d'intégration sur Postgres), ESLint, GitHub Actions |
| Hébergement | Vercel + Neon (offres gratuites) |
Une interface de terminal de marché plutôt que d'application grand public : une seule police à
chasse fixe (IBM Plex Mono), des noirs neutres, l'ambre pour tout ce qui est interactif, le vert et
le rouge réservés aux variations. Pas d'arrondis ni d'ombres : des panneaux séparés par des filets.
Les choix et leurs raisons sont dans
design-system/papertrade/MASTER.md.
aria-sort, confirmations dans un <dialog> natif. Aucun raccourci à
touche unique (WCAG 2.1.4).Le formulaire n'envoie que l'actif, le sens et la quantité. La Server Action
(src/app/actions.ts) identifie l'utilisateur par sa session, valide
l'entrée avec Zod puis relit le prix chez Binance, sans cache, avant d'exécuter l'ordre. Modifier
la requête à la main ne permet donc pas d'acheter moins cher.
Chaque ordre s'exécute dans une transaction qui verrouille le portefeuille et la position
(SELECT … FOR UPDATE, voir src/lib/data/portfolio.ts). Le test
d'intégration envoie 10 achats simultanés de 2 000 $ avec 10 000 $ : exactement 5 passent.
Sans le verrou, les 10 passent (vérifié en le retirant). Des contraintes CHECK en base
(liquidités ≥ 0, quantité > 0) servent de dernier filet de sécurité.
En JavaScript, 0.1 + 0.2 !== 0.3. Les montants sont donc stockés en numeric(28, 8) et calculés
avec decimal.js (src/lib/trading.ts). Les arrondis se font au détriment du
joueur (coût d'achat arrondi au-dessus, produit de vente en dessous), comme sur une vraie
plateforme. Ces règles sont couvertes par des tests unitaires.
Toute l'application partage une connexion WebSocket
(src/lib/price-store.ts). Elle s'ouvre quand un composant en a besoin,
se ferme quand plus personne ne l'écoute et se reconnecte avec une attente exponentielle. Les
mises à jour sont regroupées toutes les 500 ms et lues via useSyncExternalStore : un composant
ne se re-rend que si le prix de son actif a changé.
Les réponses de l'API Binance sont validées avec Zod avant usage, et mises en cache côté serveur (10 s pour les prix, 30 s pour l'historique). Les prix servis par le serveur s'affichent dès le premier rendu, puis le flux temps réel prend le relais.
Un invité qui se déconnecte est supprimé. Une tâche planifiée Vercel
(src/app/api/cron/cleanup/route.ts) efface chaque nuit les
invités dont toutes les sessions ont expiré.
Prérequis : Node.js 24 ou plus, et Docker.
npm install
cp .env.example .env.local # puis renseigner BETTER_AUTH_SECRET (openssl rand -base64 32)
docker compose up -d # Postgres sur le port 5433
npm run db:migrate
npm run dev # http://localhost:3000
| Commande | Rôle |
|---|---|
npm run dev | Serveur de développement |
npm test | Tests unitaires et d'intégration (base Docker requise) |
npm run lint | ESLint |
npm run typecheck | Vérification TypeScript |
npm run db:generate | Génère une migration après modification du schéma |
npm run db:migrate | Applique les migrations |
npm run db:studio | Explore la base dans Drizzle Studio |
DATABASE_URL est ajoutée automatiquement.BETTER_AUTH_SECRET : générée avec openssl rand -base64 32CRON_SECRET : générée avec openssl rand -hex 16Pour activer la connexion GitHub (optionnel) :
https://<votre-projet>.vercel.app/api/auth/callback/github.BETTER_AUTH_URL (l'URL publique, pour que l'adresse de retour
envoyée à GitHub soit toujours la même), GITHUB_CLIENT_ID et GITHUB_CLIENT_SECRET (en
« Sensitive »).Une OAuth App n'accepte qu'une adresse de retour : la connexion GitHub fonctionne en production, pas sur les prévisualisations des pull requests.
Bon à savoir :
vercel.json place les fonctions à Francfort (fra1), près de la base.src/
├── app/ Pages, Server Actions (actions.ts) et routes API
├── components/ Composants partagés : prix en direct, en-tête, historique…
└── lib/
├── trading.ts Règles de calcul des ordres (tests unitaires)
├── data/ Accès aux données : ordres, portefeuille, classement
├── db/ Schéma Drizzle et connexion Postgres
├── binance.ts API REST Binance (serveur)
├── price-store.ts Flux WebSocket des prix (navigateur)
└── auth.ts Configuration Better Auth
design-system/ Design system (tokens, typographie, règles d'accessibilité)
drizzle/ Migrations SQL générées
scripts/migrate.mts Application des migrations (lancé au build)
Projet éducatif : aucune transaction réelle n'est effectuée et rien ici ne constitue un conseil en investissement. Données de marché fournies par Binance (prix en USDT, assimilés au dollar).