mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 00:53:33 +00:00
feat: Add comprehensive Amazon Bedrock integration support
Add complete Amazon Bedrock integration to Claude Relay Service with: ## Core Features - ✅ Bedrock account management with encrypted AWS credential storage - ✅ Full request routing to AWS Bedrock with streaming support - ✅ Integration with unified Claude scheduler system - ✅ Support for Inference Profiles and Application Inference Profiles - ✅ Configurable default and small-fast model settings ## Backend Services - Add bedrockAccountService.js for account management - Add bedrockRelayService.js for request forwarding - Integrate Bedrock accounts into unifiedClaudeScheduler.js - Update admin and API routes to support Bedrock endpoints - Add comprehensive configuration options to config.example.js ## Frontend Integration - Complete Vue.js Web UI for Bedrock account management - Account creation form with AWS credentials and model configuration - Real-time account status monitoring and statistics - Edit/update capabilities for existing accounts ## CLI Support - Interactive CLI commands for Bedrock account operations - Account creation, listing, updating, and testing - Status monitoring and connection validation ## Security & Performance - AES encrypted storage of AWS credentials in Redis - Support for temporary credentials (session tokens) - Region-specific configuration support - Rate limiting and error handling This integration enables the relay service to support three AI platforms: 1. Claude (OAuth) - Original Claude.ai integration 2. Gemini - Google AI integration 3. Amazon Bedrock - New AWS Bedrock integration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ const express = require('express');
|
||||
const apiKeyService = require('../services/apiKeyService');
|
||||
const claudeAccountService = require('../services/claudeAccountService');
|
||||
const claudeConsoleAccountService = require('../services/claudeConsoleAccountService');
|
||||
const bedrockAccountService = require('../services/bedrockAccountService');
|
||||
const geminiAccountService = require('../services/geminiAccountService');
|
||||
const accountGroupService = require('../services/accountGroupService');
|
||||
const redis = require('../models/redis');
|
||||
@@ -1361,6 +1362,227 @@ router.put('/claude-console-accounts/:accountId/toggle-schedulable', authenticat
|
||||
}
|
||||
});
|
||||
|
||||
// ☁️ Bedrock 账户管理
|
||||
|
||||
// 获取所有Bedrock账户
|
||||
router.get('/bedrock-accounts', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const result = await bedrockAccountService.getAllAccounts();
|
||||
if (!result.success) {
|
||||
return res.status(500).json({ error: 'Failed to get Bedrock accounts', message: result.error });
|
||||
}
|
||||
|
||||
// 为每个账户添加使用统计信息
|
||||
const accountsWithStats = await Promise.all(result.data.map(async (account) => {
|
||||
try {
|
||||
const usageStats = await redis.getAccountUsageStats(account.id);
|
||||
return {
|
||||
...account,
|
||||
usage: {
|
||||
daily: usageStats.daily,
|
||||
total: usageStats.total,
|
||||
averages: usageStats.averages
|
||||
}
|
||||
};
|
||||
} catch (statsError) {
|
||||
logger.warn(`⚠️ Failed to get usage stats for Bedrock account ${account.id}:`, statsError.message);
|
||||
return {
|
||||
...account,
|
||||
usage: {
|
||||
daily: { tokens: 0, requests: 0, allTokens: 0 },
|
||||
total: { tokens: 0, requests: 0, allTokens: 0 },
|
||||
averages: { rpm: 0, tpm: 0 }
|
||||
}
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
res.json({ success: true, data: accountsWithStats });
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to get Bedrock accounts:', error);
|
||||
res.status(500).json({ error: 'Failed to get Bedrock accounts', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 创建新的Bedrock账户
|
||||
router.post('/bedrock-accounts', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
name,
|
||||
description,
|
||||
region,
|
||||
awsCredentials,
|
||||
defaultModel,
|
||||
priority,
|
||||
accountType,
|
||||
credentialType
|
||||
} = req.body;
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({ error: 'Name is required' });
|
||||
}
|
||||
|
||||
// 验证priority的有效性(1-100)
|
||||
if (priority !== undefined && (priority < 1 || priority > 100)) {
|
||||
return res.status(400).json({ error: 'Priority must be between 1 and 100' });
|
||||
}
|
||||
|
||||
// 验证accountType的有效性
|
||||
if (accountType && !['shared', 'dedicated'].includes(accountType)) {
|
||||
return res.status(400).json({ error: 'Invalid account type. Must be "shared" or "dedicated"' });
|
||||
}
|
||||
|
||||
// 验证credentialType的有效性
|
||||
if (credentialType && !['default', 'access_key', 'bearer_token'].includes(credentialType)) {
|
||||
return res.status(400).json({ error: 'Invalid credential type. Must be "default", "access_key", or "bearer_token"' });
|
||||
}
|
||||
|
||||
const result = await bedrockAccountService.createAccount({
|
||||
name,
|
||||
description: description || '',
|
||||
region: region || 'us-east-1',
|
||||
awsCredentials,
|
||||
defaultModel,
|
||||
priority: priority || 50,
|
||||
accountType: accountType || 'shared',
|
||||
credentialType: credentialType || 'default'
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
return res.status(500).json({ error: 'Failed to create Bedrock account', message: result.error });
|
||||
}
|
||||
|
||||
logger.success(`☁️ Admin created Bedrock account: ${name}`);
|
||||
res.json({ success: true, data: result.data });
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to create Bedrock account:', error);
|
||||
res.status(500).json({ error: 'Failed to create Bedrock account', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 更新Bedrock账户
|
||||
router.put('/bedrock-accounts/:accountId', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const { accountId } = req.params;
|
||||
const updates = req.body;
|
||||
|
||||
// 验证priority的有效性(1-100)
|
||||
if (updates.priority !== undefined && (updates.priority < 1 || updates.priority > 100)) {
|
||||
return res.status(400).json({ error: 'Priority must be between 1 and 100' });
|
||||
}
|
||||
|
||||
// 验证accountType的有效性
|
||||
if (updates.accountType && !['shared', 'dedicated'].includes(updates.accountType)) {
|
||||
return res.status(400).json({ error: 'Invalid account type. Must be "shared" or "dedicated"' });
|
||||
}
|
||||
|
||||
// 验证credentialType的有效性
|
||||
if (updates.credentialType && !['default', 'access_key', 'bearer_token'].includes(updates.credentialType)) {
|
||||
return res.status(400).json({ error: 'Invalid credential type. Must be "default", "access_key", or "bearer_token"' });
|
||||
}
|
||||
|
||||
const result = await bedrockAccountService.updateAccount(accountId, updates);
|
||||
|
||||
if (!result.success) {
|
||||
return res.status(500).json({ error: 'Failed to update Bedrock account', message: result.error });
|
||||
}
|
||||
|
||||
logger.success(`📝 Admin updated Bedrock account: ${accountId}`);
|
||||
res.json({ success: true, message: 'Bedrock account updated successfully' });
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to update Bedrock account:', error);
|
||||
res.status(500).json({ error: 'Failed to update Bedrock account', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 删除Bedrock账户
|
||||
router.delete('/bedrock-accounts/:accountId', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const { accountId } = req.params;
|
||||
|
||||
const result = await bedrockAccountService.deleteAccount(accountId);
|
||||
|
||||
if (!result.success) {
|
||||
return res.status(500).json({ error: 'Failed to delete Bedrock account', message: result.error });
|
||||
}
|
||||
|
||||
logger.success(`🗑️ Admin deleted Bedrock account: ${accountId}`);
|
||||
res.json({ success: true, message: 'Bedrock account deleted successfully' });
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to delete Bedrock account:', error);
|
||||
res.status(500).json({ error: 'Failed to delete Bedrock account', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 切换Bedrock账户状态
|
||||
router.put('/bedrock-accounts/:accountId/toggle', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const { accountId } = req.params;
|
||||
|
||||
const accountResult = await bedrockAccountService.getAccount(accountId);
|
||||
if (!accountResult.success) {
|
||||
return res.status(404).json({ error: 'Account not found' });
|
||||
}
|
||||
|
||||
const newStatus = !accountResult.data.isActive;
|
||||
const updateResult = await bedrockAccountService.updateAccount(accountId, { isActive: newStatus });
|
||||
|
||||
if (!updateResult.success) {
|
||||
return res.status(500).json({ error: 'Failed to toggle account status', message: updateResult.error });
|
||||
}
|
||||
|
||||
logger.success(`🔄 Admin toggled Bedrock account status: ${accountId} -> ${newStatus ? 'active' : 'inactive'}`);
|
||||
res.json({ success: true, isActive: newStatus });
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to toggle Bedrock account status:', error);
|
||||
res.status(500).json({ error: 'Failed to toggle account status', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 切换Bedrock账户调度状态
|
||||
router.put('/bedrock-accounts/:accountId/toggle-schedulable', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const { accountId } = req.params;
|
||||
|
||||
const accountResult = await bedrockAccountService.getAccount(accountId);
|
||||
if (!accountResult.success) {
|
||||
return res.status(404).json({ error: 'Account not found' });
|
||||
}
|
||||
|
||||
const newSchedulable = !accountResult.data.schedulable;
|
||||
const updateResult = await bedrockAccountService.updateAccount(accountId, { schedulable: newSchedulable });
|
||||
|
||||
if (!updateResult.success) {
|
||||
return res.status(500).json({ error: 'Failed to toggle schedulable status', message: updateResult.error });
|
||||
}
|
||||
|
||||
logger.success(`🔄 Admin toggled Bedrock account schedulable status: ${accountId} -> ${newSchedulable ? 'schedulable' : 'not schedulable'}`);
|
||||
res.json({ success: true, schedulable: newSchedulable });
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to toggle Bedrock account schedulable status:', error);
|
||||
res.status(500).json({ error: 'Failed to toggle schedulable status', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 测试Bedrock账户连接
|
||||
router.post('/bedrock-accounts/:accountId/test', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const { accountId } = req.params;
|
||||
|
||||
const result = await bedrockAccountService.testAccount(accountId);
|
||||
|
||||
if (!result.success) {
|
||||
return res.status(500).json({ error: 'Account test failed', message: result.error });
|
||||
}
|
||||
|
||||
logger.success(`🧪 Admin tested Bedrock account: ${accountId} - ${result.data.status}`);
|
||||
res.json({ success: true, data: result.data });
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to test Bedrock account:', error);
|
||||
res.status(500).json({ error: 'Failed to test Bedrock account', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 🤖 Gemini 账户管理
|
||||
|
||||
// 生成 Gemini OAuth 授权 URL
|
||||
|
||||
Reference in New Issue
Block a user