175 lines
8.7 KiB
Python
175 lines
8.7 KiB
Python
from datetime import datetime, timezone
|
|
import uuid
|
|
from sqlalchemy import (
|
|
Column, String, Integer, BigInteger, Boolean, DateTime,
|
|
ForeignKey, Text, Index
|
|
)
|
|
from sqlalchemy.orm import relationship
|
|
from app.core.database import Base
|
|
|
|
def utc_now():
|
|
return datetime.now(timezone.utc)
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
email = Column(String(255), unique=True, index=True, nullable=False)
|
|
hashed_password = Column(String(255), nullable=False)
|
|
full_name = Column(String(255), nullable=True)
|
|
role = Column(String(50), default="ADMIN", nullable=False) # ADMIN, OPERATOR, VIEWER
|
|
is_active = Column(Boolean, default=True, nullable=False)
|
|
created_at = Column(DateTime(timezone=True), default=utc_now, nullable=False)
|
|
|
|
class Client(Base):
|
|
__tablename__ = "clients"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
client_code = Column(String(50), unique=True, index=True, nullable=False) # e.g., CLIENT-0001
|
|
name = Column(String(255), nullable=False)
|
|
hostname = Column(String(255), nullable=True)
|
|
os_info = Column(String(255), nullable=True)
|
|
ip_address = Column(String(100), nullable=True)
|
|
agent_version = Column(String(50), default="1.0.0", nullable=False)
|
|
status = Column(String(50), default="OFFLINE", nullable=False) # ONLINE, OFFLINE, SYNCING, ERROR
|
|
storage_used_bytes = Column(BigInteger, default=0, nullable=False)
|
|
storage_quota_bytes = Column(BigInteger, default=100 * 1024 * 1024 * 1024, nullable=False) # 100 GB default
|
|
last_seen_at = Column(DateTime(timezone=True), nullable=True)
|
|
last_backup_at = Column(DateTime(timezone=True), nullable=True)
|
|
is_active = Column(Boolean, default=True, nullable=False)
|
|
created_at = Column(DateTime(timezone=True), default=utc_now, nullable=False)
|
|
|
|
credentials = relationship("ClientCredential", back_populates="client", cascade="all, delete-orphan")
|
|
jobs = relationship("BackupJob", back_populates="client", cascade="all, delete-orphan")
|
|
backup_files = relationship("BackupFile", back_populates="client", cascade="all, delete-orphan")
|
|
backup_sessions = relationship("BackupSession", back_populates="client", cascade="all, delete-orphan")
|
|
|
|
class ClientCredential(Base):
|
|
__tablename__ = "client_credentials"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
client_id = Column(Integer, ForeignKey("clients.id", ondelete="CASCADE"), nullable=False)
|
|
device_id = Column(String(100), unique=True, index=True, nullable=False) # Unique UUID
|
|
token_hash = Column(String(255), unique=True, index=True, nullable=False)
|
|
name = Column(String(255), default="Primary Windows Agent", nullable=False)
|
|
is_revoked = Column(Boolean, default=False, nullable=False)
|
|
created_at = Column(DateTime(timezone=True), default=utc_now, nullable=False)
|
|
last_used_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
client = relationship("Client", back_populates="credentials")
|
|
|
|
class RegistrationCode(Base):
|
|
__tablename__ = "registration_codes"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
code = Column(String(50), unique=True, index=True, nullable=False) # e.g., OED-A1B2-C3D4
|
|
is_used = Column(Boolean, default=False, nullable=False)
|
|
client_name_hint = Column(String(255), nullable=True)
|
|
expires_at = Column(DateTime(timezone=True), nullable=False)
|
|
created_at = Column(DateTime(timezone=True), default=utc_now, nullable=False)
|
|
|
|
class BackupJob(Base):
|
|
__tablename__ = "backup_jobs"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
job_code = Column(String(50), unique=True, index=True, nullable=False) # e.g., JOB-001
|
|
client_id = Column(Integer, ForeignKey("clients.id", ondelete="CASCADE"), nullable=False)
|
|
name = Column(String(255), nullable=False)
|
|
source_path = Column(String(1024), nullable=False) # e.g., C:\SQLBackups
|
|
file_patterns = Column(String(255), default="*.bak,*.mdf", nullable=False)
|
|
schedule_cron = Column(String(100), default="0 2 * * *", nullable=False) # default 02:00 AM daily
|
|
is_active = Column(Boolean, default=True, nullable=False)
|
|
|
|
# Retention policies
|
|
keep_daily = Column(Integer, default=7, nullable=False)
|
|
keep_weekly = Column(Integer, default=4, nullable=False)
|
|
keep_monthly = Column(Integer, default=12, nullable=False)
|
|
min_stable_time_seconds = Column(Integer, default=60, nullable=False)
|
|
|
|
status = Column(String(50), default="IDLE", nullable=False) # IDLE, RUNNING, ERROR, SUCCESS
|
|
last_run_at = Column(DateTime(timezone=True), nullable=True)
|
|
next_run_at = Column(DateTime(timezone=True), nullable=True)
|
|
created_at = Column(DateTime(timezone=True), default=utc_now, nullable=False)
|
|
|
|
client = relationship("Client", back_populates="jobs")
|
|
backup_files = relationship("BackupFile", back_populates="job", cascade="all, delete-orphan")
|
|
backup_sessions = relationship("BackupSession", back_populates="job", cascade="all, delete-orphan")
|
|
|
|
class BackupSession(Base):
|
|
__tablename__ = "backup_sessions"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_code = Column(String(100), unique=True, index=True, default=lambda: str(uuid.uuid4()), nullable=False)
|
|
client_id = Column(Integer, ForeignKey("clients.id", ondelete="CASCADE"), nullable=False)
|
|
job_id = Column(Integer, ForeignKey("backup_jobs.id", ondelete="CASCADE"), nullable=True)
|
|
|
|
filename = Column(String(512), nullable=False)
|
|
file_size = Column(BigInteger, nullable=False)
|
|
chunk_size = Column(Integer, default=4 * 1024 * 1024, nullable=False)
|
|
total_chunks = Column(Integer, nullable=False)
|
|
received_chunks_count = Column(Integer, default=0, nullable=False)
|
|
sha256_full = Column(String(64), nullable=False)
|
|
|
|
status = Column(String(50), default="PENDING", nullable=False) # PENDING, UPLOADING, ASSEMBLING, VERIFYING, SUCCESS, FAILED, CANCELLED
|
|
error_message = Column(Text, nullable=True)
|
|
started_at = Column(DateTime(timezone=True), default=utc_now, nullable=False)
|
|
completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
client = relationship("Client", back_populates="backup_sessions")
|
|
job = relationship("BackupJob", back_populates="backup_sessions")
|
|
chunks = relationship("BackupChunk", back_populates="session", cascade="all, delete-orphan")
|
|
|
|
__table_args__ = (
|
|
Index("idx_session_client_status", "client_id", "status"),
|
|
)
|
|
|
|
class BackupChunk(Base):
|
|
__tablename__ = "backup_chunks"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(Integer, ForeignKey("backup_sessions.id", ondelete="CASCADE"), nullable=False)
|
|
chunk_index = Column(Integer, nullable=False)
|
|
chunk_size = Column(Integer, nullable=False)
|
|
sha256 = Column(String(64), nullable=False)
|
|
is_received = Column(Boolean, default=False, nullable=False)
|
|
received_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
session = relationship("BackupSession", back_populates="chunks")
|
|
|
|
__table_args__ = (
|
|
Index("idx_chunk_session_idx", "session_id", "chunk_index", unique=True),
|
|
)
|
|
|
|
class BackupFile(Base):
|
|
__tablename__ = "backup_files"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
client_id = Column(Integer, ForeignKey("clients.id", ondelete="CASCADE"), nullable=False)
|
|
job_id = Column(Integer, ForeignKey("backup_jobs.id", ondelete="CASCADE"), nullable=True)
|
|
session_id = Column(Integer, ForeignKey("backup_sessions.id", ondelete="SET NULL"), nullable=True)
|
|
|
|
filename = Column(String(512), nullable=False)
|
|
relative_path = Column(String(1024), nullable=False)
|
|
file_size = Column(BigInteger, nullable=False)
|
|
sha256 = Column(String(64), nullable=False)
|
|
retention_tag = Column(String(50), default="DAILY", nullable=False) # DAILY, WEEKLY, MONTHLY, MANUAL
|
|
is_active = Column(Boolean, default=True, nullable=False)
|
|
created_at = Column(DateTime(timezone=True), default=utc_now, nullable=False)
|
|
|
|
client = relationship("Client", back_populates="backup_files")
|
|
job = relationship("BackupJob", back_populates="backup_files")
|
|
|
|
class EventLog(Base):
|
|
__tablename__ = "event_logs"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
timestamp = Column(DateTime(timezone=True), default=utc_now, nullable=False, index=True)
|
|
event_type = Column(String(100), nullable=False, index=True) # LOGIN, CLIENT_REGISTERED, BACKUP_STARTED, BACKUP_COMPLETED, BACKUP_FAILED, etc.
|
|
severity = Column(String(50), default="INFO", nullable=False) # INFO, WARNING, ERROR, CRITICAL
|
|
client_id = Column(Integer, nullable=True, index=True)
|
|
job_id = Column(Integer, nullable=True, index=True)
|
|
user_email = Column(String(255), nullable=True)
|
|
ip_address = Column(String(100), nullable=True)
|
|
message = Column(Text, nullable=False)
|
|
details_json = Column(Text, nullable=True)
|