from __future__ import annotations import json from typing import List, Optional from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, Table, func, UniqueConstraint from sqlalchemy.orm import relationship from app.core.database import Base # 用户-权限组关联表 user_groups = Table( "user_groups", Base.metadata, Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True), Column("group_id", Integer, ForeignKey("instance_groups.id", ondelete="CASCADE"), primary_key=True), Column("permissions", Text, nullable=True), # JSON: 覆盖组默认权限 (legacy, kept for compat) ) # 实例-权限组关联表(一个权限组可同时包含 readonly 和 write 实例) instance_group_members = Table( "instance_group_members", Base.metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("group_id", Integer, ForeignKey("instance_groups.id", ondelete="CASCADE"), nullable=False), Column("instance_id", Integer, ForeignKey("instances.id", ondelete="CASCADE"), nullable=False), Column("access_type", String(20), nullable=False, default="readonly"), # readonly / write UniqueConstraint("group_id", "instance_id", "access_type", name="uq_group_instance_access"), ) class InstanceGroup(Base): __tablename__ = "instance_groups" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(100), nullable=False, unique=True) description = Column(Text, nullable=True) db_type = Column(String(20), nullable=False, default="all") # mysql/redis/pg/mongo/all permissions = Column(Text, nullable=True) # JSON: ["query:select", ...] (legacy, kept for compat) access_type = Column(String(20), nullable=True, default="readonly") # legacy, kept for compat allowed_databases = Column(Text, nullable=True) # JSON array of database names; empty = all databases is_active = Column(Boolean, default=True) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) # 关系 — users 保持不变 users = relationship("User", secondary=user_groups, back_populates="groups", lazy="selectin") # ---- Legacy helpers (kept for backward compatibility) ---- def get_permissions(self) -> list: if self.permissions: try: return json.loads(self.permissions) except (json.JSONDecodeError, TypeError): return [] return [] def set_permissions(self, perms: list): self.permissions = json.dumps(perms, ensure_ascii=False) if perms else None # ---- New access_type / allowed_databases helpers ---- def get_allowed_databases(self) -> list[str]: """返回允许的数据库名列表,空列表表示关联实例的所有库。""" if self.allowed_databases: try: return json.loads(self.allowed_databases) except (json.JSONDecodeError, TypeError): return [] return [] def set_allowed_databases(self, databases: list[str] | None): self.allowed_databases = ( json.dumps(databases, ensure_ascii=False) if databases else None ) VALID_ACCESS_TYPES = {"readonly", "write"}