Files
NexaPG/backend/app/models/models.py
nessi c437e72c2b
All checks were successful
PostgreSQL Compatibility Matrix / PG14 smoke (push) Successful in 7s
PostgreSQL Compatibility Matrix / PG15 smoke (push) Successful in 8s
PostgreSQL Compatibility Matrix / PG16 smoke (push) Successful in 7s
PostgreSQL Compatibility Matrix / PG17 smoke (push) Successful in 7s
PostgreSQL Compatibility Matrix / PG18 smoke (push) Successful in 7s
Add customizable email templates and remove alert recipients
Replaced the fixed `alert_recipients` list with customizable subject and body templates for alerts and warnings. This allows for more flexible and dynamic email notifications using placeholder variables. Updated relevant backend and frontend components to support this feature.
2026-02-12 16:32:53 +01:00

160 lines
8.7 KiB
Python

from datetime import datetime
from sqlalchemy import JSON, Boolean, DateTime, Float, ForeignKey, Integer, String, Text, UniqueConstraint, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.core.db import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
role: Mapped[str] = mapped_column(String(20), nullable=False, default="viewer")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
audit_logs: Mapped[list["AuditLog"]] = relationship(back_populates="user")
owned_targets: Mapped[list["TargetOwner"]] = relationship(
back_populates="user",
cascade="all, delete-orphan",
foreign_keys="TargetOwner.user_id",
)
class Target(Base):
__tablename__ = "targets"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(120), unique=True, index=True, nullable=False)
host: Mapped[str] = mapped_column(String(255), nullable=False)
port: Mapped[int] = mapped_column(Integer, nullable=False, default=5432)
dbname: Mapped[str] = mapped_column(String(120), nullable=False)
username: Mapped[str] = mapped_column(String(120), nullable=False)
encrypted_password: Mapped[str] = mapped_column(Text, nullable=False)
sslmode: Mapped[str] = mapped_column(String(20), nullable=False, default="prefer")
use_pg_stat_statements: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
tags: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
metrics: Mapped[list["Metric"]] = relationship(back_populates="target", cascade="all, delete-orphan")
query_stats: Mapped[list["QueryStat"]] = relationship(back_populates="target", cascade="all, delete-orphan")
alert_definitions: Mapped[list["AlertDefinition"]] = relationship(back_populates="target", cascade="all, delete-orphan")
owners: Mapped[list["TargetOwner"]] = relationship(back_populates="target", cascade="all, delete-orphan")
class TargetOwner(Base):
__tablename__ = "target_owners"
__table_args__ = (UniqueConstraint("target_id", "user_id", name="uq_target_owner_target_user"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
target_id: Mapped[int] = mapped_column(ForeignKey("targets.id", ondelete="CASCADE"), nullable=False, index=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
assigned_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
target: Mapped[Target] = relationship(back_populates="owners")
user: Mapped[User] = relationship(foreign_keys=[user_id], back_populates="owned_targets")
class Metric(Base):
__tablename__ = "metrics"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
target_id: Mapped[int] = mapped_column(ForeignKey("targets.id", ondelete="CASCADE"), nullable=False, index=True)
ts: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), index=True)
metric_name: Mapped[str] = mapped_column(String(120), nullable=False, index=True)
value: Mapped[float] = mapped_column(Float, nullable=False)
labels: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
target: Mapped[Target] = relationship(back_populates="metrics")
class QueryStat(Base):
__tablename__ = "query_stats"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
target_id: Mapped[int] = mapped_column(ForeignKey("targets.id", ondelete="CASCADE"), nullable=False, index=True)
ts: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), index=True)
queryid: Mapped[str] = mapped_column(String(100), nullable=False)
calls: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
total_time: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
mean_time: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
rows: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
query_text: Mapped[str | None] = mapped_column(Text, nullable=True)
target: Mapped[Target] = relationship(back_populates="query_stats")
class AuditLog(Base):
__tablename__ = "audit_logs"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
ts: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), index=True)
user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
action: Mapped[str] = mapped_column(String(120), nullable=False)
payload: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
user: Mapped[User | None] = relationship(back_populates="audit_logs")
class AlertDefinition(Base):
__tablename__ = "alert_definitions"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(160), nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
target_id: Mapped[int | None] = mapped_column(ForeignKey("targets.id", ondelete="CASCADE"), nullable=True, index=True)
sql_text: Mapped[str] = mapped_column(Text, nullable=False)
comparison: Mapped[str] = mapped_column(String(10), nullable=False, default="gte")
warning_threshold: Mapped[float | None] = mapped_column(Float, nullable=True)
alert_threshold: Mapped[float] = mapped_column(Float, nullable=False)
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), index=True)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
nullable=False,
server_default=func.now(),
onupdate=func.now(),
)
target: Mapped[Target | None] = relationship(back_populates="alert_definitions")
class EmailNotificationSettings(Base):
__tablename__ = "email_notification_settings"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
smtp_host: Mapped[str | None] = mapped_column(String(255), nullable=True)
smtp_port: Mapped[int] = mapped_column(Integer, nullable=False, default=587)
smtp_username: Mapped[str | None] = mapped_column(String(255), nullable=True)
encrypted_smtp_password: Mapped[str | None] = mapped_column(Text, nullable=True)
from_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
from_email: Mapped[str | None] = mapped_column(String(255), nullable=True)
use_starttls: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
use_ssl: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
warning_subject_template: Mapped[str | None] = mapped_column(Text, nullable=True)
alert_subject_template: Mapped[str | None] = mapped_column(Text, nullable=True)
warning_body_template: Mapped[str | None] = mapped_column(Text, nullable=True)
alert_body_template: Mapped[str | None] = 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(),
)
class AlertNotificationEvent(Base):
__tablename__ = "alert_notification_events"
__table_args__ = (UniqueConstraint("alert_key", "target_id", "severity", name="uq_alert_notif_event_key_target_sev"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
alert_key: Mapped[str] = mapped_column(String(200), nullable=False, index=True)
target_id: Mapped[int] = mapped_column(ForeignKey("targets.id", ondelete="CASCADE"), nullable=False, index=True)
severity: Mapped[str] = mapped_column(String(16), nullable=False)
last_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
last_sent_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())