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:
andersonby
2025-08-06 17:41:16 +08:00
parent d6ba97381d
commit 9a9a82c86f
14 changed files with 3493 additions and 23 deletions

View File

@@ -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