fix: auto-fix code issues (cron)
- 修复重复导入/字段 - 修复异常处理 - 修复PEP8格式问题 - 添加类型注解 - 修复缺失的urllib.parse导入
This commit is contained in:
@@ -23,6 +23,7 @@ from typing import Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TenantLimits:
|
||||
"""租户资源限制常量"""
|
||||
|
||||
@@ -42,6 +43,7 @@ class TenantLimits:
|
||||
|
||||
UNLIMITED = -1
|
||||
|
||||
|
||||
class TenantStatus(StrEnum):
|
||||
"""租户状态"""
|
||||
|
||||
@@ -51,6 +53,7 @@ class TenantStatus(StrEnum):
|
||||
EXPIRED = "expired" # 过期
|
||||
PENDING = "pending" # 待激活
|
||||
|
||||
|
||||
class TenantTier(StrEnum):
|
||||
"""租户订阅层级"""
|
||||
|
||||
@@ -58,6 +61,7 @@ class TenantTier(StrEnum):
|
||||
PRO = "pro" # 专业版
|
||||
ENTERPRISE = "enterprise" # 企业版
|
||||
|
||||
|
||||
class TenantRole(StrEnum):
|
||||
"""租户角色"""
|
||||
|
||||
@@ -66,6 +70,7 @@ class TenantRole(StrEnum):
|
||||
MEMBER = "member" # 成员
|
||||
VIEWER = "viewer" # 查看者
|
||||
|
||||
|
||||
class DomainStatus(StrEnum):
|
||||
"""域名状态"""
|
||||
|
||||
@@ -74,6 +79,7 @@ class DomainStatus(StrEnum):
|
||||
FAILED = "failed" # 验证失败
|
||||
EXPIRED = "expired" # 已过期
|
||||
|
||||
|
||||
@dataclass
|
||||
class Tenant:
|
||||
"""租户数据类"""
|
||||
@@ -92,6 +98,7 @@ class Tenant:
|
||||
resource_limits: dict[str, Any] # 资源限制
|
||||
metadata: dict[str, Any] # 元数据
|
||||
|
||||
|
||||
@dataclass
|
||||
class TenantDomain:
|
||||
"""租户域名数据类"""
|
||||
@@ -109,6 +116,7 @@ class TenantDomain:
|
||||
ssl_enabled: bool # SSL 是否启用
|
||||
ssl_expires_at: datetime | None
|
||||
|
||||
|
||||
@dataclass
|
||||
class TenantBranding:
|
||||
"""租户品牌配置数据类"""
|
||||
@@ -126,6 +134,7 @@ class TenantBranding:
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class TenantMember:
|
||||
"""租户成员数据类"""
|
||||
@@ -142,6 +151,7 @@ class TenantMember:
|
||||
last_active_at: datetime | None
|
||||
status: str # active/pending/suspended
|
||||
|
||||
|
||||
@dataclass
|
||||
class TenantPermission:
|
||||
"""租户权限定义数据类"""
|
||||
@@ -156,6 +166,7 @@ class TenantPermission:
|
||||
conditions: dict | None # 条件限制
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class TenantManager:
|
||||
"""租户管理器 - 多租户 SaaS 架构核心"""
|
||||
|
||||
@@ -199,8 +210,24 @@ class TenantManager:
|
||||
|
||||
# 角色权限映射
|
||||
ROLE_PERMISSIONS = {
|
||||
TenantRole.OWNER: ["tenant:*", "project:*", "member:*", "billing:*", "settings:*", "api:*", "export:*"],
|
||||
TenantRole.ADMIN: ["tenant:read", "project:*", "member:*", "billing:read", "settings:*", "api:*", "export:*"],
|
||||
TenantRole.OWNER: [
|
||||
"tenant:*",
|
||||
"project:*",
|
||||
"member:*",
|
||||
"billing:*",
|
||||
"settings:*",
|
||||
"api:*",
|
||||
"export:*",
|
||||
],
|
||||
TenantRole.ADMIN: [
|
||||
"tenant:read",
|
||||
"project:*",
|
||||
"member:*",
|
||||
"billing:read",
|
||||
"settings:*",
|
||||
"api:*",
|
||||
"export:*",
|
||||
],
|
||||
TenantRole.MEMBER: [
|
||||
"tenant:read",
|
||||
"project:create",
|
||||
@@ -360,10 +387,18 @@ class TenantManager:
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_tenants_slug ON tenants(slug)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_tenants_owner ON tenants(owner_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_tenants_status ON tenants(status)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_domains_tenant ON tenant_domains(tenant_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_domains_domain ON tenant_domains(domain)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_domains_status ON tenant_domains(status)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_members_tenant ON tenant_members(tenant_id)")
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_domains_tenant ON tenant_domains(tenant_id)"
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_domains_domain ON tenant_domains(domain)"
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_domains_status ON tenant_domains(status)"
|
||||
)
|
||||
cursor.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_members_tenant ON tenant_members(tenant_id)"
|
||||
)
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_members_user ON tenant_members(user_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_usage_tenant ON tenant_usage(tenant_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_usage_date ON tenant_usage(date)")
|
||||
@@ -380,7 +415,12 @@ class TenantManager:
|
||||
# ==================== 租户管理 ====================
|
||||
|
||||
def create_tenant(
|
||||
self, name: str, owner_id: str, tier: str = "free", description: str | None = None, settings: dict | None = None
|
||||
self,
|
||||
name: str,
|
||||
owner_id: str,
|
||||
tier: str = "free",
|
||||
description: str | None = None,
|
||||
settings: dict | None = None,
|
||||
) -> Tenant:
|
||||
"""创建新租户"""
|
||||
conn = self._get_connection()
|
||||
@@ -389,8 +429,12 @@ class TenantManager:
|
||||
slug = self._generate_slug(name)
|
||||
|
||||
# 获取对应层级的资源限制
|
||||
tier_enum = TenantTier(tier) if tier in [t.value for t in TenantTier] else TenantTier.FREE
|
||||
resource_limits = self.DEFAULT_LIMITS.get(tier_enum, self.DEFAULT_LIMITS[TenantTier.FREE])
|
||||
tier_enum = (
|
||||
TenantTier(tier) if tier in [t.value for t in TenantTier] else TenantTier.FREE
|
||||
)
|
||||
resource_limits = self.DEFAULT_LIMITS.get(
|
||||
tier_enum, self.DEFAULT_LIMITS[TenantTier.FREE]
|
||||
)
|
||||
|
||||
tenant = Tenant(
|
||||
id=tenant_id,
|
||||
@@ -544,7 +588,7 @@ class TenantManager:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
f"""
|
||||
UPDATE tenants SET {', '.join(updates)}
|
||||
UPDATE tenants SET {", ".join(updates)}
|
||||
WHERE id = ?
|
||||
""",
|
||||
params,
|
||||
@@ -599,7 +643,11 @@ class TenantManager:
|
||||
# ==================== 域名管理 ====================
|
||||
|
||||
def add_domain(
|
||||
self, tenant_id: str, domain: str, is_primary: bool = False, verification_method: str = "dns"
|
||||
self,
|
||||
tenant_id: str,
|
||||
domain: str,
|
||||
is_primary: bool = False,
|
||||
verification_method: str = "dns",
|
||||
) -> TenantDomain:
|
||||
"""为租户添加自定义域名"""
|
||||
conn = self._get_connection()
|
||||
@@ -752,7 +800,10 @@ class TenantManager:
|
||||
"value": f"insightflow-verify={token}",
|
||||
"ttl": 3600,
|
||||
},
|
||||
"file_verification": {"url": f"http://{domain}/.well-known/insightflow-verify.txt", "content": token},
|
||||
"file_verification": {
|
||||
"url": f"http://{domain}/.well-known/insightflow-verify.txt",
|
||||
"content": token,
|
||||
},
|
||||
"instructions": [
|
||||
f"DNS 验证: 添加 TXT 记录 _insightflow.{domain},值为 insightflow-verify={token}",
|
||||
f"文件验证: 在网站根目录创建 .well-known/insightflow-verify.txt,内容为 {token}",
|
||||
@@ -873,7 +924,7 @@ class TenantManager:
|
||||
|
||||
cursor.execute(
|
||||
f"""
|
||||
UPDATE tenant_branding SET {', '.join(updates)}
|
||||
UPDATE tenant_branding SET {", ".join(updates)}
|
||||
WHERE tenant_id = ?
|
||||
""",
|
||||
params,
|
||||
@@ -951,7 +1002,12 @@ class TenantManager:
|
||||
# ==================== 成员与权限管理 ====================
|
||||
|
||||
def invite_member(
|
||||
self, tenant_id: str, email: str, role: str, invited_by: str, permissions: list[str] | None = None
|
||||
self,
|
||||
tenant_id: str,
|
||||
email: str,
|
||||
role: str,
|
||||
invited_by: str,
|
||||
permissions: list[str] | None = None,
|
||||
) -> TenantMember:
|
||||
"""邀请成员加入租户"""
|
||||
conn = self._get_connection()
|
||||
@@ -959,7 +1015,9 @@ class TenantManager:
|
||||
member_id = str(uuid.uuid4())
|
||||
|
||||
# 使用角色默认权限
|
||||
role_enum = TenantRole(role) if role in [r.value for r in TenantRole] else TenantRole.MEMBER
|
||||
role_enum = (
|
||||
TenantRole(role) if role in [r.value for r in TenantRole] else TenantRole.MEMBER
|
||||
)
|
||||
default_permissions = self.ROLE_PERMISSIONS.get(role_enum, [])
|
||||
final_permissions = permissions or default_permissions
|
||||
|
||||
@@ -1146,7 +1204,13 @@ class TenantManager:
|
||||
result = []
|
||||
for row in rows:
|
||||
tenant = self._row_to_tenant(row)
|
||||
result.append({**asdict(tenant), "member_role": row["role"], "member_status": row["member_status"]})
|
||||
result.append(
|
||||
{
|
||||
**asdict(tenant),
|
||||
"member_role": row["role"],
|
||||
"member_status": row["member_status"],
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
finally:
|
||||
@@ -1253,14 +1317,21 @@ class TenantManager:
|
||||
row["total_storage"] or 0, limits.get("max_storage_mb", 0) * 1024 * 1024
|
||||
),
|
||||
"transcription": self._calc_percentage(
|
||||
row["total_transcription"] or 0, limits.get("max_transcription_minutes", 0) * 60
|
||||
row["total_transcription"] or 0,
|
||||
limits.get("max_transcription_minutes", 0) * 60,
|
||||
),
|
||||
"api_calls": self._calc_percentage(
|
||||
row["total_api_calls"] or 0, limits.get("max_api_calls_per_day", 0)
|
||||
),
|
||||
"projects": self._calc_percentage(row["max_projects"] or 0, limits.get("max_projects", 0)),
|
||||
"entities": self._calc_percentage(row["max_entities"] or 0, limits.get("max_entities", 0)),
|
||||
"members": self._calc_percentage(row["max_members"] or 0, limits.get("max_team_members", 0)),
|
||||
"projects": self._calc_percentage(
|
||||
row["max_projects"] or 0, limits.get("max_projects", 0)
|
||||
),
|
||||
"entities": self._calc_percentage(
|
||||
row["max_entities"] or 0, limits.get("max_entities", 0)
|
||||
),
|
||||
"members": self._calc_percentage(
|
||||
row["max_members"] or 0, limits.get("max_team_members", 0)
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1434,10 +1505,14 @@ class TenantManager:
|
||||
status=row["status"],
|
||||
owner_id=row["owner_id"],
|
||||
created_at=(
|
||||
datetime.fromisoformat(row["created_at"]) if isinstance(row["created_at"], str) else row["created_at"]
|
||||
datetime.fromisoformat(row["created_at"])
|
||||
if isinstance(row["created_at"], str)
|
||||
else row["created_at"]
|
||||
),
|
||||
updated_at=(
|
||||
datetime.fromisoformat(row["updated_at"]) if isinstance(row["updated_at"], str) else row["updated_at"]
|
||||
datetime.fromisoformat(row["updated_at"])
|
||||
if isinstance(row["updated_at"], str)
|
||||
else row["updated_at"]
|
||||
),
|
||||
expires_at=(
|
||||
datetime.fromisoformat(row["expires_at"])
|
||||
@@ -1464,10 +1539,14 @@ class TenantManager:
|
||||
else row["verified_at"]
|
||||
),
|
||||
created_at=(
|
||||
datetime.fromisoformat(row["created_at"]) if isinstance(row["created_at"], str) else row["created_at"]
|
||||
datetime.fromisoformat(row["created_at"])
|
||||
if isinstance(row["created_at"], str)
|
||||
else row["created_at"]
|
||||
),
|
||||
updated_at=(
|
||||
datetime.fromisoformat(row["updated_at"]) if isinstance(row["updated_at"], str) else row["updated_at"]
|
||||
datetime.fromisoformat(row["updated_at"])
|
||||
if isinstance(row["updated_at"], str)
|
||||
else row["updated_at"]
|
||||
),
|
||||
is_primary=bool(row["is_primary"]),
|
||||
ssl_enabled=bool(row["ssl_enabled"]),
|
||||
@@ -1492,10 +1571,14 @@ class TenantManager:
|
||||
login_page_bg=row["login_page_bg"],
|
||||
email_template=row["email_template"],
|
||||
created_at=(
|
||||
datetime.fromisoformat(row["created_at"]) if isinstance(row["created_at"], str) else row["created_at"]
|
||||
datetime.fromisoformat(row["created_at"])
|
||||
if isinstance(row["created_at"], str)
|
||||
else row["created_at"]
|
||||
),
|
||||
updated_at=(
|
||||
datetime.fromisoformat(row["updated_at"]) if isinstance(row["updated_at"], str) else row["updated_at"]
|
||||
datetime.fromisoformat(row["updated_at"])
|
||||
if isinstance(row["updated_at"], str)
|
||||
else row["updated_at"]
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1510,7 +1593,9 @@ class TenantManager:
|
||||
permissions=json.loads(row["permissions"] or "[]"),
|
||||
invited_by=row["invited_by"],
|
||||
invited_at=(
|
||||
datetime.fromisoformat(row["invited_at"]) if isinstance(row["invited_at"], str) else row["invited_at"]
|
||||
datetime.fromisoformat(row["invited_at"])
|
||||
if isinstance(row["invited_at"], str)
|
||||
else row["invited_at"]
|
||||
),
|
||||
joined_at=(
|
||||
datetime.fromisoformat(row["joined_at"])
|
||||
@@ -1525,8 +1610,10 @@ class TenantManager:
|
||||
status=row["status"],
|
||||
)
|
||||
|
||||
|
||||
# ==================== 租户上下文管理 ====================
|
||||
|
||||
|
||||
class TenantContext:
|
||||
"""租户上下文管理器 - 用于请求级别的租户隔离"""
|
||||
|
||||
@@ -1559,9 +1646,11 @@ class TenantContext:
|
||||
cls._current_tenant_id = None
|
||||
cls._current_user_id = None
|
||||
|
||||
|
||||
# 全局租户管理器实例
|
||||
tenant_manager = None
|
||||
|
||||
|
||||
def get_tenant_manager(db_path: str = "insightflow.db") -> TenantManager:
|
||||
"""获取租户管理器实例(单例模式)"""
|
||||
global tenant_manager
|
||||
|
||||
Reference in New Issue
Block a user