Files
droplake/backend/database.py
T

65 lines
2.1 KiB
Python
Raw Normal View History

2026-04-02 16:05:40 +03:00
from datetime import datetime
from sqlalchemy import create_engine, Column, String, Integer, Boolean, DateTime, Text
from sqlalchemy.orm import DeclarativeBase, sessionmaker
from config import settings
engine = create_engine(
settings.database_url,
connect_args={"check_same_thread": False},
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
class Base(DeclarativeBase):
pass
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
class Share(Base):
__tablename__ = "shares"
id = Column(String(36), primary_key=True) # UUID4
share_type = Column(String(10), nullable=False) # 'file' | 'text'
# Metadata provided by client before encryption
original_filename = Column(String(512), nullable=True)
mimetype = Column(String(128), nullable=True)
filesize = Column(Integer, nullable=True) # bytes of the encrypted blob
# Storage — blob stored under a random name, never the original
storage_path = Column(String(512), nullable=False)
# AES-256-GCM key (hex-encoded, 64 chars). Server generated, stored at rest.
encryption_key = Column(String(64), nullable=False)
# Access control
password_hash = Column(String(512), nullable=True) # Argon2id; NULL = no password
# Lifecycle — all datetimes are naive UTC
created_at = Column(DateTime, default=datetime.utcnow)
expires_at = Column(DateTime, nullable=False)
burn_after_read = Column(Boolean, default=False)
downloaded = Column(Boolean, default=False)
# Brute-force protection
failed_attempts = Column(Integer, default=0)
locked_until = Column(DateTime, nullable=True)
class SecurityEvent(Base):
__tablename__ = "security_events"
id = Column(Integer, primary_key=True, autoincrement=True)
timestamp = Column(DateTime, default=datetime.utcnow)
event_type = Column(String(50), nullable=False)
share_id = Column(String(36), nullable=True)
ip_hash = Column(String(64), nullable=True) # SHA-256 of IP — no raw IPs
details = Column(Text, nullable=True)