feat: enhance monitoring and Gemini service functionality

- Add enhanced monitoring scripts (monitor-enhanced.sh, status-unified.sh)
- Improve Gemini relay service with better error handling and token management
- Update authentication middleware for better compatibility
- Add new package dependencies for enhanced functionality
- Update .gitignore and app configuration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
mouyong
2025-08-09 14:06:56 +08:00
parent 6a8a668798
commit 8bdf495ffa
7 changed files with 673 additions and 9 deletions

View File

@@ -280,13 +280,13 @@ class Application {
const health = {
status: 'healthy',
service: 'claude-relay-service',
version: version,
version,
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: {
used: Math.round(memory.heapUsed / 1024 / 1024) + 'MB',
total: Math.round(memory.heapTotal / 1024 / 1024) + 'MB',
external: Math.round(memory.external / 1024 / 1024) + 'MB'
used: `${Math.round(memory.heapUsed / 1024 / 1024)}MB`,
total: `${Math.round(memory.heapTotal / 1024 / 1024)}MB`,
external: `${Math.round(memory.external / 1024 / 1024)}MB`
},
components: {
redis: redisHealth,
@@ -364,7 +364,7 @@ class Application {
// 存储到Redis每次启动都覆盖确保与 init.json 同步)
const adminCredentials = {
username: initData.adminUsername,
passwordHash: passwordHash,
passwordHash,
createdAt: initData.initializedAt || new Date().toISOString(),
lastLogin: null,
updatedAt: initData.updatedAt || null

View File

@@ -9,12 +9,13 @@ const authenticateApiKey = async (req, res, next) => {
const startTime = Date.now()
try {
// 安全提取API Key支持多种格式
// 安全提取API Key支持多种格式包括Gemini CLI支持
const apiKey =
req.headers['x-api-key'] ||
req.headers['x-goog-api-key'] ||
req.headers['authorization']?.replace(/^Bearer\s+/i, '') ||
req.headers['api-key']
req.headers['api-key'] ||
req.query.key
if (!apiKey) {
logger.security(`🔒 Missing API key attempt from ${req.ip || 'unknown'}`)

View File

@@ -441,9 +441,135 @@ async function getAvailableModels(accessToken, proxy, projectId, location = 'us-
}
}
// Count Tokens API - 用于Gemini CLI兼容性
async function countTokens({
model,
content,
accessToken,
proxy,
projectId,
location = 'us-central1'
}) {
// 确保模型名称格式正确
if (!model.startsWith('models/')) {
model = `models/${model}`
}
// 转换内容格式 - 支持多种输入格式
let requestBody
if (Array.isArray(content)) {
// 如果content是数组直接使用
requestBody = { contents: content }
} else if (typeof content === 'string') {
// 如果是字符串转换为Gemini格式
requestBody = {
contents: [
{
parts: [{ text: content }]
}
]
}
} else if (content.parts || content.role) {
// 如果已经是Gemini格式的单个content
requestBody = { contents: [content] }
} else {
// 其他情况,尝试直接使用
requestBody = { contents: content }
}
// 构建API URL - countTokens需要使用generativelanguage API
const GENERATIVE_API_BASE = 'https://generativelanguage.googleapis.com/v1beta'
let apiUrl
if (projectId) {
// 使用项目特定的 URL 格式Google Cloud/Workspace 账号)
apiUrl = `${GENERATIVE_API_BASE}/projects/${projectId}/locations/${location}/${model}:countTokens`
logger.debug(
`Using project-specific countTokens URL with projectId: ${projectId}, location: ${location}`
)
} else {
// 使用标准 URL 格式(个人 Google 账号)
apiUrl = `${GENERATIVE_API_BASE}/${model}:countTokens`
logger.debug('Using standard countTokens URL without projectId')
}
const axiosConfig = {
method: 'POST',
url: apiUrl,
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'X-Goog-User-Project': projectId || undefined
},
data: requestBody,
timeout: 30000
}
// 添加代理配置
const proxyAgent = createProxyAgent(proxy)
if (proxyAgent) {
axiosConfig.httpsAgent = proxyAgent
logger.debug('Using proxy for Gemini countTokens request')
}
try {
logger.debug(`Sending countTokens request to: ${apiUrl}`)
logger.debug(`Request body: ${JSON.stringify(requestBody, null, 2)}`)
const response = await axios(axiosConfig)
// 返回符合Gemini API格式的响应
return {
totalTokens: response.data.totalTokens || 0,
totalBillableCharacters: response.data.totalBillableCharacters || 0,
...response.data
}
} catch (error) {
logger.error(`Gemini countTokens API request failed for URL: ${apiUrl}`)
logger.error(
'Request config:',
JSON.stringify(
{
url: apiUrl,
headers: axiosConfig.headers,
data: requestBody
},
null,
2
)
)
logger.error('Error details:', error.response?.data || error.message)
// 转换错误格式
if (error.response) {
const geminiError = error.response.data?.error
const errorObj = new Error(
geminiError?.message ||
`Gemini countTokens API request failed (Status: ${error.response.status})`
)
errorObj.status = error.response.status
errorObj.error = {
message:
geminiError?.message ||
`Gemini countTokens API request failed (Status: ${error.response.status})`,
type: geminiError?.code || 'api_error',
code: geminiError?.code
}
throw errorObj
}
const errorObj = new Error(error.message)
errorObj.status = 500
errorObj.error = {
message: error.message,
type: 'network_error'
}
throw errorObj
}
}
module.exports = {
sendGeminiRequest,
getAvailableModels,
convertMessagesToGemini,
convertGeminiResponse
convertGeminiResponse,
countTokens
}