uploader-bot/app/core/models/content_v3.py

124 lines
5.4 KiB
Python

from __future__ import annotations
from datetime import datetime
from sqlalchemy import Column, BigInteger, Integer, String, DateTime, JSON, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from .base import AlchemyBase
class EncryptedContent(AlchemyBase):
__tablename__ = 'encrypted_contents'
id = Column(Integer, autoincrement=True, primary_key=True)
# CID of encrypted source stored in IPFS (CIDv1 base32)
encrypted_cid = Column(String(128), nullable=False, unique=True)
# Public metadata
title = Column(String(512), nullable=False)
description = Column(String(4096), nullable=True)
content_type = Column(String(64), nullable=False) # e.g. audio/flac, video/mp4, application/octet-stream
# Sizes
enc_size_bytes = Column(BigInteger, nullable=True)
plain_size_bytes = Column(BigInteger, nullable=True)
# Preview flags and config (all preview params live here, not in derivatives)
preview_enabled = Column(Boolean, nullable=False, default=False)
preview_conf = Column(JSON, nullable=False, default=dict)
# Crypto parameters (fixed per network)
aead_scheme = Column(String(32), nullable=False, default='AES_GCM')
chunk_bytes = Column(Integer, nullable=False, default=1048576)
salt_b64 = Column(String(64), nullable=True) # per-content salt used for nonce derivation
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
updated_at = Column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)
class ContentKey(AlchemyBase):
__tablename__ = 'content_keys'
content_id = Column(Integer, ForeignKey('encrypted_contents.id'), primary_key=True)
key_ciphertext_b64 = Column(String(512), nullable=False)
key_fingerprint = Column(String(128), nullable=False)
issuer_node_id = Column(String(128), nullable=False)
allow_auto_grant = Column(Boolean, nullable=False, default=True)
lease_expires_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
content = relationship('EncryptedContent', uselist=False, foreign_keys=[content_id])
class IpfsSync(AlchemyBase):
__tablename__ = 'ipfs_sync'
content_id = Column(Integer, ForeignKey('encrypted_contents.id'), primary_key=True)
pin_state = Column(String(32), nullable=False, default='pinned') # not_pinned|queued|pinning|pinned|failed
pin_error = Column(String(1024), nullable=True)
bytes_total = Column(BigInteger, nullable=True)
bytes_fetched = Column(BigInteger, nullable=True)
providers_cache = Column(JSON, nullable=False, default=list)
first_seen_at = Column(DateTime, nullable=True)
pinned_at = Column(DateTime, nullable=True)
updated_at = Column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)
content = relationship('EncryptedContent', uselist=False, foreign_keys=[content_id])
class ContentDerivative(AlchemyBase):
__tablename__ = 'content_derivatives'
id = Column(Integer, autoincrement=True, primary_key=True)
content_id = Column(Integer, ForeignKey('encrypted_contents.id'), nullable=False)
kind = Column(String(64), nullable=False) # decrypted_high|decrypted_low|decrypted_thumbnail|decrypted_preview
interval_start_ms = Column(Integer, nullable=True)
interval_end_ms = Column(Integer, nullable=True)
local_path = Column(String(1024), nullable=False)
content_type = Column(String(64), nullable=True)
size_bytes = Column(BigInteger, nullable=True)
status = Column(String(32), nullable=False, default='pending') # pending|processing|ready|failed
error = Column(String(1024), nullable=True)
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
last_access_at = Column(DateTime, nullable=True)
content = relationship('EncryptedContent', uselist=False, foreign_keys=[content_id])
class ContentIndexItem(AlchemyBase):
__tablename__ = 'content_index_items'
encrypted_cid = Column(String(128), primary_key=True)
payload = Column(JSON, nullable=False, default=dict)
sig = Column(String(512), nullable=False)
updated_at = Column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)
class KeyGrant(AlchemyBase):
__tablename__ = 'key_grants'
id = Column(Integer, autoincrement=True, primary_key=True)
encrypted_cid = Column(String(128), nullable=False)
issuer_node_id = Column(String(128), nullable=False)
to_node_id = Column(String(128), nullable=False)
sealed_key_b64 = Column(String(1024), nullable=False)
aead_scheme = Column(String(32), nullable=False)
chunk_bytes = Column(Integer, nullable=False)
constraints = Column(JSON, nullable=False, default=dict)
issued_at = Column(DateTime, nullable=False, default=datetime.utcnow)
sig = Column(String(512), nullable=False)
class UploadSession(AlchemyBase):
__tablename__ = 'upload_sessions'
id = Column(String(128), primary_key=True) # tus Upload.ID
filename = Column(String(512), nullable=True)
size_bytes = Column(BigInteger, nullable=True)
state = Column(String(32), nullable=False, default='uploading') # uploading|processing|pinned|failed
encrypted_cid = Column(String(128), nullable=True)
storage_path = Column(String(1024), nullable=True)
error = Column(String(1024), nullable=True)
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
updated_at = Column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)