Spaces:
Running
Running
Download src/services/api.ts from harma-boutique-2026/Notes: direct link, hf CLI and curl.
- Browser
- Download file 10.1 kB
-
https://huggingface.co/spaces/harma-boutique-2026/Notes/resolve/main/src/services/api.ts
- Command line
-
hf download hf://spaces/harma-boutique-2026/Notes/src/services/api.ts
-
curl -L -o api.ts https://huggingface.co/spaces/harma-boutique-2026/Notes/resolve/main/src/services/api.ts
10.1 kB
| import { User, NoteItem, ChatMessage, AppNotification, GroupInfo, VoiceCall, ChatUser, GroupMember } from '../types'; | |
| const TOKEN_KEY = 'school_archive_token'; | |
| const SAVED_USER_KEY = 'school_archive_saved_user'; | |
| export function getStoredToken(): string | null { | |
| return localStorage.getItem(TOKEN_KEY); | |
| } | |
| export function setStoredToken(token: string, username?: string): void { | |
| localStorage.setItem(TOKEN_KEY, token); | |
| if (username) { | |
| localStorage.setItem(SAVED_USER_KEY, username); | |
| } | |
| } | |
| export function clearStoredToken(): void { | |
| localStorage.removeItem(TOKEN_KEY); | |
| // Keep SAVED_USER_KEY so username input can auto-fill if desired, or clear | |
| } | |
| export function getSavedUsername(): string | null { | |
| return localStorage.getItem(SAVED_USER_KEY); | |
| } | |
| async function request<T>(endpoint: string, options: RequestInit = {}): Promise<T> { | |
| const token = getStoredToken(); | |
| const headers: Record<string, string> = { | |
| ...(options.headers as Record<string, string>), | |
| }; | |
| if (token) { | |
| headers['Authorization'] = `Bearer ${token}`; | |
| } | |
| // Only set Content-Type to application/json if body is NOT FormData | |
| if (options.body && !(options.body instanceof FormData) && !headers['Content-Type']) { | |
| headers['Content-Type'] = 'application/json'; | |
| } | |
| const res = await fetch(endpoint, { | |
| ...options, | |
| headers, | |
| }); | |
| const contentType = res.headers.get('content-type') || ''; | |
| let data: any = {}; | |
| if (contentType.includes('application/json')) { | |
| try { | |
| data = await res.json(); | |
| } catch (err: any) { | |
| console.error('JSON parsing failed:', err); | |
| data = {}; | |
| } | |
| } else { | |
| // If response is non-JSON (e.g. HTML 404/500 or Vite fallback) | |
| const text = await res.text(); | |
| if (!res.ok) { | |
| if (res.status === 401) { | |
| clearStoredToken(); | |
| window.dispatchEvent(new Event('auth_unauthorized')); | |
| } | |
| throw new Error(`Server request failed (${res.status} ${res.statusText || 'Error'}). Response: ${text.substring(0, 100)}...`); | |
| } | |
| throw new Error(`Received non-JSON response from server (Content-Type: ${contentType}). Response: ${text.substring(0, 100)}...`); | |
| } | |
| if (!res.ok) { | |
| if (res.status === 401) { | |
| clearStoredToken(); | |
| window.dispatchEvent(new Event('auth_unauthorized')); | |
| } | |
| throw new Error(data.error || `Request failed with status ${res.status}`); | |
| } | |
| return data as T; | |
| } | |
| export const api = { | |
| // AUTH | |
| async login(username: string, password: string): Promise<{ token: string; user: User }> { | |
| const data = await request<{ token: string; user: User }>('/api/auth/login', { | |
| method: 'POST', | |
| body: JSON.stringify({ username, password }), | |
| }); | |
| setStoredToken(data.token, data.user.username); | |
| return data; | |
| }, | |
| async getMe(): Promise<User> { | |
| const data = await request<{ user: User }>('/api/auth/me'); | |
| return data.user; | |
| }, | |
| async changePassword(newPassword: string): Promise<string> { | |
| const data = await request<{ message: string }>('/api/auth/change-password', { | |
| method: 'POST', | |
| body: JSON.stringify({ newPassword }), | |
| }); | |
| return data.message; | |
| }, | |
| // USER MANAGEMENT | |
| async getUsers(): Promise<User[]> { | |
| const data = await request<{ users: User[] }>('/api/users'); | |
| return data.users; | |
| }, | |
| async createUser(userData: { | |
| username: string; | |
| password: string; | |
| role?: 'admin' | 'user'; | |
| permissions?: any; | |
| }): Promise<{ user: User; message: string }> { | |
| return request<{ user: User; message: string }>('/api/users', { | |
| method: 'POST', | |
| body: JSON.stringify(userData), | |
| }); | |
| }, | |
| async updateUser( | |
| username: string, | |
| updateData: { password?: string; permissions?: any; role?: 'admin' | 'user' } | |
| ): Promise<string> { | |
| const data = await request<{ message: string }>(`/api/users/${encodeURIComponent(username)}`, { | |
| method: 'PUT', | |
| body: JSON.stringify(updateData), | |
| }); | |
| return data.message; | |
| }, | |
| async deleteUser(username: string): Promise<string> { | |
| const data = await request<{ message: string }>(`/api/users/${encodeURIComponent(username)}`, { | |
| method: 'DELETE', | |
| }); | |
| return data.message; | |
| }, | |
| // NOTES | |
| async getNotes(subject?: string, subSubject?: string, search?: string): Promise<NoteItem[]> { | |
| const params = new URLSearchParams(); | |
| if (subject) params.append('subject', subject); | |
| if (subSubject) params.append('subSubject', subSubject); | |
| if (search) params.append('search', search); | |
| const queryStr = params.toString() ? `?${params.toString()}` : ''; | |
| const data = await request<{ notes: NoteItem[] }>(`/api/notes${queryStr}`); | |
| return data.notes; | |
| }, | |
| async uploadNote( | |
| file: File, | |
| subject: string, | |
| subSubject?: string, | |
| description?: string | |
| ): Promise<{ note: NoteItem; message: string }> { | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| formData.append('subject', subject); | |
| if (subSubject) formData.append('subSubject', subSubject); | |
| if (description) formData.append('description', description); | |
| return request<{ note: NoteItem; message: string }>('/api/notes/upload', { | |
| method: 'POST', | |
| body: formData, | |
| }); | |
| }, | |
| async deleteNote(noteId: string): Promise<string> { | |
| const data = await request<{ message: string }>(`/api/notes/${noteId}`, { | |
| method: 'DELETE', | |
| }); | |
| return data.message; | |
| }, | |
| // GROUPS & CHATS | |
| async getGroups(): Promise<GroupInfo[]> { | |
| const data = await request<{ groups: GroupInfo[] }>('/api/groups'); | |
| return data.groups; | |
| }, | |
| async createGroup(name: string, description?: string): Promise<{ group: GroupInfo; message: string }> { | |
| return request<{ group: GroupInfo; message: string }>('/api/groups', { | |
| method: 'POST', | |
| body: JSON.stringify({ name, description }), | |
| }); | |
| }, | |
| async deleteGroup(groupName: string): Promise<string> { | |
| const data = await request<{ message: string }>(`/api/groups/${encodeURIComponent(groupName)}`, { | |
| method: 'DELETE', | |
| }); | |
| return data.message; | |
| }, | |
| async removeGroupMember(groupName: string, username: string): Promise<string> { | |
| const data = await request<{ message: string }>('/api/groups/remove-member', { | |
| method: 'POST', | |
| body: JSON.stringify({ groupName, username }), | |
| }); | |
| return data.message; | |
| }, | |
| async getGroupMembers(groupName: string): Promise<GroupMember[]> { | |
| const data = await request<{ members: GroupMember[] }>(`/api/groups/${encodeURIComponent(groupName)}/members`); | |
| return data.members; | |
| }, | |
| async blockUser(targetUsername: string, action: 'block' | 'unblock'): Promise<{ blockedUsers: string[]; message: string }> { | |
| return request<{ blockedUsers: string[]; message: string }>('/api/users/block', { | |
| method: 'POST', | |
| body: JSON.stringify({ targetUsername, action }), | |
| }); | |
| }, | |
| async joinLeaveGroup(groupName: string, action: 'join' | 'leave'): Promise<string[]> { | |
| const data = await request<{ joinedGroups: string[]; message: string }>('/api/groups/join-leave', { | |
| method: 'POST', | |
| body: JSON.stringify({ groupName, action }), | |
| }); | |
| return data.joinedGroups; | |
| }, | |
| async getChats(group?: string): Promise<ChatMessage[]> { | |
| const query = group ? `?group=${encodeURIComponent(group)}` : ''; | |
| const data = await request<{ chats: ChatMessage[] }>(`/api/chats${query}`); | |
| return data.chats; | |
| }, | |
| async uploadChatFile(file: File, videoDuration?: number): Promise<any> { | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| if (videoDuration !== undefined) { | |
| formData.append('videoDuration', videoDuration.toString()); | |
| } | |
| const data = await request<{ attachment: any }>('/api/chats/upload-file', { | |
| method: 'POST', | |
| body: formData, | |
| }); | |
| return data.attachment; | |
| }, | |
| async sendChatMessage(group: string, text?: string, attachment?: any): Promise<ChatMessage> { | |
| const data = await request<{ message: ChatMessage }>('/api/chats', { | |
| method: 'POST', | |
| body: JSON.stringify({ group, text, attachment }), | |
| }); | |
| return data.message; | |
| }, | |
| // NOTIFICATIONS | |
| async getNotifications(): Promise<AppNotification[]> { | |
| const data = await request<{ notifications: AppNotification[] }>('/api/notifications'); | |
| return data.notifications; | |
| }, | |
| async markNotificationsRead(): Promise<void> { | |
| await request('/api/notifications/read', { method: 'POST' }); | |
| }, | |
| // VOICE MEETINGS / CALLS | |
| async getCalls(group?: string): Promise<VoiceCall[]> { | |
| const query = group ? `?group=${encodeURIComponent(group)}` : ''; | |
| const data = await request<{ calls: VoiceCall[] }>(`/api/calls${query}`); | |
| return data.calls; | |
| }, | |
| async createCall(group: string, mode: 'public' | 'secret', invitedUsers: string[] = []): Promise<VoiceCall> { | |
| const data = await request<{ call: VoiceCall }>('/api/calls', { | |
| method: 'POST', | |
| body: JSON.stringify({ group, mode, invitedUsers }), | |
| }); | |
| return data.call; | |
| }, | |
| async joinCall(callId: string): Promise<VoiceCall> { | |
| const data = await request<{ call: VoiceCall }>(`/api/calls/${encodeURIComponent(callId)}/join`, { | |
| method: 'POST', | |
| }); | |
| return data.call; | |
| }, | |
| async leaveCall(callId: string): Promise<void> { | |
| await request(`/api/calls/${encodeURIComponent(callId)}/leave`, { | |
| method: 'POST', | |
| }); | |
| }, | |
| async sendCallSignal(callId: string, to: string, type: string, data: any): Promise<void> { | |
| await request(`/api/calls/${encodeURIComponent(callId)}/signals`, { | |
| method: 'POST', | |
| body: JSON.stringify({ to, type, data }), | |
| }); | |
| }, | |
| async getCallSignals(callId: string): Promise<any[]> { | |
| const data = await request<{ signals: any[] }>(`/api/calls/${encodeURIComponent(callId)}/signals`); | |
| return data.signals; | |
| }, | |
| async getChatUsers(group: string): Promise<ChatUser[]> { | |
| const query = group ? `?group=${encodeURIComponent(group)}` : ''; | |
| const data = await request<{ users: ChatUser[] }>(`/api/chat-users${query}`); | |
| return data.users; | |
| }, | |
| }; | |