arbret/frontend/app/auth-context.tsx

140 lines
3.5 KiB
TypeScript
Raw Normal View History

2025-12-18 22:08:31 +01:00
"use client";
2025-12-18 23:54:51 +01:00
import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from "react";
2025-12-18 22:08:31 +01:00
2025-12-18 22:31:19 +01:00
import { API_URL } from "./config";
2025-12-18 22:24:46 +01:00
2025-12-18 23:33:32 +01:00
// Permission constants matching backend
export const Permission = {
VIEW_COUNTER: "view_counter",
INCREMENT_COUNTER: "increment_counter",
USE_SUM: "use_sum",
VIEW_AUDIT: "view_audit",
} as const;
export type PermissionType = typeof Permission[keyof typeof Permission];
2025-12-18 22:08:31 +01:00
interface User {
id: number;
email: string;
2025-12-18 23:33:32 +01:00
roles: string[];
permissions: string[];
2025-12-18 22:08:31 +01:00
}
interface AuthContextType {
user: User | null;
isLoading: boolean;
login: (email: string, password: string) => Promise<void>;
register: (email: string, password: string) => Promise<void>;
2025-12-18 22:24:46 +01:00
logout: () => Promise<void>;
2025-12-18 23:33:32 +01:00
hasPermission: (permission: PermissionType) => boolean;
hasAnyPermission: (...permissions: PermissionType[]) => boolean;
hasRole: (role: string) => boolean;
2025-12-18 22:08:31 +01:00
}
const AuthContext = createContext<AuthContextType | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
2025-12-18 22:24:46 +01:00
checkAuth();
2025-12-18 22:08:31 +01:00
}, []);
2025-12-18 22:24:46 +01:00
const checkAuth = async () => {
2025-12-18 22:08:31 +01:00
try {
2025-12-18 22:24:46 +01:00
const res = await fetch(`${API_URL}/api/auth/me`, {
credentials: "include",
2025-12-18 22:08:31 +01:00
});
if (res.ok) {
const userData = await res.json();
setUser(userData);
}
} catch {
2025-12-18 22:24:46 +01:00
// Not authenticated
2025-12-18 22:08:31 +01:00
} finally {
setIsLoading(false);
}
};
const login = async (email: string, password: string) => {
2025-12-18 22:24:46 +01:00
const res = await fetch(`${API_URL}/api/auth/login`, {
2025-12-18 22:08:31 +01:00
method: "POST",
headers: { "Content-Type": "application/json" },
2025-12-18 22:24:46 +01:00
credentials: "include",
2025-12-18 22:08:31 +01:00
body: JSON.stringify({ email, password }),
});
if (!res.ok) {
const error = await res.json();
throw new Error(error.detail || "Login failed");
}
2025-12-18 22:24:46 +01:00
const userData = await res.json();
setUser(userData);
2025-12-18 22:08:31 +01:00
};
const register = async (email: string, password: string) => {
2025-12-18 22:24:46 +01:00
const res = await fetch(`${API_URL}/api/auth/register`, {
2025-12-18 22:08:31 +01:00
method: "POST",
headers: { "Content-Type": "application/json" },
2025-12-18 22:24:46 +01:00
credentials: "include",
2025-12-18 22:08:31 +01:00
body: JSON.stringify({ email, password }),
});
if (!res.ok) {
const error = await res.json();
throw new Error(error.detail || "Registration failed");
}
2025-12-18 22:24:46 +01:00
const userData = await res.json();
setUser(userData);
2025-12-18 22:08:31 +01:00
};
2025-12-18 22:24:46 +01:00
const logout = async () => {
await fetch(`${API_URL}/api/auth/logout`, {
method: "POST",
credentials: "include",
});
2025-12-18 22:08:31 +01:00
setUser(null);
};
2025-12-18 23:54:51 +01:00
const hasPermission = useCallback((permission: PermissionType): boolean => {
2025-12-18 23:33:32 +01:00
return user?.permissions.includes(permission) ?? false;
2025-12-18 23:54:51 +01:00
}, [user]);
2025-12-18 23:33:32 +01:00
2025-12-18 23:54:51 +01:00
const hasAnyPermission = useCallback((...permissions: PermissionType[]): boolean => {
2025-12-18 23:33:32 +01:00
return permissions.some((p) => user?.permissions.includes(p) ?? false);
2025-12-18 23:54:51 +01:00
}, [user]);
2025-12-18 23:33:32 +01:00
2025-12-18 23:54:51 +01:00
const hasRole = useCallback((role: string): boolean => {
2025-12-18 23:33:32 +01:00
return user?.roles.includes(role) ?? false;
2025-12-18 23:54:51 +01:00
}, [user]);
2025-12-18 23:33:32 +01:00
2025-12-18 22:08:31 +01:00
return (
2025-12-18 23:33:32 +01:00
<AuthContext.Provider
value={{
user,
isLoading,
login,
register,
logout,
hasPermission,
hasAnyPermission,
hasRole,
}}
>
2025-12-18 22:08:31 +01:00
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
}