feat: Implement initial Homelab Automation API v2 with new models, routes, and core architecture, including a SQLAlchemy model refactoring script.
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
This commit is contained in:
+6
-6
@@ -20,16 +20,16 @@ class Alert(Base):
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("users.id", ondelete="SET NULL"))
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
||||
|
||||
category: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
level: Mapped[Optional[str]] = mapped_column(String(20))
|
||||
title: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
level: Mapped[str] = mapped_column(String(20), nullable=True)
|
||||
title: Mapped[str] = mapped_column(String(255), nullable=True)
|
||||
message: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
source: Mapped[Optional[str]] = mapped_column(String(50))
|
||||
details: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
source: Mapped[str] = mapped_column(String(50), nullable=True)
|
||||
details: Mapped[dict] = mapped_column(JSON, nullable=True)
|
||||
|
||||
read_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
read_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
||||
@@ -17,7 +17,7 @@ class AppSetting(Base):
|
||||
)
|
||||
|
||||
key: Mapped[str] = mapped_column(String(100), primary_key=True)
|
||||
value: Mapped[Optional[str]] = mapped_column(Text)
|
||||
value: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())
|
||||
|
||||
@@ -16,9 +16,9 @@ class BootstrapStatus(Base):
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
host_id: Mapped[str] = mapped_column(String, ForeignKey("hosts.id", ondelete="CASCADE"), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String, nullable=False)
|
||||
automation_user: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
||||
last_attempt: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
automation_user: Mapped[str] = mapped_column(String, nullable=True)
|
||||
last_attempt: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
error_message: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
|
||||
host: Mapped["Host"] = relationship("Host", back_populates="bootstrap_statuses")
|
||||
|
||||
@@ -14,14 +14,14 @@ class ContainerCustomization(Base):
|
||||
__tablename__ = "container_customizations"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True)
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True)
|
||||
|
||||
host_id: Mapped[str] = mapped_column(String, ForeignKey("hosts.id", ondelete="CASCADE"), nullable=False)
|
||||
container_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
|
||||
icon_key: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
icon_color: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
|
||||
bg_color: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
|
||||
icon_key: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
icon_color: Mapped[str] = mapped_column(String(20), nullable=True)
|
||||
bg_color: Mapped[str] = mapped_column(String(20), nullable=True)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())
|
||||
|
||||
@@ -20,12 +20,12 @@ class DockerAlert(Base):
|
||||
container_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
severity: Mapped[str] = mapped_column(String(20), nullable=False, default="warning") # warning/error/critical
|
||||
state: Mapped[str] = mapped_column(String(20), nullable=False, default="open") # open/closed/acknowledged
|
||||
message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
message: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
opened_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
closed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
acknowledged_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
acknowledged_by: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
last_notified_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
closed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
acknowledged_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
acknowledged_by: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
last_notified_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Relationship to host
|
||||
host: Mapped["Host"] = relationship("Host", back_populates="docker_alerts")
|
||||
|
||||
@@ -19,14 +19,14 @@ class DockerContainer(Base):
|
||||
host_id: Mapped[str] = mapped_column(String, ForeignKey("hosts.id", ondelete="CASCADE"), nullable=False)
|
||||
container_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
image: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||
image: Mapped[str] = mapped_column(String(255), nullable=True)
|
||||
state: Mapped[str] = mapped_column(String(20), nullable=False, default="unknown") # running/exited/paused/created/dead
|
||||
status: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) # Up 2 hours, Exited (0) 5 minutes ago
|
||||
health: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) # healthy/unhealthy/starting/none
|
||||
created_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
ports: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
|
||||
labels: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
|
||||
compose_project: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) # com.docker.compose.project
|
||||
status: Mapped[str] = mapped_column(String(255), nullable=True) # Up 2 hours, Exited (0) 5 minutes ago
|
||||
health: Mapped[str] = mapped_column(String(20), nullable=True) # healthy/unhealthy/starting/none
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
ports: Mapped[dict] = mapped_column(JSON, nullable=True)
|
||||
labels: Mapped[dict] = mapped_column(JSON, nullable=True)
|
||||
compose_project: Mapped[str] = mapped_column(String(255), nullable=True) # com.docker.compose.project
|
||||
last_update_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
@@ -18,9 +18,9 @@ class DockerImage(Base):
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
host_id: Mapped[str] = mapped_column(String, ForeignKey("hosts.id", ondelete="CASCADE"), nullable=False)
|
||||
image_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
repo_tags: Mapped[Optional[list]] = mapped_column(JSON, nullable=True) # ["nginx:latest", "nginx:1.25"]
|
||||
size: Mapped[Optional[int]] = mapped_column(BigInteger, nullable=True)
|
||||
created: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
repo_tags: Mapped[list] = mapped_column(JSON, nullable=True) # ["nginx:latest", "nginx:1.25"]
|
||||
size: Mapped[int] = mapped_column(BigInteger, nullable=True)
|
||||
created: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
last_update_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
@@ -18,9 +18,9 @@ class DockerVolume(Base):
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
host_id: Mapped[str] = mapped_column(String, ForeignKey("hosts.id", ondelete="CASCADE"), nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
driver: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
|
||||
mountpoint: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
scope: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) # local/global
|
||||
driver: Mapped[str] = mapped_column(String(50), nullable=True)
|
||||
mountpoint: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
scope: Mapped[str] = mapped_column(String(20), nullable=True) # local/global
|
||||
last_update_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
@@ -14,13 +14,13 @@ class FavoriteContainer(Base):
|
||||
__tablename__ = "favorite_containers"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True)
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True)
|
||||
|
||||
docker_container_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("docker_containers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
|
||||
group_id: Mapped[Optional[int]] = mapped_column(
|
||||
group_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("favorite_groups.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ class FavoriteGroup(Base):
|
||||
__tablename__ = "favorite_groups"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True)
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True)
|
||||
name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
sort_order: Mapped[int] = mapped_column(Integer, nullable=False, server_default=text("0"))
|
||||
color: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
|
||||
icon_key: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
color: Mapped[str] = mapped_column(String(20), nullable=True)
|
||||
icon_key: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())
|
||||
|
||||
|
||||
+6
-6
@@ -17,18 +17,18 @@ class Host(Base):
|
||||
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
ip_address: Mapped[str] = mapped_column(String, nullable=False, unique=True)
|
||||
status: Mapped[str] = mapped_column(String, nullable=False, server_default=text("'unknown'"))
|
||||
ansible_group: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
||||
last_seen: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
ansible_group: Mapped[str] = mapped_column(String, nullable=True)
|
||||
last_seen: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
reachable: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("0"))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())
|
||||
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
deleted_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Docker-related fields
|
||||
docker_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("0"))
|
||||
docker_version: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
|
||||
docker_status: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) # online/offline/error
|
||||
docker_last_collect_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
docker_version: Mapped[str] = mapped_column(String(50), nullable=True)
|
||||
docker_status: Mapped[str] = mapped_column(String(20), nullable=True) # online/offline/error
|
||||
docker_last_collect_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
bootstrap_statuses: Mapped[List["BootstrapStatus"]] = relationship(
|
||||
"BootstrapStatus", back_populates="host", cascade="all, delete-orphan"
|
||||
|
||||
+40
-40
@@ -24,60 +24,60 @@ class HostMetrics(Base):
|
||||
metric_type: Mapped[str] = mapped_column(String(50), nullable=False) # 'system_info', 'disk_usage', 'memory', etc.
|
||||
|
||||
# Métriques CPU
|
||||
cpu_count: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
cpu_model: Mapped[Optional[str]] = mapped_column(String(200))
|
||||
cpu_cores: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
cpu_threads: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
cpu_threads_per_core: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
cpu_sockets: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
cpu_mhz: Mapped[Optional[float]] = mapped_column(Float)
|
||||
cpu_max_mhz: Mapped[Optional[float]] = mapped_column(Float)
|
||||
cpu_min_mhz: Mapped[Optional[float]] = mapped_column(Float)
|
||||
cpu_load_1m: Mapped[Optional[float]] = mapped_column(Float)
|
||||
cpu_load_5m: Mapped[Optional[float]] = mapped_column(Float)
|
||||
cpu_load_15m: Mapped[Optional[float]] = mapped_column(Float)
|
||||
cpu_usage_percent: Mapped[Optional[float]] = mapped_column(Float)
|
||||
cpu_temperature: Mapped[Optional[float]] = mapped_column(Float)
|
||||
cpu_count: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
cpu_model: Mapped[str] = mapped_column(String(200), nullable=True)
|
||||
cpu_cores: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
cpu_threads: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
cpu_threads_per_core: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
cpu_sockets: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
cpu_mhz: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
cpu_max_mhz: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
cpu_min_mhz: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
cpu_load_1m: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
cpu_load_5m: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
cpu_load_15m: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
cpu_usage_percent: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
cpu_temperature: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
|
||||
# Métriques mémoire
|
||||
memory_total_mb: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
memory_used_mb: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
memory_free_mb: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
memory_usage_percent: Mapped[Optional[float]] = mapped_column(Float)
|
||||
swap_total_mb: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
swap_used_mb: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
swap_usage_percent: Mapped[Optional[float]] = mapped_column(Float)
|
||||
memory_total_mb: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
memory_used_mb: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
memory_free_mb: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
memory_usage_percent: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
swap_total_mb: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
swap_used_mb: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
swap_usage_percent: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
|
||||
# Métriques disque (stockées en JSON pour flexibilité - plusieurs disques)
|
||||
disk_info: Mapped[Optional[object]] = mapped_column(JSON) # Liste des points de montage avec usage
|
||||
disk_devices: Mapped[Optional[object]] = mapped_column(JSON) # Liste des disques + partitions (layout)
|
||||
disk_root_total_gb: Mapped[Optional[float]] = mapped_column(Float)
|
||||
disk_root_used_gb: Mapped[Optional[float]] = mapped_column(Float)
|
||||
disk_root_usage_percent: Mapped[Optional[float]] = mapped_column(Float)
|
||||
disk_info: Mapped[object] = mapped_column(JSON, nullable=True) # Liste des points de montage avec usage
|
||||
disk_devices: Mapped[object] = mapped_column(JSON) # Liste des disques + partitions (layout, nullable=True)
|
||||
disk_root_total_gb: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
disk_root_used_gb: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
disk_root_usage_percent: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
|
||||
# Storage stacks (JSON)
|
||||
lvm_info: Mapped[Optional[object]] = mapped_column(JSON)
|
||||
zfs_info: Mapped[Optional[object]] = mapped_column(JSON)
|
||||
lvm_info: Mapped[object] = mapped_column(JSON, nullable=True)
|
||||
zfs_info: Mapped[object] = mapped_column(JSON, nullable=True)
|
||||
|
||||
# Stockage détaillé (JSON) - données normalisées avec métadonnées de collecte
|
||||
storage_details: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
storage_details: Mapped[dict] = mapped_column(JSON, nullable=True)
|
||||
|
||||
# Informations système
|
||||
os_name: Mapped[Optional[str]] = mapped_column(String(100))
|
||||
os_version: Mapped[Optional[str]] = mapped_column(String(100))
|
||||
kernel_version: Mapped[Optional[str]] = mapped_column(String(100))
|
||||
hostname: Mapped[Optional[str]] = mapped_column(String(200))
|
||||
uptime_seconds: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
uptime_human: Mapped[Optional[str]] = mapped_column(String(100))
|
||||
os_name: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
os_version: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
kernel_version: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
hostname: Mapped[str] = mapped_column(String(200), nullable=True)
|
||||
uptime_seconds: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
uptime_human: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
|
||||
# Réseau (stocké en JSON pour flexibilité)
|
||||
network_info: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
network_info: Mapped[dict] = mapped_column(JSON, nullable=True)
|
||||
|
||||
# Données brutes et métadonnées
|
||||
raw_data: Mapped[Optional[dict]] = mapped_column(JSON) # Données brutes du playbook
|
||||
collection_source: Mapped[Optional[str]] = mapped_column(String(100)) # Nom du builtin playbook
|
||||
collection_duration_ms: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text)
|
||||
raw_data: Mapped[dict] = mapped_column(JSON, nullable=True) # Données brutes du playbook
|
||||
collection_source: Mapped[str] = mapped_column(String(100), nullable=True) # Nom du builtin playbook
|
||||
collection_duration_ms: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
error_message: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
|
||||
collected_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
|
||||
+5
-5
@@ -20,12 +20,12 @@ class Log(Base):
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
level: Mapped[str] = mapped_column(String, nullable=False)
|
||||
source: Mapped[Optional[str]] = mapped_column(String)
|
||||
source: Mapped[str] = mapped_column(String, nullable=True)
|
||||
message: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
details: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
host_id: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
||||
task_id: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
||||
schedule_id: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
||||
details: Mapped[dict] = mapped_column(JSON, nullable=True)
|
||||
host_id: Mapped[str] = mapped_column(String, nullable=True)
|
||||
task_id: Mapped[str] = mapped_column(String, nullable=True)
|
||||
schedule_id: Mapped[str] = mapped_column(String, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from __future__ import annotations
|
||||
"""
|
||||
Model for storing ansible-lint results per playbook.
|
||||
"""
|
||||
@@ -33,10 +34,10 @@ class PlaybookLintResult(Base):
|
||||
execution_time_ms: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||
|
||||
# Full issues list as JSON
|
||||
issues_json: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
issues_json: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
|
||||
# Raw output from ansible-lint (for copy to clipboard)
|
||||
raw_output: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
raw_output: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
|
||||
# Timestamps
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
|
||||
+22
-22
@@ -15,35 +15,35 @@ class Schedule(Base):
|
||||
|
||||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||||
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
description: Mapped[Optional[str]] = mapped_column(Text)
|
||||
description: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
playbook: Mapped[str] = mapped_column(String, nullable=False)
|
||||
target_type: Mapped[Optional[str]] = mapped_column(String, default="group")
|
||||
target_type: Mapped[str] = mapped_column(String, default="group", nullable=True)
|
||||
target: Mapped[str] = mapped_column(String, nullable=False)
|
||||
extra_vars: Mapped[Optional[Dict[str, Any]]] = mapped_column(JSON)
|
||||
extra_vars: Mapped[Dict[str, Any]] = mapped_column(JSON, nullable=True)
|
||||
schedule_type: Mapped[str] = mapped_column(String, nullable=False)
|
||||
schedule_time: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
recurrence_type: Mapped[Optional[str]] = mapped_column(String)
|
||||
recurrence_time: Mapped[Optional[str]] = mapped_column(String)
|
||||
recurrence_days: Mapped[Optional[str]] = mapped_column(Text)
|
||||
cron_expression: Mapped[Optional[str]] = mapped_column(String)
|
||||
timezone: Mapped[Optional[str]] = mapped_column(String, default="America/Montreal")
|
||||
start_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
end_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
schedule_time: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
recurrence_type: Mapped[str] = mapped_column(String, nullable=True)
|
||||
recurrence_time: Mapped[str] = mapped_column(String, nullable=True)
|
||||
recurrence_days: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
cron_expression: Mapped[str] = mapped_column(String, nullable=True)
|
||||
timezone: Mapped[str] = mapped_column(String, default="America/Montreal", nullable=True)
|
||||
start_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
end_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
||||
tags: Mapped[Optional[str]] = mapped_column(Text)
|
||||
next_run: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
last_run: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
last_status: Mapped[Optional[str]] = mapped_column(String, default="never")
|
||||
retry_on_failure: Mapped[Optional[int]] = mapped_column(Integer, default=0)
|
||||
timeout: Mapped[Optional[int]] = mapped_column(Integer, default=3600)
|
||||
tags: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
next_run: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
last_run: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
last_status: Mapped[str] = mapped_column(String, default="never", nullable=True)
|
||||
retry_on_failure: Mapped[int] = mapped_column(Integer, default=0, nullable=True)
|
||||
timeout: Mapped[int] = mapped_column(Integer, default=3600, nullable=True)
|
||||
# Type de notification: "none" (aucune), "all" (toujours), "errors" (erreurs seulement)
|
||||
notification_type: Mapped[Optional[str]] = mapped_column(String, default="all")
|
||||
run_count: Mapped[Optional[int]] = mapped_column(Integer, default=0)
|
||||
success_count: Mapped[Optional[int]] = mapped_column(Integer, default=0)
|
||||
failure_count: Mapped[Optional[int]] = mapped_column(Integer, default=0)
|
||||
notification_type: Mapped[str] = mapped_column(String, default="all", nullable=True)
|
||||
run_count: Mapped[int] = mapped_column(Integer, default=0, nullable=True)
|
||||
success_count: Mapped[int] = mapped_column(Integer, default=0, nullable=True)
|
||||
failure_count: Mapped[int] = mapped_column(Integer, default=0, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())
|
||||
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
deleted_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
runs: Mapped[List["ScheduleRun"]] = relationship(
|
||||
"ScheduleRun", back_populates="schedule", cascade="all, delete-orphan"
|
||||
|
||||
@@ -15,18 +15,18 @@ class ScheduleRun(Base):
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
schedule_id: Mapped[str] = mapped_column(String, ForeignKey("schedules.id", ondelete="CASCADE"), nullable=False)
|
||||
task_id: Mapped[Optional[str]] = mapped_column(String, ForeignKey("tasks.id", ondelete="SET NULL"))
|
||||
task_id: Mapped[str] = mapped_column(String, ForeignKey("tasks.id", ondelete="SET NULL"), nullable=True)
|
||||
status: Mapped[str] = mapped_column(String, nullable=False)
|
||||
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
duration: Mapped[Optional[float]] = mapped_column(Float)
|
||||
hosts_impacted: Mapped[Optional[int]] = mapped_column(Integer, default=0)
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text)
|
||||
output: Mapped[Optional[str]] = mapped_column(Text)
|
||||
completed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
duration: Mapped[float] = mapped_column(Float, nullable=True)
|
||||
hosts_impacted: Mapped[int] = mapped_column(Integer, default=0, nullable=True)
|
||||
error_message: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
output: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
|
||||
schedule: Mapped["Schedule"] = relationship("Schedule", back_populates="runs")
|
||||
task: Mapped[Optional["Task"]] = relationship("Task", back_populates="schedule_runs")
|
||||
task: Mapped["Task"] = relationship("Task", back_populates="schedule_runs")
|
||||
|
||||
def __repr__(self) -> str: # pragma: no cover - debug helper
|
||||
return f"<ScheduleRun id={self.id} schedule_id={self.schedule_id} status={self.status}>"
|
||||
|
||||
+5
-5
@@ -17,11 +17,11 @@ class Task(Base):
|
||||
action: Mapped[str] = mapped_column(String, nullable=False)
|
||||
target: Mapped[str] = mapped_column(String, nullable=False)
|
||||
status: Mapped[str] = mapped_column(String, nullable=False, server_default=text("'pending'"))
|
||||
playbook: Mapped[Optional[str]] = mapped_column(String)
|
||||
started_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text)
|
||||
result_data: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
playbook: Mapped[str] = mapped_column(String, nullable=True)
|
||||
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
completed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
error_message: Mapped[str] = mapped_column(Text, nullable=True)
|
||||
result_data: Mapped[dict] = mapped_column(JSON, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
|
||||
schedule_runs: Mapped[list["ScheduleRun"]] = relationship("ScheduleRun", back_populates="task")
|
||||
|
||||
@@ -38,12 +38,12 @@ class TerminalCommandLog(Base):
|
||||
host_id: Mapped[str] = mapped_column(
|
||||
String, ForeignKey("hosts.id", ondelete="CASCADE"), nullable=False, index=True
|
||||
)
|
||||
user_id: Mapped[Optional[str]] = mapped_column(
|
||||
user_id: Mapped[str] = mapped_column(
|
||||
String, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
|
||||
)
|
||||
|
||||
# Session reference (not FK as sessions may be cleaned up)
|
||||
terminal_session_id: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, index=True)
|
||||
terminal_session_id: Mapped[str] = mapped_column(String(64), nullable=True, index=True)
|
||||
|
||||
# Command data (masked/normalized version only)
|
||||
command: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
@@ -58,11 +58,11 @@ class TerminalCommandLog(Base):
|
||||
|
||||
# If command was blocked (for audit - no raw command stored)
|
||||
is_blocked: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("0"))
|
||||
blocked_reason: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||
blocked_reason: Mapped[str] = mapped_column(String(255), nullable=True)
|
||||
|
||||
# Additional metadata
|
||||
username: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
host_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
username: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
host_name: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
|
||||
# Relationships
|
||||
host = relationship("Host", back_populates="terminal_commands", lazy="selectin")
|
||||
|
||||
@@ -57,15 +57,15 @@ class TerminalSession(Base):
|
||||
host_id: Mapped[str] = mapped_column(String, nullable=False, index=True)
|
||||
host_name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
host_ip: Mapped[str] = mapped_column(String, nullable=False)
|
||||
user_id: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
||||
username: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
||||
user_id: Mapped[str] = mapped_column(String, nullable=True)
|
||||
username: Mapped[str] = mapped_column(String, nullable=True)
|
||||
|
||||
# Token hash for session authentication (never store plain token)
|
||||
token_hash: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
|
||||
# ttyd process management
|
||||
ttyd_port: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
ttyd_pid: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
|
||||
ttyd_pid: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
|
||||
# Session mode: 'embedded' or 'popout'
|
||||
mode: Mapped[str] = mapped_column(String(20), nullable=False, server_default=text("'embedded'"))
|
||||
@@ -74,7 +74,7 @@ class TerminalSession(Base):
|
||||
status: Mapped[str] = mapped_column(String(20), nullable=False, server_default=text("'active'"))
|
||||
|
||||
# Close reason: 'user_close', 'ttl', 'idle', 'quota', 'server_shutdown', 'client_lost', 'reused'
|
||||
reason_closed: Mapped[Optional[str]] = mapped_column(String(30), nullable=True)
|
||||
reason_closed: Mapped[str] = mapped_column(String(30), nullable=True)
|
||||
|
||||
# Timestamps
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
@@ -84,7 +84,7 @@ class TerminalSession(Base):
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
closed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
closed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<TerminalSession id={self.id[:8]}... host={self.host_name} status={self.status}>"
|
||||
|
||||
+5
-5
@@ -39,7 +39,7 @@ class User(Base):
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
username: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, index=True)
|
||||
email: Mapped[Optional[str]] = mapped_column(String(255), unique=True, nullable=True)
|
||||
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=True)
|
||||
hashed_password: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
|
||||
# Role-based access control (prepared for future)
|
||||
@@ -54,7 +54,7 @@ class User(Base):
|
||||
is_superuser: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("0"))
|
||||
|
||||
# Display name (optional)
|
||||
display_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
display_name: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
|
||||
# Timestamps
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
@@ -68,11 +68,11 @@ class User(Base):
|
||||
server_default=func.now(),
|
||||
onupdate=func.now()
|
||||
)
|
||||
last_login: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
password_changed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
last_login: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
password_changed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Soft delete support
|
||||
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
deleted_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Relationships
|
||||
terminal_commands: Mapped[List["TerminalCommandLog"]] = relationship(
|
||||
|
||||
Reference in New Issue
Block a user