- Add company module (3-tier org, CEO planning, parallel departments) - Add company orchestrator, knowledge extractor, presets, scheduler - Add company API endpoints, models, and frontend views - Add 今天吃啥 PWA app (69 dishes, real images, offline support) - Add team_projects output directory structure - Add unified manage.ps1 for service lifecycle - Add Windows startup guide v1.0 - Add TTS troubleshooting doc - Update frontend (AgentChat UX overhaul, new views) - Update backend (voice engine fix, multi-tenant, RBAC) - Remove deprecated startup scripts and old docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
63 lines
2.3 KiB
Python
63 lines
2.3 KiB
Python
"""
|
||
API Key 模型 — 用于第三方应用/脚本通过 API 调用平台服务
|
||
"""
|
||
import hashlib
|
||
import logging
|
||
from datetime import datetime
|
||
from typing import Optional
|
||
|
||
from sqlalchemy import Column, String, DateTime, JSON, func, ForeignKey
|
||
from sqlalchemy.dialects.mysql import CHAR
|
||
from sqlalchemy.orm import relationship, Session
|
||
from app.core.database import Base
|
||
import uuid
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
KEY_PREFIX = "sk-tg-"
|
||
|
||
|
||
class ApiKey(Base):
|
||
"""API Key 表"""
|
||
__tablename__ = "api_keys"
|
||
|
||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="密钥ID")
|
||
user_id = Column(CHAR(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, comment="所属用户ID")
|
||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False, comment="所属工作区ID")
|
||
name = Column(String(100), nullable=False, comment="密钥名称(用于标识)")
|
||
key_prefix = Column(String(12), nullable=False, comment="密钥前缀(如 sk-tg-)")
|
||
key_hash = Column(String(255), nullable=False, unique=True, comment="SHA256 哈希存储")
|
||
permissions = Column(JSON, nullable=True, comment="权限范围(null=全部权限)")
|
||
last_used_at = Column(DateTime, nullable=True, comment="最后使用时间")
|
||
expires_at = Column(DateTime, nullable=True, comment="过期时间(null=永不过期)")
|
||
status = Column(String(20), default="active", comment="状态: active/revoked")
|
||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||
|
||
# 关系
|
||
user = relationship("User", backref="api_keys")
|
||
|
||
def __repr__(self):
|
||
return f"<ApiKey(id={self.id}, name={self.name}, status={self.status})>"
|
||
|
||
|
||
def verify_api_key(api_key_str: str, db: Session) -> Optional["ApiKey"]:
|
||
"""验证 API Key 字符串,返回 ApiKey 对象或 None。"""
|
||
if not api_key_str or not api_key_str.startswith(KEY_PREFIX):
|
||
return None
|
||
|
||
key_hash = hashlib.sha256(api_key_str.encode()).hexdigest()
|
||
|
||
api_key = db.query(ApiKey).filter(
|
||
ApiKey.key_hash == key_hash,
|
||
ApiKey.status == "active",
|
||
).first()
|
||
|
||
if not api_key:
|
||
return None
|
||
|
||
if api_key.expires_at and api_key.expires_at < datetime.utcnow():
|
||
logger.warning("API Key %s 已过期", api_key.id)
|
||
return None
|
||
|
||
return api_key
|