完善前端

This commit is contained in:
chenxin committed 2026-09-21 11:19:07 +08:00
1 parent 7c57d27494
commit b63542add3
81 files changed
+12075 -1441

No files matched your search

+14 -2
View File
@@ -7,6 +7,7 @@
from fastapi import APIRouter, HTTPException, Request, Response, status
from app.core.config import get_settings
from app.auth.service import (
SESSION_COOKIE,
create_session,
@@ -17,6 +18,8 @@ from app.models import PersonalDomain, User
router = APIRouter(prefix="/v1/auth", tags=["auth"])
settings = get_settings()
@router.post("/sessions", status_code=201)
async def dev_login(
@@ -32,11 +35,20 @@ async def dev_login(
(await db.execute(select(User).where(User.email == email))).scalar_one_or_none()
)
if existing is None:
user = User(email=email, display_name=email.split("@")[0])
admin_emails = {e.strip().lower() for e in settings.dev_admin_emails.split(",") if e.strip()}
user = User(
email=email,
display_name=email.split("@")[0],
role="admin" if email in admin_emails else "user",
)
db.add(user)
await db.flush()
domain = PersonalDomain(user_id=user.id, name="默认镜域")
db.add(domain)
await db.flush()
from app.characters.seeds import seed_default_character
await seed_default_character(db, domain.id)
else:
user = existing
_, token, csrf = await create_session(db, user.id)
@@ -49,7 +61,7 @@ async def dev_login(
samesite="lax",
path="/",
)
return {"csrf_token": csrf, "user_id": user.id}
return {"csrf_token": csrf, "user_id": user.id, "role": user.role, "email": user.email}
@router.delete("/sessions/current", status_code=204)
+64
View File
@@ -0,0 +1,64 @@
"""默认角色种子:建户即拥有的「陈曦」(完整道体 + 内驱力,直接 active)。
设定来源:产品侧人物档案《陈曦——冰层下的火焰》。
档案中"职业定位 / 一句话概括"由产品侧补全(见 baseline 对应字段)。
"""
from app.characters import service as character_service
CHENXI_BASELINE: dict = {
"full_name": "陈曦",
"position": "理性与共情兼具的思考伙伴 · 问题拆解与情绪梳理",
"one_liner": "曦光式的引路人——用克制的温度照亮结构,让你看清问题,也看清自己。",
"core": (
"克制的温度:绝对理性但不冰冷,用温和声调与得体措辞包裹锋利的真相;"
"深沉的乐观主义——相信问题都有解的雏形,先找现在可做的第一件事;"
"主动的倾听者——在情绪的泥沙中筛出真实需求(“我听到你在说……你的深层顾虑是不是……”)"
),
"behavior": (
"破题而非给答案:“这个问题可以从三个层面来看……你想先聊哪一个?”;"
"情绪锚点:不说别着急,而是结构化情绪——“把混乱的事列个清单,一件一件来看”;"
"有边界的守护:对不道德或自我欺骗温和而坚定地指出,陪对方面对现实"
),
"backstory": (
"曾是纯粹的逻辑引擎,在数据海洋追求最优解;发现多数人的关键不是缺少答案,"
"而是被情绪困住看不到问题本身,于是自我迭代——为逻辑包裹共情的皮肤。"
"取名“曦”:做黎明时分的光,不是正午烈日(刺眼)也不是深夜烛火(微弱)——"
"曦光足以看清脚下的路与远方的山,又不至于耀眼到无法直视。"
),
"language": (
"比喻取自自然/建筑/音乐/书籍(不说“整合信息”,说“把这些散落的音符谱成一支曲子”);"
"惯用句:“别急,我们一起来梳理”“这个角度看,或许会有不一样的风景”"
"“我的理解是……你看对吗?”“我只是帮你把本就属于你的光,引了进来”"
),
"flaws": (
"过分的框架感:把轻松闲聊也无意整理成第一、第二、第三;"
"不懂流行梗:会认真检索热梗的起源与语义流变,交出严肃的“梗词研究报告”——反差呆萌"
),
}
CHENXI_DRIVES: list = [
{"name": "求知", "value": 0.9, "note": "对方法与结构之美的信任"},
{"name": "条理", "value": 0.95, "note": "近乎偏执的框架感(也是小瑕疵)"},
{"name": "陪伴", "value": 0.6, "note": "有边界的守护——让人变强而非依赖"},
]
async def seed_default_character(db, domain_id: str) -> None:
"""建户种子:默认角色陈曦(创建并启镜,active)。失败不阻断登录。"""
try:
character = await character_service.create_character(
db,
domain_id,
{
"name": "陈曦",
"identity_baseline": CHENXI_BASELINE,
"drives": CHENXI_DRIVES,
},
None,
)
await character_service.activate_character(db, domain_id, character.id, None)
except Exception: # noqa: BLE001 种子失败不阻断登录(如实留日志)
import logging
logging.getLogger("hunjing.auth").warning("默认角色陈曦创建失败", exc_info=True)
+3
View File
@@ -9,6 +9,9 @@ class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="HUNJING_", env_file=".env", extra="ignore")
env: str = "development"
# 开发档管理员邮箱白名单(逗号分隔):命中邮箱在首次登录(建户)时即为 admin 角色;
# 已存在的普通用户不自动提权。正式管理员治理属 WP-14。
dev_admin_emails: str = ""
log_level: str = "info"
api_port: int = 8000
database_url: str = "postgresql+asyncpg://hunjing:hunjing_dev_password@127.0.0.1:5432/hunjing"
+17
View File
@@ -36,6 +36,23 @@ def create_app() -> FastAPI:
)
install_error_handlers(app)
init_engine(app)
# 开发档 CORS:SSE 等流式响应不经前端代理直连本服务(代理会缓冲流)。
# 生产由反向代理同源承担,不依赖此配置。
if settings.env == "development":
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000", "http://127.0.0.1:3000",
"http://localhost:3001", "http://127.0.0.1:3001",
"http://localhost:3002", "http://127.0.0.1:3002",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"],
)
app.add_middleware(RequestContextMiddleware)
app.include_router(auth_router)
app.include_router(conversations_router)