11import axios from 'axios' ;
22import cockpit from 'cockpit' ;
33
4- // content type
5- axios . defaults . headers . common [ 'Content-Type' ] = 'application/json; charset=utf-8' ;
4+ // 配置缓存
5+ const configCache = new Map ( ) ;
6+ const fetchPromise = new Map ( ) ;
67
7- // 配置缓存对象
8- const configCache = {
9- apiKey : null ,
10- nginxPort : null ,
11- fetching : new Set ( )
12- } ;
8+ // 获取完整配置(一次性获取所有配置信息)
9+ const getFullConfig = async ( ) => {
10+ const cacheKey = 'fullConfig' ;
1311
14- const getNginxConfig = async ( ) => {
1512 // 检查缓存
16- if ( configCache . nginxPort ) {
17- return configCache . nginxPort ;
13+ if ( configCache . has ( cacheKey ) ) {
14+ return configCache . get ( cacheKey ) ;
1815 }
1916
2017 // 防止并发重复请求
21- if ( configCache . fetching . has ( 'nginx' ) ) {
22- return new Promise ( ( resolve , reject ) => {
23- const checkCache = ( ) => {
24- if ( configCache . nginxPort ) {
25- resolve ( configCache . nginxPort ) ;
26- } else if ( ! configCache . fetching . has ( 'nginx' ) ) {
27- reject ( new Error ( "Failed to get nginx config" ) ) ;
28- } else {
29- setTimeout ( checkCache , 100 ) ;
30- }
31- } ;
32- checkCache ( ) ;
33- } ) ;
18+ if ( fetchPromise . has ( cacheKey ) ) {
19+ return fetchPromise . get ( cacheKey ) ;
3420 }
3521
36- configCache . fetching . add ( 'nginx' ) ;
37-
38- try {
39- var script = "docker exec -i websoft9-apphub apphub getconfig --section nginx_proxy_manager" ;
40- let content = ( await cockpit . spawn ( [ "/bin/bash" , "-c" , script ] , { superuser : "try" } ) ) . trim ( ) ;
41- content = JSON . parse ( content ) ;
42- let listen_port = content . listen_port ;
22+ // 创建并缓存Promise
23+ const promise = ( async ( ) => {
24+ try {
25+ const script = "docker exec -i websoft9-apphub apphub getconfig" ;
26+ const result = await cockpit . spawn ( [ "/bin/bash" , "-c" , script ] , { superuser : "try" } ) ;
27+ const config = JSON . parse ( result . trim ( ) ) ;
4328
44- if ( ! listen_port ) {
45- throw new Error ( "Nginx Listen Port Not Set" ) ;
46- }
29+ // 验证必要的配置项
30+ const listenPort = config . nginx_proxy_manager ?. listen_port ;
31+ const apiKey = config . api_key ?. key ;
4732
48- // 缓存结果
49- configCache . nginxPort = listen_port ;
50- return listen_port ;
51- }
52- catch ( error ) {
53- const errorText = [ error . problem , error . reason , error . message ]
54- . filter ( item => typeof item === 'string' )
55- . join ( ' ' ) ;
56-
57- if ( errorText . includes ( "permission denied" ) ) {
58- throw new Error ( "Your user does not have Docker permissions. Grant Docker permissions to this user by command: sudo usermod -aG docker <username>" ) ;
59- }
60- else {
61- throw new Error ( errorText || "Get Nginx Listen Port Error" ) ;
62- }
63- } finally {
64- configCache . fetching . delete ( 'nginx' ) ;
65- }
66- }
33+ if ( ! listenPort ) {
34+ throw new Error ( "Nginx Listen Port Not Set" ) ;
35+ }
6736
68- const getApiKey = async ( ) => {
69- // 检查缓存
70- if ( configCache . apiKey ) {
71- return configCache . apiKey ;
72- }
37+ if ( ! apiKey ) {
38+ throw new Error ( "Api Key Not Set" ) ;
39+ }
7340
74- // 防止并发重复请求
75- if ( configCache . fetching . has ( 'apikey' ) ) {
76- return new Promise ( ( resolve , reject ) => {
77- const checkCache = ( ) => {
78- if ( configCache . apiKey ) {
79- resolve ( configCache . apiKey ) ;
80- } else if ( ! configCache . fetching . has ( 'apikey' ) ) {
81- reject ( new Error ( "Failed to get api key" ) ) ;
82- } else {
83- setTimeout ( checkCache , 100 ) ;
84- }
41+ const processedConfig = {
42+ nginxPort : listenPort ,
43+ apiKey : apiKey ,
44+ rawConfig : config // 保留原始配置以备后用
8545 } ;
86- checkCache ( ) ;
87- } ) ;
88- }
8946
90- configCache . fetching . add ( 'apikey' ) ;
47+ configCache . set ( cacheKey , processedConfig ) ;
48+ return processedConfig ;
9149
92- try {
93- var script = "docker exec -i websoft9-apphub apphub getconfig --section api_key --key key" ;
94- const api_key = ( await cockpit . spawn ( [ "/bin/bash" , "-c" , script ] , { superuser : "try" } ) ) . trim ( ) ;
95- if ( ! api_key ) {
96- throw new Error ( " Api Key Not Set" ) ;
50+ } catch ( error ) {
51+ const errorText = [ error . problem , error . reason , error . message ]
52+ . filter ( Boolean )
53+ . join ( ' ' ) ;
54+
55+ if ( errorText . includes ( "permission denied" ) ) {
56+ throw new Error ( "Your user does not have Docker permissions. Grant Docker permissions to this user by command: sudo usermod -aG docker <username>" ) ;
57+ }
58+ throw new Error ( errorText || "Get Configuration Error" ) ;
59+ } finally {
60+ fetchPromise . delete ( cacheKey ) ;
9761 }
62+ } ) ( ) ;
9863
99- // 缓存结果
100- configCache . apiKey = api_key ;
101- return api_key ;
102- }
103- catch ( error ) {
104- const errorText = [ error . problem , error . reason , error . message ]
105- . filter ( item => typeof item === 'string' )
106- . join ( ' ' ) ;
64+ fetchPromise . set ( cacheKey , promise ) ;
65+ return promise ;
66+ } ;
10767
108- if ( errorText . includes ( "permission denied" ) ) {
109- throw new Error ( "Your user does not have Docker permissions. Grant Docker permissions to this user by command: sudo usermod -aG docker <username>" ) ;
110- }
111- else {
112- throw new Error ( errorText || "Get The Apphub's Api Key Error" ) ;
113- }
114- } finally {
115- configCache . fetching . delete ( 'apikey' ) ;
116- }
117- }
68+ // 获取Nginx端口(向后兼容)
69+ const getNginxConfig = async ( ) => {
70+ const config = await getFullConfig ( ) ;
71+ return config . nginxPort ;
72+ } ;
73+
74+ // 获取API Key(向后兼容)
75+ const getApiKey = async ( ) => {
76+ const config = await getFullConfig ( ) ;
77+ return config . apiKey ;
78+ } ;
79+
80+ // 清除配置缓存
81+ const clearConfigCache = ( ) => {
82+ configCache . clear ( ) ;
83+ fetchPromise . clear ( ) ;
84+ console . log ( '[ApiCore] Configuration cache cleared' ) ;
85+ } ;
11886
11987// 检查是否为配置错误并清除相应缓存
12088const handleConfigError = ( error ) => {
12189 const status = error . response ?. status ;
12290 const details = error . response ?. data ?. details ;
12391
124- // API Key 无效 - 后端返回 400 + "Invalid API Key"
125- if ( status === 400 && details === "Invalid API Key" ) {
126- console . warn ( '[Config Error] Invalid API Key detected, clearing cache' ) ;
127- configCache . apiKey = null ;
128- return true ;
129- }
130-
131- // Nginx 端口/连接错误
132- if ( status === 404 || status === 502 || status === 503 ||
133- error . code === 'ECONNREFUSED' || error . code === 'ETIMEDOUT' ) {
134- console . warn ( '[Config Error] Nginx connection error detected, clearing cache' ) ;
135- configCache . nginxPort = null ;
92+ // API Key 无效或连接错误
93+ if ( ( status === 400 && details === "Invalid API Key" ) ||
94+ [ 404 , 502 , 503 ] . includes ( status ) ||
95+ [ 'ECONNREFUSED' , 'ETIMEDOUT' ] . includes ( error . code ) ) {
96+ console . warn ( '[Config Error] Configuration error detected, clearing cache' ) ;
97+ clearConfigCache ( ) ;
13698 return true ;
13799 }
138100
@@ -144,120 +106,87 @@ class APICore {
144106 this . axiosInstance = null ;
145107 }
146108
147- // 创建带缓存配置的 axios 实例
109+ // 获取或创建配置好的axios实例
148110 async getAxiosInstance ( ) {
149111 if ( ! this . axiosInstance ) {
150- // 并行获取配置,避免串行等待
151- const [ apiKey , nginxPort ] = await Promise . all ( [
152- getApiKey ( ) ,
153- getNginxConfig ( )
154- ] ) ;
155-
156- this . axiosInstance = axios . create ( {
157- baseURL : `${ window . location . protocol } //${ window . location . hostname } :${ nginxPort } /api` ,
158- headers : {
159- 'Content-Type' : 'application/json; charset=utf-8' ,
160- 'x-api-key' : apiKey
161- } ,
162- timeout : 30000
163- } ) ;
164-
165- // 只为这个实例设置拦截器,不影响全局
166- this . setupInstanceInterceptors ( ) ;
112+ await this . initializeAxiosInstance ( ) ;
167113 }
168114 return this . axiosInstance ;
169115 }
170116
171- // 为当前实例设置拦截器
172- setupInstanceInterceptors ( ) {
173- // 响应拦截器
174- this . axiosInstance . interceptors . response . use (
175- ( response ) => {
176- // 成功响应,返回数据
177- return response . status === 200 ? response . data : response ;
117+ // 初始化axios实例(优化:一次获取所有配置)
118+ async initializeAxiosInstance ( ) {
119+ // 一次性获取所有配置
120+ const config = await getFullConfig ( ) ;
121+
122+ this . axiosInstance = axios . create ( {
123+ baseURL : `${ window . location . protocol } //${ window . location . hostname } :${ config . nginxPort } /api` ,
124+ headers : {
125+ 'Content-Type' : 'application/json; charset=utf-8' ,
126+ 'x-api-key' : config . apiKey
178127 } ,
179- ( error ) => {
180- // 检查并处理配置错误
181- const isConfigError = handleConfigError ( error ) ;
182- if ( isConfigError ) {
183- // 标记为配置错误,调用方可以选择重试
128+ timeout : 30000
129+ } ) ;
130+
131+ this . setupInterceptors ( ) ;
132+ }
133+
134+ // 设置拦截器
135+ setupInterceptors ( ) {
136+ this . axiosInstance . interceptors . response . use (
137+ response => response . status === 200 ? response . data : response ,
138+ error => {
139+ if ( handleConfigError ( error ) ) {
184140 error . configError = true ;
185141 }
186-
187142 error . message = error . response ?. data ?. details || error . message || "Unknown Error" ;
188143 return Promise . reject ( error ) ;
189144 }
190145 ) ;
191146 }
192147
193- // 重置实例(配置错误时使用)
148+ // 重置实例
194149 resetInstance ( ) {
195150 this . axiosInstance = null ;
196151 }
197152
198- // 带重试的请求方法
199- async requestWithRetry ( method , url , data = null , params = null ) {
200- let lastError ;
201-
202- // 最多重试一次
153+ // 通用请求方法,支持重试
154+ async request ( method , url , data = null , params = null ) {
203155 for ( let attempt = 0 ; attempt < 2 ; attempt ++ ) {
204156 try {
205157 const instance = await this . getAxiosInstance ( ) ;
158+ const config = { params } ;
206159
207- let response ;
208160 switch ( method . toLowerCase ( ) ) {
209161 case 'get' :
210- response = await instance . get ( url , { params } ) ;
211- break ;
162+ return await instance . get ( url , config ) ;
212163 case 'post' :
213- response = await instance . post ( url , data , { params } ) ;
214- break ;
164+ return await instance . post ( url , data , config ) ;
215165 case 'put' :
216- response = await instance . put ( url , data , { params } ) ;
217- break ;
166+ return await instance . put ( url , data , config ) ;
218167 case 'delete' :
219- response = await instance . delete ( url , { params } ) ;
220- break ;
168+ return await instance . delete ( url , config ) ;
221169 default :
222170 throw new Error ( `Unsupported method: ${ method } ` ) ;
223171 }
224-
225- // 直接返回拦截器处理后的结果
226- return response ;
227172 } catch ( error ) {
228- lastError = error ;
229-
230- // 如果是配置错误且是第一次尝试,重置实例并重试
173+ // 配置错误且是第一次尝试,重置并重试
231174 if ( error . configError && attempt === 0 ) {
232175 console . info ( `[API Retry] Config error detected, resetting instance and retrying...` ) ;
233176 this . resetInstance ( ) ;
234- await new Promise ( resolve => setTimeout ( resolve , 200 ) ) ; // 短暂等待
177+ await new Promise ( resolve => setTimeout ( resolve , 200 ) ) ;
235178 continue ;
236179 }
237-
238- // 其他情况直接抛出错误
239- break ;
180+ throw error ;
240181 }
241182 }
242-
243- throw lastError ;
244183 }
245184
246- get = async ( url , params ) => {
247- return this . requestWithRetry ( 'get' , url , null , params ) ;
248- } ;
249-
250- post = async ( url , data , params ) => {
251- return this . requestWithRetry ( 'post' , url , data , params ) ;
252- } ;
253-
254- put = async ( url , data , params ) => {
255- return this . requestWithRetry ( 'put' , url , data , params ) ;
256- } ;
257-
258- delete = async ( url , params ) => {
259- return this . requestWithRetry ( 'delete' , url , null , params ) ;
260- } ;
185+ // HTTP方法的简化封装
186+ get = ( url , params ) => this . request ( 'get' , url , null , params ) ;
187+ post = ( url , data , params ) => this . request ( 'post' , url , data , params ) ;
188+ put = ( url , data , params ) => this . request ( 'put' , url , data , params ) ;
189+ delete = ( url , params ) => this . request ( 'delete' , url , null , params ) ;
261190}
262191
263- export { APICore , getNginxConfig , getApiKey } ;
192+ export { APICore , getNginxConfig , getApiKey , getFullConfig } ;
0 commit comments