Let native clients sync attachment blobs deterministically: - note_attachments gains sha256 (migration 0018, nullable, no backfill). The delta feed's attachment metadata now carries size + sha256 so a client knows exactly which blobs it already has (dedupe) and can verify integrity after download. - Upload accepts an optional client-supplied attachment id (multipart form field), so a file attached offline keeps its identity across sync; re-uploading an id the note already has is an idempotent no-op. The server hashes the stored bytes (sha256) on upload. Download by id already exists (owner/shared scoped). Frontend Attachment type carries the new optional size/sha256. (Still image-only mimes — broadening to any-file is task 1900. Blob sync behavior is operator-verified on deploy.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
26 lines
696 B
Python
26 lines
696 B
Python
"""note_attachments.sha256 (M8 sync hub, step 5 — attachment blob sync)
|
|
|
|
Revision ID: 0018
|
|
Revises: 0017
|
|
Create Date: 2026-07-23
|
|
|
|
A content hash so a native client can tell which attachment blobs it already has
|
|
(dedupe) and verify integrity after downloading. Nullable; existing rows are left
|
|
un-hashed (no backfill — the file bytes may not be reachable at migration time).
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
revision = "0018"
|
|
down_revision = "0017"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column("note_attachments", sa.Column("sha256", sa.Text(), nullable=True))
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("note_attachments", "sha256")
|