fix pylance problems

This commit is contained in:
Von committed 2026-04-06 13:00:31 +08:00
1 parent bd7552f23b
commit c744bc0bc3
26 files changed
+405 -251

No files matched your search

+23 -17
View File
@@ -2,7 +2,11 @@
# -*- coding: utf-8 -*-
"""认证控制器。"""
from flask import Response, jsonify, make_response, redirect, render_template, request
from typing import Any
from flask import jsonify, make_response, redirect, render_template, request
from flask.typing import ResponseReturnValue
from ..application.app_context import AppContext
from ..services import AuthenticationService
@@ -17,15 +21,20 @@ class AuthenticationController:
self._auth_service = auth_service
self._register_routes()
@staticmethod
def _get_request_payload() -> dict[str, Any]:
payload = request.get_json(silent=True)
if isinstance(payload, dict):
return dict(payload)
return {}
def _register_routes(self) -> None:
"""注册认证相关路由。"""
self._app.route('/login')(self.login_page)
self._app.route('/api/login', methods=['POST'])(self.api_login)
self._app.route('/logout')(self.logout)
self._app.route('/api/logout', methods=['POST'])(self.api_logout)
def login_page(self) -> Response:
"""返回登录页,已登录用户会被重定向到首页。"""
def login_page(self) -> ResponseReturnValue:
if not self._auth_service.is_auth_enabled():
return redirect('/')
@@ -35,47 +44,44 @@ class AuthenticationController:
return render_template('login.html')
def api_login(self) -> Response:
"""处理登录请求并写入 session cookie。"""
def api_login(self) -> ResponseReturnValue:
if not self._auth_service.is_auth_enabled():
return jsonify({'message': 'Authentication not enabled'}), 200
data = request.get_json(silent=True) or {}
data = self._get_request_payload()
username = data.get('username')
password = data.get('password')
if not username or not password:
self._logger.warning("Login rejected: missing username or password")
if not isinstance(username, str) or not username or not isinstance(password, str) or not password:
self._logger.warning('Login rejected: missing username or password')
return jsonify({'error': 'Username and password are required'}), 400
if not self._auth_service.authenticate(username, password):
self._logger.warning(f"Login failed: invalid credentials for username={username!r}")
self._logger.warning('Login failed: invalid credentials for username=%r', username)
return jsonify({'error': 'Invalid username or password'}), 401
session_token = self._auth_service.create_session(username)
self._logger.info(f"Login succeeded: username={username!r}")
self._logger.info('Login succeeded: username=%r', username)
response = make_response(jsonify({'message': 'Login successful', 'username': username}))
cookie_settings = self._auth_service.get_cookie_settings()
response.set_cookie('session_token', session_token, **cookie_settings)
return response
def logout(self) -> Response:
"""处理页面登出并清理 cookie。"""
def logout(self) -> ResponseReturnValue:
session_token = request.cookies.get('session_token')
if session_token:
self._auth_service.destroy_session(session_token)
self._logger.info("Logout succeeded via page route")
self._logger.info('Logout succeeded via page route')
response = make_response(redirect('/login'))
response.delete_cookie('session_token')
return response
def api_logout(self) -> Response:
"""处理 API 登出并清理 cookie。"""
def api_logout(self) -> ResponseReturnValue:
session_token = request.cookies.get('session_token')
if session_token:
self._auth_service.destroy_session(session_token)
self._logger.info("Logout succeeded via API route")
self._logger.info('Logout succeeded via API route')
response = make_response(jsonify({'message': 'Logout successful'}))
response.delete_cookie('session_token')
+9 -4
View File
@@ -3,19 +3,24 @@
"""认证装饰器。"""
from functools import wraps
from typing import Callable
from typing import Callable, ParamSpec
from flask import jsonify, redirect, request
from flask.typing import ResponseReturnValue
from ..services import AuthenticationService
P = ParamSpec("P")
def require_authentication(auth_service: AuthenticationService):
def require_authentication(
auth_service: AuthenticationService,
) -> Callable[[Callable[P, ResponseReturnValue]], Callable[P, ResponseReturnValue]]:
"""生成认证校验装饰器。"""
def decorator(func: Callable) -> Callable:
def decorator(func: Callable[P, ResponseReturnValue]) -> Callable[P, ResponseReturnValue]:
@wraps(func)
def decorated_function(*args, **kwargs):
def decorated_function(*args: P.args, **kwargs: P.kwargs) -> ResponseReturnValue:
if not auth_service.is_auth_enabled():
return func(*args, **kwargs)
+51 -34
View File
@@ -2,7 +2,10 @@
# -*- coding: utf-8 -*-
"""Provider 管理 API。"""
from flask import Response, jsonify, request
from typing import Any
from flask import jsonify, request
from flask.typing import ResponseReturnValue
from ..application.app_context import AppContext
from ..services import (
@@ -16,7 +19,7 @@ from .decorators import require_authentication
class ProviderController:
"""处理 provider 配置的增删改查、模型拉取与配置开关更新。"""
"""处理 provider 配置、认证分组与相关管理 API。"""
def __init__(
self,
@@ -36,6 +39,19 @@ class ProviderController:
self._auth_service = auth_service
self._register_routes()
@staticmethod
def _get_request_payload() -> dict[str, Any]:
payload = request.get_json(silent=True)
if isinstance(payload, dict):
return dict(payload)
return {}
@staticmethod
def _coerce_name_list(value: Any) -> list[str]:
if not isinstance(value, list):
raise ValueError('Provider names must be a non-empty list')
return [str(item) for item in value]
def _register_routes(self) -> None:
auth = require_authentication(self._auth_service)
@@ -75,14 +91,14 @@ class ProviderController:
auth(self.restore_auth_group_entry)
)
def get_providers(self) -> Response:
def get_providers(self) -> ResponseReturnValue:
try:
return jsonify(self._provider_service.list_providers())
except Exception as exc:
self._logger.error('Error getting providers: %s', exc)
return jsonify({'error': str(exc)}), 500
def get_provider(self, name: str) -> Response:
def get_provider(self, name: str) -> ResponseReturnValue:
try:
provider = self._provider_service.get_provider(name)
if provider is None:
@@ -92,9 +108,9 @@ class ProviderController:
self._logger.error('Error getting provider: %s', exc)
return jsonify({'error': str(exc)}), 500
def create_provider(self) -> Response:
def create_provider(self) -> ResponseReturnValue:
try:
payload = request.get_json(silent=True) or {}
payload = self._get_request_payload()
provider = self._provider_service.create_provider(payload)
self._logger.info('Provider created: %s', provider.get('name'))
return jsonify(provider), 201
@@ -104,9 +120,9 @@ class ProviderController:
self._logger.error('Error creating provider: %s', exc)
return jsonify({'error': str(exc)}), 500
def update_provider(self, name: str) -> Response:
def update_provider(self, name: str) -> ResponseReturnValue:
try:
payload = request.get_json(silent=True) or {}
payload = self._get_request_payload()
provider = self._provider_service.update_provider(name, payload)
self._logger.info('Provider updated: %s -> %s', name, provider.get('name'))
return jsonify(provider)
@@ -118,7 +134,7 @@ class ProviderController:
self._logger.error('Error updating provider: %s', exc)
return jsonify({'error': str(exc)}), 500
def delete_provider(self, name: str) -> Response:
def delete_provider(self, name: str) -> ResponseReturnValue:
try:
self._provider_service.delete_provider(name)
self._logger.info('Provider deleted: %s', name)
@@ -131,7 +147,7 @@ class ProviderController:
self._logger.error('Error deleting provider: %s', exc)
return jsonify({'error': str(exc)}), 500
def disable_provider(self, name: str) -> Response:
def disable_provider(self, name: str) -> ResponseReturnValue:
try:
provider = self._provider_service.set_provider_enabled(name, enabled=False)
self._logger.info('Provider disabled: %s', name)
@@ -144,7 +160,7 @@ class ProviderController:
self._logger.error('Error disabling provider: %s', exc)
return jsonify({'error': str(exc)}), 500
def enable_provider(self, name: str) -> Response:
def enable_provider(self, name: str) -> ResponseReturnValue:
try:
provider = self._provider_service.set_provider_enabled(name, enabled=True)
self._logger.info('Provider enabled: %s', name)
@@ -157,11 +173,11 @@ class ProviderController:
self._logger.error('Error enabling provider: %s', exc)
return jsonify({'error': str(exc)}), 500
def batch_providers(self) -> Response:
def batch_providers(self) -> ResponseReturnValue:
try:
payload = request.get_json(silent=True) or {}
payload = self._get_request_payload()
action = str(payload.get('action') or '').strip().lower()
names = payload.get('names')
names = self._coerce_name_list(payload.get('names'))
if action == 'enable':
result = self._provider_service.batch_set_provider_enabled(names, enabled=True)
elif action == 'disable':
@@ -181,14 +197,14 @@ class ProviderController:
self._logger.error('Error applying provider batch action: %s', exc)
return jsonify({'error': str(exc)}), 500
def get_auth_groups(self) -> Response:
def get_auth_groups(self) -> ResponseReturnValue:
try:
return jsonify(self._auth_group_service.list_auth_groups())
except Exception as exc:
self._logger.error('Error getting auth groups: %s', exc)
return jsonify({'error': str(exc)}), 500
def get_auth_group(self, name: str) -> Response:
def get_auth_group(self, name: str) -> ResponseReturnValue:
try:
auth_group = self._auth_group_service.get_auth_group(name)
if auth_group is None:
@@ -198,9 +214,9 @@ class ProviderController:
self._logger.error('Error getting auth group: %s', exc)
return jsonify({'error': str(exc)}), 500
def create_auth_group(self) -> Response:
def create_auth_group(self) -> ResponseReturnValue:
try:
payload = request.get_json(silent=True) or {}
payload = self._get_request_payload()
auth_group = self._auth_group_service.create_auth_group(payload)
self._logger.info('Auth group created: %s', auth_group.get('name'))
return jsonify(auth_group), 201
@@ -210,10 +226,11 @@ class ProviderController:
self._logger.error('Error creating auth group: %s', exc)
return jsonify({'error': str(exc)}), 500
def import_auth_group_entries(self) -> Response:
def import_auth_group_entries(self) -> ResponseReturnValue:
try:
payload = request.get_json(silent=True) or {}
entries = self._auth_group_service.import_auth_entries(payload.get('yaml', ''))
payload = self._get_request_payload()
yaml_text = str(payload.get('yaml', '') or '')
entries = self._auth_group_service.import_auth_entries(yaml_text)
return jsonify({'entries': entries})
except ValueError as exc:
return jsonify({'error': str(exc)}), 400
@@ -221,9 +238,9 @@ class ProviderController:
self._logger.error('Error importing auth group entries: %s', exc)
return jsonify({'error': str(exc)}), 500
def update_auth_group(self, name: str) -> Response:
def update_auth_group(self, name: str) -> ResponseReturnValue:
try:
payload = request.get_json(silent=True) or {}
payload = self._get_request_payload()
auth_group = self._auth_group_service.update_auth_group(name, payload)
self._logger.info('Auth group updated: %s -> %s', name, auth_group.get('name'))
return jsonify(auth_group)
@@ -235,7 +252,7 @@ class ProviderController:
self._logger.error('Error updating auth group: %s', exc)
return jsonify({'error': str(exc)}), 500
def delete_auth_group(self, name: str) -> Response:
def delete_auth_group(self, name: str) -> ResponseReturnValue:
try:
self._auth_group_service.delete_auth_group(name)
self._logger.info('Auth group deleted: %s', name)
@@ -248,7 +265,7 @@ class ProviderController:
self._logger.error('Error deleting auth group: %s', exc)
return jsonify({'error': str(exc)}), 500
def get_auth_group_runtime(self, name: str) -> Response:
def get_auth_group_runtime(self, name: str) -> ResponseReturnValue:
try:
return jsonify(self._auth_group_service.get_auth_group_runtime(name))
except ValueError as exc:
@@ -257,7 +274,7 @@ class ProviderController:
self._logger.error('Error getting auth group runtime: %s', exc)
return jsonify({'error': str(exc)}), 500
def clear_auth_group_entry_cooldown(self, name: str, entry_id: str) -> Response:
def clear_auth_group_entry_cooldown(self, name: str, entry_id: str) -> ResponseReturnValue:
try:
self._auth_group_service.clear_entry_cooldown(name, entry_id)
self._logger.info('Auth group entry cooldown cleared: %s/%s', name, entry_id)
@@ -270,7 +287,7 @@ class ProviderController:
self._logger.error('Error clearing auth group entry cooldown: %s', exc)
return jsonify({'error': str(exc)}), 500
def disable_auth_group_entry(self, name: str, entry_id: str) -> Response:
def disable_auth_group_entry(self, name: str, entry_id: str) -> ResponseReturnValue:
try:
self._auth_group_service.set_entry_disabled(name, entry_id, disabled=True)
self._logger.info('Auth group entry disabled: %s/%s', name, entry_id)
@@ -283,7 +300,7 @@ class ProviderController:
self._logger.error('Error disabling auth group entry: %s', exc)
return jsonify({'error': str(exc)}), 500
def enable_auth_group_entry(self, name: str, entry_id: str) -> Response:
def enable_auth_group_entry(self, name: str, entry_id: str) -> ResponseReturnValue:
try:
self._auth_group_service.set_entry_disabled(name, entry_id, disabled=False)
self._logger.info('Auth group entry enabled: %s/%s', name, entry_id)
@@ -296,7 +313,7 @@ class ProviderController:
self._logger.error('Error enabling auth group entry: %s', exc)
return jsonify({'error': str(exc)}), 500
def reset_auth_group_entry_minute_usage(self, name: str, entry_id: str) -> Response:
def reset_auth_group_entry_minute_usage(self, name: str, entry_id: str) -> ResponseReturnValue:
try:
self._auth_group_service.reset_entry_minute_usage(name, entry_id)
self._logger.info('Auth group entry minute usage reset: %s/%s', name, entry_id)
@@ -309,7 +326,7 @@ class ProviderController:
self._logger.error('Error resetting auth group entry minute usage: %s', exc)
return jsonify({'error': str(exc)}), 500
def reset_auth_group_entry_runtime(self, name: str, entry_id: str) -> Response:
def reset_auth_group_entry_runtime(self, name: str, entry_id: str) -> ResponseReturnValue:
try:
self._auth_group_service.reset_entry_runtime(name, entry_id)
self._logger.info('Auth group entry runtime reset: %s/%s', name, entry_id)
@@ -322,7 +339,7 @@ class ProviderController:
self._logger.error('Error resetting auth group entry runtime: %s', exc)
return jsonify({'error': str(exc)}), 500
def restore_auth_group_entry(self, name: str, entry_id: str) -> Response:
def restore_auth_group_entry(self, name: str, entry_id: str) -> ResponseReturnValue:
try:
self._auth_group_service.restore_entry(name, entry_id)
self._logger.info('Auth group entry restored: %s/%s', name, entry_id)
@@ -335,7 +352,7 @@ class ProviderController:
self._logger.error('Error restoring auth group entry: %s', exc)
return jsonify({'error': str(exc)}), 500
def fetch_models(self) -> Response:
def fetch_models(self) -> ResponseReturnValue:
try:
result = self._model_discovery_service.fetch_models_preview(
api=request.args.get('api', ''),
@@ -356,9 +373,9 @@ class ProviderController:
self._logger.error('Error fetching provider models: %s', exc)
return jsonify({'error': str(exc)}), 500
def update_chat_whitelist(self) -> Response:
def update_chat_whitelist(self) -> ResponseReturnValue:
try:
payload = request.get_json(silent=True) or {}
payload = self._get_request_payload()
enabled = self._settings_service.update_chat_whitelist_enabled(payload.get('enabled'))
self._logger.info('Chat whitelist updated: enabled=%s', enabled)
return jsonify({'enabled': enabled})
+19 -13
View File
@@ -7,6 +7,7 @@ from __future__ import annotations
from typing import Any, Dict, Iterable, Optional
from flask import Response, jsonify, request
from flask.typing import ResponseReturnValue
from ..application.app_context import AppContext
from ..config import ConfigManager, ProviderManager
@@ -56,7 +57,7 @@ class ProxyController:
*,
error_format: str,
) -> tuple[Optional[Dict[str, Any]], Optional[tuple[Response, int]]]:
"""在开启白名单时解析当前请求对应用户。"""
"""在启用白名单时解析当前请求对应的用户。"""
if not self._is_whitelist_required():
return None, None
@@ -151,21 +152,21 @@ class ProxyController:
return normalized[0]
return ", ".join(normalized)
def chat_completions(self) -> Response:
def chat_completions(self) -> ResponseReturnValue:
return self._proxy_completion_request(
route_name="chat_completions",
expected_target_formats=("openai_chat",),
inspect_stream_usage=True,
)
def responses(self) -> Response:
def responses(self) -> ResponseReturnValue:
return self._proxy_completion_request(
route_name="responses",
expected_target_formats=("openai_responses", "codex"),
inspect_stream_usage=False,
)
def messages(self) -> Response:
def messages(self) -> ResponseReturnValue:
return self._proxy_completion_request(
route_name="messages",
expected_target_formats=("claude_chat",),
@@ -179,7 +180,7 @@ class ProxyController:
expected_target_formats: Iterable[str],
inspect_stream_usage: bool,
error_format: Optional[str] = None,
) -> Response:
) -> ResponseReturnValue:
normalized_expected_target_formats = tuple(
str(item or "").strip().lower() for item in expected_target_formats if str(item or "").strip()
)
@@ -196,10 +197,10 @@ class ProxyController:
if denial_response is not None:
return denial_response
request_data = request.get_json(silent=True)
if request_data is None:
request_data = {}
if not isinstance(request_data, dict):
raw_request_data = request.get_json(silent=True)
if raw_request_data is None:
request_data: Dict[str, Any] = {}
elif not isinstance(raw_request_data, dict):
self._logger.warning("Proxy rejected: request body is not a JSON object route=%s", route_name)
return self._error_response(
"Request body must be a JSON object",
@@ -208,9 +209,11 @@ class ProxyController:
code="invalid_request_body",
error_format=resolved_error_format,
)
else:
request_data = dict(raw_request_data)
request_data = dict(request_data)
if "model" not in request_data:
model_name_value = request_data.get("model")
if not isinstance(model_name_value, str) or not model_name_value.strip():
self._logger.warning("Proxy rejected: missing model in request body route=%s", route_name)
return self._error_response(
"Missing 'model' in request body",
@@ -220,7 +223,7 @@ class ProxyController:
error_format=resolved_error_format,
)
model_name = request_data["model"]
model_name = model_name_value.strip()
provider = self._provider_manager.get_provider_for_model(model_name)
if not provider:
self._logger.warning("Proxy rejected: unknown model=%r route=%s", model_name, route_name)
@@ -390,7 +393,7 @@ class ProxyController:
error_format=resolved_error_format,
)
def list_models(self) -> Response:
def list_models(self) -> ResponseReturnValue:
try:
client_ip = normalize_ip(request.remote_addr)
user, denial_response = self._get_authorized_user_for_request(
@@ -453,3 +456,6 @@ class ProxyController:
"upgrade",
}
return {k: v for k, v in headers.items() if k.lower() not in excluded}
+73 -55
View File
@@ -2,7 +2,10 @@
# -*- coding: utf-8 -*-
"""用户控制器。"""
from flask import Response, jsonify, request
from typing import Any
from flask import jsonify, request
from flask.typing import ResponseReturnValue
from ..application.app_context import AppContext
from ..services import AuthenticationService, UserService
@@ -22,8 +25,14 @@ class UserController:
self._auth_service = auth_service
self._register_routes()
@staticmethod
def _get_request_payload() -> dict[str, Any]:
payload = request.get_json(silent=True)
if isinstance(payload, dict):
return dict(payload)
return {}
def _register_routes(self) -> None:
"""注册用户管理路由。"""
auth = require_authentication(self._auth_service)
self._app.route('/api/users', methods=['GET'])(auth(self.get_users))
@@ -34,14 +43,13 @@ class UserController:
self._app.route('/api/users/<int:user_id>', methods=['DELETE'])(auth(self.delete_user))
self._app.route('/api/users/<int:user_id>/toggle', methods=['POST'])(auth(self.toggle_user))
def get_users(self) -> Response:
"""分页查询用户列表。"""
def get_users(self) -> ResponseReturnValue:
try:
page = request.args.get('page', 1, type=int)
page_size = request.args.get('page_size', 50, type=int)
keyword = (request.args.get('keyword', '', type=str) or '').strip()
self._logger.debug(
"List users requested: page=%s, page_size=%s, keyword=%r",
'List users requested: page=%s, page_size=%s, keyword=%r',
page,
page_size,
keyword,
@@ -50,71 +58,84 @@ class UserController:
users = self._user_service.get_users(page=page, page_size=page_size, keyword=keyword)
total = self._user_service.get_total_users_count(keyword=keyword)
return jsonify({
'users': users,
'total': total,
'page': page,
'page_size': page_size,
'total_pages': (total + page_size - 1) // page_size,
'keyword': keyword,
'available_models': self._user_service.get_available_models(),
})
return jsonify(
{
'users': users,
'total': total,
'page': page,
'page_size': page_size,
'total_pages': (total + page_size - 1) // page_size,
'keyword': keyword,
'available_models': self._user_service.get_available_models(),
}
)
except Exception as exc:
self._logger.error(f'Error getting users: {exc}')
self._logger.error('Error getting users: %s', exc)
return jsonify({'error': str(exc)}), 500
def get_user(self, user_id: int) -> Response:
"""查询单个用户。"""
def get_user(self, user_id: int) -> ResponseReturnValue:
try:
user = self._user_service.get_user_by_id(user_id)
if not user:
self._logger.warning(f"Get user failed: user_id={user_id} not found")
self._logger.warning('Get user failed: user_id=%s not found', user_id)
return jsonify({'error': 'User not found'}), 404
return jsonify(user)
except Exception as exc:
self._logger.error(f'Error getting user: {exc}')
self._logger.error('Error getting user: %s', exc)
return jsonify({'error': str(exc)}), 500
def create_user(self) -> Response:
"""创建用户。"""
def create_user(self) -> ResponseReturnValue:
try:
data = request.get_json(silent=True) or {}
data = self._get_request_payload()
username = data.get('username')
ip_address = data.get('ip_address')
if not username:
self._logger.warning("Create user rejected: username is empty")
if not isinstance(username, str) or not username.strip():
self._logger.warning('Create user rejected: username is empty')
return jsonify({'error': 'Username is required'}), 400
if not ip_address:
self._logger.warning("Create user rejected: ip_address is empty")
if not isinstance(ip_address, str) or not ip_address.strip():
self._logger.warning('Create user rejected: ip_address is empty')
return jsonify({'error': 'IP address is required'}), 400
normalized_username = username.strip()
normalized_ip = normalize_ip(ip_address)
if not is_valid_ip(normalized_ip):
self._logger.warning(f"Create user rejected: invalid ip_address={ip_address!r}")
self._logger.warning('Create user rejected: invalid ip_address=%r', ip_address)
return jsonify({'error': 'Invalid IP address'}), 400
user_id = self._user_service.create_user(username, normalized_ip)
user_id = self._user_service.create_user(normalized_username, normalized_ip)
if not user_id:
self._logger.warning(f"Create user failed: username={username!r}, ip={normalized_ip}")
self._logger.warning('Create user failed: username=%r, ip=%s', normalized_username, normalized_ip)
return jsonify({'error': 'Failed to create user or IP already exists'}), 400
self._logger.info(f"Create user succeeded: user_id={user_id}, username={username!r}, ip={normalized_ip}")
self._logger.info(
'Create user succeeded: user_id=%s, username=%r, ip=%s',
user_id,
normalized_username,
normalized_ip,
)
return jsonify({'id': user_id, 'message': 'User created successfully'}), 201
except Exception as exc:
self._logger.error(f'Error creating user: {exc}')
self._logger.error('Error creating user: %s', exc)
return jsonify({'error': str(exc)}), 500
def update_user(self, user_id: int) -> Response:
"""更新用户信息。"""
def update_user(self, user_id: int) -> ResponseReturnValue:
try:
data = request.get_json(silent=True)
data = self._get_request_payload()
if not data:
self._logger.warning(f"Update user rejected: no payload, user_id={user_id}")
self._logger.warning('Update user rejected: no payload, user_id=%s', user_id)
return jsonify({'error': 'No data provided'}), 400
username = data.get('username')
ip_address = data.get('ip_address')
username_value = data.get('username')
if username_value is not None and not isinstance(username_value, str):
return jsonify({'error': 'Username must be a string'}), 400
username = username_value
ip_address_value = data.get('ip_address')
if ip_address_value is not None and not isinstance(ip_address_value, str):
return jsonify({'error': 'IP address must be a string'}), 400
ip_address = ip_address_value
whitelist_access_enabled = data.get('whitelist_access_enabled')
model_permissions_provided = 'model_permissions' in data
model_permissions = data.get('model_permissions')
@@ -123,7 +144,7 @@ class UserController:
if ip_address is not None:
normalized_ip = normalize_ip(ip_address)
if not is_valid_ip(normalized_ip):
self._logger.warning(f"Update user rejected: invalid ip={ip_address!r}, user_id={user_id}")
self._logger.warning('Update user rejected: invalid ip=%r, user_id=%s', ip_address, user_id)
return jsonify({'error': 'Invalid IP address'}), 400
success = self._user_service.update_user(
@@ -135,21 +156,20 @@ class UserController:
model_permissions=model_permissions,
)
if not success:
self._logger.warning(f"Update user failed: user_id={user_id}")
self._logger.warning('Update user failed: user_id=%s', user_id)
return jsonify({'error': 'Failed to update user'}), 400
self._logger.info(f"Update user succeeded: user_id={user_id}")
self._logger.info('Update user succeeded: user_id=%s', user_id)
return jsonify({'message': 'User updated successfully'})
except ValueError as exc:
return jsonify({'error': str(exc)}), 400
except Exception as exc:
self._logger.error(f'Error updating user: {exc}')
self._logger.error('Error updating user: %s', exc)
return jsonify({'error': str(exc)}), 500
def batch_users(self) -> Response:
"""执行用户批量操作。"""
def batch_users(self) -> ResponseReturnValue:
try:
payload = request.get_json(silent=True) or {}
payload = self._get_request_payload()
action = str(payload.get('action') or '').strip().lower()
if action != 'set_model_permissions':
raise ValueError(f'Unsupported user batch action: {action or "<empty>"}')
@@ -169,31 +189,29 @@ class UserController:
status_code = 404 if 'not found' in message.lower() else 400
return jsonify({'error': message}), status_code
except Exception as exc:
self._logger.error(f'Error applying user batch action: {exc}')
self._logger.error('Error applying user batch action: %s', exc)
return jsonify({'error': str(exc)}), 500
def delete_user(self, user_id: int) -> Response:
"""删除用户。"""
def delete_user(self, user_id: int) -> ResponseReturnValue:
try:
if not self._user_service.delete_user(user_id):
self._logger.warning(f"Delete user failed: user_id={user_id}")
self._logger.warning('Delete user failed: user_id=%s', user_id)
return jsonify({'error': 'Failed to delete user'}), 400
self._logger.info(f"Delete user succeeded: user_id={user_id}")
self._logger.info('Delete user succeeded: user_id=%s', user_id)
return jsonify({'message': 'User deleted successfully'})
except Exception as exc:
self._logger.error(f'Error deleting user: {exc}')
self._logger.error('Error deleting user: %s', exc)
return jsonify({'error': str(exc)}), 500
def toggle_user(self, user_id: int) -> Response:
"""切换用户白名单状态。"""
def toggle_user(self, user_id: int) -> ResponseReturnValue:
try:
if not self._config_manager.is_chat_whitelist_enabled():
return jsonify({'error': 'Whitelist control is disabled'}), 400
if not self._user_service.toggle_user_status(user_id):
self._logger.warning(f"Toggle user whitelist failed: user_id={user_id}")
self._logger.warning('Toggle user whitelist failed: user_id=%s', user_id)
return jsonify({'error': 'Failed to toggle user status'}), 400
self._logger.info(f"Toggle user whitelist succeeded: user_id={user_id}")
self._logger.info('Toggle user whitelist succeeded: user_id=%s', user_id)
return jsonify({'message': 'User status toggled successfully'})
except Exception as exc:
self._logger.error(f'Error toggling user status: {exc}')
self._logger.error('Error toggling user status: %s', exc)
return jsonify({'error': str(exc)}), 500
+49 -48
View File
@@ -2,7 +2,8 @@
# -*- coding: utf-8 -*-
"""页面与统计控制器。"""
from flask import Response, jsonify, render_template, request
from flask import jsonify, render_template, request
from flask.typing import ResponseReturnValue
from ..application.app_context import AppContext
from ..services import AuthenticationService, LogService
@@ -24,27 +25,27 @@ class WebController:
def _register_routes(self) -> None:
auth = require_authentication(self._auth_service)
self._app.route("/")(auth(self.index))
self._app.route("/users")(auth(self.users_page))
self._app.route("/providers")(auth(self.providers_page))
self._app.route('/')(auth(self.index))
self._app.route('/users')(auth(self.users_page))
self._app.route('/providers')(auth(self.providers_page))
self._app.route("/api/statistics", methods=["GET"])(auth(self.get_statistics))
self._app.route("/api/request-logs", methods=["GET"])(auth(self.get_request_logs))
self._app.route("/api/usernames", methods=["GET"])(auth(self.get_usernames))
self._app.route("/api/request-models", methods=["GET"])(auth(self.get_request_models))
self._app.route('/api/statistics', methods=['GET'])(auth(self.get_statistics))
self._app.route('/api/request-logs', methods=['GET'])(auth(self.get_request_logs))
self._app.route('/api/usernames', methods=['GET'])(auth(self.get_usernames))
self._app.route('/api/request-models', methods=['GET'])(auth(self.get_request_models))
def index(self) -> str:
return render_template(
"index.html",
active_page="index",
'index.html',
active_page='index',
current_username=self._get_current_username(),
auth_enabled=self._auth_service.is_auth_enabled(),
)
def users_page(self) -> str:
return render_template(
"users.html",
active_page="users",
'users.html',
active_page='users',
chat_whitelist_enabled=self._config_manager.is_chat_whitelist_enabled(),
current_username=self._get_current_username(),
auth_enabled=self._auth_service.is_auth_enabled(),
@@ -52,8 +53,8 @@ class WebController:
def providers_page(self) -> str:
return render_template(
"providers.html",
active_page="providers",
'providers.html',
active_page='providers',
chat_whitelist_enabled=self._config_manager.is_chat_whitelist_enabled(),
current_username=self._get_current_username(),
auth_enabled=self._auth_service.is_auth_enabled(),
@@ -61,66 +62,66 @@ class WebController:
def _get_current_username(self) -> str:
if not self._auth_service.is_auth_enabled():
return ""
return ''
session_token = request.cookies.get("session_token")
return self._auth_service.get_session_username(session_token) or ""
session_token = request.cookies.get('session_token')
return self._auth_service.get_session_username(session_token) or ''
def get_statistics(self) -> Response:
def get_statistics(self) -> ResponseReturnValue:
try:
self._logger.debug(
"Statistics queried: start_date=%s end_date=%s username=%s request_model=%s",
request.args.get("start_date"),
request.args.get("end_date"),
request.args.get("username"),
request.args.get("request_model"),
'Statistics queried: start_date=%s end_date=%s username=%s request_model=%s',
request.args.get('start_date'),
request.args.get('end_date'),
request.args.get('username'),
request.args.get('request_model'),
)
stats = self._log_service.get_statistics(
request.args.get("start_date"),
request.args.get("end_date"),
request.args.get("username"),
request.args.get("request_model"),
request.args.get('start_date'),
request.args.get('end_date'),
request.args.get('username'),
request.args.get('request_model'),
)
return jsonify(stats)
except Exception as exc:
self._logger.error(f"Error getting statistics: {exc}")
return jsonify({"error": str(exc)}), 500
self._logger.error('Error getting statistics: %s', exc)
return jsonify({'error': str(exc)}), 500
def get_request_logs(self) -> Response:
def get_request_logs(self) -> ResponseReturnValue:
try:
page = max(int(request.args.get("page", 1)), 1)
page_size = min(max(int(request.args.get("page_size", 50)), 1), 200)
self._logger.debug(f"Request logs queried: page={page}, page_size={page_size}")
page = max(int(request.args.get('page', 1)), 1)
page_size = min(max(int(request.args.get('page_size', 50)), 1), 200)
self._logger.debug('Request logs queried: page=%s, page_size=%s', page, page_size)
logs = self._log_service.get_request_logs(
page,
page_size,
request.args.get("start_date"),
request.args.get("end_date"),
request.args.get("username"),
request.args.get("request_model"),
request.args.get('start_date'),
request.args.get('end_date'),
request.args.get('username'),
request.args.get('request_model'),
)
return jsonify(logs)
except ValueError:
return jsonify({"error": "page and page_size must be integers"}), 400
return jsonify({'error': 'page and page_size must be integers'}), 400
except Exception as exc:
self._logger.error(f"Error getting request logs: {exc}")
return jsonify({"error": str(exc)}), 500
self._logger.error('Error getting request logs: %s', exc)
return jsonify({'error': str(exc)}), 500
def get_usernames(self) -> Response:
def get_usernames(self) -> ResponseReturnValue:
try:
usernames = self._log_service.get_unique_usernames()
self._logger.debug(f"Usernames queried: count={len(usernames)}")
self._logger.debug('Usernames queried: count=%s', len(usernames))
return jsonify(usernames)
except Exception as exc:
self._logger.error(f"Error getting usernames: {exc}")
return jsonify({"error": str(exc)}), 500
self._logger.error('Error getting usernames: %s', exc)
return jsonify({'error': str(exc)}), 500
def get_request_models(self) -> Response:
def get_request_models(self) -> ResponseReturnValue:
try:
models = self._log_service.get_unique_request_models()
self._logger.debug(f"Request models queried: count={len(models)}")
self._logger.debug('Request models queried: count=%s', len(models))
return jsonify(models)
except Exception as exc:
self._logger.error(f"Error getting request models: {exc}")
return jsonify({"error": str(exc)}), 500
self._logger.error('Error getting request models: %s', exc)
return jsonify({'error': str(exc)}), 500