/
h0tnanny
/
IotPlatform
Обзор
Документация
Войти
/
h0tnanny
/
IotPlatform
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
client/src/api/client.ts
383 строки
11 KB
h0tnanny
Добавление функциональных требований
08 фев 2026, 23:02
08 фев 2026, 23:02
a8a23c5
Код
Авторство
О чём код?
import type { Equipment, Workflow, SystemState, ApiResponse, User, UserGroup, ResourcePermission, ResourceShare, EmulatedDevice, EmulatedDeviceVariable, EmulatorMode, } from '../types/api.types'; const API_BASE = '/api'; function getToken(): string | null { return localStorage.getItem('accessToken'); } // Обертка для fetch с обработкой ошибок и JWT async function apiFetch<T>(url: string, options?: RequestInit): Promise<T> { const token = getToken(); const headers: Record<string, string> = { 'Content-Type': 'application/json', ...(options?.headers as Record<string, string> || {}), }; if (token) { headers['Authorization'] = `Bearer ${token}`; } let response = await fetch(`${API_BASE}${url}`, { ...options, headers, }); // Если 401, пробуем refresh if (response.status === 401) { const refreshToken = localStorage.getItem('refreshToken'); if (refreshToken) { const refreshResponse = await fetch(`${API_BASE}/auth/refresh`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken }), }); if (refreshResponse.ok) { const refreshData = await refreshResponse.json(); localStorage.setItem('accessToken', refreshData.data.accessToken); localStorage.setItem('refreshToken', refreshData.data.refreshToken); // Повторяем запрос с новым токеном headers['Authorization'] = `Bearer ${refreshData.data.accessToken}`; response = await fetch(`${API_BASE}${url}`, { ...options, headers }); } else { localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); window.location.href = '/login'; throw new Error('Сессия истекла'); } } else { window.location.href = '/login'; throw new Error('Необходима авторизация'); } } if (!response.ok) { const error = await response.json(); throw new Error(error?.error?.message || 'Network error'); } return response.json(); } export const apiClient = { // Equipment API (DataSource) createEquipment: (equipment: Omit<Equipment, 'variables'> & { variableList: Equipment['variables'] }) => apiFetch<ApiResponse<{ id: string; name: string; protocol: string; dataMode: string; endpoint: string; pollInterval: number; variablesCount: number }>>( '/equipment', { method: 'POST', body: JSON.stringify(equipment), } ), updateEquipment: (id: string, equipment: Omit<Equipment, 'variables'> & { variableList: Equipment['variables'] }) => apiFetch<ApiResponse<{ id: string; name: string; protocol: string; dataMode: string; endpoint: string; pollInterval: number; variablesCount: number }>>( `/equipment/${id}`, { method: 'PUT', body: JSON.stringify(equipment), } ), // Workflow API validateWorkflow: (data: { startNodeId: string; nodes: Workflow['nodes']; variableList: Workflow['variableList'] }) => apiFetch<ApiResponse<{ valid: boolean; errors: Array<{ type: 'error'; nodeId?: string; message: string }>; warnings: Array<{ type: 'warning'; nodeId?: string; message: string }>; }>>('/workflows/validate', { method: 'POST', body: JSON.stringify(data), }), createWorkflow: (workflow: Workflow) => apiFetch<ApiResponse<{ id: string; isActivated: boolean; startNodeId: string; nodesCount: number }>>( '/workflows', { method: 'POST', body: JSON.stringify(workflow), } ), getWorkflow: (id: string) => apiFetch<ApiResponse<Workflow>>(`/workflows/${id}`), updateWorkflow: (id: string, workflow: Workflow) => apiFetch<ApiResponse<{ id: string; isActivated: boolean; startNodeId: string; nodesCount: number }>>( `/workflows/${id}`, { method: 'PUT', body: JSON.stringify(workflow), } ), deleteWorkflow: (id: string) => apiFetch<ApiResponse<{ id: string; removed: boolean; message: string }>>( `/workflows/${id}`, { method: 'DELETE', } ), executeWorkflow: (id: string) => apiFetch<ApiResponse<{ message: string }>>(`/workflows/${id}/execute`, { method: 'POST', }), getWorkflowExecution: (id: string) => apiFetch<ApiResponse<{ isRunning: boolean; currentNodeId: string | null; executedNodes: string[]; executionPath: string[]; logs: Array<{ timestamp: number; level: 'info' | 'success' | 'warning' | 'error'; message: string; nodeId?: string; }>; }>>(`/workflows/${id}/execution`), // State API getState: () => apiFetch<ApiResponse<SystemState>>('/state'), // ===== Auth & Users API ===== getUsers: () => apiFetch<ApiResponse<User[]>>('/users'), createUser: (data: { username: string; email: string; password: string; role?: 'admin' | 'user'; firstName?: string; lastName?: string; patronymic?: string; position?: string; }) => apiFetch<ApiResponse<User>>('/users', { method: 'POST', body: JSON.stringify(data), }), updateProfile: (id: string, data: { username?: string; firstName?: string; lastName?: string; patronymic?: string; position?: string }) => apiFetch<ApiResponse<User>>(`/users/${id}`, { method: 'PUT', body: JSON.stringify(data), }), changePassword: (id: string, data: { currentPassword?: string; newPassword: string }) => apiFetch<ApiResponse<{ message: string }>>(`/users/${id}/password`, { method: 'PUT', body: JSON.stringify(data), }), changeUserRole: (id: string, role: string) => apiFetch<ApiResponse<{ id: string; username: string; role: string }>>(`/users/${id}/role`, { method: 'PUT', body: JSON.stringify({ role }), }), deleteUser: (id: string) => apiFetch<ApiResponse<{ message: string }>>(`/users/${id}`, { method: 'DELETE', }), // ===== Groups API ===== getGroups: () => apiFetch<ApiResponse<UserGroup[]>>('/groups'), createGroup: (name: string, description?: string, defaultSharePermission?: 'viewer' | 'editor') => apiFetch<ApiResponse<UserGroup>>('/groups', { method: 'POST', body: JSON.stringify({ name, description, default_share_permission: defaultSharePermission }), }), updateGroup: ( id: string, name: string, description?: string, defaultSharePermission?: 'viewer' | 'editor', ) => apiFetch<ApiResponse<UserGroup>>(`/groups/${id}`, { method: 'PUT', body: JSON.stringify({ name, description, ...(defaultSharePermission !== undefined && { default_share_permission: defaultSharePermission }), }), }), deleteGroup: (id: string) => apiFetch<ApiResponse<{ message: string }>>(`/groups/${id}`, { method: 'DELETE', }), addGroupMember: (groupId: string, userId: string, role: string = 'viewer') => apiFetch<ApiResponse<unknown>>(`/groups/${groupId}/members`, { method: 'POST', body: JSON.stringify({ userId, role }), }), removeGroupMember: (groupId: string, userId: string) => apiFetch<ApiResponse<{ message: string }>>(`/groups/${groupId}/members/${userId}`, { method: 'DELETE', }), updateGroupMemberRole: (groupId: string, userId: string, role: string) => apiFetch<ApiResponse<unknown>>(`/groups/${groupId}/members/${userId}`, { method: 'PUT', body: JSON.stringify({ role }), }), // ===== Permissions API ===== getMyResources: () => apiFetch<ApiResponse<ResourcePermission[]>>('/permissions/my-resources'), getSharedWithMe: () => apiFetch<ApiResponse<ResourceShare[]>>('/permissions/shared-with-me'), shareResource: (data: { resourceType: string; resourceId: string; sharedWithType: 'user' | 'group'; sharedWithId: string; permission: 'viewer' | 'editor'; }) => apiFetch<ApiResponse<ResourceShare>>('/permissions/share', { method: 'POST', body: JSON.stringify(data), }), removeShare: (shareId: string) => apiFetch<ApiResponse<{ message: string }>>(`/permissions/share/${shareId}`, { method: 'DELETE', }), // ===== Emulator API (Admin) ===== getEmulatedDevices: () => apiFetch<ApiResponse<EmulatedDevice[]>>('/emulator/devices'), getEmulatedDevice: (id: string) => apiFetch<ApiResponse<EmulatedDevice>>(`/emulator/devices/${id}`), createEmulatedDevice: (data: { name: string; description?: string; mode: EmulatorMode; targetEquipmentId?: string; pushInterval?: number; udpPort?: number; variables: EmulatedDeviceVariable[]; }) => apiFetch<ApiResponse<EmulatedDevice>>('/emulator/devices', { method: 'POST', body: JSON.stringify(data), }), updateEmulatedDevice: (id: string, data: { name: string; description?: string; mode: EmulatorMode; targetEquipmentId?: string; pushInterval?: number; udpPort?: number; variables: EmulatedDeviceVariable[]; }) => apiFetch<ApiResponse<EmulatedDevice>>(`/emulator/devices/${id}`, { method: 'PUT', body: JSON.stringify(data), }), deleteEmulatedDevice: (id: string) => apiFetch<ApiResponse<{ message: string }>>(`/emulator/devices/${id}`, { method: 'DELETE', }), startEmulatedDevice: (id: string) => apiFetch<ApiResponse<{ id: string; isRunning: boolean; message: string }>>(`/emulator/devices/${id}/start`, { method: 'POST', }), stopEmulatedDevice: (id: string) => apiFetch<ApiResponse<{ id: string; isRunning: boolean; message: string }>>(`/emulator/devices/${id}/stop`, { method: 'POST', }), getEmulatedDeviceValues: (id: string) => apiFetch<ApiResponse<Record<string, unknown>>>(`/emulator/devices/${id}/values`), sendEquipmentCommand: (id: string, command: string, payload?: Record<string, unknown>) => apiFetch<ApiResponse<{ message: string }>>(`/equipment/${id}/command`, { method: 'POST', body: JSON.stringify({ command, payload }), }), // ===== Logs API (Admin) ===== getLogs: (params?: { level?: string; category?: string; dateFrom?: string; dateTo?: string; page?: number; limit?: number; }) => { const query = new URLSearchParams(); if (params?.level) query.set('level', params.level); if (params?.category) query.set('category', params.category); if (params?.dateFrom) query.set('dateFrom', params.dateFrom); if (params?.dateTo) query.set('dateTo', params.dateTo); if (params?.page) query.set('page', String(params.page)); if (params?.limit) query.set('limit', String(params.limit)); const qs = query.toString(); return apiFetch<ApiResponse<{ logs: Array<{ id: number; timestamp: string; level: string; category: string; message: string; details: unknown; userId: string | null; resourceId: string | null; resourceType: string | null; }>; total: number; page: number; limit: number; totalPages: number; }>>(`/admin/logs${qs ? `?${qs}` : ''}`); }, getLogStats: () => apiFetch<ApiResponse<{ byLevel: Record<string, number>; byCategory: Record<string, number>; total: number; }>>('/admin/logs/stats'), };