feat: add MQTT client for camera configuration and enhance camera management

This commit is contained in:
2026-02-08 11:58:57 +07:00
parent 78162fc0cb
commit d619534a73
14 changed files with 1254 additions and 111 deletions

View File

@@ -9,8 +9,9 @@ import {
getRefreshToken,
setAccessToken,
} from '@/utils/storage';
import { history, request, RequestConfig } from '@umijs/max';
import { history, RequestConfig } from '@umijs/max';
import { message } from 'antd';
import axios from 'axios';
// Trạng thái dùng chung cho cơ chế refresh token + hàng đợi
let refreshingTokenPromise: Promise<string | null> | null = null;
@@ -52,7 +53,8 @@ export const handleRequestConfig: RequestConfig = {
return (
(status >= 200 && status < 300) ||
status === HTTPSTATUS.HTTP_NOTFOUND ||
status === HTTPSTATUS.HTTP_UNAUTHORIZED
status === HTTPSTATUS.HTTP_UNAUTHORIZED ||
status === HTTPSTATUS.HTTP_FORBIDDEN
);
},
headers: { 'X-Requested-With': 'XMLHttpRequest' },
@@ -123,15 +125,17 @@ export const handleRequestConfig: RequestConfig = {
// Unwrap data from backend response
responseInterceptors: [
async (response: any, options: any) => {
const isRefreshRequest = response.url?.includes(API_PATH_REFRESH_TOKEN);
const alreadyRetried = options?.skipAuthRefresh === true;
// const isRefreshRequest = response.url?.includes(API_PATH_REFRESH_TOKEN); // response.url may be undefined or different in prod
const isRefreshRequest = response.config.url?.includes(
API_PATH_REFRESH_TOKEN,
);
// const alreadyRetried = options?.skipAuthRefresh === true;
// Xử lý 401: đưa request vào hàng đợi, gọi refresh token, rồi gọi lại
if (
response.status === HTTPSTATUS.HTTP_UNAUTHORIZED &&
// Không tự refresh cho chính API refresh token để tránh vòng lặp vô hạn
!isRefreshRequest &&
!alreadyRetried
!isRefreshRequest
) {
const newToken = await getValidAccessToken();
@@ -151,24 +155,35 @@ export const handleRequestConfig: RequestConfig = {
// Rebuild request options from config
const originalConfig = response.config;
// Parse data if it is a JSON string to avoid double serialization when using axios directly
let data = originalConfig.data;
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) {
// Ignore parse error, use original string
}
}
const newOptions = {
url: originalConfig.url,
method: originalConfig.method,
headers: {
...(originalConfig.headers || {}),
Authorization: `${newToken}`,
},
data: originalConfig.data,
data: data,
params: originalConfig.params,
skipAuthRefresh: true,
};
// Gọi lại request gốc với accessToken mới
return request(originalConfig.url, newOptions);
// Gọi lại request gốc với accessToken mới bằng axios để tránh double-unwrap
return axios(newOptions);
}
if (
response.status === HTTPSTATUS.HTTP_UNAUTHORIZED &&
(isRefreshRequest || alreadyRetried)
isRefreshRequest
) {
clearAllData();
history.push(ROUTE_LOGIN);