Files
gooseek/backend/webui/src/lib/auth.ts
home e6b9cfc60a feat: spaces redesign, model selector, auth fixes
Spaces:
- Perplexity-like UI with collaboration features
- Space detail page with threads/members tabs
- Invite members via email, role management
- New space creation with icon/color picker

Model selector:
- Added Ollama client for free Auto model
- GooSeek 1.0 via Timeweb (tariff-based)
- Frontend model dropdown in ChatInput

Auth & Infrastructure:
- Fixed auth-svc missing from Dockerfile.all
- Removed duplicate ratelimit_tiered.go (conflict)
- Added Redis to api-gateway for rate limiting
- Fixed Next.js proxy for local development

UI improvements:
- Redesigned login button in sidebar (gradient)
- Settings page with tabs (account/billing/prefs)
- Auth pages visual refresh

Made-with: Cursor
2026-02-28 02:30:05 +03:00

278 lines
6.6 KiB
TypeScript

const API_BASE = process.env.NEXT_PUBLIC_API_URL || '';
export interface User {
id: string;
email: string;
name: string;
avatar?: string;
role: 'user' | 'admin';
tier: 'free' | 'pro' | 'business';
balance: number;
emailVerified: boolean;
provider: string;
createdAt: string;
updatedAt: string;
}
export interface AuthTokens {
accessToken: string;
refreshToken: string;
expiresIn: number;
tokenType: string;
user: User;
}
export interface RegisterRequest {
email: string;
password: string;
name: string;
}
export interface LoginRequest {
email: string;
password: string;
}
export interface ChangePasswordRequest {
currentPassword: string;
newPassword: string;
}
export interface ResetPasswordRequest {
email: string;
}
export interface ResetPasswordConfirm {
token: string;
newPassword: string;
}
export interface UpdateProfileRequest {
name: string;
avatar?: string;
}
const TOKEN_KEY = 'token';
const REFRESH_TOKEN_KEY = 'refreshToken';
const USER_KEY = 'user';
export function getStoredToken(): string | null {
if (typeof window === 'undefined') return null;
return localStorage.getItem(TOKEN_KEY);
}
export function getStoredRefreshToken(): string | null {
if (typeof window === 'undefined') return null;
return localStorage.getItem(REFRESH_TOKEN_KEY);
}
export function getStoredUser(): User | null {
if (typeof window === 'undefined') return null;
const data = localStorage.getItem(USER_KEY);
if (!data) return null;
try {
return JSON.parse(data);
} catch {
return null;
}
}
export function storeAuth(tokens: AuthTokens): void {
localStorage.setItem(TOKEN_KEY, tokens.accessToken);
localStorage.setItem(REFRESH_TOKEN_KEY, tokens.refreshToken);
localStorage.setItem(USER_KEY, JSON.stringify(tokens.user));
}
export function clearAuth(): void {
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem(REFRESH_TOKEN_KEY);
localStorage.removeItem(USER_KEY);
}
async function handleResponse<T>(response: Response): Promise<T> {
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.error || `Request failed: ${response.status}`);
}
return response.json();
}
export async function register(data: RegisterRequest): Promise<AuthTokens> {
const response = await fetch(`${API_BASE}/api/v1/auth/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
const tokens = await handleResponse<AuthTokens>(response);
storeAuth(tokens);
return tokens;
}
export async function login(data: LoginRequest): Promise<AuthTokens> {
const response = await fetch(`${API_BASE}/api/v1/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
const tokens = await handleResponse<AuthTokens>(response);
storeAuth(tokens);
return tokens;
}
export async function refreshTokens(): Promise<AuthTokens | null> {
const refreshToken = getStoredRefreshToken();
if (!refreshToken) return null;
try {
const response = await fetch(`${API_BASE}/api/v1/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
});
if (!response.ok) {
clearAuth();
return null;
}
const tokens = await response.json();
storeAuth(tokens);
return tokens;
} catch {
clearAuth();
return null;
}
}
export async function logout(): Promise<void> {
const token = getStoredToken();
const refreshToken = getStoredRefreshToken();
if (token) {
try {
await fetch(`${API_BASE}/api/v1/auth/logout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({ refreshToken }),
});
} catch {
// Ignore errors during logout
}
}
clearAuth();
}
export async function logoutAll(): Promise<void> {
const token = getStoredToken();
if (token) {
try {
await fetch(`${API_BASE}/api/v1/auth/logout-all`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
},
});
} catch {
// Ignore errors
}
}
clearAuth();
}
export async function getMe(): Promise<User | null> {
const token = getStoredToken();
if (!token) return null;
try {
const response = await fetch(`${API_BASE}/api/v1/auth/me`, {
headers: {
'Authorization': `Bearer ${token}`,
},
});
if (!response.ok) {
if (response.status === 401) {
const refreshed = await refreshTokens();
if (refreshed) {
return getMe();
}
return null;
}
return null;
}
return response.json();
} catch {
return null;
}
}
export async function updateProfile(data: UpdateProfileRequest): Promise<User> {
const token = getStoredToken();
if (!token) throw new Error('Not authenticated');
const response = await fetch(`${API_BASE}/api/v1/auth/me`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(data),
});
const user = await handleResponse<User>(response);
const storedUser = getStoredUser();
if (storedUser) {
localStorage.setItem(USER_KEY, JSON.stringify({ ...storedUser, ...user }));
}
return user;
}
export async function changePassword(data: ChangePasswordRequest): Promise<void> {
const token = getStoredToken();
if (!token) throw new Error('Not authenticated');
const response = await fetch(`${API_BASE}/api/v1/auth/change-password`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(data),
});
await handleResponse<{ message: string }>(response);
}
export async function forgotPassword(email: string): Promise<void> {
const response = await fetch(`${API_BASE}/api/v1/auth/forgot-password`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});
await handleResponse<{ message: string }>(response);
}
export async function resetPassword(data: ResetPasswordConfirm): Promise<void> {
const response = await fetch(`${API_BASE}/api/v1/auth/reset-password`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
await handleResponse<{ message: string }>(response);
}
export function isAuthenticated(): boolean {
return !!getStoredToken();
}