from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text, ForeignKey, JSON from sqlalchemy.orm import relationship from datetime import datetime, timezone from .database import Base class Source(Base): __tablename__ = "sources" id = Column(Integer, primary_key=True) name = Column(String, nullable=False) type = Column(String, nullable=False) # local, ssh, database config = Column(JSON, default=lambda: {}) created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) updated_at = Column( DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), ) jobs = relationship("Job", back_populates="source", cascade="all, delete-orphan") def __repr__(self): return f"" class Job(Base): __tablename__ = "jobs" id = Column(Integer, primary_key=True) name = Column(String, nullable=False) source_id = Column(Integer, ForeignKey("sources.id"), nullable=False, index=True) strategy = Column(String, nullable=False, default="full") # full, incremental destination_path = Column(String, nullable=False) exclude_patterns = Column(JSON, default=lambda: []) enabled = Column(Boolean, default=True) created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) updated_at = Column( DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), ) source = relationship("Source", back_populates="jobs") schedule = relationship( "Schedule", back_populates="job", uselist=False, cascade="all, delete-orphan" ) executions = relationship( "JobExecution", back_populates="job", cascade="all, delete-orphan" ) def __repr__(self): return f"" class Schedule(Base): __tablename__ = "schedules" id = Column(Integer, primary_key=True) job_id = Column( Integer, ForeignKey("jobs.id"), unique=True, nullable=False, index=True ) cron_expression = Column(String, nullable=False) enabled = Column(Boolean, default=True) created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) job = relationship("Job", back_populates="schedule") def __repr__(self): return f"" class JobExecution(Base): __tablename__ = "job_executions" id = Column(Integer, primary_key=True) job_id = Column(Integer, ForeignKey("jobs.id"), nullable=False, index=True) status = Column( String, nullable=False, default="pending" ) # pending, running, success, failed, cancelled started_at = Column(DateTime, nullable=True) completed_at = Column(DateTime, nullable=True) bytes_processed = Column(Integer, default=0) bytes_backed_up = Column(Integer, default=0) error_message = Column(Text, nullable=True) triggered_by = Column(String, nullable=False) # manual, schedule job = relationship("Job", back_populates="executions") backups = relationship( "Backup", back_populates="execution", cascade="all, delete-orphan" ) def __repr__(self): return f"" class Backup(Base): __tablename__ = "backups" id = Column(Integer, primary_key=True) execution_id = Column( Integer, ForeignKey("job_executions.id"), nullable=False, index=True ) storage_path = Column(String, nullable=False) size_bytes = Column(Integer, default=0) checksum = Column(String, nullable=True) type = Column(String, nullable=False) # full, incremental parent_backup_id = Column( Integer, ForeignKey("backups.id"), nullable=True, index=True ) created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) execution = relationship("JobExecution", back_populates="backups") parent_backup = relationship("Backup", remote_side=[id]) def __repr__(self): return f"" class Setting(Base): __tablename__ = "settings" key = Column(String, primary_key=True) value = Column(Text, nullable=True) updated_at = Column( DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), ) def __repr__(self): return f""