| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- from flask_sqlalchemy import SQLAlchemy
- from datetime import datetime
- db = SQLAlchemy()
- # ---------------------------------------------------------------------------
- # Helpers partagés
- # ---------------------------------------------------------------------------
- def _size_human(size_bytes):
- if not size_bytes:
- return "—"
- n = float(size_bytes)
- for unit in ("o", "Ko", "Mo", "Go"):
- if n < 1024:
- return f"{n:.0f} {unit}"
- n /= 1024
- return f"{n:.1f} To"
- class Destination(db.Model):
- __tablename__ = "destinations"
- id = db.Column(db.Integer, primary_key=True)
- name = db.Column(db.Text, nullable=False)
- host = db.Column(db.Text, nullable=False)
- port = db.Column(db.Integer, default=22)
- user = db.Column(db.Text, nullable=False, default="root")
- remote_path = db.Column(db.Text, nullable=False)
- key_name = db.Column(db.Text) # nom du fichier clé dans data_dir/keys/
- enabled = db.Column(db.Boolean, default=True)
- created_at = db.Column(db.DateTime, default=datetime.utcnow)
- jobs = db.relationship("Job", backref="destination", lazy=True)
- @property
- def remote_str(self):
- return f"{self.user}@{self.host}:{self.remote_path}"
- class Job(db.Model):
- __tablename__ = "jobs"
- id = db.Column(db.Integer, primary_key=True)
- name = db.Column(db.Text, nullable=False)
- type = db.Column(db.Text, nullable=False) # ynh_app|ynh_system|custom_dir|mysql|postgresql
- config_json = db.Column(db.Text)
- cron_expr = db.Column(db.Text, nullable=False)
- retention_mode = db.Column(db.Text, nullable=False) # count|daily|gfs
- retention_value = db.Column(db.Integer, nullable=False)
- enabled = db.Column(db.Boolean, default=True)
- core_only = db.Column(db.Boolean, default=False)
- destination_id = db.Column(db.Integer, db.ForeignKey("destinations.id"), nullable=True)
- created_at = db.Column(db.DateTime, default=datetime.utcnow)
- updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
- runs = db.relationship("Run", backref="job", lazy=True, cascade="all, delete-orphan")
- def next_run(self):
- from scheduler import get_next_run
- return get_next_run(self.id)
- class Setting(db.Model):
- __tablename__ = "settings"
- key = db.Column(db.Text, primary_key=True)
- value = db.Column(db.Text, nullable=False, default="")
- class Run(db.Model):
- __tablename__ = "runs"
- id = db.Column(db.Integer, primary_key=True)
- job_id = db.Column(db.Integer, db.ForeignKey("jobs.id"), nullable=False)
- started_at = db.Column(db.DateTime)
- finished_at = db.Column(db.DateTime)
- status = db.Column(db.Text) # running|success|error
- log_text = db.Column(db.Text)
- archive_name = db.Column(db.Text)
- size_bytes = db.Column(db.Integer)
- @property
- def duration_seconds(self):
- if self.started_at and self.finished_at:
- return int((self.finished_at - self.started_at).total_seconds())
- return None
- @property
- def size_human(self):
- return _size_human(self.size_bytes)
- # ---------------------------------------------------------------------------
- # Fédération
- # ---------------------------------------------------------------------------
- class RemoteInstance(db.Model):
- __tablename__ = "remote_instances"
- id = db.Column(db.Integer, primary_key=True)
- name = db.Column(db.Text, nullable=False)
- url = db.Column(db.Text, nullable=False) # https://tom.domaine.fr
- api_key = db.Column(db.Text, nullable=False)
- last_seen = db.Column(db.DateTime)
- status = db.Column(db.Text, default="unknown") # online|offline|error|unknown
- created_at = db.Column(db.DateTime, default=datetime.utcnow)
- remote_runs = db.relationship("RemoteRun", backref="instance", lazy=True,
- cascade="all, delete-orphan")
- @property
- def url_display(self):
- return self.url.rstrip("/")
- class RemoteRun(db.Model):
- __tablename__ = "remote_runs"
- id = db.Column(db.Integer, primary_key=True)
- instance_id = db.Column(db.Integer, db.ForeignKey("remote_instances.id"), nullable=False)
- job_id = db.Column(db.Integer) # id sur l'instance distante
- job_name = db.Column(db.Text)
- job_type = db.Column(db.Text)
- last_run_at = db.Column(db.DateTime)
- last_status = db.Column(db.Text)
- last_archive_name = db.Column(db.Text)
- last_size_bytes = db.Column(db.Integer)
- # Alias pour compatibilité avec le template dashboard_network
- @property
- def name(self):
- return self.job_name
- @property
- def type(self):
- return self.job_type
- @property
- def size_human(self):
- return _size_human(self.last_size_bytes)
- @property
- def last_size_human(self):
- return _size_human(self.last_size_bytes)
- class Upload(db.Model):
- __tablename__ = "uploads"
- upload_id = db.Column(db.Text, primary_key=True) # uuid4
- filename = db.Column(db.Text)
- total_size = db.Column(db.Integer)
- chunk_size = db.Column(db.Integer)
- chunks_total = db.Column(db.Integer)
- chunks_received = db.Column(db.Integer, default=0)
- checksum = db.Column(db.Text) # SHA256 de l'archive complète
- started_at = db.Column(db.DateTime, default=datetime.utcnow)
- status = db.Column(db.Text, default="pending") # pending|in_progress|complete|error
|