import axios, { AxiosInstance, AxiosResponse, AxiosError } from 'axios';
import { config } from './config.js';
// EventHorizon API Types based on Django models
export interface User {
id: number;
username: string;
email: string;
first_name: string;
last_name: string;
date_joined: string;
}
export interface Profile {
bio: string;
location: string;
phone_number: string;
avatar: string;
social_links: SocialLink[];
}
export interface SocialLink {
platform: 'github' | 'twitter' | 'linkedin' | 'instagram' | 'facebook' | 'website' | 'other';
url: string;
}
export interface Event {
id: number;
title: string;
slug: string;
description: string;
start_time: string;
end_time: string;
location: string;
capacity: number;
organizer: User;
registration_schema: Record<string, unknown>[];
is_registered: boolean;
created_at: string;
updated_at: string;
}
export interface Registration {
id: number;
event: Event | number;
user: User | number;
status: 'pending' | 'approved' | 'waitlisted' | 'cancelled';
answers: Record<string, unknown>;
registered_at: string;
updated_at: string;
}
export interface EventCreateRequest {
title: string;
description: string;
start_time: string;
end_time: string;
location: string;
capacity: number;
registration_schema?: Record<string, unknown>[];
}
export interface EventListParams {
search?: string;
location?: string;
}
// Helper to extract error message
function getErrorMessage(error: unknown): string {
if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError<{ detail?: string; message?: string }>;
if (axiosError.response?.data?.detail) {
return axiosError.response.data.detail;
}
if (axiosError.response?.data?.message) {
return axiosError.response.data.message;
}
if (axiosError.message) {
return axiosError.message;
}
}
if (error instanceof Error) {
return error.message;
}
return String(error);
}
export class EventHorizonClient {
private client: AxiosInstance;
private baseURL: string;
constructor(baseURL?: string, apiToken?: string) {
const url = baseURL || config.eventhorizon.baseURL;
const token = apiToken || config.eventhorizon.apiToken;
if (!url || !token) {
throw new Error('EventHorizon base URL and API token are required. Set EVENTHORIZON_BASE_URL and EVENTHORIZON_API_TOKEN environment variables.');
}
this.baseURL = url.replace(/\/$/, ''); // Remove trailing slash
this.client = axios.create({
baseURL: this.baseURL,
timeout: config.apiTimeout,
headers: {
'Authorization': `Token ${token}`,
'Content-Type': 'application/json'
}
});
}
// Event methods
async listEvents(params: EventListParams = {}): Promise<Event[]> {
try {
const response: AxiosResponse<Event[]> = await this.client.get('/api/events/', { params });
return response.data;
} catch (error) {
throw new Error(`Failed to list events: ${getErrorMessage(error)}`);
}
}
async getEvent(eventId: number): Promise<Event> {
try {
const response: AxiosResponse<Event> = await this.client.get(`/api/events/${eventId}/`);
return response.data;
} catch (error) {
throw new Error(`Failed to get event ${eventId}: ${getErrorMessage(error)}`);
}
}
async createEvent(eventData: EventCreateRequest): Promise<Event> {
try {
const response: AxiosResponse<Event> = await this.client.post('/api/events/', eventData);
return response.data;
} catch (error) {
throw new Error(`Failed to create event: ${getErrorMessage(error)}`);
}
}
async updateEvent(eventId: number, eventData: Partial<EventCreateRequest>): Promise<Event> {
try {
const response: AxiosResponse<Event> = await this.client.put(`/api/events/${eventId}/`, eventData);
return response.data;
} catch (error) {
throw new Error(`Failed to update event ${eventId}: ${getErrorMessage(error)}`);
}
}
async deleteEvent(eventId: number): Promise<void> {
try {
await this.client.delete(`/api/events/${eventId}/`);
} catch (error) {
throw new Error(`Failed to delete event ${eventId}: ${getErrorMessage(error)}`);
}
}
async registerForEvent(eventId: number, answers: Record<string, unknown> = {}): Promise<Registration> {
try {
const response: AxiosResponse<Registration> = await this.client.post(`/api/events/${eventId}/register/`, { answers });
return response.data;
} catch (error) {
throw new Error(`Failed to register for event ${eventId}: ${getErrorMessage(error)}`);
}
}
async unregisterFromEvent(eventId: number): Promise<void> {
try {
await this.client.delete(`/api/events/${eventId}/unregister/`);
} catch (error) {
throw new Error(`Failed to unregister from event ${eventId}: ${getErrorMessage(error)}`);
}
}
async getEventRegistrations(eventId: number): Promise<Registration[]> {
try {
const response: AxiosResponse<Registration[]> = await this.client.get(`/api/events/${eventId}/registrations/`);
return response.data;
} catch (error) {
throw new Error(`Failed to get registrations for event ${eventId}: ${getErrorMessage(error)}`);
}
}
async manageRegistration(registrationId: number, action: 'approve' | 'waitlist' | 'cancel'): Promise<Registration> {
try {
const response: AxiosResponse<Registration> = await this.client.post(`/api/registrations/${registrationId}/manage/`, { action });
return response.data;
} catch (error) {
throw new Error(`Failed to manage registration ${registrationId}: ${getErrorMessage(error)}`);
}
}
// User methods
async getCurrentProfile(): Promise<User & { profile: Profile }> {
try {
const response: AxiosResponse<User & { profile: Profile }> = await this.client.get('/accounts/api/me/');
return response.data;
} catch (error) {
throw new Error(`Failed to get user profile: ${getErrorMessage(error)}`);
}
}
async getUserRegistrations(): Promise<Registration[]> {
try {
const response: AxiosResponse<Registration[]> = await this.client.get('/api/registrations/');
return response.data;
} catch (error) {
throw new Error(`Failed to get user registrations: ${getErrorMessage(error)}`);
}
}
async getUserHostedEvents(): Promise<Event[]> {
try {
const response: AxiosResponse<Event[]> = await this.client.get('/api/events/', { params: { organizer: 'me' } });
return response.data;
} catch (error) {
throw new Error(`Failed to get user hosted events: ${getErrorMessage(error)}`);
}
}
// Health check method
async healthCheck(): Promise<boolean> {
try {
await this.client.get('/accounts/api/me/');
return true;
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
throw new Error('Authentication failed: Invalid or expired Knox token');
}
return false;
}
}
getBaseURL(): string {
return this.baseURL;
}
}