WebUI后端整体重构

This commit is contained in:
墨梓柒
2026-01-13 07:24:27 +08:00
parent 812296590e
commit ffafbf0a26
36 changed files with 927 additions and 294 deletions

View File

@@ -0,0 +1,109 @@
"""WebUI Schemas - Pydantic models for API requests and responses."""
# Auth schemas
from .auth import (
TokenVerifyRequest,
TokenVerifyResponse,
TokenUpdateRequest,
TokenUpdateResponse,
TokenRegenerateResponse,
FirstSetupStatusResponse,
CompleteSetupResponse,
ResetSetupResponse,
)
# Statistics schemas
from .statistics import (
StatisticsSummary,
ModelStatistics,
TimeSeriesData,
DashboardData,
)
# Emoji schemas
from .emoji import (
EmojiResponse,
EmojiListResponse,
EmojiDetailResponse,
EmojiUpdateRequest,
EmojiUpdateResponse,
EmojiDeleteResponse,
BatchDeleteRequest,
BatchDeleteResponse,
EmojiUploadResponse,
ThumbnailCacheStatsResponse,
ThumbnailCleanupResponse,
ThumbnailPreheatResponse,
)
# Chat schemas
from .chat import (
VirtualIdentityConfig,
ChatHistoryMessage,
)
# Plugin schemas
from .plugin import (
FetchRawFileRequest,
FetchRawFileResponse,
CloneRepositoryRequest,
CloneRepositoryResponse,
MirrorConfigResponse,
AvailableMirrorsResponse,
AddMirrorRequest,
UpdateMirrorRequest,
GitStatusResponse,
InstallPluginRequest,
VersionResponse,
UninstallPluginRequest,
UpdatePluginRequest,
UpdatePluginConfigRequest,
)
__all__ = [
# Auth
"TokenVerifyRequest",
"TokenVerifyResponse",
"TokenUpdateRequest",
"TokenUpdateResponse",
"TokenRegenerateResponse",
"FirstSetupStatusResponse",
"CompleteSetupResponse",
"ResetSetupResponse",
# Statistics
"StatisticsSummary",
"ModelStatistics",
"TimeSeriesData",
"DashboardData",
# Emoji
"EmojiResponse",
"EmojiListResponse",
"EmojiDetailResponse",
"EmojiUpdateRequest",
"EmojiUpdateResponse",
"EmojiDeleteResponse",
"BatchDeleteRequest",
"BatchDeleteResponse",
"EmojiUploadResponse",
"ThumbnailCacheStatsResponse",
"ThumbnailCleanupResponse",
"ThumbnailPreheatResponse",
# Chat
"VirtualIdentityConfig",
"ChatHistoryMessage",
# Plugin
"FetchRawFileRequest",
"FetchRawFileResponse",
"CloneRepositoryRequest",
"CloneRepositoryResponse",
"MirrorConfigResponse",
"AvailableMirrorsResponse",
"AddMirrorRequest",
"UpdateMirrorRequest",
"GitStatusResponse",
"InstallPluginRequest",
"VersionResponse",
"UninstallPluginRequest",
"UpdatePluginRequest",
"UpdatePluginConfigRequest",
]

41
src/webui/schemas/auth.py Normal file
View File

@@ -0,0 +1,41 @@
from pydantic import BaseModel, Field
class TokenVerifyRequest(BaseModel):
token: str = Field(..., description="访问令牌")
class TokenVerifyResponse(BaseModel):
valid: bool = Field(..., description="Token 是否有效")
message: str = Field(..., description="验证结果消息")
is_first_setup: bool = Field(False, description="是否为首次设置")
class TokenUpdateRequest(BaseModel):
new_token: str = Field(..., description="新的访问令牌", min_length=10)
class TokenUpdateResponse(BaseModel):
success: bool = Field(..., description="是否更新成功")
message: str = Field(..., description="更新结果消息")
class TokenRegenerateResponse(BaseModel):
success: bool = Field(..., description="是否生成成功")
token: str = Field(..., description="新生成的令牌")
message: str = Field(..., description="生成结果消息")
class FirstSetupStatusResponse(BaseModel):
is_first_setup: bool = Field(..., description="是否为首次配置")
message: str = Field(..., description="状态消息")
class CompleteSetupResponse(BaseModel):
success: bool = Field(..., description="是否成功")
message: str = Field(..., description="结果消息")
class ResetSetupResponse(BaseModel):
success: bool = Field(..., description="是否成功")
message: str = Field(..., description="结果消息")

26
src/webui/schemas/chat.py Normal file
View File

