Skip to content

Commit 531304c

Browse files
committed
Optimize page performance
1 parent 19e85bb commit 531304c

4 files changed

Lines changed: 170 additions & 260 deletions

File tree

public/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "0.2.14",
2+
"version": "0.3",
33
"menu": {
44
"appstore": {
55
"label": "App Store",
@@ -8,4 +8,4 @@
88
}
99
},
1010
"content-security-policy": "default-src 'unsafe-inline' 'unsafe-eval' *;font-src * data:;style-src 'unsafe-inline' 'unsafe-eval' *;img-src * data:;connect-src http://redhattext http://overpass-webfont *"
11-
}
11+
}

src/helpers/api/apiCore.js

Lines changed: 111 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -1,138 +1,100 @@
11
import axios from 'axios';
22
import 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
// 检查是否为配置错误并清除相应缓存
12088
const 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

Comments
 (0)