diff --git a/.umirc.ts b/.umirc.ts index c0f1e5d..989c760 100644 --- a/.umirc.ts +++ b/.umirc.ts @@ -12,6 +12,8 @@ export default defineConfig({ request: {}, locale: { default: 'vi-VN', + // baseNavigator: false, + // antd: true, }, favicons: ['/logo.png'], layout: { diff --git a/DYNAMIC_API_CONFIG.md b/DYNAMIC_API_CONFIG.md new file mode 100644 index 0000000..bdaeb70 --- /dev/null +++ b/DYNAMIC_API_CONFIG.md @@ -0,0 +1,39 @@ +# Cấu hình API động theo IP thiết bị + +## Cách hoạt động + +1. **ApiConfigService** tự động detect IP từ `window.location.hostname` +2. **Request interceptor** tự động thêm base URL vào mọi request +3. Không cần cấu hình proxy phức tạp nữa + +## Cách deploy trên nhiều thiết bị + +### 1. Build ứng dụng + +```bash +npm run build +``` + +### 2. Deploy lên từng thiết bị + +- Copy folder `dist` lên thiết bị +- Serve static files (nginx, apache, hoặc simple HTTP server) + +### 3. Truy cập từ thiết bị + +- Nếu thiết bị có IP `192.168.1.100`, truy cập: `http://192.168.1.100` +- Ứng dụng sẽ tự động gọi API đến `http://192.168.1.100:81` + +## Ví dụ các thiết bị + +| Thiết bị | IP | URL truy cập | API endpoint | +| --- | --- | --- | --- | +| Device 1 | 192.168.1.100 | http://192.168.1.100 | http://192.168.1.100:81/api/* | +| Device 2 | 192.168.1.101 | http://192.168.1.101 | http://192.168.1.101:81/api/* | +| Device 3 | 10.0.0.50 | http://10.0.0.50 | http://10.0.0.50:81/api/* | + +## Lưu ý + +- Backend API cần chạy trên port 81 của mỗi thiết bị +- Đảm bảo CORS được cấu hình đúng trên backend +- Nếu dùng domain, cần cấu hình HTTPS tương ứng diff --git a/config/Request.ts b/config/Request.ts index 548ca40..9b540b0 100644 --- a/config/Request.ts +++ b/config/Request.ts @@ -1,4 +1,5 @@ import { ROUTE_LOGIN } from '@/constants'; +import { apiConfig } from '@/services/ApiConfigService'; import { getToken, removeToken } from '@/utils/localStorageUtils'; import { history, RequestConfig } from '@umijs/max'; import { message } from 'antd'; @@ -84,9 +85,18 @@ export const handleRequestConfig: RequestConfig = { // Request interceptors requestInterceptors: [ (url: string, options: any) => { + console.log('URL Request:', url, options); + + // Nếu URL không phải absolute URL, thêm base URL + let finalUrl = url; + if (!url.startsWith('http')) { + const baseUrl = apiConfig.getApiBaseUrl(); + finalUrl = `${baseUrl}${url}`; + } + const token = getToken(); return { - url, + url: finalUrl, options: { ...options, headers: { diff --git a/config/proxy.ts b/config/proxy.ts index ea18c12..a99bbb4 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -1,24 +1,59 @@ -const proxy: Record = { - dev: { - '/api': { - target: 'http://192.168.30.103:81', - changeOrigin: true, - }, - }, - test: { - '/test': { - target: 'https://test-sgw-device.gms.vn', - changeOrigin: true, - secure: false, - }, - }, - prod: { - '/test': { - target: 'https://prod-sgw-device.gms.vn', - changeOrigin: true, - secure: false, - }, - }, +// Hàm lấy IP từ hostname hiện tại (chỉ hoạt động runtime) +const getCurrentIP = () => { + if (typeof window !== 'undefined') { + const hostname = window.location.hostname; + // Nếu là localhost hoặc IP local, trả về IP mặc định + if ( + hostname === 'localhost' || + hostname.startsWith('192.168.') || + hostname.startsWith('10.') + ) { + console.log('Host name: ', hostname); + + return hostname; + } + // Nếu là domain, có thể cần map sang IP tương ứng + return hostname; + } + return process.env.REACT_APP_API_HOST || '192.168.30.102'; // fallback từ env }; +// Hàm tạo proxy config động +const createDynamicProxy = () => { + const currentIP = getCurrentIP(); + const isLocalIP = + currentIP.startsWith('192.168.') || + currentIP.startsWith('10.') || + currentIP === 'localhost'; + + return { + dev: { + '/api': { + target: `http://${currentIP}:81`, + changeOrigin: true, + }, + }, + test: { + '/test': { + target: isLocalIP + ? `http://${currentIP}:81` + : 'https://test-sgw-device.gms.vn', + changeOrigin: true, + secure: !isLocalIP, + }, + }, + prod: { + '/test': { + target: isLocalIP + ? `http://${currentIP}:81` + : 'https://prod-sgw-device.gms.vn', + changeOrigin: true, + secure: !isLocalIP, + }, + }, + }; +}; + +const proxy: Record = createDynamicProxy(); + export default proxy; diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index e2a7627..fd7539a 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -7,7 +7,7 @@ const Footer = () => { background: 'none', color: 'white', }} - copyright="2025 Sản phẩm của Mobifone v1.2.1" + copyright="2025 Sản phẩm của Mobifone v1.2.2" /> ); }; diff --git a/src/services/ApiConfigService.ts b/src/services/ApiConfigService.ts new file mode 100644 index 0000000..1577449 --- /dev/null +++ b/src/services/ApiConfigService.ts @@ -0,0 +1,54 @@ +// Service để quản lý API endpoint động +class ApiConfigService { + private static instance: ApiConfigService; + private currentIP: string = ''; + + private constructor() {} + + static getInstance(): ApiConfigService { + if (!ApiConfigService.instance) { + ApiConfigService.instance = new ApiConfigService(); + } + return ApiConfigService.instance; + } + + // Lấy IP từ URL hiện tại + getCurrentIP(): string { + if (typeof window !== 'undefined') { + const hostname = window.location.hostname; + // Nếu là localhost hoặc IP local + if ( + hostname === 'localhost' || + hostname.startsWith('192.168.') || + hostname.startsWith('10.') + ) { + return hostname; + } + // Nếu là domain, trả về hostname + return hostname; + } + return '192.168.30.102'; // fallback + } + + // Lấy base URL cho API calls + getApiBaseUrl(): string { + const ip = this.getCurrentIP(); + const isLocal = + ip.startsWith('192.168.') || ip.startsWith('10.') || ip === 'localhost'; + + if (isLocal) { + return `http://${ip}:81`; + } + + // Nếu là domain, có thể cần HTTPS + return `https://${ip}`; + } + + // Lấy full API URL + getApiUrl(endpoint: string): string { + const baseUrl = this.getApiBaseUrl(); + return `${baseUrl}${endpoint}`; + } +} + +export const apiConfig = ApiConfigService.getInstance();