from datetime import datetime, timezone from app.extensions import db class File(db.Model): __tablename__ = 'files' id = db.Column(db.Integer, primary_key=True) owner_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True) parent_id = db.Column(db.Integer, db.ForeignKey('files.id'), nullable=True, index=True) name = db.Column(db.String(255), nullable=False) is_folder = db.Column(db.Boolean, default=False, nullable=False) mime_type = db.Column(db.String(255), nullable=True) size = db.Column(db.BigInteger, default=0) storage_path = db.Column(db.String(500), nullable=True) # UUID-based path on disk checksum = db.Column(db.String(64), nullable=True) # SHA-256 for sync is_trashed = db.Column(db.Boolean, default=False, nullable=False, index=True) trashed_at = db.Column(db.DateTime, nullable=True) original_parent_id = db.Column(db.Integer, nullable=True) # to restore to original location created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc)) updated_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) # Relationships children = db.relationship('File', backref=db.backref('parent', remote_side='File.id'), lazy='dynamic') permissions = db.relationship('FilePermission', backref='file', lazy='dynamic', cascade='all, delete-orphan') share_links = db.relationship('ShareLink', backref='file', lazy='dynamic', cascade='all, delete-orphan') def to_dict(self): d = { 'id': self.id, 'owner_id': self.owner_id, 'parent_id': self.parent_id, 'name': self.name, 'is_folder': self.is_folder, 'mime_type': self.mime_type, 'size': self.size, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None, } if self.is_trashed: d['is_trashed'] = True d['trashed_at'] = self.trashed_at.isoformat() if self.trashed_at else None return d class FilePermission(db.Model): __tablename__ = 'file_permissions' id = db.Column(db.Integer, primary_key=True) file_id = db.Column(db.Integer, db.ForeignKey('files.id'), nullable=False, index=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True) permission = db.Column(db.String(20), nullable=False) # 'read', 'write', 'admin' user = db.relationship('User', backref='file_permissions') __table_args__ = ( db.UniqueConstraint('file_id', 'user_id', name='uq_file_user_permission'), ) class ShareLink(db.Model): __tablename__ = 'share_links' id = db.Column(db.Integer, primary_key=True) file_id = db.Column(db.Integer, db.ForeignKey('files.id'), nullable=False, index=True) token = db.Column(db.String(64), unique=True, nullable=False, index=True) permission = db.Column(db.String(20), default='read', nullable=False) # 'read' or 'write' password_hash = db.Column(db.String(255), nullable=True) expires_at = db.Column(db.DateTime, nullable=True) created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) download_count = db.Column(db.Integer, default=0) max_downloads = db.Column(db.Integer, nullable=True) created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc)) creator = db.relationship('User', backref='share_links') def is_expired(self): if self.expires_at is None: return False return datetime.now(timezone.utc) > self.expires_at def is_download_limit_reached(self): if self.max_downloads is None: return False return self.download_count >= self.max_downloads