| |
| |
| |
|
|
| const BASE = '/api/v1'; |
|
|
| function getToken() { |
| if (typeof localStorage === 'undefined') return null; |
| return localStorage.getItem('mac_token'); |
| } |
|
|
| function headers(extra = {}) { |
| |
| const h = { 'Content-Type': 'application/json', ...extra }; |
| const token = getToken(); |
| if (token) h.Authorization = `Bearer ${token}`; |
| return h; |
| } |
|
|
| async function handleResponse(res) { |
| if (res.ok) { |
| const ct = res.headers.get('content-type') || ''; |
| if (ct.includes('application/json')) return res.json(); |
| return res.text(); |
| } |
| let detail = `HTTP ${res.status}`; |
| try { |
| const body = await res.json(); |
| detail = body?.detail?.message || body?.detail || body?.message || JSON.stringify(body); |
| } catch {} |
| const err = (new Error(detail)); |
| err.status = res.status; |
| throw err; |
| } |
|
|
| async function get(path, params = {}) { |
| const url = new URL(BASE + path, location.origin); |
| Object.entries(params).forEach(([k, v]) => { |
| if (v !== undefined && v !== null && v !== '') url.searchParams.set(k, v); |
| }); |
| return handleResponse(await fetch(url, { headers: headers() })); |
| } |
|
|
| async function post(path, body = {}) { |
| return handleResponse(await fetch(BASE + path, { |
| method: 'POST', |
| headers: headers(), |
| body: JSON.stringify(body), |
| })); |
| } |
|
|
| async function patch(path, body = {}) { |
| return handleResponse(await fetch(BASE + path, { |
| method: 'PATCH', |
| headers: headers(), |
| body: JSON.stringify(body), |
| })); |
| } |
|
|
| async function put(path, body = {}) { |
| return handleResponse(await fetch(BASE + path, { |
| method: 'PUT', |
| headers: headers(), |
| body: JSON.stringify(body), |
| })); |
| } |
|
|
| async function del(path) { |
| return handleResponse(await fetch(BASE + path, { method: 'DELETE', headers: headers() })); |
| } |
|
|
| async function formPost(path, formData) { |
| const token = getToken(); |
| return handleResponse(await fetch(BASE + path, { |
| method: 'POST', |
| headers: token ? { Authorization: `Bearer ${token}` } : {}, |
| body: formData, |
| })); |
| } |
|
|
| export const auth = { |
| login: (roll_number, password) => post('/auth/login', { roll_number, password }), |
| verify: (roll_number, dob) => post('/auth/verify', { roll_number, dob }), |
| refresh: (refresh_token) => post('/auth/refresh', { refresh_token }), |
| me: () => get('/auth/me'), |
| logout: () => post('/auth/logout'), |
| changePassword: (old_password, new_password) => post('/auth/change-password', { old_password, new_password }), |
| setPassword: (new_password, confirm_password) => post('/auth/set-password', { new_password, confirm_password }), |
| updateProfile: (data) => put('/auth/me/profile', data), |
| }; |
|
|
| export const setup = { |
| status: () => get('/setup/status'), |
| createAdmin: (name, email, password) => post('/setup/create-admin', { name, email, password }), |
| recovery: () => get('/setup/recovery'), |
| }; |
|
|
| export const query = { |
| chatStream(messages, model = 'auto', options = {}) { |
| const token = getToken(); |
| return fetch(BASE + '/query/chat', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, |
| body: JSON.stringify({ messages, model, stream: true, ...options }), |
| }); |
| }, |
| complete: (messages, model = 'auto') => post('/query/chat', { messages, model, stream: false }), |
| }; |
|
|
| export const models = { |
| list: () => get('/models'), |
| legacyList: () => get('/models/list'), |
| health: () => get('/models/health'), |
| community: () => get('/models/community'), |
| submissions: () => get('/models/submissions'), |
| }; |
|
|
| export const usage = { |
| mine: (_days = 30) => get('/usage/me'), |
| stats: () => get('/usage/me'), |
| history: (page = 1, per_page = 50, filters = {}) => get('/usage/me/history', { page, per_page, ...filters }), |
| quota: () => get('/usage/me/quota'), |
| all: (page = 1, per_page = 50, department = '') => get('/usage/admin/all', { page, per_page, department }), |
| models: () => get('/usage/admin/models'), |
| }; |
|
|
| export const quota = { |
| limits: () => get('/quota/limits'), |
| mine: () => get('/quota/me'), |
| remaining: () => get('/quota/me'), |
| setUser: (roll_number, data) => put(`/quota/admin/user/${roll_number}`, data), |
| exceeded: () => get('/quota/admin/exceeded'), |
| }; |
|
|
| export const keys = { |
| list: () => get('/keys/my-key').then((key) => (key ? [key] : [])), |
| generate: (label = '') => post('/keys/generate', { label }), |
| stats: () => get('/keys/my-key/stats'), |
| revoke: (_key_id = null) => del('/keys/my-key'), |
| adminAll: () => get('/keys/admin/all'), |
| adminRevoke: (roll_number) => post('/keys/admin/revoke', { roll_number }), |
| }; |
|
|
| export const scopedKeys = { |
| create: (data) => post('/scoped-keys', data), |
| mine: () => get('/scoped-keys/my'), |
| revoke: (id) => del(`/scoped-keys/${id}`), |
| adminAll: (page = 1, per_page = 100) => get('/scoped-keys/admin/all', { page, per_page }), |
| adminRevoke: (id) => del(`/scoped-keys/admin/${id}`), |
| }; |
|
|
| export const features = { |
| status: () => get('/features/status'), |
| toggle: (key, enabled) => patch(`/admin/features/${key}`, { enabled }), |
| }; |
|
|
| export const hardware = { |
| local: () => get('/hardware/local'), |
| recommendations: () => get('/hardware/recommendations'), |
| }; |
|
|
| export const network = { |
| localIp: () => get('/network/local-ip'), |
| discover: (timeout = 2) => get('/network/discover', { timeout }), |
| }; |
|
|
| export const system = { |
| version: () => get('/system/version'), |
| updateStatus: () => get('/system/update-status'), |
| restart: () => post('/admin/system/restart'), |
| logs: (lines = 200) => get('/admin/system/logs', { lines }), |
| }; |
|
|
| export const users = { |
| list: (page = 1, limit = 50, search = '') => get('/auth/admin/users', { page, per_page: limit, search }), |
| update: (user_id, data) => put(`/auth/admin/users/${user_id}`, data), |
| updateRole: (user_id, role) => put(`/auth/admin/users/${user_id}/role`, { role }), |
| updateStatus: (user_id, is_active) => put(`/auth/admin/users/${user_id}/status`, { is_active }), |
| create: (data) => post('/auth/admin/users', data), |
| delete: (user_id) => del(`/auth/admin/users/${user_id}`), |
| resetPassword: (user_id) => post(`/auth/admin/users/${user_id}/reset-password`), |
| regenerateKey: (user_id) => post(`/auth/admin/users/${user_id}/regenerate-key`), |
| stats: () => get('/auth/admin/stats'), |
| registry: () => get('/auth/admin/registry'), |
| addRegistry: (data) => post('/auth/admin/registry', data), |
| bulkRegistry: (students) => post('/auth/admin/registry/bulk', { students }), |
| uploadRegistry: (formData) => formPost('/auth/admin/registry/upload', formData), |
| }; |
|
|
| export const guardrails = { |
| getRules: () => get('/guardrails/rules'), |
| updateRules: (data) => put('/guardrails/rules', data), |
| addRule: (data) => post('/guardrails/rules', data), |
| toggleRule: (id) => patch(`/guardrails/rules/${id}/toggle`), |
| deleteRule: (id) => del(`/guardrails/rules/${id}`), |
| }; |
|
|
| export const rag = { |
| list: () => get('/rag/documents'), |
| upload: (formData) => formPost('/rag/ingest', formData), |
| delete: (doc_id) => del(`/rag/documents/${doc_id}`), |
| collections: () => get('/rag/collections'), |
| }; |
|
|
| export const notifications = { |
| list: (page = 1, per_page = 30) => get('/notifications', { page, per_page }), |
| markRead: (id) => post(`/notifications/${id}/read`), |
| markAllRead: () => post('/notifications/read-all'), |
| auditLogs: (page = 1, per_page = 100) => get('/notifications/audit-logs', { page, per_page }), |
| activity: (limit = 100) => get('/notifications/activity-stream', { limit }), |
| }; |
|
|
| export const cluster = { |
| nodes: () => get('/cluster/nodes'), |
| node: (id) => get(`/cluster/nodes/${id}`), |
| nodeAction: (id, action) => post(`/cluster/nodes/${id}/action`, { action }), |
| history: (id, limit = 60) => get(`/cluster/nodes/${id}/history`, { limit }), |
| enrollTokens: () => get('/cluster/enroll-tokens'), |
| createEnrollToken: (label = 'Worker Node', expires_hours = 24) => post('/cluster/enroll-token', { label, expires_hours }), |
| deployModel: (node_id, data) => post(`/cluster/nodes/${node_id}/deploy`, data), |
| removeDeployment: (node_id, dep_id) => del(`/cluster/nodes/${node_id}/deploy/${dep_id}`), |
| }; |
|
|
| export const academic = { |
| branches: () => get('/academic/branches'), |
| createBranch: (data) => post('/academic/branches', data), |
| updateBranch: (id, data) => patch(`/academic/branches/${id}`, data), |
| deleteBranch: (id) => del(`/academic/branches/${id}`), |
| sections: () => get('/academic/sections'), |
| branchSections: (branch_id) => get(`/academic/branches/${branch_id}/sections`), |
| createSection: (data) => post('/academic/sections', data), |
| }; |
|
|
| export const files = { |
| list: () => get('/files'), |
| upload: (formData) => formPost('/files/upload', formData), |
| download: (id) => `${BASE}/files/${id}/download`, |
| deleteFile: (id) => del(`/files/${id}`), |
| stats: (id) => get(`/files/${id}/stats`), |
| }; |
|
|
| export const notebooks = { |
| list: (include_archived = false) => get('/notebooks', { include_archived }), |
| create: (data) => post('/notebooks', data), |
| get: (id) => get(`/notebooks/${id}`), |
| update: (id, data) => patch(`/notebooks/${id}`, data), |
| delete: (id) => del(`/notebooks/${id}`), |
| addCell: (id, data) => post(`/notebooks/${id}/cells`, data), |
| updateCell: (cell_id, data) => patch(`/notebooks/cells/${cell_id}`, data), |
| deleteCell: (cell_id) => del(`/notebooks/cells/${cell_id}`), |
| runCell: (cell_id) => post(`/notebooks/cells/${cell_id}/run`), |
| executions: (cell_id) => get(`/notebooks/cells/${cell_id}/executions`), |
| reorder: (id, cell_ids) => post(`/notebooks/${id}/reorder`, { cell_ids }), |
| }; |
|
|
| export const doubts = { |
| create: (data) => post('/doubts', data), |
| mine: (page = 1, per_page = 20) => get('/doubts/my', { page, per_page }), |
| all: (filters = {}) => get('/doubts/all', filters), |
| get: (id) => get(`/doubts/${id}`), |
| reply: (id, body) => post(`/doubts/${id}/reply`, { body }), |
| close: (id) => post(`/doubts/${id}/close`), |
| }; |
|
|
| export const attendance = { |
| settings: () => get('/attendance/settings'), |
| updateSettings: (data) => put('/attendance/settings', data), |
| subjects: () => get('/attendance/subjects'), |
| faceStatus: () => get('/attendance/face-status'), |
| registerFace: (face_image_base64) => post('/attendance/register-face', { face_image_base64 }), |
| sessions: (filters = {}) => get('/attendance/sessions', filters), |
| createSession: (data) => post('/attendance/sessions', data), |
| closeSession: (id) => post(`/attendance/sessions/${id}/close`), |
| mark: (session_id, face_image_base64) => post('/attendance/mark', { session_id, face_image_base64 }), |
| report: (id) => get(`/attendance/sessions/${id}/report`), |
| adminOverview: (filters = {}) => get('/attendance/admin/overview', filters), |
| summary: (department = '') => get('/attendance/summary', { department }), |
| reportCsvUrl: (id) => `${BASE}/attendance/sessions/${id}/report/csv`, |
| reportPdfUrl: (id) => `${BASE}/attendance/sessions/${id}/report/pdf`, |
| summaryCsvUrl: (department = '') => `${BASE}/attendance/summary/csv${department ? `?department=${encodeURIComponent(department)}` : ''}`, |
| }; |
|
|
| export const copyCheck = { |
| sessions: (page = 1, per_page = 50) => get('/copy-check/sessions', { page, per_page }), |
| createSession: (formData) => formPost('/copy-check/sessions', formData), |
| getSession: (id) => get(`/copy-check/sessions/${id}`), |
| students: (id) => get(`/copy-check/sessions/${id}/students`), |
| uploadSheet: (id, formData) => formPost(`/copy-check/sessions/${id}/sheets`, formData), |
| evaluate: (id) => post(`/copy-check/sessions/${id}/evaluate`), |
| plagiarism: (id) => post(`/copy-check/sessions/${id}/plagiarism`), |
| archive: (id) => patch(`/copy-check/sessions/${id}/archive`), |
| reportUrl: (id) => `${BASE}/copy-check/sessions/${id}/report/pdf`, |
| }; |
|
|