feat: initial commit web-cad-neu with docker-compose, frontend and backend

This commit is contained in:
2026-06-26 10:50:24 +02:00
commit 4ec76fe406
102 changed files with 25722 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
/**
* AuthContext Frontend authentication state management
*/
import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from 'react';
const API_BASE = 'http://localhost:3001';
export interface AuthUser {
id: string;
email: string;
name: string;
role: string;
created_at: string;
updated_at: string;
}
interface AuthContextValue {
user: AuthUser | null;
token: string | null;
loading: boolean;
error: string | null;
login: (email: string, password: string) => Promise<void>;
register: (email: string, password: string, name: string) => Promise<void>;
logout: () => void;
clearError: () => void;
}
const AuthContext = createContext<AuthContextValue | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<AuthUser | null>(null);
const [token, setToken] = useState<string | null>(() => localStorage.getItem('auth_token'));
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// Validate token on mount
useEffect(() => {
if (!token) return;
fetch(`${API_BASE}/api/auth/me`, {
headers: { Authorization: `Bearer ${token}` },
})
.then(res => res.ok ? res.json() : null)
.then(data => {
if (data) setUser(data);
else {
localStorage.removeItem('auth_token');
setToken(null);
}
})
.catch(() => {
localStorage.removeItem('auth_token');
setToken(null);
});
}, [token]);
const login = useCallback(async (email: string, password: string) => {
setLoading(true);
setError(null);
try {
const res = await fetch(`${API_BASE}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Login failed');
setUser(data.user);
setToken(data.session.token);
localStorage.setItem('auth_token', data.session.token);
} catch (err: any) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
}, []);
const register = useCallback(async (email: string, password: string, name: string) => {
setLoading(true);
setError(null);
try {
const res = await fetch(`${API_BASE}/api/auth/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password, name }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Registration failed');
setUser(data.user);
setToken(data.session.token);
localStorage.setItem('auth_token', data.session.token);
} catch (err: any) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
}, []);
const logout = useCallback(() => {
if (token) {
fetch(`${API_BASE}/api/auth/logout`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
}).catch(() => {});
}
localStorage.removeItem('auth_token');
setUser(null);
setToken(null);
}, [token]);
const clearError = useCallback(() => setError(null), []);
return (
<AuthContext.Provider value={{ user, token, loading, error, login, register, logout, clearError }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
return ctx;
}