diff --git a/alembic/versions/20251218_0631_f30f9936f6a2_add_proxy_field_to_provider_endpoints.py b/alembic/versions/20251218_0631_f30f9936f6a2_add_proxy_field_to_provider_endpoints.py new file mode 100644 index 0000000..1e7e2df --- /dev/null +++ b/alembic/versions/20251218_0631_f30f9936f6a2_add_proxy_field_to_provider_endpoints.py @@ -0,0 +1,57 @@ +"""add proxy field to provider_endpoints + +Revision ID: f30f9936f6a2 +Revises: 1cc6942cf06f +Create Date: 2025-12-18 06:31:58.451112+00:00 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import JSONB +from sqlalchemy import inspect + +# revision identifiers, used by Alembic. +revision = 'f30f9936f6a2' +down_revision = '1cc6942cf06f' +branch_labels = None +depends_on = None + + +def column_exists(table_name: str, column_name: str) -> bool: + """检查列是否存在""" + bind = op.get_bind() + inspector = inspect(bind) + columns = [col['name'] for col in inspector.get_columns(table_name)] + return column_name in columns + + +def get_column_type(table_name: str, column_name: str) -> str: + """获取列的类型""" + bind = op.get_bind() + inspector = inspect(bind) + for col in inspector.get_columns(table_name): + if col['name'] == column_name: + return str(col['type']).upper() + return '' + + +def upgrade() -> None: + """添加 proxy 字段到 provider_endpoints 表""" + if not column_exists('provider_endpoints', 'proxy'): + # 字段不存在,直接添加 JSONB 类型 + op.add_column('provider_endpoints', sa.Column('proxy', JSONB(), nullable=True)) + else: + # 字段已存在,检查是否需要转换类型 + col_type = get_column_type('provider_endpoints', 'proxy') + if 'JSONB' not in col_type: + # 如果是 JSON 类型,转换为 JSONB + op.execute( + 'ALTER TABLE provider_endpoints ' + 'ALTER COLUMN proxy TYPE JSONB USING proxy::jsonb' + ) + + +def downgrade() -> None: + """移除 proxy 字段""" + if column_exists('provider_endpoints', 'proxy'): + op.drop_column('provider_endpoints', 'proxy') diff --git a/frontend/src/api/endpoints/endpoints.ts b/frontend/src/api/endpoints/endpoints.ts index 642048f..9427e4d 100644 --- a/frontend/src/api/endpoints/endpoints.ts +++ b/frontend/src/api/endpoints/endpoints.ts @@ -1,6 +1,15 @@ import client from '../client' import type { ProviderEndpoint } from './types' +/** + * 代理配置类型 + */ +export interface ProxyConfig { + url: string + username?: string + password?: string +} + /** * 获取指定 Provider 的所有 Endpoints */ @@ -38,6 +47,7 @@ export async function createEndpoint( rate_limit?: number is_active?: boolean config?: Record + proxy?: ProxyConfig } ): Promise { const response = await client.post(`/api/admin/endpoints/providers/${providerId}/endpoints`, data) @@ -63,6 +73,7 @@ export async function updateEndpoint( rate_limit: number is_active: boolean config: Record + proxy: ProxyConfig }> ): Promise { const response = await client.put(`/api/admin/endpoints/${endpointId}`, data) diff --git a/frontend/src/api/endpoints/types.ts b/frontend/src/api/endpoints/types.ts index ea8ee22..e901fae 100644 --- a/frontend/src/api/endpoints/types.ts +++ b/frontend/src/api/endpoints/types.ts @@ -41,6 +41,11 @@ export interface ProviderEndpoint { last_failure_at?: string is_active: boolean config?: Record + proxy?: { + url: string + username?: string + password?: string + } total_keys: number active_keys: number created_at: string diff --git a/frontend/src/features/providers/components/EndpointFormDialog.vue b/frontend/src/features/providers/components/EndpointFormDialog.vue index c2a8c0e..6b8f83c 100644 --- a/frontend/src/features/providers/components/EndpointFormDialog.vue +++ b/frontend/src/features/providers/components/EndpointFormDialog.vue @@ -132,6 +132,61 @@ + + +
+
+

+ 代理配置 +

+ +
+ +
+
+ + +

+ 支持 HTTP、HTTPS、SOCKS4、SOCKS5 代理 +

+
+ +
+
+ + +
+ +
+ + +
+
+
+