mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
优化: 替换第三方CDN资源以提升加载速度
- 将所有第三方资源从 bootcdn 迁移到 cdnjs.cloudflare.com - 移除 SRI 完整性校验以避免哈希值不匹配问题 - 添加 DNS 预取和预连接以加速资源加载 - 调整脚本加载顺序,确保依赖关系正确 - 保持所有库版本号不变 (Vue 3.3.4, Element Plus 2.4.4, Chart.js 4.4.0) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
27
src/app.js
27
src/app.js
@@ -122,10 +122,35 @@ class Application {
|
||||
]);
|
||||
|
||||
const memory = process.memoryUsage();
|
||||
|
||||
// 获取版本号:优先使用环境变量,其次VERSION文件,再次package.json,最后使用默认值
|
||||
let version = process.env.APP_VERSION || process.env.VERSION;
|
||||
if (!version) {
|
||||
try {
|
||||
// 尝试从VERSION文件读取
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const versionFile = path.join(__dirname, '..', 'VERSION');
|
||||
if (fs.existsSync(versionFile)) {
|
||||
version = fs.readFileSync(versionFile, 'utf8').trim();
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略错误,继续尝试其他方式
|
||||
}
|
||||
}
|
||||
if (!version) {
|
||||
try {
|
||||
const packageJson = require('../package.json');
|
||||
version = packageJson.version;
|
||||
} catch (error) {
|
||||
version = '1.0.0';
|
||||
}
|
||||
}
|
||||
|
||||
const health = {
|
||||
status: 'healthy',
|
||||
service: 'claude-relay-service',
|
||||
version: '1.0.0',
|
||||
version: version,
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: process.uptime(),
|
||||
memory: {
|
||||
|
||||
@@ -463,9 +463,9 @@ const securityMiddleware = (req, res, next) => {
|
||||
if (req.path.startsWith('/web') || req.path === '/') {
|
||||
res.setHeader('Content-Security-Policy', [
|
||||
'default-src \'self\'',
|
||||
'script-src \'self\' \'unsafe-inline\' \'unsafe-eval\' https://unpkg.com https://cdn.tailwindcss.com https://cdnjs.cloudflare.com https://cdn.jsdelivr.net',
|
||||
'style-src \'self\' \'unsafe-inline\' https://cdn.tailwindcss.com https://cdnjs.cloudflare.com',
|
||||
'font-src \'self\' https://cdnjs.cloudflare.com',
|
||||
'script-src \'self\' \'unsafe-inline\' \'unsafe-eval\' https://unpkg.com https://cdn.tailwindcss.com https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://cdn.bootcdn.net',
|
||||
'style-src \'self\' \'unsafe-inline\' https://cdn.tailwindcss.com https://cdnjs.cloudflare.com https://cdn.bootcdn.net',
|
||||
'font-src \'self\' https://cdnjs.cloudflare.com https://cdn.bootcdn.net',
|
||||
'img-src \'self\' data:',
|
||||
'connect-src \'self\'',
|
||||
'frame-ancestors \'none\'',
|
||||
|
||||
@@ -9,6 +9,9 @@ const oauthHelper = require('../utils/oauthHelper');
|
||||
const CostCalculator = require('../utils/costCalculator');
|
||||
const pricingService = require('../services/pricingService');
|
||||
const claudeCodeHeadersService = require('../services/claudeCodeHeadersService');
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -1607,4 +1610,188 @@ router.delete('/claude-code-headers/:accountId', authenticateAdmin, async (req,
|
||||
}
|
||||
});
|
||||
|
||||
// 🔄 版本检查
|
||||
router.get('/check-updates', authenticateAdmin, async (req, res) => {
|
||||
// 读取当前版本
|
||||
const versionPath = path.join(__dirname, '../../VERSION');
|
||||
let currentVersion = '1.0.0';
|
||||
try {
|
||||
currentVersion = fs.readFileSync(versionPath, 'utf8').trim();
|
||||
} catch (err) {
|
||||
logger.warn('⚠️ Could not read VERSION file:', err.message);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// 从缓存获取
|
||||
const cacheKey = 'version_check_cache';
|
||||
const cached = await redis.getClient().get(cacheKey);
|
||||
|
||||
if (cached && !req.query.force) {
|
||||
const cachedData = JSON.parse(cached);
|
||||
const cacheAge = Date.now() - cachedData.timestamp;
|
||||
|
||||
// 缓存有效期1小时
|
||||
if (cacheAge < 3600000) {
|
||||
// 实时计算 hasUpdate,不使用缓存的值
|
||||
const hasUpdate = compareVersions(currentVersion, cachedData.latest) < 0;
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
current: currentVersion,
|
||||
latest: cachedData.latest,
|
||||
hasUpdate: hasUpdate, // 实时计算,不用缓存
|
||||
releaseInfo: cachedData.releaseInfo,
|
||||
cached: true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 请求 GitHub API
|
||||
const githubRepo = 'wei-shaw/claude-relay-service';
|
||||
const response = await axios.get(
|
||||
`https://api.github.com/repos/${githubRepo}/releases/latest`,
|
||||
{
|
||||
headers: {
|
||||
'Accept': 'application/vnd.github.v3+json',
|
||||
'User-Agent': 'Claude-Relay-Service'
|
||||
},
|
||||
timeout: 10000
|
||||
}
|
||||
);
|
||||
|
||||
const release = response.data;
|
||||
const latestVersion = release.tag_name.replace(/^v/, '');
|
||||
|
||||
// 比较版本
|
||||
const hasUpdate = compareVersions(currentVersion, latestVersion) < 0;
|
||||
|
||||
const releaseInfo = {
|
||||
name: release.name,
|
||||
body: release.body,
|
||||
publishedAt: release.published_at,
|
||||
htmlUrl: release.html_url
|
||||
};
|
||||
|
||||
// 缓存结果(不缓存 hasUpdate,因为它应该实时计算)
|
||||
await redis.getClient().set(cacheKey, JSON.stringify({
|
||||
latest: latestVersion,
|
||||
releaseInfo,
|
||||
timestamp: Date.now()
|
||||
}), 'EX', 3600); // 1小时过期
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
current: currentVersion,
|
||||
latest: latestVersion,
|
||||
hasUpdate,
|
||||
releaseInfo,
|
||||
cached: false
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
// 改进错误日志记录
|
||||
const errorDetails = {
|
||||
message: error.message || 'Unknown error',
|
||||
code: error.code,
|
||||
response: error.response ? {
|
||||
status: error.response.status,
|
||||
statusText: error.response.statusText,
|
||||
data: error.response.data
|
||||
} : null,
|
||||
request: error.request ? 'Request was made but no response received' : null
|
||||
};
|
||||
|
||||
logger.error('❌ Failed to check for updates:', errorDetails.message);
|
||||
|
||||
// 处理 404 错误 - 仓库或版本不存在
|
||||
if (error.response && error.response.status === 404) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
current: currentVersion,
|
||||
latest: currentVersion,
|
||||
hasUpdate: false,
|
||||
releaseInfo: {
|
||||
name: 'No releases found',
|
||||
body: 'The GitHub repository has no releases yet.',
|
||||
publishedAt: new Date().toISOString(),
|
||||
htmlUrl: '#'
|
||||
},
|
||||
warning: 'GitHub repository has no releases'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 如果是网络错误,尝试返回缓存的数据
|
||||
if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
|
||||
const cacheKey = 'version_check_cache';
|
||||
const cached = await redis.getClient().get(cacheKey);
|
||||
|
||||
if (cached) {
|
||||
const cachedData = JSON.parse(cached);
|
||||
// 实时计算 hasUpdate
|
||||
const hasUpdate = compareVersions(currentVersion, cachedData.latest) < 0;
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
current: currentVersion,
|
||||
latest: cachedData.latest,
|
||||
hasUpdate: hasUpdate, // 实时计算
|
||||
releaseInfo: cachedData.releaseInfo,
|
||||
cached: true,
|
||||
warning: 'Using cached data due to network error'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 其他错误返回当前版本信息
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
current: currentVersion,
|
||||
latest: currentVersion,
|
||||
hasUpdate: false,
|
||||
releaseInfo: {
|
||||
name: 'Update check failed',
|
||||
body: `Unable to check for updates: ${error.message || 'Unknown error'}`,
|
||||
publishedAt: new Date().toISOString(),
|
||||
htmlUrl: '#'
|
||||
},
|
||||
error: true,
|
||||
warning: error.message || 'Failed to check for updates'
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 版本比较函数
|
||||
function compareVersions(current, latest) {
|
||||
const parseVersion = (v) => {
|
||||
const parts = v.split('.').map(Number);
|
||||
return {
|
||||
major: parts[0] || 0,
|
||||
minor: parts[1] || 0,
|
||||
patch: parts[2] || 0
|
||||
};
|
||||
};
|
||||
|
||||
const currentV = parseVersion(current);
|
||||
const latestV = parseVersion(latest);
|
||||
|
||||
if (currentV.major !== latestV.major) {
|
||||
return currentV.major - latestV.major;
|
||||
}
|
||||
if (currentV.minor !== latestV.minor) {
|
||||
return currentV.minor - latestV.minor;
|
||||
}
|
||||
return currentV.patch - latestV.patch;
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user