@@ -0,0 +1,26 @@
from pydantic import BaseModel
from typing import Optional
class VirtualIdentityConfig(BaseModel):
"""虚拟身份配置"""
enabled: bool = False
platform: Optional[str] = None
person_id: Optional[str] = None
user_id: Optional[str] = None
user_nickname: Optional[str] = None
group_id: Optional[str] = None
group_name: Optional[str] = None
class ChatHistoryMessage(BaseModel):
"""聊天历史消息"""
id: str
type: str # 'user' | 'bot' | 'system'
content: str
timestamp: float
sender_name: str
sender_id: Optional[str] = None
is_bot: bool = False

115
src/webui/schemas/emoji.py Normal file
View File

@@ -0,0 +1,115 @@
from pydantic import BaseModel
from typing import Optional, List
class EmojiResponse(BaseModel):
"""表情包响应"""
id: int
full_path: str
format: str
emoji_hash: str
description: str
query_count: int
is_registered: bool
is_banned: bool
emotion: Optional[str]
record_time: float
register_time: Optional[float]
usage_count: int
last_used_time: Optional[float]
class EmojiListResponse(BaseModel):
"""表情包列表响应"""
success: bool
total: int
page: int
page_size: int
data: List[EmojiResponse]
class EmojiDetailResponse(BaseModel):
"""表情包详情响应"""
success: bool
data: EmojiResponse
class EmojiUpdateRequest(BaseModel):
"""表情包更新请求"""
description: Optional[str] = None
is_registered: Optional[bool] = None
is_banned: Optional[bool] = None
emotion: Optional[str] = None
class EmojiUpdateResponse(BaseModel):
"""表情包更新响应"""
success: bool
message: str
data: Optional[EmojiResponse] = None
class EmojiDeleteResponse(BaseModel):
"""表情包删除响应"""
success: bool
message: str
class BatchDeleteRequest(BaseModel):
"""批量删除请求"""
emoji_ids: List[int]
class BatchDeleteResponse(BaseModel):
"""批量删除响应"""
success: bool
message: str
deleted_count: int
failed_count: int
failed_ids: List[int] = []
class EmojiUploadResponse(BaseModel):
"""表情包上传响应"""
success: bool
message: str
data: Optional[EmojiResponse] = None
class ThumbnailCacheStatsResponse(BaseModel):
"""缩略图缓存统计响应"""
success: bool
cache_dir: str
total_count: int
total_size_mb: float
emoji_count: int
coverage_percent: float
class ThumbnailCleanupResponse(BaseModel):
"""缩略图清理响应"""
success: bool
message: str
cleaned_count: int
kept_count: int
class ThumbnailPreheatResponse(BaseModel):
"""缩略图预热响应"""
success: bool
message: str
generated_count: int
skipped_count: int
failed_count: int

135
src/webui/schemas/plugin.py Normal file
View File

