from __future__ import annotations from fastapi import APIRouter, HTTPException from yuxi.channels.adapters.nostr.models import NostrProfile from yuxi.utils.logging_config import logger profile_router = APIRouter(prefix="/nostr/profile", tags=["nostr-profile"]) class NostrProfileAPI: """Nostr Profile HTTP API — 管理 Kind 0 元数据""" def __init__(self, adapter): self._adapter = adapter @property def _crypto(self): return self._adapter._crypto if self._adapter else None @property def _relay_manager(self): return self._adapter._relay_manager if self._adapter else None async def get_profile(self, account_id: str = "default") -> dict: if not self._crypto: raise HTTPException(status_code=503, detail="Adapter 未初始化") pubkey = self._crypto.pubkey_hex() publish_state_raw = ( self._adapter._state_store.load_profile("last_publish_state") if self._adapter._state_store else None ) profile = await self._adapter.import_profile(pubkey) if profile: return { "status": "ok", "profile": profile, "publishState": publish_state_raw or { "lastPublishedAt": None, "lastPublishedEventId": None, "lastPublishResults": None, }, } from yuxi.channels.adapters.nostr.profile import NostrProfileManager if self._adapter._state_store: manager = NostrProfileManager(self._crypto, self._relay_manager, self._adapter._state_store) local = manager.load_local_profile(account_id) if local: return { "status": "ok", "profile": local.model_dump(), "source": "local", "publishState": manager.publish_state or { "lastPublishedAt": None, "lastPublishedEventId": None, "lastPublishResults": None, }, } return { "status": "ok", "profile": None, "message": "Profile 未设置", "publishState": { "lastPublishedAt": None, "lastPublishedEventId": None, "lastPublishResults": None, }, } async def publish_profile(self, profile_data: dict, account_id: str = "default") -> dict: if not self._crypto: raise HTTPException(status_code=503, detail="Adapter 未初始化") try: result = await self._adapter.publish_profile(profile_data, account_id) if result: publish_state = { "lastPublishedAt": result.get("created_at"), "lastPublishedEventId": result.get("id"), "lastPublishResults": {"success": True}, } return {"status": "ok", "message": "Profile 已发布", "publishState": publish_state} return {"status": "error", "message": "发布失败:所有 Relay 不可达"} except Exception as e: logger.error(f"[NostrProfile] 发布失败: {e}") raise HTTPException(status_code=500, detail=str(e)) async def import_profile(self, pubkey: str) -> dict: if not self._crypto: raise HTTPException(status_code=503, detail="Adapter 未初始化") try: from yuxi.channels.adapters.nostr.profile import NostrProfileManager manager = NostrProfileManager(self._crypto, self._relay_manager) profile = await manager.import_profile_from_relays(pubkey) if profile: return {"status": "ok", "profile": profile.model_dump()} return {"status": "not_found", "message": f"未找到 pubkey {pubkey[:12]}... 的 Profile"} except Exception as e: logger.error(f"[NostrProfile] 导入失败: {e}") raise HTTPException(status_code=500, detail=str(e)) async def merge_profile(self, local_data: dict, imported_pubkey: str) -> dict: if not self._crypto: raise HTTPException(status_code=503, detail="Adapter 未初始化") try: from yuxi.channels.adapters.nostr.profile import NostrProfileManager manager = NostrProfileManager(self._crypto, self._relay_manager) imported = await manager.import_profile_from_relays(imported_pubkey) if not imported: return { "status": "not_found", "message": f"未找到 pubkey {imported_pubkey[:12]}... 的 Profile", } local = NostrProfile(**local_data) merged = manager.merge_profiles(local, imported) return {"status": "ok", "profile": merged.model_dump()} except Exception as e: logger.error(f"[NostrProfile] 合并失败: {e}") raise HTTPException(status_code=500, detail=str(e))