mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-03 03:10:50 +08:00
feat(versioning): implement UI and server version tracking with build date display in footer
This commit is contained in:
4
app.js
4
app.js
@@ -42,6 +42,9 @@ class CLIProxyManager {
|
|||||||
this.managementKey = '';
|
this.managementKey = '';
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
this.isLoggedIn = false;
|
this.isLoggedIn = false;
|
||||||
|
this.uiVersion = null;
|
||||||
|
this.serverVersion = null;
|
||||||
|
this.serverBuildDate = null;
|
||||||
|
|
||||||
// 配置缓存 - 改为分段缓存
|
// 配置缓存 - 改为分段缓存
|
||||||
this.configCache = {}; // 改为对象,按配置段缓存
|
this.configCache = {}; // 改为对象,按配置段缓存
|
||||||
@@ -129,6 +132,7 @@ class CLIProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
this.initUiVersion();
|
||||||
this.initializeTheme();
|
this.initializeTheme();
|
||||||
this.checkLoginStatus();
|
this.checkLoginStatus();
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
|||||||
8
i18n.js
8
i18n.js
@@ -508,7 +508,9 @@ const i18n = {
|
|||||||
'sidebar.toggle_collapse': '收起侧边栏',
|
'sidebar.toggle_collapse': '收起侧边栏',
|
||||||
|
|
||||||
// 页脚
|
// 页脚
|
||||||
'footer.version': '版本',
|
'footer.api_version': 'CLI Proxy API 版本',
|
||||||
|
'footer.build_date': '构建时间',
|
||||||
|
'footer.version': '管理中心版本',
|
||||||
'footer.author': '作者'
|
'footer.author': '作者'
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1013,7 +1015,9 @@ const i18n = {
|
|||||||
'sidebar.toggle_collapse': 'Collapse sidebar',
|
'sidebar.toggle_collapse': 'Collapse sidebar',
|
||||||
|
|
||||||
// Footer
|
// Footer
|
||||||
'footer.version': 'Version',
|
'footer.api_version': 'CLI Proxy API Version',
|
||||||
|
'footer.build_date': 'Build Time',
|
||||||
|
'footer.version': 'Management UI Version',
|
||||||
'footer.author': 'Author'
|
'footer.author': 'Author'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1003,7 +1003,14 @@
|
|||||||
<!-- 版本信息 -->
|
<!-- 版本信息 -->
|
||||||
<footer class="version-footer">
|
<footer class="version-footer">
|
||||||
<div class="version-info">
|
<div class="version-info">
|
||||||
<span data-i18n="footer.version">版本</span>: __VERSION__
|
<span data-i18n="footer.api_version">CLI Proxy API 版本</span>:
|
||||||
|
<span id="api-version">-</span>
|
||||||
|
<span class="separator">•</span>
|
||||||
|
<span data-i18n="footer.build_date">构建时间</span>:
|
||||||
|
<span id="api-build-date">-</span>
|
||||||
|
<span class="separator">•</span>
|
||||||
|
<span data-i18n="footer.version">管理中心版本</span>:
|
||||||
|
<span id="ui-version" data-ui-version="__VERSION__">-</span>
|
||||||
<span class="separator">•</span>
|
<span class="separator">•</span>
|
||||||
<span data-i18n="footer.author">作者</span>: CLI Proxy API Team
|
<span data-i18n="footer.author">作者</span>: CLI Proxy API Team
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -58,6 +58,95 @@ export const connectionModule = {
|
|||||||
this.updateLoginConnectionInfo();
|
this.updateLoginConnectionInfo();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 读取并填充管理中心版本号(可能来自构建时注入或占位符)
|
||||||
|
initUiVersion() {
|
||||||
|
const uiVersion = this.readUiVersionFromDom();
|
||||||
|
this.uiVersion = uiVersion;
|
||||||
|
this.renderVersionInfo();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 从 DOM 获取版本占位符,并处理空值、引号或未替换的占位符
|
||||||
|
readUiVersionFromDom() {
|
||||||
|
const el = document.getElementById('ui-version');
|
||||||
|
if (!el) return null;
|
||||||
|
|
||||||
|
const raw = (el.dataset && el.dataset.uiVersion) ? el.dataset.uiVersion : el.textContent;
|
||||||
|
if (typeof raw !== 'string') return null;
|
||||||
|
|
||||||
|
const cleaned = raw.replace(/^"+|"+$/g, '').trim();
|
||||||
|
if (!cleaned || cleaned === '__VERSION__') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return cleaned;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 根据响应头更新版本与构建时间
|
||||||
|
updateVersionFromHeaders(headers) {
|
||||||
|
if (!headers || typeof headers.get !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const version = headers.get('X-CPA-VERSION');
|
||||||
|
const buildDate = headers.get('X-CPA-BUILD-DATE');
|
||||||
|
let updated = false;
|
||||||
|
|
||||||
|
if (version && version !== this.serverVersion) {
|
||||||
|
this.serverVersion = version;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildDate && buildDate !== this.serverBuildDate) {
|
||||||
|
this.serverBuildDate = buildDate;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
this.renderVersionInfo();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 渲染底栏的版本与构建时间
|
||||||
|
renderVersionInfo() {
|
||||||
|
const versionEl = document.getElementById('api-version');
|
||||||
|
const buildDateEl = document.getElementById('api-build-date');
|
||||||
|
const uiVersionEl = document.getElementById('ui-version');
|
||||||
|
|
||||||
|
if (versionEl) {
|
||||||
|
versionEl.textContent = this.serverVersion || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildDateEl) {
|
||||||
|
buildDateEl.textContent = this.serverBuildDate
|
||||||
|
? this.formatBuildDate(this.serverBuildDate)
|
||||||
|
: '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uiVersionEl) {
|
||||||
|
const domVersion = this.readUiVersionFromDom();
|
||||||
|
uiVersionEl.textContent = this.uiVersion || domVersion || 'v0.0.0-dev';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 清空版本信息(例如登出时)
|
||||||
|
resetVersionInfo() {
|
||||||
|
this.serverVersion = null;
|
||||||
|
this.serverBuildDate = null;
|
||||||
|
this.renderVersionInfo();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 格式化构建时间,优先使用界面语言对应的本地格式
|
||||||
|
formatBuildDate(buildDate) {
|
||||||
|
if (!buildDate) return '-';
|
||||||
|
|
||||||
|
const parsed = Date.parse(buildDate);
|
||||||
|
if (!Number.isNaN(parsed)) {
|
||||||
|
const locale = i18n?.currentLanguage || undefined;
|
||||||
|
return new Date(parsed).toLocaleString(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildDate;
|
||||||
|
},
|
||||||
|
|
||||||
// API 请求方法
|
// API 请求方法
|
||||||
async makeRequest(endpoint, options = {}) {
|
async makeRequest(endpoint, options = {}) {
|
||||||
const url = `${this.apiUrl}${endpoint}`;
|
const url = `${this.apiUrl}${endpoint}`;
|
||||||
@@ -73,6 +162,8 @@ export const connectionModule = {
|
|||||||
headers
|
headers
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.updateVersionFromHeaders(response.headers);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
throw new Error(errorData.error || `HTTP ${response.status}`);
|
throw new Error(errorData.error || `HTTP ${response.status}`);
|
||||||
|
|||||||
@@ -732,6 +732,8 @@ export const authFilesModule = {
|
|||||||
body: formData
|
body: formData
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.updateVersionFromHeaders(response.headers);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
let errorMessage = `HTTP ${response.status}`;
|
let errorMessage = `HTTP ${response.status}`;
|
||||||
try {
|
try {
|
||||||
@@ -891,6 +893,8 @@ export const authFilesModule = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.updateVersionFromHeaders(response.headers);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP ${response.status}`);
|
throw new Error(`HTTP ${response.status}`);
|
||||||
}
|
}
|
||||||
@@ -1029,6 +1033,8 @@ export const authFilesModule = {
|
|||||||
body: formData
|
body: formData
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.updateVersionFromHeaders(response.headers);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
throw new Error(errorData.error || `HTTP ${response.status}`);
|
throw new Error(errorData.error || `HTTP ${response.status}`);
|
||||||
|
|||||||
@@ -160,6 +160,8 @@ export const configEditorModule = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.updateVersionFromHeaders(response.headers);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text().catch(() => '');
|
const errorText = await response.text().catch(() => '');
|
||||||
const message = errorText || `HTTP ${response.status}`;
|
const message = errorText || `HTTP ${response.status}`;
|
||||||
@@ -235,6 +237,8 @@ export const configEditorModule = {
|
|||||||
body: yamlText
|
body: yamlText
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.updateVersionFromHeaders(response.headers);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const contentType = response.headers.get('content-type') || '';
|
const contentType = response.headers.get('content-type') || '';
|
||||||
let errorText = '';
|
let errorText = '';
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ export const loginModule = {
|
|||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
this.clearCache();
|
this.clearCache();
|
||||||
this.stopStatusUpdateTimer();
|
this.stopStatusUpdateTimer();
|
||||||
|
this.resetVersionInfo();
|
||||||
|
|
||||||
localStorage.removeItem('isLoggedIn');
|
localStorage.removeItem('isLoggedIn');
|
||||||
secureStorage.removeItem('managementKey');
|
secureStorage.removeItem('managementKey');
|
||||||
|
|||||||
Reference in New Issue
Block a user