feat: 添加版本号显示功能并更新CI流程

- 创建VERSION文件存储版本号
- 在系统信息面板添加版本号显示
- 更新Docker发布流程以自动读取VERSION文件并创建Git标签
- 在OAuth授权模态框中添加端口号提示
- 添加相关i18n翻译字段
This commit is contained in:
hex2077 2025-12-24 18:26:25 +08:00
parent 531b9af1e0
commit 8f843f50f6
6 changed files with 78 additions and 8 deletions

View file

@ -2,8 +2,10 @@ name: 构建并发布 Docker 镜像
on:
push:
tags:
- 'v*.*.*' # 匹配 v1.0.0, v2.1.3 等版本号格式
branches:
- main
paths:
- 'VERSION' # 仅当 VERSION 文件变更时触发
env:
REGISTRY: docker.io
@ -13,12 +15,42 @@ jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
contents: write # 需要写权限来创建 tag
packages: write
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0 # 获取完整历史以便创建 tag
- name: 读取版本号
id: version
run: |
VERSION=$(cat VERSION | tr -d '\n\r ')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=v$VERSION" >> $GITHUB_OUTPUT
echo "读取到版本号: $VERSION"
- name: 检查 tag 是否已存在
id: check_tag
run: |
if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Tag v${{ steps.version.outputs.version }} 已存在"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Tag v${{ steps.version.outputs.version }} 不存在,将创建新 tag"
fi
- name: 创建 Git Tag
if: steps.check_tag.outputs.exists == 'false'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "v${{ steps.version.outputs.version }}" -m "Release v${{ steps.version.outputs.version }}"
git push origin "v${{ steps.version.outputs.version }}"
echo "已创建并推送 tag: v${{ steps.version.outputs.version }}"
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@v3
@ -35,10 +67,8 @@ jobs:
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ steps.version.outputs.version }}
type=raw,value=latest
- name: 构建并推送 Docker 镜像
uses: docker/build-push-action@v5
@ -56,7 +86,8 @@ jobs:
run: |
echo "## Docker 镜像构建成功 :rocket:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**版本标签:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "**版本号:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**Git Tag:** ${{ steps.version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**镜像标签:**" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY

1
VERSION Normal file
View file

@ -0,0 +1 @@
2.2.7

View file

@ -758,8 +758,21 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
// Get system information
if (method === 'GET' && pathParam === '/api/system') {
const memUsage = process.memoryUsage();
// 读取版本号
let appVersion = 'unknown';
try {
const versionFilePath = path.join(process.cwd(), 'VERSION');
if (existsSync(versionFilePath)) {
appVersion = readFileSync(versionFilePath, 'utf8').trim();
}
} catch (error) {
console.warn('[UI API] Failed to read VERSION file:', error.message);
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
appVersion: appVersion,
nodeVersion: process.version,
serverTime: new Date().toLocaleString(),
memoryUsage: `${Math.round(memUsage.heapUsed / 1024 / 1024)} MB / ${Math.round(memUsage.heapTotal / 1024 / 1024)} MB`,

View file

@ -22,6 +22,7 @@ const translations = {
'dashboard.title': '系统概览',
'dashboard.uptime': '运行时间',
'dashboard.systemInfo': '系统信息',
'dashboard.version': '版本号',
'dashboard.nodeVersion': 'Node.js版本',
'dashboard.serverTime': '服务器时间',
'dashboard.memoryUsage': '内存使用',
@ -60,6 +61,8 @@ const translations = {
// OAuth
'oauth.modal.title': 'OAuth 授权',
'oauth.modal.provider': '提供商:',
'oauth.modal.requiredPort': '需要开放端口:',
'oauth.modal.portNote': '请确保此端口可被外部访问,用于接收授权回调',
'oauth.modal.steps': '授权步骤:',
'oauth.modal.step1': '点击下方按钮在浏览器中打开授权页面',
'oauth.modal.step2.qwen': '完成授权后,系统会自动获取凭据文件',
@ -391,6 +394,7 @@ const translations = {
'dashboard.title': 'System Overview',
'dashboard.uptime': 'Uptime',
'dashboard.systemInfo': 'System Information',
'dashboard.version': 'Version',
'dashboard.nodeVersion': 'Node.js Version',
'dashboard.serverTime': 'Server Time',
'dashboard.memoryUsage': 'Memory Usage',
@ -429,6 +433,8 @@ const translations = {
// OAuth
'oauth.modal.title': 'OAuth Authorization',
'oauth.modal.provider': 'Provider:',
'oauth.modal.requiredPort': 'Required Port:',
'oauth.modal.portNote': 'Please ensure this port is accessible externally for receiving authorization callbacks',
'oauth.modal.steps': 'Authorization Steps:',
'oauth.modal.step1': 'Click the button below to open the authorization page in your browser',
'oauth.modal.step2.qwen': 'After authorization, the system will automatically fetch the credentials file',

View file

@ -18,11 +18,13 @@ async function loadSystemInfo() {
try {
const data = await window.apiClient.get('/system');
const appVersionEl = document.getElementById('appVersion');
const nodeVersionEl = document.getElementById('nodeVersion');
const serverTimeEl = document.getElementById('serverTime');
const memoryUsageEl = document.getElementById('memoryUsage');
const uptimeEl = document.getElementById('uptime');
if (appVersionEl) appVersionEl.textContent = data.appVersion ? `v${data.appVersion}` : '--';
if (nodeVersionEl) nodeVersionEl.textContent = data.nodeVersion || '--';
if (memoryUsageEl) memoryUsageEl.textContent = data.memoryUsage || '--';
@ -488,6 +490,9 @@ function showAuthModal(authUrl, authInfo) {
// 获取授权文件路径
const authFilePath = getAuthFilePath(authInfo.provider);
// 获取需要开放的端口号(从 authInfo 或当前页面 URL
const requiredPort = authInfo.callbackPort || authInfo.port || window.location.port || '3000';
let instructionsHtml = '';
if (authInfo.provider === 'openai-qwen-oauth') {
instructionsHtml = `
@ -539,6 +544,14 @@ function showAuthModal(authUrl, authInfo) {
<div class="modal-body">
<div class="auth-info">
<p><strong data-i18n="oauth.modal.provider">${t('oauth.modal.provider')}</strong> ${authInfo.provider}</p>
<div class="port-info-section" style="margin: 12px 0; padding: 12px; background: #fef3c7; border: 1px solid #fcd34d; border-radius: 8px;">
<p style="margin: 0; display: flex; align-items: center; gap: 8px;">
<i class="fas fa-network-wired" style="color: #d97706;"></i>
<strong data-i18n="oauth.modal.requiredPort">${t('oauth.modal.requiredPort')}</strong>
<code style="background: #fff; padding: 2px 8px; border-radius: 4px; font-weight: bold; color: #d97706;">${requiredPort}</code>
</p>
<p style="margin: 8px 0 0 0; font-size: 0.85rem; color: #92400e;" data-i18n="oauth.modal.portNote">${t('oauth.modal.portNote')}</p>
</div>
${instructionsHtml}
<div class="auth-url-section">
<label data-i18n="oauth.modal.urlLabel">${t('oauth.modal.urlLabel')}</label>

View file

@ -100,6 +100,12 @@
<div class="system-info-panel">
<h3 data-i18n="dashboard.systemInfo">系统信息</h3>
<div class="info-grid">
<div class="info-item">
<span class="info-label">
<i class="fas fa-tag"></i> <span data-i18n="dashboard.version">版本号</span>
</span>
<span class="info-value" id="appVersion">--</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-code"></i> <span data-i18n="dashboard.nodeVersion">Node.js版本</span>