import uuid from datetime import datetime from sqlalchemy import Boolean, DateTime, Integer, String, Text, ForeignKey, Float from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base def _uuid_str() -> str: return str(uuid.uuid4()) class Service(Base): __tablename__ = "services" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid_str) name: Mapped[str] = mapped_column(String(100), nullable=False) slug: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, index=True) description: Mapped[str | None] = mapped_column(Text, nullable=True) group_name: Mapped[str | None] = mapped_column(String(50), nullable=True) position: Mapped[int] = mapped_column(Integer, default=0) is_visible: Mapped[bool] = mapped_column(Boolean, default=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.utcnow, onupdate=datetime.utcnow ) incidents: Mapped[list["Incident"]] = relationship(back_populates="service") monitors: Mapped[list["Monitor"]] = relationship(back_populates="service") class Incident(Base): __tablename__ = "incidents" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid_str) service_id: Mapped[str] = mapped_column( String(36), ForeignKey("services.id"), nullable=False, index=True ) title: Mapped[str] = mapped_column(String(200), nullable=False) status: Mapped[str] = mapped_column(String(20), nullable=False, index=True) # investigating | identified | monitoring | resolved severity: Mapped[str] = mapped_column(String(20), nullable=False) # minor | major | outage started_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow) resolved_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.utcnow, onupdate=datetime.utcnow ) service: Mapped["Service"] = relationship(back_populates="incidents") updates: Mapped[list["IncidentUpdate"]] = relationship( back_populates="incident", cascade="all, delete-orphan" ) notifications: Mapped[list["NotificationLog"]] = relationship(back_populates="incident") class IncidentUpdate(Base): __tablename__ = "incident_updates" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid_str) incident_id: Mapped[str] = mapped_column( String(36), ForeignKey("incidents.id"), nullable=False, index=True ) status: Mapped[str] = mapped_column(String(20), nullable=False) body: Mapped[str] = mapped_column(Text, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) incident: Mapped["Incident"] = relationship(back_populates="updates") class Monitor(Base): __tablename__ = "monitors" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid_str) service_id: Mapped[str] = mapped_column( String(36), ForeignKey("services.id"), nullable=False, index=True ) url: Mapped[str] = mapped_column(String(500), nullable=False) method: Mapped[str] = mapped_column(String(10), default="GET") expected_status: Mapped[int] = mapped_column(Integer, default=200) timeout_seconds: Mapped[int] = mapped_column(Integer, default=10) interval_seconds: Mapped[int] = mapped_column(Integer, default=60) is_active: Mapped[bool] = mapped_column(Boolean, default=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.utcnow, onupdate=datetime.utcnow ) service: Mapped["Service"] = relationship(back_populates="monitors") results: Mapped[list["MonitorResult"]] = relationship( back_populates="monitor", cascade="all, delete-orphan" ) class MonitorResult(Base): __tablename__ = "monitor_results" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid_str) monitor_id: Mapped[str] = mapped_column( String(36), ForeignKey("monitors.id"), nullable=False, index=True ) status: Mapped[str] = mapped_column(String(20), nullable=False) # up | down | degraded response_time_ms: Mapped[int | None] = mapped_column(Integer, nullable=True) status_code: Mapped[int | None] = mapped_column(Integer, nullable=True) error_message: Mapped[str | None] = mapped_column(Text, nullable=True) checked_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow) monitor: Mapped["Monitor"] = relationship(back_populates="results") class Subscriber(Base): __tablename__ = "subscribers" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid_str) email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True) is_confirmed: Mapped[bool] = mapped_column(Boolean, default=False) confirm_token: Mapped[str | None] = mapped_column(String(100), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) notifications: Mapped[list["NotificationLog"]] = relationship(back_populates="subscriber") class NotificationLog(Base): __tablename__ = "notification_logs" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid_str) incident_id: Mapped[str] = mapped_column( String(36), ForeignKey("incidents.id"), nullable=False, index=True ) subscriber_id: Mapped[str] = mapped_column( String(36), ForeignKey("subscribers.id"), nullable=False ) channel: Mapped[str] = mapped_column(String(20), nullable=False) # email | webhook status: Mapped[str] = mapped_column(String(20), nullable=False) # sent | failed created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) incident: Mapped["Incident"] = relationship(back_populates="notifications") subscriber: Mapped["Subscriber"] = relationship(back_populates="notifications") class SiteSetting(Base): __tablename__ = "site_settings" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid_str) key: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, index=True) value: Mapped[str | None] = mapped_column(Text, nullable=True) updated_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.utcnow, onupdate=datetime.utcnow )