@@ -0,0 +1,135 @@
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any
class FetchRawFileRequest(BaseModel):
"""获取 Raw 文件请求"""
owner: str = Field(..., description="仓库所有者", example="MaiM-with-u")
repo: str = Field(..., description="仓库名称", example="plugin-repo")
branch: str = Field(..., description="分支名称", example="main")
file_path: str = Field(..., description="文件路径", example="plugin_details.json")
mirror_id: Optional[str] = Field(None, description="指定镜像源 ID")
custom_url: Optional[str] = Field(None, description="自定义完整 URL")
class FetchRawFileResponse(BaseModel):
"""获取 Raw 文件响应"""
success: bool = Field(..., description="是否成功")
data: Optional[str] = Field(None, description="文件内容")
error: Optional[str] = Field(None, description="错误信息")
mirror_used: Optional[str] = Field(None, description="使用的镜像源")
attempts: int = Field(..., description="尝试次数")
url: Optional[str] = Field(None, description="实际请求的 URL")
class CloneRepositoryRequest(BaseModel):
"""克隆仓库请求"""
owner: str = Field(..., description="仓库所有者", example="MaiM-with-u")
repo: str = Field(..., description="仓库名称", example="plugin-repo")
target_path: str = Field(..., description="目标路径(相对于插件目录)")
branch: Optional[str] = Field(None, description="分支名称", example="main")
mirror_id: Optional[str] = Field(None, description="指定镜像源 ID")
custom_url: Optional[str] = Field(None, description="自定义克隆 URL")
depth: Optional[int] = Field(None, description="克隆深度(浅克隆)", ge=1)
class CloneRepositoryResponse(BaseModel):
"""克隆仓库响应"""
success: bool = Field(..., description="是否成功")
path: Optional[str] = Field(None, description="克隆路径")
error: Optional[str] = Field(None, description="错误信息")
mirror_used: Optional[str] = Field(None, description="使用的镜像源")
attempts: int = Field(..., description="尝试次数")
url: Optional[str] = Field(None, description="实际克隆的 URL")
message: Optional[str] = Field(None, description="附加信息")
class MirrorConfigResponse(BaseModel):
"""镜像源配置响应"""
id: str = Field(..., description="镜像源 ID")
name: str = Field(..., description="镜像源名称")
raw_prefix: str = Field(..., description="Raw 文件前缀")
clone_prefix: str = Field(..., description="克隆前缀")
enabled: bool = Field(..., description="是否启用")
priority: int = Field(..., description="优先级(数字越小优先级越高)")
class AvailableMirrorsResponse(BaseModel):
"""可用镜像源列表响应"""
mirrors: List[MirrorConfigResponse] = Field(..., description="镜像源列表")
default_priority: List[str] = Field(..., description="默认优先级顺序ID 列表)")
class AddMirrorRequest(BaseModel):
"""添加镜像源请求"""
id: str = Field(..., description="镜像源 ID", example="custom-mirror")
name: str = Field(..., description="镜像源名称", example="自定义镜像源")
raw_prefix: str = Field(..., description="Raw 文件前缀", example="https://example.com/raw")
clone_prefix: str = Field(..., description="克隆前缀", example="https://example.com/clone")
enabled: bool = Field(True, description="是否启用")
priority: Optional[int] = Field(None, description="优先级")
class UpdateMirrorRequest(BaseModel):
"""更新镜像源请求"""
name: Optional[str] = Field(None, description="镜像源名称")
raw_prefix: Optional[str] = Field(None, description="Raw 文件前缀")
clone_prefix: Optional[str] = Field(None, description="克隆前缀")
enabled: Optional[bool] = Field(None, description="是否启用")
priority: Optional[int] = Field(None, description="优先级")
class GitStatusResponse(BaseModel):
"""Git 安装状态响应"""
installed: bool = Field(..., description="是否已安装 Git")
version: Optional[str] = Field(None, description="Git 版本号")
path: Optional[str] = Field(None, description="Git 可执行文件路径")
error: Optional[str] = Field(None, description="错误信息")
class InstallPluginRequest(BaseModel):
"""安装插件请求"""
plugin_id: str = Field(..., description="插件 ID")
repository_url: str = Field(..., description="插件仓库 URL")
branch: Optional[str] = Field("main", description="分支名称")
mirror_id: Optional[str] = Field(None, description="指定镜像源 ID")
class VersionResponse(BaseModel):
"""麦麦版本响应"""
version: str = Field(..., description="麦麦版本号")
version_major: int = Field(..., description="主版本号")
version_minor: int = Field(..., description="次版本号")
version_patch: int = Field(..., description="补丁版本号")
class UninstallPluginRequest(BaseModel):
"""卸载插件请求"""
plugin_id: str = Field(..., description="插件 ID")
class UpdatePluginRequest(BaseModel):
"""更新插件请求"""
plugin_id: str = Field(..., description="插件 ID")
repository_url: str = Field(..., description="插件仓库 URL")
branch: Optional[str] = Field("main", description="分支名称")
mirror_id: Optional[str] = Field(None, description="指定镜像源 ID")
class UpdatePluginConfigRequest(BaseModel):
"""更新插件配置请求"""
config: Dict[str, Any] = Field(..., description="配置数据")

View File

@@ -0,0 +1,45 @@
from pydantic import BaseModel, Field
from typing import Dict, Any, List
class StatisticsSummary(BaseModel):
"""统计数据摘要"""
total_requests: int = Field(0, description="总请求数")
total_cost: float = Field(0.0, description="总花费")
total_tokens: int = Field(0, description="总token数")
online_time: float = Field(0.0, description="在线时间(秒)")
total_messages: int = Field(0, description="总消息数")
total_replies: int = Field(0, description="总回复数")
avg_response_time: float = Field(0.0, description="平均响应时间")
cost_per_hour: float = Field(0.0, description="每小时花费")
tokens_per_hour: float = Field(0.0, description="每小时token数")
class ModelStatistics(BaseModel):
"""模型统计"""
model_name: str
request_count: int
total_cost: float
total_tokens: int
avg_response_time: float
class TimeSeriesData(BaseModel):
"""时间序列数据"""
timestamp: str
requests: int = 0
cost: float = 0.0
tokens: int = 0
class DashboardData(BaseModel):
"""仪表盘数据"""
summary: StatisticsSummary
model_stats: List[ModelStatistics]
hourly_data: List[TimeSeriesData]
daily_data: List[TimeSeriesData]
recent_activity: List[Dict[str, Any]]