Files
cameleer-server/ui/src/auth/auth-store.ts

110 lines
3.0 KiB
TypeScript
Raw Normal View History

import { create } from 'zustand';
import { config } from '../config';
interface AuthState {
accessToken: string | null;
refreshToken: string | null;
username: string | null;
isAuthenticated: boolean;
error: string | null;
loading: boolean;
login: (username: string, password: string) => Promise<void>;
refresh: () => Promise<boolean>;
logout: () => void;
}
function loadTokens() {
return {
accessToken: localStorage.getItem('cameleer-access-token'),
refreshToken: localStorage.getItem('cameleer-refresh-token'),
username: localStorage.getItem('cameleer-username'),
};
}
function persistTokens(access: string, refresh: string, username: string) {
localStorage.setItem('cameleer-access-token', access);
localStorage.setItem('cameleer-refresh-token', refresh);
localStorage.setItem('cameleer-username', username);
}
function clearTokens() {
localStorage.removeItem('cameleer-access-token');
localStorage.removeItem('cameleer-refresh-token');
localStorage.removeItem('cameleer-username');
}
const initial = loadTokens();
export const useAuthStore = create<AuthState>((set, get) => ({
accessToken: initial.accessToken,
refreshToken: initial.refreshToken,
username: initial.username,
isAuthenticated: !!initial.accessToken,
error: null,
loading: false,
login: async (username, password) => {
set({ loading: true, error: null });
try {
const res = await fetch(`${config.apiBaseUrl}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(body.message || 'Invalid credentials');
}
const { accessToken, refreshToken } = await res.json();
persistTokens(accessToken, refreshToken, username);
set({
accessToken,
refreshToken,
username,
isAuthenticated: true,
loading: false,
});
} catch (e: unknown) {
set({
error: e instanceof Error ? e.message : 'Login failed',
loading: false,
});
}
},
refresh: async () => {
const { refreshToken } = get();
if (!refreshToken) return false;
try {
const res = await fetch(`${config.apiBaseUrl}/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
});
if (!res.ok) return false;
const data = await res.json();
const username = get().username ?? '';
persistTokens(data.accessToken, data.refreshToken, username);
set({
accessToken: data.accessToken,
refreshToken: data.refreshToken,
isAuthenticated: true,
});
return true;
} catch {
return false;
}
},
logout: () => {
clearTokens();
set({
accessToken: null,
refreshToken: null,
username: null,
isAuthenticated: false,
error: null,
});
},
}));