feat(project): base smatec's frontend
This commit is contained in:
44
config/proxy_dev.ts
Normal file
44
config/proxy_dev.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
const getTarget = () => {
|
||||
const env = process.env.DOMAIN_ENV;
|
||||
switch (env) {
|
||||
case 'gms':
|
||||
return 'https://gms.smatec.com.vn';
|
||||
case 'spole':
|
||||
return 'https://spole.gms.vn';
|
||||
case 'sgw':
|
||||
return 'https://sgw.gms.vn';
|
||||
default:
|
||||
return 'https://sgw.gms.vn';
|
||||
}
|
||||
};
|
||||
|
||||
const target = getTarget();
|
||||
|
||||
const proxyDev: Record<string, any> = {
|
||||
dev: {
|
||||
'/api': {
|
||||
target: target,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/geoserver': {
|
||||
target: target,
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
test: {
|
||||
'/test': {
|
||||
target: 'https://proapi.azurewebsites.net',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
prod: {
|
||||
'/test': {
|
||||
target: 'https://prod-sgw-device.gms.vn',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default proxyDev;
|
||||
86
config/proxy_prod.ts
Normal file
86
config/proxy_prod.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
// Lấy hostname từ URL hiện tại của trang web
|
||||
const getCurrentHostname = () => {
|
||||
// Trong build-time, sử dụng environment variable
|
||||
if (typeof window === 'undefined') {
|
||||
return process.env.REACT_APP_HOSTNAME || 'localhost';
|
||||
}
|
||||
|
||||
// Trong runtime, lấy từ URL của trang web
|
||||
return window.location.hostname;
|
||||
};
|
||||
|
||||
// Lấy port cho GeoServer từ environment variable hoặc default là 8080
|
||||
const getGeoserverPort = () => {
|
||||
return process.env.REACT_APP_GEOSERVER_PORT || '8080';
|
||||
};
|
||||
|
||||
// Tạo proxy config động dựa trên hostname hiện tại
|
||||
const createDynamicProxy = () => {
|
||||
const hostname = getCurrentHostname();
|
||||
const geoserverPort = getGeoserverPort();
|
||||
const isLocalhost =
|
||||
hostname === 'localhost' ||
|
||||
hostname.startsWith('192.168.') ||
|
||||
hostname.startsWith('10.') ||
|
||||
hostname.startsWith('127.');
|
||||
|
||||
// Với production, ta sẽ proxy:
|
||||
// - /api đến cùng hostname với port 81
|
||||
// - /geoserver đến cùng hostname với port GEOSERVER_PORT (default: 8080)
|
||||
// Ví dụ: nếu truy cập https://example.com thì API sẽ đến http://example.com:81/api
|
||||
// và GeoServer sẽ đến http://example.com:[GEOSERVER_PORT]/geoserver
|
||||
return {
|
||||
dev: {
|
||||
'/api': {
|
||||
target: `http://${hostname}:81`,
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^/api': '/api' },
|
||||
},
|
||||
'/geoserver': {
|
||||
target: `http://${hostname}:${geoserverPort}`,
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^/geoserver': '/geoserver' },
|
||||
},
|
||||
},
|
||||
test: {
|
||||
'/api': {
|
||||
target: isLocalhost
|
||||
? `http://${hostname}:81`
|
||||
: `https://${hostname}:81`,
|
||||
changeOrigin: true,
|
||||
secure: !isLocalhost,
|
||||
pathRewrite: { '^/api': '/api' },
|
||||
},
|
||||
'/geoserver': {
|
||||
target: isLocalhost
|
||||
? `http://${hostname}:${geoserverPort}`
|
||||
: `https://${hostname}:${geoserverPort}`,
|
||||
changeOrigin: true,
|
||||
secure: !isLocalhost,
|
||||
pathRewrite: { '^/geoserver': '/geoserver' },
|
||||
},
|
||||
},
|
||||
prod: {
|
||||
'/api': {
|
||||
target: isLocalhost
|
||||
? `http://${hostname}:81`
|
||||
: `https://${hostname}:81`,
|
||||
changeOrigin: true,
|
||||
secure: !isLocalhost,
|
||||
pathRewrite: { '^/api': '/api' },
|
||||
},
|
||||
'/geoserver': {
|
||||
target: isLocalhost
|
||||
? `http://${hostname}:${geoserverPort}`
|
||||
: `https://${hostname}:${geoserverPort}`,
|
||||
changeOrigin: true,
|
||||
secure: !isLocalhost,
|
||||
pathRewrite: { '^/geoserver': '/geoserver' },
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const proxyProd: Record<string, any> = createDynamicProxy();
|
||||
|
||||
export default proxyProd;
|
||||
131
config/request_dev.ts
Normal file
131
config/request_dev.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { HTTPSTATUS } from '@/constants';
|
||||
import { ROUTE_LOGIN } from '@/constants/routes';
|
||||
import { getToken, removeToken } from '@/utils/storage';
|
||||
import { history, RequestConfig } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
|
||||
// Error handling scheme: Error types
|
||||
// enum ErrorShowType {
|
||||
// SILENT = 0,
|
||||
// WARN_MESSAGE = 1,
|
||||
// ERROR_MESSAGE = 2,
|
||||
// NOTIFICATION = 3,
|
||||
// REDIRECT = 9,
|
||||
// }
|
||||
// Response data structure agreed with the backend
|
||||
// interface ResponseStructure<T = any> {
|
||||
// success: boolean;
|
||||
// data: T;
|
||||
// errorCode?: number;
|
||||
// errorMessage?: string;
|
||||
// showType?: ErrorShowType;
|
||||
// }
|
||||
|
||||
// const codeMessage = {
|
||||
// 200: 'The server successfully returned the requested data。',
|
||||
// 201: 'New or modified data succeeded。',
|
||||
// 202: 'A request has been queued in the background (asynchronous task)。',
|
||||
// 204: 'Data deleted successfully。',
|
||||
// 400: 'There is an error in the request sent, the server did not perform the operation of creating or modifying data。',
|
||||
// 401: 'The user does not have permission (token, username, password is wrong) 。',
|
||||
// 403: 'User is authorized, but access is prohibited。',
|
||||
// 404: 'The request issued was for a non-existent record, the server did not operate。',
|
||||
// 406: 'The requested format is not available。',
|
||||
// 410: 'The requested resource is permanently deleted and will no longer be available。',
|
||||
// 422: 'When creating an object, a validation error occurred。',
|
||||
// 500: 'Server error, please check the server。',
|
||||
// 502: 'Gateway error。',
|
||||
// 503: 'Service unavailable, server temporarily overloaded or maintained。',
|
||||
// 504: 'Gateway timeout。',
|
||||
// };
|
||||
|
||||
// Runtime configuration
|
||||
export const handleRequestConfig: RequestConfig = {
|
||||
// Unified request settings
|
||||
timeout: 20000,
|
||||
validateStatus: (status) => {
|
||||
return (
|
||||
(status >= 200 && status < 300) || status === HTTPSTATUS.HTTP_NOTFOUND
|
||||
);
|
||||
},
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||
// Error handling: umi@3's error handling scheme.
|
||||
errorConfig: {
|
||||
// Error throwing
|
||||
errorThrower: (res: any) => {
|
||||
// console.log('Response from backend:', res);
|
||||
const { success, data, errorCode, errorMessage, showType } = res;
|
||||
if (!success) {
|
||||
const error: any = new Error(errorMessage);
|
||||
error.name = 'BizError';
|
||||
error.info = { errorCode, errorMessage, showType, data };
|
||||
throw error; // Throw custom error
|
||||
}
|
||||
},
|
||||
|
||||
// Error catching and handling
|
||||
errorHandler: (error: any) => {
|
||||
if (error.response) {
|
||||
const { status } = error.response;
|
||||
|
||||
// Ưu tiên: codeMessage → backend message → statusText
|
||||
// const errMsg =
|
||||
// codeMessage[status as keyof typeof codeMessage] ||
|
||||
// data?.message ||
|
||||
// statusText ||
|
||||
// 'Unknown error';
|
||||
|
||||
if (status === HTTPSTATUS.HTTP_UNAUTHORIZED) {
|
||||
removeToken();
|
||||
history.push(ROUTE_LOGIN);
|
||||
} else if (status === HTTPSTATUS.HTTP_SERVERERROR) {
|
||||
// message.error('💥 Internal server error!');
|
||||
} else {
|
||||
message.error(`❌ ${status}: ${error.message || 'Error'}`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
message.error('🚨 No response from server!');
|
||||
} else {
|
||||
message.error(`⚠️ Request setup error: ${error.message}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
// Request interceptors
|
||||
requestInterceptors: [
|
||||
(url: string, options: any) => {
|
||||
const token = getToken();
|
||||
// console.log('Token: ', token);
|
||||
|
||||
return {
|
||||
url,
|
||||
options: {
|
||||
...options,
|
||||
headers: {
|
||||
...options.headers,
|
||||
...(token ? { Authorization: `${token}` } : {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
],
|
||||
|
||||
// Unwrap data from backend response
|
||||
responseInterceptors: [
|
||||
(response) => {
|
||||
// console.log('Response from server: ', response);
|
||||
|
||||
// const res = response.data as ResponseStructure<any>;
|
||||
// if (res && res.success) {
|
||||
// // ✅ Trả ra data luôn thay vì cả object
|
||||
// return res.data;
|
||||
// }
|
||||
return {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: response.headers,
|
||||
config: response.config,
|
||||
data: response.data,
|
||||
};
|
||||
},
|
||||
],
|
||||
};
|
||||
107
config/request_prod.ts
Normal file
107
config/request_prod.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { HTTPSTATUS } from '@/constants';
|
||||
import { ROUTE_LOGIN } from '@/constants/routes';
|
||||
import { getToken, removeToken } from '@/utils/storage';
|
||||
import { history, RequestConfig } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
|
||||
const codeMessage = {
|
||||
200: 'The server successfully returned the requested data。',
|
||||
201: 'New or modified data succeeded。',
|
||||
202: 'A request has been queued in the background (asynchronous task)。',
|
||||
204: 'Data deleted successfully。',
|
||||
400: 'There is an error in the request sent, the server did not perform the operation of creating or modifying data。',
|
||||
401: 'The user does not have permission (token, username, password is wrong) 。',
|
||||
403: 'User is authorized, but access is prohibited。',
|
||||
404: 'The request issued was for a non-existent record, the server did not operate。',
|
||||
406: 'The requested format is not available。',
|
||||
410: 'The requested resource is permanently deleted and will no longer be available。',
|
||||
422: 'When creating an object, a validation error occurred。',
|
||||
500: 'Server error, please check the server。',
|
||||
502: 'Gateway error。',
|
||||
503: 'Service unavailable, server temporarily overloaded or maintained。',
|
||||
504: 'Gateway timeout。',
|
||||
};
|
||||
|
||||
// Runtime configuration
|
||||
export const handleRequestConfig: RequestConfig = {
|
||||
// Unified request settings
|
||||
timeout: 20000,
|
||||
validateStatus: (status) => {
|
||||
return (
|
||||
(status >= 200 && status < 300) || status === HTTPSTATUS.HTTP_NOTFOUND
|
||||
);
|
||||
},
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||
// Error handling: umi@3's error handling scheme.
|
||||
errorConfig: {
|
||||
// Error throwing
|
||||
errorThrower: (res: any) => {
|
||||
// console.log('Response from backend:', res);
|
||||
const { success, data, errorCode, errorMessage, showType } = res;
|
||||
if (!success) {
|
||||
const error: any = new Error(errorMessage);
|
||||
error.name = 'BizError';
|
||||
error.info = { errorCode, errorMessage, showType, data };
|
||||
throw error; // Throw custom error
|
||||
}
|
||||
},
|
||||
// Error catching and handling
|
||||
errorHandler: (error: any) => {
|
||||
if (error.response) {
|
||||
const { status, statusText, data } = error.response;
|
||||
|
||||
// Ưu tiên: codeMessage → backend message → statusText
|
||||
const errMsg =
|
||||
codeMessage[status as keyof typeof codeMessage] ||
|
||||
data?.message ||
|
||||
statusText ||
|
||||
'Unknown error';
|
||||
|
||||
message.error(`❌ ${status}: ${errMsg}`);
|
||||
if (status === 401) {
|
||||
removeToken();
|
||||
history.push(ROUTE_LOGIN);
|
||||
}
|
||||
} else if (error.request) {
|
||||
message.error('🚨 No response from server!');
|
||||
} else if (status) {
|
||||
// message.error('💥 Internal server error!');
|
||||
} else {
|
||||
message.error(`⚠️ Request setup error: ${error.message}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// Request interceptors
|
||||
requestInterceptors: [
|
||||
(url: string, options: any) => {
|
||||
// console.log('URL Request:', url, options);
|
||||
|
||||
// Chỉ cần thêm token, proxy sẽ xử lý việc redirect đến đúng port
|
||||
// URL sẽ bắt đầu với /api và proxy sẽ chuyển đến hostname:81/api
|
||||
const token = getToken();
|
||||
return {
|
||||
url: url,
|
||||
options: {
|
||||
...options,
|
||||
headers: {
|
||||
...options.headers,
|
||||
...(token ? { Authorization: `${token}` } : {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
],
|
||||
|
||||
// Unwrap data from backend response
|
||||
// responseInterceptors: [
|
||||
// (response) => {
|
||||
// const res = response.data as ResponseStructure<any>;
|
||||
// if (res && res.success) {
|
||||
// // ✅ Trả ra data luôn thay vì cả object
|
||||
// return res.data;
|
||||
// }
|
||||
// return response.data;
|
||||
// },
|
||||
// ],
|
||||
};
|
||||
89
config/routes.ts
Normal file
89
config/routes.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
export const loginRoute = {
|
||||
title: 'Login',
|
||||
path: '/login',
|
||||
component: './Auth',
|
||||
layout: false,
|
||||
};
|
||||
|
||||
export const profileRoute = {
|
||||
name: 'profile',
|
||||
icon: 'icon-user',
|
||||
path: '/profile',
|
||||
routes: [
|
||||
{
|
||||
path: '/profile',
|
||||
component: './Profile',
|
||||
},
|
||||
],
|
||||
flatMenu: true,
|
||||
};
|
||||
export const alarmsRoute = {
|
||||
name: 'alarms',
|
||||
icon: 'icon-alarm',
|
||||
path: '/alarms',
|
||||
routes: [
|
||||
{
|
||||
path: '/alarms',
|
||||
component: './Alarm',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const commonManagerRoutes = [
|
||||
{
|
||||
name: 'devices',
|
||||
icon: 'icon-gateway',
|
||||
path: '/manager/devices',
|
||||
routes: [
|
||||
{
|
||||
path: '/manager/devices',
|
||||
component: './Manager/Device',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'groups',
|
||||
icon: 'icon-tree',
|
||||
path: '/manager/groups',
|
||||
routes: [
|
||||
{
|
||||
path: '/manager/groups',
|
||||
component: './Manager/Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'users',
|
||||
icon: 'icon-users',
|
||||
path: '/manager/users',
|
||||
routes: [
|
||||
{
|
||||
path: '/manager/users',
|
||||
component: './Manager/User',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'logs',
|
||||
icon: 'icon-diary',
|
||||
path: '/manager/logs',
|
||||
routes: [
|
||||
{
|
||||
path: '/manager/logs',
|
||||
component: './Manager/Log',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const managerRouteBase = {
|
||||
name: 'manager',
|
||||
icon: 'icon-setting',
|
||||
path: '/manager',
|
||||
access: 'canAdmin_SysAdmin',
|
||||
};
|
||||
|
||||
export const notFoundRoute = {
|
||||
path: '*',
|
||||
component: './Exception/NotFound',
|
||||
};
|
||||
Reference in New Issue
Block a user