core PK: id 12 required 2 unique

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.

14
Attributes
5
Indexes
6
Validation Rules
17
CRUD Operations

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
btree

Columns: activity_id

idx_activity_attachments_uploaded_by
btree

Columns: uploaded_by_user_id

idx_activity_attachments_activity_status
btree

Columns: activity_id, upload_status, is_deleted

idx_activity_attachments_storage_key
btree unique

Columns: storage_key

idx_activity_attachments_created_at
btree

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
on_create

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
on_create

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
on_delete

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
on_create

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
always

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.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage