generated from VLADIMIR/template_frontend
271 lines
9.0 KiB
TypeScript
271 lines
9.0 KiB
TypeScript
import axios, { type AxiosInstance, type InternalAxiosRequestConfig, AxiosError } from 'axios';
|
|
import type {
|
|
LoginReq,
|
|
LoginRsp,
|
|
SignupReq,
|
|
SignupRsp,
|
|
RefreshPasswordReq,
|
|
RefreshPasswordRsp,
|
|
RefreshReq,
|
|
RefreshRsp,
|
|
GetUsersReq,
|
|
GetUsersRsp,
|
|
GetUserByIdReq,
|
|
GetUserByIdRsp,
|
|
GetMeReq,
|
|
GetMeRsp,
|
|
AddUserRoleReq,
|
|
AddUserRoleRsp,
|
|
DeleteUserRoleReq,
|
|
DeleteUserRoleRsp,
|
|
GetPermissionsReq,
|
|
GetPermissionsRsp,
|
|
UploadFileReq,
|
|
UploadFileRsp,
|
|
DownloadFileReq,
|
|
AddScenarioReq,
|
|
AddScenarioRsp,
|
|
GetMyScenariosReq,
|
|
GetMyScenariosRsp,
|
|
GetScenarioReq,
|
|
GetScenarioRsp,
|
|
UpdateScenarioReq,
|
|
UpdateScenarioRsp,
|
|
DeleteScenarioReq,
|
|
DeleteScenarioRsp,
|
|
AddScenarioPlaceReq,
|
|
AddScenarioPlaceRsp,
|
|
UpdateScenarioPlaceReq,
|
|
UpdateScenarioPlaceRsp,
|
|
DeleteScenarioPlaceReq,
|
|
DeleteScenarioPlaceRsp,
|
|
PingReq,
|
|
PingRsp,
|
|
EchoReq,
|
|
EchoRsp,
|
|
} from './generated/main';
|
|
|
|
// Типизированный ответ с error полем
|
|
type ApiResponse<T> = T & { error?: string };
|
|
|
|
class ApiClient {
|
|
private client: AxiosInstance;
|
|
private baseUrl: string;
|
|
|
|
constructor() {
|
|
this.baseUrl = import.meta.env.VITE_API_URL || 'http://localhost:8080';
|
|
this.client = axios.create({
|
|
baseURL: this.baseUrl,
|
|
timeout: 30000,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
// Интерсептор для токена
|
|
this.client.interceptors.request.use((config) => {
|
|
const token = localStorage.getItem('accessToken');
|
|
if (token) {
|
|
config.headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
return config;
|
|
});
|
|
|
|
// Интерсептор для рефреша токена
|
|
this.client.interceptors.response.use(
|
|
(response) => response,
|
|
async (error: AxiosError) => {
|
|
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
|
|
|
|
if (error.response?.status === 401 && !originalRequest._retry) {
|
|
originalRequest._retry = true;
|
|
|
|
try {
|
|
const refreshToken = localStorage.getItem('refreshToken');
|
|
if (!refreshToken) {
|
|
throw new Error('No refresh token');
|
|
}
|
|
|
|
const response = await this.refresh({ refreshToken });
|
|
localStorage.setItem('accessToken', response.accessToken!);
|
|
localStorage.setItem('refreshToken', response.refreshToken!);
|
|
|
|
originalRequest.headers.Authorization = `Bearer ${response.accessToken}`;
|
|
return this.client(originalRequest);
|
|
} catch (refreshError) {
|
|
// Рефреш не удался — разлогиниваем
|
|
localStorage.removeItem('accessToken');
|
|
localStorage.removeItem('refreshToken');
|
|
window.location.href = '/login';
|
|
return Promise.reject(refreshError);
|
|
}
|
|
}
|
|
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
}
|
|
|
|
// ---------- AUTH ----------
|
|
|
|
async ping(params: PingReq = {}): Promise<ApiResponse<PingRsp>> {
|
|
const { data } = await this.client.get('/api/test/ping');
|
|
return data;
|
|
}
|
|
|
|
async echo(params: EchoReq): Promise<ApiResponse<EchoRsp>> {
|
|
const { data } = await this.client.post('/api/test/echo', params);
|
|
return data;
|
|
}
|
|
|
|
async signup(params: SignupReq): Promise<ApiResponse<SignupRsp>> {
|
|
const { data } = await this.client.post('/api/auth/signup', params);
|
|
return data;
|
|
}
|
|
|
|
async refreshPassword(params: RefreshPasswordReq): Promise<ApiResponse<RefreshPasswordRsp>> {
|
|
const { data } = await this.client.post('/api/auth/refresh-password', params);
|
|
return data;
|
|
}
|
|
|
|
async login(params: LoginReq): Promise<ApiResponse<LoginRsp>> {
|
|
const { data } = await this.client.post('/api/auth/login', params);
|
|
if (data.accessToken) {
|
|
localStorage.setItem('accessToken', data.accessToken);
|
|
if (data.refreshToken) {
|
|
localStorage.setItem('refreshToken', data.refreshToken);
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
async refresh(params: RefreshReq): Promise<ApiResponse<RefreshRsp>> {
|
|
const { data } = await this.client.post('/api/auth/refresh', params);
|
|
return data;
|
|
}
|
|
|
|
async logout(): Promise<void> {
|
|
localStorage.removeItem('accessToken');
|
|
localStorage.removeItem('refreshToken');
|
|
}
|
|
|
|
// ---------- USERS ----------
|
|
|
|
async getUsers(params: GetUsersReq = {}): Promise<ApiResponse<GetUsersRsp>> {
|
|
const { data } = await this.client.get('/api/users');
|
|
return data;
|
|
}
|
|
|
|
async getUserById(params: GetUserByIdReq): Promise<ApiResponse<GetUserByIdRsp>> {
|
|
const { data } = await this.client.get(`/api/users/${params.id}`);
|
|
return data;
|
|
}
|
|
|
|
async getMe(params: GetMeReq = {}): Promise<ApiResponse<GetMeRsp>> {
|
|
const { data } = await this.client.get('/api/users/me');
|
|
return data;
|
|
}
|
|
|
|
async addUserRole(params: AddUserRoleReq): Promise<ApiResponse<AddUserRoleRsp>> {
|
|
const { id, role } = params;
|
|
const { data } = await this.client.post(`/api/users/${id}/role/add`, { role });
|
|
return data;
|
|
}
|
|
|
|
async deleteUserRole(params: DeleteUserRoleReq): Promise<ApiResponse<DeleteUserRoleRsp>> {
|
|
const { id, role } = params;
|
|
const { data } = await this.client.post(`/api/users/${id}/role/delete`, { role });
|
|
return data;
|
|
}
|
|
|
|
// ---------- PERMISSIONS ----------
|
|
|
|
async getPermissions(params: GetPermissionsReq = {}): Promise<ApiResponse<GetPermissionsRsp>> {
|
|
const { data } = await this.client.get('/api/ui/permissions');
|
|
return data;
|
|
}
|
|
|
|
// ---------- FILES ----------
|
|
|
|
async uploadFile(params: UploadFileReq): Promise<ApiResponse<UploadFileRsp>> {
|
|
if (!params.data) {
|
|
throw new Error('File data is required');
|
|
}
|
|
|
|
const formData = new FormData();
|
|
formData.append('filename', params.filename!);
|
|
// Преобразуем bytes в Blob
|
|
const dataArray = new Uint8Array(params.data);
|
|
const blob = new Blob([dataArray]);
|
|
formData.append('data', blob);
|
|
|
|
const { data } = await this.client.post('/api/files/upload', formData, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data',
|
|
},
|
|
});
|
|
return data;
|
|
}
|
|
|
|
async downloadFile(params: DownloadFileReq): Promise<Blob> {
|
|
const response = await this.client.get(`/api/files/${params.filename}`, {
|
|
responseType: 'blob',
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
// ---------- SCENARIOS ----------
|
|
|
|
async addScenario(params: AddScenarioReq): Promise<ApiResponse<AddScenarioRsp>> {
|
|
const { data } = await this.client.post('/api/scenario', params);
|
|
return data;
|
|
}
|
|
|
|
async getMyScenarios(params: GetMyScenariosReq = {}): Promise<ApiResponse<GetMyScenariosRsp>> {
|
|
const { data } = await this.client.get('/api/my-scenarios');
|
|
return data;
|
|
}
|
|
|
|
async getScenario(params: GetScenarioReq): Promise<ApiResponse<GetScenarioRsp>> {
|
|
const { data } = await this.client.get(`/api/scenarios/${params.id}`);
|
|
return data;
|
|
}
|
|
|
|
async updateScenario(params: UpdateScenarioReq): Promise<ApiResponse<UpdateScenarioRsp>> {
|
|
const { id, ...body } = params;
|
|
const { data } = await this.client.put(`/api/scenarios/${id}`, body);
|
|
return data;
|
|
}
|
|
|
|
async deleteScenario(params: DeleteScenarioReq): Promise<ApiResponse<DeleteScenarioRsp>> {
|
|
const { data } = await this.client.delete(`/api/scenarios/${params.id}`);
|
|
return data;
|
|
}
|
|
|
|
// ---------- SCENARIO PLACES ----------
|
|
|
|
async addScenarioPlace(params: AddScenarioPlaceReq): Promise<ApiResponse<AddScenarioPlaceRsp>> {
|
|
const { id, place } = params;
|
|
const { data } = await this.client.post(`/api/scenarios/${id}/places`, place);
|
|
return data;
|
|
}
|
|
|
|
async updateScenarioPlace(params: UpdateScenarioPlaceReq): Promise<ApiResponse<UpdateScenarioPlaceRsp>> {
|
|
const { id, code, place } = params;
|
|
const { data } = await this.client.put(`/api/scenarios/${id}/places/${code}`, place);
|
|
return data;
|
|
}
|
|
|
|
async deleteScenarioPlace(params: DeleteScenarioPlaceReq): Promise<ApiResponse<DeleteScenarioPlaceRsp>> {
|
|
const { id, code } = params;
|
|
const { data } = await this.client.delete(`/api/scenarios/${id}/places/${code}`);
|
|
return data;
|
|
}
|
|
}
|
|
|
|
// Единый экспорт
|
|
export const api = new ApiClient();
|
|
|
|
// Экспорт типов для удобства
|
|
export * from './generated/main';
|