diff --git a/apps/blogai/app/[locale]/layout.tsx b/apps/blogai/app/[locale]/layout.tsx index 6222db3..3ae813c 100644 --- a/apps/blogai/app/[locale]/layout.tsx +++ b/apps/blogai/app/[locale]/layout.tsx @@ -73,7 +73,12 @@ export default async function RootLayout({ return ( - + + + {/* 在这里注入 env.js(路径按你实际放置的 public 目录) */} + + + = 200 && status < 300 // 默认的 - // }, - //`maxRedirects`定义了在nodejs中重定向的最大数量 - // maxRedirects: 5 -} as any; \ No newline at end of file +// export default { +// // baseURL: 'http://103.39.218.177:8082', +// baseURL: `${process.env.NEXT_PUBLIC_CLIENT_BASE_URL}`, +// method: 'post', +// //`timeout`选项定义了请求发出的延迟毫秒数 +// //如果请求花费的时间超过延迟的时间,那么请求会被终止 +// timeout: 60 * 1000, +// //发送请求前允许修改数据 +// // transformRequest: [function (data: any) { +// // return data; +// // }], +// // //数据发送到then/catch方法之前允许数据改动 +// // transformResponse: [function (data: any) { +// // return data; +// // }], +// // headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, +// headers: { 'Content-Type': 'application/json; charset=UTF-8' }, +// // withCredentials: false,//跨域请求时是否携带cookie +// responseType: 'json',//响应数据类型 +// // xsrfCookieName: 'XSRF-TOKEN', +// // xsrfHeaderName: 'X-XSRF-TOKEN', +// // onUploadProgress: function (progressEvent: any) { },//上传进度事件 +// // onDownloadProgress: function (progressEvent: any) { },//下载进度事件 +// //`validateStatus`定义了是否根据http相应状态码,来resolve或者reject promise +// //如果`validateStatus`返回true(或者设置为`null`或者`undefined`),那么promise的状态将会是resolved,否则其状态就是rejected +// // validateStatus: function (status: number) { +// // return status >= 200 && status < 300 // 默认的 +// // }, +// //`maxRedirects`定义了在nodejs中重定向的最大数量 +// // maxRedirects: 5 +// } as any; + + +import { getRuntimeEnv } from "./ipconfig"; +export async function getAxiosConfig() { + const ip = await getRuntimeEnv("SUPABASE_URL"); // 直接用你 lib/ipconfig 里的方法 + if (!ip) throw new Error("SUPABASE_URL 获取失败,无法构建 axios 配置"); + return { + baseURL: `http://${ip}:80`, // 端口如需动态可再加参数 + method: 'post', + timeout: 60 * 1000, + headers: { 'Content-Type': 'application/json; charset=UTF-8' }, + responseType: 'json' + } as const; +} \ No newline at end of file diff --git a/apps/blogai/lib/http/service.ts b/apps/blogai/lib/http/service.ts index 7f213a2..46dff15 100644 --- a/apps/blogai/lib/http/service.ts +++ b/apps/blogai/lib/http/service.ts @@ -1,42 +1,90 @@ -import axiosConfig from "./axios_config"; -import axios from 'axios'; +// import axiosConfig from "./axios_config"; +// import axios from 'axios'; -const service = axios.create(axiosConfig); +// const service = axios.create(axiosConfig); -// 请求拦截 -service.interceptors.request.use( - config => { - // (config.headers as any).Authorization = 'Bearer' + ' ' + store.getState().chatgpt.token; - // if (config.data) { - // config.data = JSON.stringify(config.data); - // }; - // if (config.method?.toLocaleLowerCase() === 'get') { - // config.paramsSerializer = function (params) { - // return qs.stringify(params, { arrayFormat: 'repeat' }) - // } - // }; - return config; - }, - ( error: any) => { - return Promise.reject(error); - } -); +// // 请求拦截 +// service.interceptors.request.use( +// config => { +// // (config.headers as any).Authorization = 'Bearer' + ' ' + store.getState().chatgpt.token; +// // if (config.data) { +// // config.data = JSON.stringify(config.data); +// // }; +// // if (config.method?.toLocaleLowerCase() === 'get') { +// // config.paramsSerializer = function (params) { +// // return qs.stringify(params, { arrayFormat: 'repeat' }) +// // } +// // }; +// return config; +// }, +// ( error: any) => { +// return Promise.reject(error); +// } +// ); -// 响应拦截 -service.interceptors.response.use( - (res: any) => { - // let data = JSON.parse(res.data); - let data = res.data; - console.log(data) - return data; - }, - ( error: { response: { data: string; }; }) => { - let data = JSON.parse(error.response.data); - // if (data.code === 500) { - // console.log(error) - // }; - return data - } -) +// // 响应拦截 +// service.interceptors.response.use( +// (res: any) => { +// // let data = JSON.parse(res.data); +// let data = res.data; +// console.log(data) +// return data; +// }, +// ( error: { response: { data: string; }; }) => { +// let data = JSON.parse(error.response.data); +// // if (data.code === 500) { +// // console.log(error) +// // }; +// return data +// } +// ) -export default service; \ No newline at end of file +// export default service; + + + +import { getAxiosConfig } from "./axios_config"; +import axios from "axios"; + +// 动态生成 axios 实例,每次都能拿到最新 IP +export async function getService() { + const axiosConfig = await getAxiosConfig(); + const service = axios.create(axiosConfig); + + // 请求拦截 + service.interceptors.request.use( + config => { + // (config.headers as any).Authorization = 'Bearer' + ' ' + store.getState().chatgpt.token; + // if (config.data) { + // config.data = JSON.stringify(config.data); + // }; + // if (config.method?.toLocaleLowerCase() === 'get') { + // config.paramsSerializer = function (params) { + // return qs.stringify(params, { arrayFormat: 'repeat' }) + // } + // }; + return config; + }, + (error: any) => Promise.reject(error) + ); + + // 响应拦截 + service.interceptors.response.use( + (res: any) => { + let data = res.data; + console.log(data); + return data; + }, + (error: { response: { data: string } }) => { + let data = {}; + try { + data = JSON.parse(error.response.data); + } catch (e) { + data = error.response?.data || {}; + } + return data; + } + ); + + return service; +} diff --git a/apps/blogai/lib/ipconfig.ts b/apps/blogai/lib/ipconfig.ts new file mode 100644 index 0000000..8d9867d --- /dev/null +++ b/apps/blogai/lib/ipconfig.ts @@ -0,0 +1,70 @@ +const LOCAL_KEY = "SUPABASE_URL"; +const MAX_RETRIES = 5; +const RETRY_DELAY = 1000; + +export async function getRuntimeEnv(key: string): Promise { + if (typeof window !== "undefined") { + let retries = 0; + while (retries < MAX_RETRIES) { + // 1. 尝试 window.RUNTIME_ENV 提取IP + const val = window.RUNTIME_ENV?.[key]; + const ip = extractIp(val); + if (ip) { + if (key === "SUPABASE_URL") { + window.localStorage.setItem(LOCAL_KEY, val!); // 这里存原始值 + console.log(`[env] [${key}] 命中 window.RUNTIME_ENV: ${val},提取IP: ${ip},已同步到localStorage`); + } else { + console.log(`[env] [${key}] 命中 window.RUNTIME_ENV: ${val},提取IP: ${ip}`); + } + return ip; + } else if (val) { + console.warn(`[env] [${key}] window.RUNTIME_ENV 有值但无法提取合法IP: ${val}`); + } + + retries++; + // 2. 第3次开始查localStorage兜底 + if (retries >= 2) { + const cached = window.localStorage.getItem(LOCAL_KEY); + const cachedIp = extractIp(cached); + if (cachedIp && key === "SUPABASE_URL") { + console.warn(`[env] [${key}] 第${retries}次重试后window.RUNTIME_ENV还没拿到合法IP,localStorage兜底: ${cached},提取IP: ${cachedIp}`); + return cachedIp; + } + } + + if (retries < MAX_RETRIES) { + console.log(`[env] [${key}] window.RUNTIME_ENV 暂无合法IP,第${retries}次重试,${RETRY_DELAY}ms后再试...`); + await new Promise(res => setTimeout(res, RETRY_DELAY)); + } + } + + // 3. 所有重试和localStorage都失败 + console.error(`[env] [${key}] window.RUNTIME_ENV、localStorage都无合法IP,返回undefined`); + return undefined; + } else { + // 服务端 + const val = process.env[key]; + const ip = extractIp(val); + console.log(`[env][server] 直接读取 process.env[${key}]:`, val, "提取IP:", ip); + return ip; + } +} + +// --------- 需要配合的工具函数 --------- +function isValidIp(ip: string | undefined): boolean { + if (!ip) return false; + const reg = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/; + return reg.test(ip); +} + +function extractIp(str: string | undefined): string | undefined { + if (!str) return undefined; + if (isValidIp(str)) return str; + try { + const url = new URL(str); + if (isValidIp(url.hostname)) return url.hostname; + } catch {} + const match = str.match(/(\d{1,3}(?:\.\d{1,3}){3})/); + if (match && isValidIp(match[1])) return match[1]; + return undefined; +}