Photo Library
A personal photo collection for images sent frequently. Upload photos or save them from conversations, then send to any chat with one click. The purge feature removes a photo from every conversation it was sent to.
Architecture
| Layer | Files | Role |
|---|---|---|
| Database | photo_library, photo_library_sends |
Storage metadata, send tracking |
| Backend | libraryController.ts, libraryRoutes.ts |
10 REST endpoints, file I/O, WhatsApp integration |
| Frontend | PhotoLibraryPage.tsx, LibraryPickerModal.tsx |
Grid UI, lightbox, send modal, purge flow |
Storage
Photos and thumbnails stored inside the Docker container at:
- Full-size:
/app/media/_library/photos/ - Thumbnails:
/app/media/_library/thumbs/(200px, Q60 JPEG viasharp)
Filename format: lib_{timestamp}_{hash6}.{ext}
Duplicate Detection
Every photo is MD5-hashed on upload/save. The md5_hash column has a UNIQUE constraint – uploading the same image twice returns the existing record instead of creating a duplicate.
Data Model
photo_library
| Column | Type | Notes |
|---|---|---|
| id | SERIAL PK | Auto-increment |
| filename | VARCHAR(255) | Stored filename |
| thumbnail_name | VARCHAR(255) | Thumbnail filename |
| original_source | TEXT | e.g. “chat:+504xxx” or “upload” |
| mime_type | VARCHAR(100) | Default: image/jpeg |
| file_size_bytes | INTEGER | File size |
| width | INTEGER | Image dimensions |
| height | INTEGER | Image dimensions |
| md5_hash | VARCHAR(32) UNIQUE | For dedup + purge matching |
| caption | TEXT | Optional caption |
| created_at | TIMESTAMPTZ | Auto-set |
photo_library_sends
| Column | Type | Notes |
|---|---|---|
| id | SERIAL PK | Auto-increment |
| library_photo_id | INTEGER FK | References photo_library(id) ON DELETE CASCADE |
| message_wa_id | TEXT | WhatsApp message ID |
| chat_wa_id | TEXT | Which chat it was sent to |
| sent_at | TIMESTAMPTZ | Auto-set |
Status
Complete and deployed.