159 lines
6.8 KiB
Python
159 lines
6.8 KiB
Python
from __future__ import annotations
|
||
|
||
import enum
|
||
from datetime import datetime
|
||
from typing import Any
|
||
|
||
from sqlalchemy import JSON, Boolean, Column, DateTime, Enum, ForeignKey, Integer, String, Table, Text, func
|
||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
||
|
||
|
||
class Base(DeclarativeBase):
|
||
pass
|
||
|
||
|
||
user_roles = Table(
|
||
"user_roles",
|
||
Base.metadata,
|
||
Column("user_id", ForeignKey("users.id"), primary_key=True),
|
||
Column("role_id", ForeignKey("roles.id"), primary_key=True),
|
||
)
|
||
|
||
role_permissions = Table(
|
||
"role_permissions",
|
||
Base.metadata,
|
||
Column("role_id", ForeignKey("roles.id"), primary_key=True),
|
||
Column("permission_id", ForeignKey("permissions.id"), primary_key=True),
|
||
)
|
||
|
||
|
||
class Job(Base):
|
||
__tablename__ = "jobs"
|
||
|
||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||
cron_expr: Mapped[str] = mapped_column(String, nullable=False)
|
||
handler_path: Mapped[str] = mapped_column(String, nullable=False)
|
||
public_cfg: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False)
|
||
# 密文 token(Fernet 加密后的字符串)
|
||
secret_cfg: Mapped[str] = mapped_column(Text, default="", nullable=False)
|
||
|
||
enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||
last_run_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||
updated_at: Mapped[datetime] = mapped_column(
|
||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False
|
||
)
|
||
|
||
logs: Mapped[list["JobLog"]] = relationship(back_populates="job", cascade="all, delete-orphan")
|
||
|
||
|
||
class JobStatus(str, enum.Enum):
|
||
RUNNING = "RUNNING"
|
||
SUCCESS = "SUCCESS"
|
||
FAILURE = "FAILURE"
|
||
RETRY = "RETRY"
|
||
|
||
|
||
class JobLog(Base):
|
||
__tablename__ = "job_logs"
|
||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
job_id: Mapped[str] = mapped_column(ForeignKey("jobs.id"), index=True, nullable=False)
|
||
|
||
status: Mapped[JobStatus] = mapped_column(
|
||
Enum(JobStatus, native_enum=False, length=16),
|
||
nullable=False,
|
||
)
|
||
snapshot_params: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False)
|
||
|
||
message: Mapped[str] = mapped_column(Text, default="", nullable=False)
|
||
traceback: Mapped[str] = mapped_column(Text, default="", nullable=False)
|
||
# 本次执行期间捕获到的完整运行日志(可能很长,按上层捕获器做截断)
|
||
run_log: Mapped[str] = mapped_column(Text, default="", nullable=False)
|
||
celery_task_id: Mapped[str] = mapped_column(String, default="", nullable=False)
|
||
attempt: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||
|
||
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||
|
||
job: Mapped[Job] = relationship(back_populates="logs")
|
||
|
||
|
||
class User(Base):
|
||
__tablename__ = "users"
|
||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
username: Mapped[str] = mapped_column(String, unique=True, index=True, nullable=False)
|
||
password_hash: Mapped[str] = mapped_column(Text, default="", nullable=False)
|
||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||
is_superuser: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||
is_ldap: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||
last_login_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||
updated_at: Mapped[datetime] = mapped_column(
|
||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False
|
||
)
|
||
|
||
roles: Mapped[list["Role"]] = relationship(secondary=user_roles, back_populates="users")
|
||
|
||
|
||
class Role(Base):
|
||
__tablename__ = "roles"
|
||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
name: Mapped[str] = mapped_column(String, unique=True, index=True, nullable=False)
|
||
description: Mapped[str] = mapped_column(Text, default="", nullable=False)
|
||
|
||
users: Mapped[list[User]] = relationship(secondary=user_roles, back_populates="roles")
|
||
permissions: Mapped[list["Permission"]] = relationship(secondary=role_permissions, back_populates="roles")
|
||
|
||
|
||
class Permission(Base):
|
||
__tablename__ = "permissions"
|
||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
code: Mapped[str] = mapped_column(String, unique=True, index=True, nullable=False)
|
||
description: Mapped[str] = mapped_column(Text, default="", nullable=False)
|
||
|
||
roles: Mapped[list[Role]] = relationship(secondary=role_permissions, back_populates="permissions")
|
||
|
||
|
||
class LdapGroup(Base):
|
||
__tablename__ = "ldap_groups"
|
||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
dn: Mapped[str] = mapped_column(String, unique=True, index=True, nullable=False)
|
||
name: Mapped[str] = mapped_column(String, index=True, nullable=False)
|
||
|
||
|
||
class LdapGroupRole(Base):
|
||
__tablename__ = "ldap_group_roles"
|
||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
ldap_group_id: Mapped[int] = mapped_column(ForeignKey("ldap_groups.id"), nullable=False)
|
||
role_id: Mapped[int] = mapped_column(ForeignKey("roles.id"), nullable=False)
|
||
|
||
|
||
class AuditLog(Base):
|
||
__tablename__ = "audit_logs"
|
||
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
actor_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
|
||
action: Mapped[str] = mapped_column(String, index=True, nullable=False)
|
||
target: Mapped[str] = mapped_column(String, default="", nullable=False)
|
||
detail: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False)
|
||
ip: Mapped[str] = mapped_column(String, default="", nullable=False)
|
||
user_agent: Mapped[str] = mapped_column(Text, default="", nullable=False)
|
||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||
|
||
|
||
class Session(Base):
|
||
__tablename__ = "sessions"
|
||
|
||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=False, index=True)
|
||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||
ip: Mapped[str] = mapped_column(String, default="", nullable=False)
|
||
user_agent: Mapped[str] = mapped_column(Text, default="", nullable=False)
|