import { Injectable } from '@angular/core'; interface RegisterResponse { id: string; token: string; } @Injectable({ providedIn: 'root' }) export class TournamentAuthService { private readonly inflight = new Map>(); async getToken(serverUrl: string): Promise { const key = this.cacheKey(serverUrl); const cached = localStorage.getItem(key); if (cached) return cached; const existing = this.inflight.get(key); if (existing) return existing; const promise = this.register(serverUrl) .then(token => { localStorage.setItem(key, token); this.inflight.delete(key); return token; }) .catch(err => { this.inflight.delete(key); throw err; }); this.inflight.set(key, promise); return promise; } clearToken(serverUrl: string): void { localStorage.removeItem(this.cacheKey(serverUrl)); } private async register(serverUrl: string): Promise { const base = serverUrl.replace(/\/+$/, ''); const localName = localStorage.getItem('username') ?? 'viewer'; const res = await fetch(`${base}/api/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: `${localName}-watch`, isBot: false }) }); if (!res.ok) throw new Error(`tournament-server register failed: ${res.status}`); const body = (await res.json()) as RegisterResponse; return body.token; } private cacheKey(serverUrl: string): string { return `tournament-token:${serverUrl.replace(/\/+$/, '')}`; } }