Activity Attachment
Data Entity
Description
Stores metadata for files attached to activity records. Supports NHF's requirement for document evidence (invitations, screenshots) for Bufdir audit trails. Actual file binaries are stored in object storage; this table holds the reference, ownership, and classification metadata.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Surrogate primary key, generated on upload initiation | PKrequiredunique |
activity_id |
uuid |
Foreign key to the parent activity record | required |
uploaded_by_user_id |
uuid |
Foreign key to the user who uploaded the attachment. Required for access control and Bufdir audit trail attribution. | required |
file_name |
string |
Original filename as provided by the client (e.g. 'invitation_nhf_oslo.pdf'). Displayed in UI; not used as storage key. | required |
storage_key |
string |
Object storage key (path) for the binary. Format: attachments/{activity_id}/{uuid}.{ext}. Used by object-storage-adapter to retrieve or delete the file. | requiredunique |
file_url |
string |
Pre-signed or CDN URL for reading the file. May be regenerated on access; stored as a cache hint. Treated as ephemeral — consumers should re-fetch if expired. | - |
file_size_bytes |
integer |
File size in bytes, recorded at upload time. Used for storage quota enforcement and display in UI. | required |
mime_type |
string |
MIME type validated server-side (e.g. image/jpeg, image/png, application/pdf). Drives icon rendering and download behavior in the wizard summary step. | required |
attachment_type |
enum |
High-level classification of the attachment for filtering and Bufdir export categorization. | required |
description |
text |
Optional free-text description provided by the uploader. Supports NHF use case of annotating Facebook screenshots or invitation documents. | - |
is_deleted |
boolean |
Soft-delete flag. Deleted attachments are excluded from all reads but retained for Bufdir audit trail integrity. The storage object is not purged until a scheduled cleanup job confirms the activity has been archived. | required |
upload_status |
enum |
Tracks the upload lifecycle. 'pending' while the binary is in transit (supports offline outbox); 'complete' once the object storage confirms receipt; 'failed' if the upload did not complete within the retry window. | required |
created_at |
datetime |
Timestamp when the attachment record was inserted. Used for ordering in wizard summary and audit logs. | required |
updated_at |
datetime |
Timestamp of last record mutation (status changes, soft delete). Managed by an UPDATE trigger. | required |
Database Indexes
idx_activity_attachments_activity_id
Columns: activity_id
idx_activity_attachments_uploaded_by
Columns: uploaded_by_user_id
idx_activity_attachments_activity_status
Columns: activity_id, upload_status, is_deleted
idx_activity_attachments_storage_key
Columns: storage_key
idx_activity_attachments_created_at
Columns: created_at
Validation Rules
allowed_mime_types
error
Validation failed
max_file_size_20mb
error
Validation failed
file_name_sanitization
error
Validation failed
storage_key_uniqueness
error
Validation failed
description_length
error
Validation failed
activity_must_exist
error
Validation failed
Business Rules
attachment_belongs_to_accessible_activity
The uploading user must be the owner of the parent activity, a coordinator overseeing the activity's peer mentor, or a global admin. Coordinators uploading via proxy registration also satisfy this check. Enforced at the API layer before the storage upload URL is issued.
max_attachments_per_activity
An activity may have at most 10 non-deleted attachments. Enforced to prevent storage abuse and keep the wizard summary step performant on mobile. Returns a 422 with a user-readable message when the limit is reached.
soft_delete_only
Attachments are never hard-deleted via the application API. Setting is_deleted = true is the only permitted removal path. Physical object storage cleanup is deferred to a background job that runs after activity archival. This preserves the Bufdir audit trail for the required retention period.
complete_before_activity_submit
If an activity has attachments in 'pending' or 'failed' upload_status at the time of activity submission, the submission is blocked with a warning listing the incomplete files. This prevents activities with broken attachment references from entering the approval queue.
offline_outbox_attachment_handling
When created offline, attachments are stored locally with upload_status = 'pending' and queued in the mutation outbox. The sync queue service uploads the binary to object storage and then PATCHes the record to upload_status = 'complete' once connectivity is restored. Activities containing pending attachments display a sync-pending indicator.