Upload workflow enhancements: S3 verification, timing, client checksum support (#19)
- Add S3 object verification after upload (size validation before DB commit) - Add cleanup of S3 objects if DB commit fails - Record upload duration_ms and user_agent - Support X-Checksum-SHA256 header for client-side checksum verification - Add already_existed flag to StorageResult for deduplication tracking - Add status, error_message, client_checksum columns to Upload model - Add UploadLock model for future 409 conflict detection - Add consistency-check admin endpoint for detecting orphaned S3 objects - Add migration 005_upload_enhancements.sql
This commit is contained in:
@@ -208,6 +208,11 @@ class Upload(Base):
|
||||
duration_ms = Column(Integer) # Upload timing in milliseconds
|
||||
deduplicated = Column(Boolean, default=False) # Whether artifact was deduplicated
|
||||
checksum_verified = Column(Boolean, default=True) # Whether checksum was verified
|
||||
status = Column(
|
||||
String(20), default="completed", nullable=False
|
||||
) # pending, completed, failed
|
||||
error_message = Column(Text) # Error details for failed uploads
|
||||
client_checksum = Column(String(64)) # Client-provided SHA256 for verification
|
||||
uploaded_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
||||
uploaded_by = Column(String(255), nullable=False)
|
||||
source_ip = Column(String(45))
|
||||
@@ -221,6 +226,35 @@ class Upload(Base):
|
||||
Index("idx_uploads_uploaded_at", "uploaded_at"),
|
||||
Index("idx_uploads_package_uploaded_at", "package_id", "uploaded_at"),
|
||||
Index("idx_uploads_uploaded_by_at", "uploaded_by", "uploaded_at"),
|
||||
Index("idx_uploads_status", "status"),
|
||||
Index("idx_uploads_status_uploaded_at", "status", "uploaded_at"),
|
||||
CheckConstraint(
|
||||
"status IN ('pending', 'completed', 'failed')", name="check_upload_status"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class UploadLock(Base):
|
||||
"""Track in-progress uploads for conflict detection (409 responses)."""
|
||||
|
||||
__tablename__ = "upload_locks"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
sha256_hash = Column(String(64), nullable=False)
|
||||
package_id = Column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("packages.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
)
|
||||
locked_at = Column(DateTime(timezone=True), default=datetime.utcnow)
|
||||
locked_by = Column(String(255), nullable=False)
|
||||
expires_at = Column(DateTime(timezone=True), nullable=False)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_upload_locks_expires_at", "expires_at"),
|
||||
Index(
|
||||
"idx_upload_locks_hash_package", "sha256_hash", "package_id", unique=True
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user