/** * API Client for E-Voting Backend * Handles all communication with the FastAPI backend */ // Use relative paths to go through Next.js proxy routes // which handle the conversion between camelCase (frontend) and snake_case (backend) const API_URL = "" export interface ApiResponse { data?: T error?: string status: number } export interface AuthToken { access_token: string expires_in: number id: number email: string first_name: string last_name: string } export interface VoterProfile { id: number email: string first_name: string last_name: string has_voted: boolean created_at: string } export interface Election { id: number name: string description: string start_date: string end_date: string is_active: boolean results_published: boolean candidates: Candidate[] } export interface Candidate { id: number name: string description: string election_id: number } export interface Vote { id: number election_id: number candidate_id: number voter_id: number ballot_hash: string timestamp: string } export interface VoteHistory { vote_id: number election_id: number election_name: string candidate_name: string vote_date: string election_status: "active" | "closed" | "upcoming" } /** * Get stored authentication token from localStorage */ export function getAuthToken(): string | null { if (typeof window === "undefined") return null return localStorage.getItem("auth_token") } /** * Store authentication token in localStorage */ export function setAuthToken(token: string): void { if (typeof window !== "undefined") { localStorage.setItem("auth_token", token) } } /** * Remove authentication token from localStorage */ export function clearAuthToken(): void { if (typeof window !== "undefined") { localStorage.removeItem("auth_token") } } /** * Make API request with authentication */ async function apiRequest( endpoint: string, options: RequestInit & { skipAuth?: boolean } = {} ): Promise> { const { skipAuth = false, ...fetchOptions } = options try { const headers: Record = { "Content-Type": "application/json", ...((fetchOptions.headers as Record) || {}), } // Add authentication token if available if (!skipAuth) { const token = getAuthToken() if (token) { headers["Authorization"] = `Bearer ${token}` } } const response = await fetch(`${API_URL}${endpoint}`, { ...fetchOptions, headers, }) if (response.status === 401) { // Token expired or invalid clearAuthToken() throw new Error("Unauthorized - please login again") } if (!response.ok) { const error = await response.json().catch(() => ({})) throw new Error(error.detail || `HTTP ${response.status}`) } const data = await response.json() return { data, status: response.status } } catch (error) { const message = error instanceof Error ? error.message : "Unknown error" return { error: message, status: 500 } } } /** * Authentication APIs */ export const authApi = { async register(email: string, password: string, firstName: string, lastName: string, citizenId: string) { return apiRequest("/api/auth/register", { method: "POST", skipAuth: true, body: JSON.stringify({ email, password, firstName, lastName, citizenId, }), }) }, async login(email: string, password: string) { return apiRequest("/api/auth/login", { method: "POST", skipAuth: true, body: JSON.stringify({ email, password }), }) }, async getProfile() { return apiRequest("/api/auth/profile") }, logout() { clearAuthToken() return { data: null, status: 200 } }, } /** * Elections APIs */ export const electionsApi = { async getActive() { return apiRequest("/api/elections/active") }, async getUpcoming() { return apiRequest("/api/elections/upcoming") }, async getCompleted() { return apiRequest("/api/elections/completed") }, async getById(id: number) { return apiRequest(`/api/elections/${id}`) }, async getCandidates(electionId: number) { return apiRequest(`/api/elections/${electionId}/candidates`) }, async getResults(electionId: number) { return apiRequest(`/api/elections/${electionId}/results`) }, async publishResults(electionId: number) { return apiRequest(`/api/elections/${electionId}/publish-results`, { method: "POST", }) }, } /** * Votes APIs */ export const votesApi = { async submitVote(electionId: number, choix: string) { return apiRequest("/api/votes", { method: "POST", body: JSON.stringify({ election_id: electionId, choix: choix, }), }) }, async getStatus(electionId: number) { return apiRequest<{ has_voted: boolean }>(`/api/votes/status?election_id=${electionId}`) }, async getHistory() { return apiRequest("/api/votes/history") }, async getBlockchain(electionId: number) { return apiRequest(`/api/votes/blockchain?election_id=${electionId}`) }, async getResults(electionId: number) { return apiRequest(`/api/votes/results?election_id=${electionId}`) }, async verifyBlockchain(electionId: number) { return apiRequest("/api/votes/verify-blockchain", { method: "POST", body: JSON.stringify({ election_id: electionId }), }) }, async getTransactionStatus(transactionId: string, electionId: number) { return apiRequest(`/api/votes/transaction-status?transaction_id=${transactionId}&election_id=${electionId}`) }, } /** * Health check */ export async function healthCheck() { try { const response = await fetch(`${API_URL}/health`) return response.ok } catch { return false } }