feat: Initialize frontend and backend structure with essential configurations
Some checks failed
Backend CI / lint-and-test (push) Failing after 2m15s
Frontend CI / lint-and-build (push) Successful in 1m1s

- Added TypeScript build info for frontend.
- Created Vite configuration for React application.
- Implemented pre-commit hook to run checks before commits.
- Set up PostgreSQL Dockerfile with PostGIS support and initialization scripts.
- Added database creation script for PostgreSQL with necessary extensions.
- Established Python project configuration with dependencies and development tools.
- Developed pre-commit script to enforce code quality checks for backend and frontend.
- Created PowerShell script to set up Git hooks path.
This commit is contained in:
2025-10-11 15:25:32 +02:00
commit fc1e874309
74 changed files with 9477 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
import {
createContext,
type ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import { login as loginRequest, register as registerRequest } from '../services/api';
import type { AuthenticatedUser } from '../types/auth';
const STORAGE_TOKEN_KEY = 'rail-game/auth/token';
const STORAGE_USER_KEY = 'rail-game/auth/user';
type AuthStatus = 'unauthenticated' | 'authenticating' | 'authenticated';
interface AuthContextValue {
readonly token: string | null;
readonly user: AuthenticatedUser | null;
readonly status: AuthStatus;
readonly error: string | null;
login: (username: string, password: string) => Promise<boolean>;
register: (
username: string,
password: string,
fullName?: string | null
) => Promise<boolean>;
logout: (reason?: string) => void;
}
const AuthContext = createContext<AuthContextValue | undefined>(undefined);
function readFromStorage<T>(key: string): T | null {
if (typeof window === 'undefined') {
return null;
}
const raw = window.localStorage.getItem(key);
if (!raw) {
return null;
}
try {
return JSON.parse(raw) as T;
} catch {
return null;
}
}
function writeToStorage(key: string, value: unknown): void {
if (typeof window === 'undefined') {
return;
}
window.localStorage.setItem(key, JSON.stringify(value));
}
function removeFromStorage(key: string): void {
if (typeof window === 'undefined') {
return;
}
window.localStorage.removeItem(key);
}
interface AuthProviderProps {
readonly children: ReactNode;
}
export function AuthProvider({ children }: AuthProviderProps): JSX.Element {
const [token, setToken] = useState<string | null>(null);
const [user, setUser] = useState<AuthenticatedUser | null>(null);
const [status, setStatus] = useState<AuthStatus>('unauthenticated');
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const storedToken = readFromStorage<string>(STORAGE_TOKEN_KEY);
const storedUser = readFromStorage<AuthenticatedUser>(STORAGE_USER_KEY);
if (storedToken && storedUser) {
setToken(storedToken);
setUser(storedUser);
setStatus('authenticated');
}
}, []);
const logout = useCallback((reason?: string) => {
setToken(null);
setUser(null);
setStatus('unauthenticated');
setError(reason ?? null);
removeFromStorage(STORAGE_TOKEN_KEY);
removeFromStorage(STORAGE_USER_KEY);
}, []);
const login = useCallback(async (username: string, password: string) => {
setStatus('authenticating');
setError(null);
try {
const response = await loginRequest({ username, password });
setToken(response.accessToken);
setUser(response.user);
setStatus('authenticated');
writeToStorage(STORAGE_TOKEN_KEY, response.accessToken);
writeToStorage(STORAGE_USER_KEY, response.user);
return true;
} catch (err) {
setStatus('unauthenticated');
const message = err instanceof Error ? err.message : 'Authentication failed';
setError(message);
return false;
}
}, []);
const register = useCallback(
async (username: string, password: string, fullName?: string | null) => {
setStatus('authenticating');
setError(null);
try {
const response = await registerRequest({ username, password, fullName });
setToken(response.accessToken);
setUser(response.user);
setStatus('authenticated');
writeToStorage(STORAGE_TOKEN_KEY, response.accessToken);
writeToStorage(STORAGE_USER_KEY, response.user);
return true;
} catch (err) {
setStatus('unauthenticated');
const message = err instanceof Error ? err.message : 'Registration failed';
setError(message);
return false;
}
},
[]
);
const value = useMemo<AuthContextValue>(
() => ({ token, user, status, error, login, register, logout }),
[token, user, status, error, login, register, logout]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth(): AuthContextValue {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}