1/**
2 * API Client for SvelteKit endpoints
3 * Provides typed helper functions for all API calls
4 */
5
6import type { SendMessageRequest, SendMessageResponse } from '$lib/types/payloads';
7import type { TextStyle, UserTextPreferences } from '$lib/types/text-formatting';
8
9// ============ TYPES ============
10
11export interface ApiResponse<T> {
12 success: boolean;
13 data?: T;
14 error?: string;
15}
16
17export interface TextPreferencesResponse {
18 success: boolean;
19 preferences: UserTextPreferences | null;
20}
21
22// ============ BASE CLIENT ============
23
24async function request<T>(endpoint: string, options: RequestInit = {}): Promise<ApiResponse<T>> {
25 try {
26 const response = await fetch(endpoint, {
27 ...options,
28 credentials: 'include',
29 headers: {
30 'Content-Type': 'application/json',
31 ...(options.headers ?? {})
32 }
33 });
34
35 const data = await response.json();
36
37 if (!response.ok) {
38 return {
39 success: false,
40 error: data.error || `Request failed with status ${response.status}`,
41 data
42 };
43 }
44
45 return { success: true, data };
46 } catch (error) {
47 return {
48 success: false,
49 error: error instanceof Error ? error.message : 'Unknown error'
50 };
51 }
52}
53
54// ============ TEXT PREFERENCES ============
55
56export const textPreferences = {
57 /**
58 * Get current user's text preferences
59 */
60 async get(): Promise<UserTextPreferences | null> {
61 const result = await request<TextPreferencesResponse>('/api/user/text-preferences');
62 if (result.success && result.data?.preferences) {
63 return result.data.preferences;
64 }
65 return null;
66 },
67
68 /**
69 * Save user's text preferences
70 */
71 async save(preferences: {
72 defaultStyle: TextStyle;
73 allowFormatting: boolean;
74 maxMessageLength: number;
75 }): Promise<boolean> {
76 const result = await request('/api/user/text-preferences', {
77 method: 'POST',
78 body: JSON.stringify(preferences)
79 });
80 return result.success;
81 }
82};
83
84// ============ MENTIONS ============
85
86export const mentions = {
87 /**
88 * Update the timestamp of the last read mention
89 */
90 async markRead(timestamp: number): Promise<boolean> {
91 const result = await request('/api/chat/mention-read', {
92 method: 'POST',
93 body: JSON.stringify({ timestamp })
94 });
95 return result.success;
96 }
97};
98
99// ============ SESSION ============
100
101export interface LoginResponse {
102 success: boolean;
103 user?: {
104 id: string;
105 nickname: string;
106 status: string;
107 };
108 error?: string;
109}
110
111export const session = {
112 /**
113 * Login with username and password
114 */
115 async login(username: string, password: string): Promise<LoginResponse> {
116 const result = await request<LoginResponse>('/api/session/login', {
117 method: 'POST',
118 body: JSON.stringify({ username, password })
119 });
120 return result.data ?? { success: false, error: result.error };
121 },
122
123 /**
124 * Register a new user
125 */
126 async register(
127 username: string,
128 password: string,
129 confirmPassword: string,
130 captchaAnswer: string,
131 turnstileToken?: string
132 ): Promise<LoginResponse> {
133 const result = await request<LoginResponse>('/api/register', {
134 method: 'POST',
135 body: JSON.stringify({
136 suUsername: username,
137 suPassword: password,
138 suConfirmPassword: confirmPassword,
139 captchaAnswer,
140 ...(turnstileToken ? { turnstileToken } : {})
141 })
142 });
143 return result.data ?? { success: false, error: result.error };
144 }
145};
146
147// ============ CHAT ============
148
149export interface SendMessageParams {
150 content: string;
151 userId: string;
152 chatRoomId?: string;
153 type?: SendMessageRequest['type'];
154 styleData?: string;
155}
156
157export const chat = {
158 /**
159 * Send a message to a chat room
160 */
161 async sendMessage(params: SendMessageParams): Promise<SendMessageResponse> {
162 const result = await request<SendMessageResponse>('/api/chat/messages', {
163 method: 'POST',
164 body: JSON.stringify(params)
165 });
166 if (result.data) {
167 return result.data;
168 }
169 return {
170 success: false,
171 error: result.error ?? 'Failed to send message'
172 };
173 }
174};
175
176// ============ EXPORT ============
177
178export const api = {
179 textPreferences,
180 mentions,
181 session,
182 chat
183};
184
185export default api;