"""ORM 模型。 存储要点: - users.password_hash: bcrypt 哈希,不存明文。 - api_keys.key_hash: API Key 明文只给用户看一次,库内存 SHA-256 哈希, 索引建在 key_hash 上以便 O(1) 校验。 """ import datetime as dt import hashlib from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean from sqlalchemy.orm import relationship from .database import Base def utcnow(): return dt.datetime.utcnow() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) username = Column(String(64), unique=True, index=True, nullable=False) password_hash = Column(String(255), nullable=False) created_at = Column(DateTime, default=utcnow) api_keys = relationship( "ApiKey", back_populates="user", cascade="all, delete-orphan" ) class ApiKey(Base): __tablename__ = "api_keys" id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True) # 存 key 的 SHA-256 哈希,唯一索引支持快速校验 key_hash = Column(String(64), unique=True, index=True, nullable=False) # key 的前缀,用于在列表里展示,方便用户辨认(无安全价值) key_prefix = Column(String(16), nullable=False) name = Column(String(128), default="default") created_at = Column(DateTime, default=utcnow) last_used_at = Column(DateTime, nullable=True) revoked = Column(Boolean, default=False, nullable=False) user = relationship("User", back_populates="api_keys") @staticmethod def hash_key(raw: str) -> str: """对 API Key 明文做 SHA-256。""" return hashlib.sha256(raw.encode("utf-8")).hexdigest()