Add Hono backend with PostgreSQL, auth, cloud saves, and leaderboard
Server app (apps/server) with Hono framework and Drizzle ORM: - PostgreSQL schema: users, saves, leaderboard, achievements tables - Anonymous auth with UUID tokens, optional email/password linking - Cloud save API: list, get, upsert, delete with auto-save hook - Leaderboard API: per-category rankings with score submission - CORS configured for dev server ports - Typed middleware with Hono env variables Frontend cloud save integration: - API client with auth token management in localStorage - useCloudSave hook auto-saves every 300 ticks when authenticated - Vite env type declarations for VITE_API_URL Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useGameStore } from '@/store';
|
||||
import { api, getAuthToken, setAuthToken } from '@/lib/api';
|
||||
import { AUTO_SAVE_INTERVAL_TICKS } from '@ai-tycoon/shared';
|
||||
|
||||
export function useCloudSave() {
|
||||
const tickCount = useGameStore((s) => s.meta.tickCount);
|
||||
const companyName = useGameStore((s) => s.meta.companyName);
|
||||
const lastSaveTick = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!companyName) return;
|
||||
if (tickCount - lastSaveTick.current < AUTO_SAVE_INTERVAL_TICKS * 5) return;
|
||||
|
||||
const token = getAuthToken();
|
||||
if (!token) return;
|
||||
|
||||
lastSaveTick.current = tickCount;
|
||||
|
||||
const state = useGameStore.getState();
|
||||
const { activePage, notifications, ...gameState } = state;
|
||||
|
||||
api.saves.put({
|
||||
companyName: state.meta.companyName,
|
||||
saveVersion: state.meta.saveVersion,
|
||||
gameData: gameState,
|
||||
tickCount: state.meta.tickCount,
|
||||
era: state.meta.currentEra,
|
||||
}).catch(() => {});
|
||||
}, [tickCount, companyName]);
|
||||
}
|
||||
|
||||
export async function ensureAuth(): Promise<string | null> {
|
||||
let token = getAuthToken();
|
||||
if (token) return token;
|
||||
|
||||
try {
|
||||
const result = await api.auth.anonymous();
|
||||
setAuthToken(result.token);
|
||||
return result.token;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user