← vibesdk
worker/api/controllers/modelProviders/controller.ts
10.3 KB
Download file
/**
* Model Providers Controller
*/
import { BaseController } from '../baseController';
import { RouteContext } from '../../types/route-context';
import { ApiResponse, ControllerResponse } from '../types';
import { ModelProvidersService } from '../../../database/services/ModelProvidersService';
import { z } from 'zod';
import {
ModelProvidersListData,
ModelProviderData,
ModelProviderCreateData,
ModelProviderUpdateData,
ModelProviderDeleteData,
ModelProviderTestData,
CreateProviderRequest,
UpdateProviderRequest,
TestProviderRequest
} from './types';
import { createLogger } from '../../../logger';
// Validation schemas
const createProviderSchema = z.object({
name: z.string().min(1).max(100),
baseUrl: z.string().url(),
apiKey: z.string().min(1)
});
const updateProviderSchema = z.object({
name: z.string().min(1).max(100).optional(),
baseUrl: z.string().url().optional(),
apiKey: z.string().min(1).optional(),
isActive: z.boolean().optional()
});
const testProviderSchema = z.object({
providerId: z.string().optional(),
baseUrl: z.string().url().optional(),
apiKey: z.string().min(1).optional()
}).refine(
(data) => data.providerId || (data.baseUrl && data.apiKey),
"Either providerId or both baseUrl and apiKey must be provided"
);
export class ModelProvidersController extends BaseController {
static logger = createLogger('ModelProvidersController');
/**
* Get all custom providers for the authenticated user
*/
static async getProviders(_request: Request, env: Env, _ctx: ExecutionContext, context: RouteContext): Promise<ControllerResponse<ApiResponse<ModelProvidersListData>>> {
try {
const user = context.user!;
const modelProvidersService = new ModelProvidersService(env);
const providers = await modelProvidersService.getUserProviders(user.id);
return ModelProvidersController.createSuccessResponse({
providers: providers.filter(p => p.isActive)
});
} catch (error) {
this.logger.error('Error getting providers:', error);
return ModelProvidersController.createErrorResponse<ModelProvidersListData>('Failed to get providers', 500);
}
}
/**
* Get a specific provider by ID
*/
static async getProvider(request: Request, env: Env, _ctx: ExecutionContext, context: RouteContext): Promise<ControllerResponse<ApiResponse<ModelProviderData>>> {
try {
const user = context.user!;
const url = new URL(request.url);
const providerId = url.pathname.split('/').pop();
if (!providerId) {
return ModelProvidersController.createErrorResponse<ModelProviderData>('Provider ID is required', 400);
}
const modelProvidersService = new ModelProvidersService(env);
const provider = await modelProvidersService.getProvider(user.id, providerId);
if (!provider) {
throw new Error('Provider not found');
}
return ModelProvidersController.createSuccessResponse({
provider
});
} catch (error) {
this.logger.error('Error getting provider:', error);
return ModelProvidersController.createErrorResponse<ModelProviderData>('Failed to get provider', 500);
}
}
/**
* Create a new custom provider
*/
static async createProvider(request: Request, _env: Env, _ctx: ExecutionContext, _context: RouteContext): Promise<ControllerResponse<ApiResponse<ModelProviderCreateData>>> {
try {
const bodyResult = await ModelProvidersController.parseJsonBody<CreateProviderRequest>(request);
if (!bodyResult.success) {
return bodyResult.response as ControllerResponse<ApiResponse<ModelProviderCreateData>>;
}
const validation = createProviderSchema.safeParse(bodyResult.data);
if (!validation.success) {
return ModelProvidersController.createErrorResponse<ModelProviderCreateData>(
`Validation error: ${validation.error.issues.map(e => e.message).join(', ')}`,
400
);
}
return ModelProvidersController.createErrorResponse<ModelProviderCreateData>(
'Custom model providers are temporarily disabled. Please use BYOK (Bring Your Own Key) in the vault settings.',
503
);
} catch (error) {
return ModelProvidersController.createErrorResponse<ModelProviderCreateData>(
'Failed to create provider',
500
);
}
}
/**
* Update an existing provider
*/
static async updateProvider(request: Request, _env: Env, _ctx: ExecutionContext, _context: RouteContext): Promise<ControllerResponse<ApiResponse<ModelProviderUpdateData>>> {
try {
const url = new URL(request.url);
const providerId = url.pathname.split('/').pop();
if (!providerId) {
return ModelProvidersController.createErrorResponse<ModelProviderUpdateData>('Provider ID is required', 400);
}
const bodyResult = await ModelProvidersController.parseJsonBody<UpdateProviderRequest>(request);
if (!bodyResult.success) {
return bodyResult.response as ControllerResponse<ApiResponse<ModelProviderUpdateData>>;
}
const validation = updateProviderSchema.safeParse(bodyResult.data);
if (!validation.success) {
return ModelProvidersController.createErrorResponse<ModelProviderUpdateData>(
`Validation error: ${validation.error.issues.map(e => e.message).join(', ')}`,
400
);
}
return ModelProvidersController.createErrorResponse<ModelProviderUpdateData>(
'Custom model providers are temporarily disabled. Please use BYOK (Bring Your Own Key) in the vault settings.',
503
);
} catch (error) {
this.logger.error('Error updating provider:', error);
return ModelProvidersController.createErrorResponse<ModelProviderUpdateData>('Failed to update provider', 500);
}
}
/**
* Delete a provider
*/
static async deleteProvider(request: Request, _env: Env, _ctx: ExecutionContext, _context: RouteContext): Promise<ControllerResponse<ApiResponse<ModelProviderDeleteData>>> {
try {
const url = new URL(request.url);
const providerId = url.pathname.split('/').pop();
if (!providerId) {
return ModelProvidersController.createErrorResponse<ModelProviderDeleteData>('Provider ID is required', 400);
}
return ModelProvidersController.createErrorResponse<ModelProviderDeleteData>(
'Custom model providers are temporarily disabled.',
503
);
} catch (error) {
this.logger.error('Error deleting provider:', error);
return ModelProvidersController.createErrorResponse<ModelProviderDeleteData>('Failed to delete provider', 500);
}
}
/**
* Test provider connection
*/
static async testProvider(request: Request, _env: Env, _ctx: ExecutionContext, _context: RouteContext): Promise<ControllerResponse<ApiResponse<ModelProviderTestData>>> {
try {
const bodyResult = await ModelProvidersController.parseJsonBody<TestProviderRequest>(request);
if (!bodyResult.success) {
return bodyResult.response as ControllerResponse<ApiResponse<ModelProviderTestData>>;
}
const validation = testProviderSchema.safeParse(bodyResult.data);
if (!validation.success) {
return ModelProvidersController.createErrorResponse<ModelProviderTestData>(
`Validation error: ${validation.error.issues.map(e => e.message).join(', ')}`,
400
);
}
if (validation.data.providerId) {
return ModelProvidersController.createErrorResponse<ModelProviderTestData>(
'Testing stored providers is temporarily disabled. Please provide baseUrl and apiKey directly.',
503
);
}
const baseUrl = validation.data.baseUrl!;
const apiKey = validation.data.apiKey!;
const startTime = Date.now();
try {
const testUrl = `${baseUrl.replace(/\/$/, '')}/models`;
const response = await fetch(testUrl, {
method: 'GET',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
const responseTime = Date.now() - startTime;
if (response.ok) {
return ModelProvidersController.createSuccessResponse<ModelProviderTestData>({
success: true,
responseTime
});
} else {
const errorText = await response.text();
return ModelProvidersController.createErrorResponse<ModelProviderTestData>(
`API request failed: ${response.status} ${errorText}`,
500
);
}
} catch (error) {
return ModelProvidersController.createErrorResponse<ModelProviderTestData>(
`Connection failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
500
);
}
} catch (error) {
this.logger.error('Error testing provider:', error);
return ModelProvidersController.createErrorResponse<ModelProviderTestData>('Failed to test provider', 500);
}
}
}