diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-13 22:40:45 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-13 23:07:26 +0900 |
| commit | e239fe743fc66a8712cf9886d3dfed3cc41fce36 (patch) | |
| tree | e3452fb13dce114cea0e8371dbb049118aa1229e /frontend/app/auth.ts | |
| parent | 482c3a52a0fcc5870a7db4a190475caf61b211a3 (diff) | |
| download | phperkaigi-2026-albatross-e239fe743fc66a8712cf9886d3dfed3cc41fce36.tar.gz phperkaigi-2026-albatross-e239fe743fc66a8712cf9886d3dfed3cc41fce36.tar.zst phperkaigi-2026-albatross-e239fe743fc66a8712cf9886d3dfed3cc41fce36.zip | |
refactor(frontend): replace React Router BFF with Wouter SPA
Remove React Router 7 SSR/BFF architecture (server-side loaders,
actions, sessions, remix-auth) and replace with a client-side SPA
using Wouter for routing and cookie-based JWT auth.
- Replace reactRouter() Vite plugin with @vitejs/plugin-react
- Add index.html + app/main.tsx as SPA entry points
- Add Wouter routing with auth guards (ProtectedRoute/PublicOnlyRoute)
- Add client-side auth (app/auth.ts) and useAuth hook
- Migrate all route files to app/pages/ with client-side data fetching
- Update NavigateLink and GolfPlayAppGaming to use Wouter Link
- Remove .server/, routes/, root.tsx, react-router.config.ts
- Clean up tsconfig.json (remove .react-router references)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'frontend/app/auth.ts')
| -rw-r--r-- | frontend/app/auth.ts | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/frontend/app/auth.ts b/frontend/app/auth.ts new file mode 100644 index 0000000..7a3d10d --- /dev/null +++ b/frontend/app/auth.ts @@ -0,0 +1,45 @@ +import { type JwtPayload, jwtDecode } from "jwt-decode"; +import type { components } from "./api/schema"; + +export type User = components["schemas"]["User"]; + +const COOKIE_NAME = "albatross_token"; + +export function getToken(): string | null { + const match = document.cookie + .split("; ") + .find((row) => row.startsWith(`${COOKIE_NAME}=`)); + if (!match) return null; + return match.split("=").slice(1).join("="); +} + +export function setToken(token: string): void { + document.cookie = `${COOKIE_NAME}=${token}; path=/; SameSite=Lax`; +} + +export function clearToken(): void { + document.cookie = `${COOKIE_NAME}=; path=/; SameSite=Lax; max-age=0`; +} + +export function getUserFromToken(): User | null { + const token = getToken(); + if (!token) return null; + try { + return jwtDecode<User & JwtPayload>(token); + } catch { + return null; + } +} + +export function isTokenExpired(): boolean { + const token = getToken(); + if (!token) return true; + try { + const decoded = jwtDecode<JwtPayload>(token); + if (decoded.exp == null) return false; + // If the token will expire in less than an hour, treat it as expired. + return new Date((decoded.exp - 3600) * 1000) < new Date(); + } catch { + return true; + } +} |
