From 72174c780a66b90145bd2b2f618c61913b3c41a7 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Tue, 2 Jun 2026 16:10:43 +0200 Subject: [PATCH] feat(file-storage): add document_files model and enum --- .../app/file_storage/persistence/__init__.py | 11 ++++ .../app/file_storage/persistence/enums.py | 11 ++++ .../app/file_storage/persistence/models.py | 66 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 surfsense_backend/app/file_storage/persistence/__init__.py create mode 100644 surfsense_backend/app/file_storage/persistence/enums.py create mode 100644 surfsense_backend/app/file_storage/persistence/models.py diff --git a/surfsense_backend/app/file_storage/persistence/__init__.py b/surfsense_backend/app/file_storage/persistence/__init__.py new file mode 100644 index 000000000..4664da737 --- /dev/null +++ b/surfsense_backend/app/file_storage/persistence/__init__.py @@ -0,0 +1,11 @@ +"""Models and enums for the document file-storage tables.""" + +from __future__ import annotations + +from .enums import DocumentFileKind +from .models import DocumentFile + +__all__ = [ + "DocumentFile", + "DocumentFileKind", +] diff --git a/surfsense_backend/app/file_storage/persistence/enums.py b/surfsense_backend/app/file_storage/persistence/enums.py new file mode 100644 index 000000000..368dc8a9e --- /dev/null +++ b/surfsense_backend/app/file_storage/persistence/enums.py @@ -0,0 +1,11 @@ +"""DocumentFile kinds: the original upload plus future derived artifacts.""" + +from __future__ import annotations + +from enum import StrEnum + + +class DocumentFileKind(StrEnum): + ORIGINAL = "ORIGINAL" + REDACTED = "REDACTED" + FILLED_FORM = "FILLED_FORM" diff --git a/surfsense_backend/app/file_storage/persistence/models.py b/surfsense_backend/app/file_storage/persistence/models.py new file mode 100644 index 000000000..7433f35ed --- /dev/null +++ b/surfsense_backend/app/file_storage/persistence/models.py @@ -0,0 +1,66 @@ +"""``document_files`` table: durable blobs associated with a document.""" + +from __future__ import annotations + +from sqlalchemy import ( + BigInteger, + Column, + Enum as SQLAlchemyEnum, + ForeignKey, + Integer, + String, +) +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship + +from app.db import BaseModel, TimestampMixin + +from .enums import DocumentFileKind + + +class DocumentFile(BaseModel, TimestampMixin): + """One stored file for a document (its original upload, or a derived copy).""" + + __tablename__ = "document_files" + + document_id = Column( + Integer, + ForeignKey("documents.id", ondelete="CASCADE"), + nullable=False, + index=True, + ) + search_space_id = Column( + Integer, + ForeignKey("searchspaces.id", ondelete="CASCADE"), + nullable=False, + index=True, + ) + kind = Column( + SQLAlchemyEnum( + DocumentFileKind, + name="document_file_kind", + values_callable=lambda x: [e.value for e in x], + ), + nullable=False, + default=DocumentFileKind.ORIGINAL, + server_default=DocumentFileKind.ORIGINAL.value, + index=True, + ) + + # Where the bytes live: the backend that stored them and its object key. + storage_backend = Column(String(32), nullable=False) + storage_key = Column(String, nullable=False) + + original_filename = Column(String, nullable=False) + mime_type = Column(String, nullable=True) + size_bytes = Column(BigInteger, nullable=False) + checksum_sha256 = Column(String(64), nullable=True) + + created_by_id = Column( + UUID(as_uuid=True), + ForeignKey("user.id", ondelete="SET NULL"), + nullable=True, + index=True, + ) + + document = relationship("Document", back_populates="files")