feat: 添加视觉设计语言系统并重构认证页面UI
- 新增 visual_design_language.md 设计规范文档 - 新增 auth 设计 tokens (authBackground, authCard, authInput, feedback 系列等) - 重构登录/注册/验证码/重置密码页面为新设计系统 - 新增 AuthHeroHeader, AuthSurfaceCard, AuthSection, AuthField, PasswordField 组件 - 重构 AppBanner 和 Toast 支持多类型配置 (info/success/warning/error) - 后端 AgentScope: 重整 schemas/prompts/tools 作用域, 新增协议文档 - 更新 AGENTS.md 集成视觉设计语言约束
This commit is contained in:
@@ -64,22 +64,14 @@ class AgentAttachmentStorage:
|
||||
if storage is None:
|
||||
raise RuntimeError("Supabase storage client unavailable")
|
||||
get_bucket = getattr(storage, "get_bucket", None)
|
||||
if callable(get_bucket):
|
||||
try:
|
||||
get_bucket(bucket)
|
||||
return
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
|
||||
create_bucket = getattr(storage, "create_bucket", None)
|
||||
if not callable(create_bucket):
|
||||
raise RuntimeError("Supabase storage create_bucket is unavailable")
|
||||
if not callable(get_bucket):
|
||||
raise RuntimeError("Supabase storage get_bucket is unavailable")
|
||||
try:
|
||||
create_bucket(bucket, options={"public": False})
|
||||
get_bucket(bucket)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
message = str(exc).lower()
|
||||
if "already exists" in message or "duplicate" in message:
|
||||
return
|
||||
msg = str(exc).lower()
|
||||
if "bucket" in msg and "not found" in msg:
|
||||
raise RuntimeError(f"Storage bucket '{bucket}' does not exist")
|
||||
raise
|
||||
|
||||
await asyncio.to_thread(_ensure)
|
||||
|
||||
@@ -1,39 +1,38 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
import asyncio
|
||||
from datetime import date
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import time
|
||||
from collections.abc import AsyncIterator
|
||||
from datetime import date
|
||||
from typing import Annotated, Union
|
||||
|
||||
from ag_ui.core import RunAgentInput
|
||||
from core.agentscope.events import to_sse_event
|
||||
from core.auth.jwt_verifier import JwtVerifier, TokenValidationError
|
||||
from core.auth.models import CurrentUser
|
||||
from core.config.settings import config
|
||||
from core.logging import get_logger
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
File,
|
||||
Form,
|
||||
Header,
|
||||
HTTPException,
|
||||
Query,
|
||||
Request,
|
||||
status,
|
||||
UploadFile,
|
||||
status,
|
||||
)
|
||||
from fastapi import HTTPException
|
||||
from fastapi.responses import JSONResponse, StreamingResponse
|
||||
|
||||
from core.agentscope.events import to_sse_event
|
||||
from core.agentscope.schemas.agui_input import (
|
||||
from schemas.agent.agui_input import (
|
||||
extract_latest_tool_result,
|
||||
parse_run_input,
|
||||
validate_run_request_messages_contract,
|
||||
)
|
||||
from core.auth.jwt_verifier import JwtVerifier, TokenValidationError
|
||||
from core.auth.models import CurrentUser
|
||||
from core.config.settings import config
|
||||
from core.logging import get_logger
|
||||
from services.base.redis import get_or_init_redis_client
|
||||
from v1.agent.dependencies import get_agent_service
|
||||
from v1.agent.schemas import (
|
||||
|
||||
@@ -11,6 +11,7 @@ from core.db.base_repository import BaseRepository
|
||||
from core.logging import get_logger
|
||||
from models.friendships import Friendship, FriendshipStatus
|
||||
from models.inbox_messages import InboxMessage, InboxMessageStatus, InboxMessageType
|
||||
from schemas.inbox.messages import FriendshipContent
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -80,7 +81,7 @@ class SQLAlchemyFriendshipRepository(BaseRepository[Friendship]):
|
||||
super().__init__(session, Friendship)
|
||||
|
||||
async def create_request(
|
||||
self, initiator_id: UUID, recipient_id: UUID, content: str | None = None
|
||||
self, initiator_id: UUID, recipient_id: UUID, message: str | None = None
|
||||
) -> tuple[Friendship, InboxMessage]:
|
||||
try:
|
||||
user_low_id = min(initiator_id, recipient_id)
|
||||
@@ -99,12 +100,13 @@ class SQLAlchemyFriendshipRepository(BaseRepository[Friendship]):
|
||||
self._session.add(friendship)
|
||||
await self._session.flush()
|
||||
|
||||
inbox_content = FriendshipContent(type="request", message=message)
|
||||
inbox = InboxMessage(
|
||||
recipient_id=recipient_id,
|
||||
sender_id=initiator_id,
|
||||
message_type=InboxMessageType.FRIEND_REQUEST,
|
||||
friendship_id=friendship.id,
|
||||
content=content,
|
||||
content=inbox_content.model_dump(),
|
||||
status=InboxMessageStatus.PENDING,
|
||||
created_by=initiator_id,
|
||||
)
|
||||
@@ -124,7 +126,7 @@ class SQLAlchemyFriendshipRepository(BaseRepository[Friendship]):
|
||||
self,
|
||||
friendship: Friendship,
|
||||
initiator_id: UUID,
|
||||
content: str | None = None,
|
||||
message: str | None = None,
|
||||
) -> tuple[Friendship, InboxMessage]:
|
||||
try:
|
||||
now = datetime.now(timezone.utc)
|
||||
@@ -133,6 +135,7 @@ class SQLAlchemyFriendshipRepository(BaseRepository[Friendship]):
|
||||
friendship.initiator_id = initiator_id
|
||||
friendship.updated_by = initiator_id
|
||||
|
||||
inbox_content = FriendshipContent(type="request", message=message)
|
||||
inbox = InboxMessage(
|
||||
recipient_id=(
|
||||
friendship.user_low_id
|
||||
@@ -142,7 +145,7 @@ class SQLAlchemyFriendshipRepository(BaseRepository[Friendship]):
|
||||
sender_id=initiator_id,
|
||||
message_type=InboxMessageType.FRIEND_REQUEST,
|
||||
friendship_id=friendship.id,
|
||||
content=content,
|
||||
content=inbox_content.model_dump(),
|
||||
status=InboxMessageStatus.PENDING,
|
||||
created_by=initiator_id,
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ class FriendRequestResponse(BaseModel):
|
||||
id: UUID
|
||||
sender: UserContext
|
||||
recipient: UserContext
|
||||
content: str | None
|
||||
content: dict | None
|
||||
status: Literal["pending", "accepted", "rejected", "canceled"]
|
||||
created_at: datetime
|
||||
|
||||
|
||||
@@ -6,17 +6,19 @@ from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, EmailStr, Field
|
||||
|
||||
from schemas.schedule.items import (
|
||||
AttachmentType,
|
||||
from schemas.inbox.messages import (
|
||||
CalendarContent,
|
||||
CalendarDeleteContent,
|
||||
CalendarInviteContent,
|
||||
CalendarUpdateContent,
|
||||
parse_calendar_content,
|
||||
)
|
||||
from schemas.schedule.items import (
|
||||
AttachmentType,
|
||||
ScheduleItemMetadata,
|
||||
ScheduleItemMetadataAttachment,
|
||||
ScheduleItemSourceType,
|
||||
ScheduleItemStatus,
|
||||
parse_calendar_content,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import TYPE_CHECKING, Protocol, Literal
|
||||
from uuid import UUID
|
||||
|
||||
@@ -345,26 +344,22 @@ class ScheduleItemService(BaseService):
|
||||
)
|
||||
elif existing_msg.status == InboxMessageStatus.REJECTED:
|
||||
existing_msg.status = InboxMessageStatus.PENDING
|
||||
existing_msg.content = json.dumps(
|
||||
{
|
||||
"type": "invite",
|
||||
"permission": request_permission,
|
||||
"action": "pending",
|
||||
}
|
||||
)
|
||||
existing_msg.content = {
|
||||
"type": "invite",
|
||||
"permission": request_permission,
|
||||
"action": "pending",
|
||||
}
|
||||
else:
|
||||
message = InboxMessage(
|
||||
recipient_id=recipient_id,
|
||||
sender_id=user_id,
|
||||
message_type=InboxMessageType.CALENDAR,
|
||||
schedule_item_id=item.id,
|
||||
content=json.dumps(
|
||||
{
|
||||
"type": "invite",
|
||||
"permission": request_permission,
|
||||
"action": "pending",
|
||||
}
|
||||
),
|
||||
content={
|
||||
"type": "invite",
|
||||
"permission": request_permission,
|
||||
"action": "pending",
|
||||
},
|
||||
created_by=user_id,
|
||||
)
|
||||
self._session.add(message)
|
||||
@@ -432,7 +427,7 @@ class ScheduleItemService(BaseService):
|
||||
status_code=404, detail="No pending invitation found"
|
||||
)
|
||||
|
||||
content = json.loads(inbox.content or "{}")
|
||||
content = inbox.content or {}
|
||||
permission = content.get("permission", 1)
|
||||
|
||||
existing = await self._repository.get_subscription(item_id, user_id)
|
||||
@@ -505,13 +500,11 @@ class ScheduleItemService(BaseService):
|
||||
if sub.subscriber_id == user_id:
|
||||
continue
|
||||
|
||||
content = json.dumps(
|
||||
{
|
||||
"type": action_type,
|
||||
"title": title,
|
||||
"action": action_type,
|
||||
}
|
||||
)
|
||||
content = {
|
||||
"type": action_type,
|
||||
"title": title,
|
||||
"action": action_type,
|
||||
}
|
||||
|
||||
message = InboxMessage(
|
||||
recipient_id=sub.subscriber_id,
|
||||
|
||||
@@ -11,26 +11,19 @@ from pydantic import (
|
||||
model_validator,
|
||||
)
|
||||
|
||||
from schemas.user.context import UserContext
|
||||
|
||||
class UserResponse(BaseModel):
|
||||
id: str
|
||||
username: str
|
||||
email: str | None = None
|
||||
avatar_url: str | None = None
|
||||
bio: str | None = None
|
||||
|
||||
class UserResponse(UserContext):
|
||||
"""当前用户,含 email,无 settings"""
|
||||
|
||||
settings: None = Field(default=None, exclude=True) # type: ignore[assignment]
|
||||
|
||||
|
||||
class UserSearchRequest(BaseModel):
|
||||
query: str = Field(min_length=1, max_length=100)
|
||||
|
||||
|
||||
class UserSearchResult(BaseModel):
|
||||
id: str
|
||||
username: str
|
||||
avatar_url: str | None = None
|
||||
bio: str | None = None
|
||||
|
||||
|
||||
class UserUpdateRequest(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user