from __future__ import annotations import json from typing import List, Optional from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, Table, func 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) ) 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") # readonly / write 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()) # 关系 instances = relationship("Instance", back_populates="group", lazy="selectin") 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"}