feat: Improve file display, timezone handling, and LOT management
Changes: - Fix datetime serialization with UTC 'Z' suffix for correct timezone display - Add PDF upload support with extension fallback for MIME detection - Fix LOT add/remove by creating new list for SQLAlchemy JSON change detection - Add file message components (FileMessage, ImageLightbox, UploadPreview) - Add multi-file upload support with progress tracking - Link uploaded files to chat messages via message_id - Include file attachments in AI report generation - Update specs for file-storage, realtime-messaging, and ai-report-generation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
TBD - created by archiving change add-file-upload-minio. Update Purpose after archive.
|
||||
## Requirements
|
||||
### Requirement: File Upload with Validation
|
||||
The system SHALL accept multipart file uploads to incident rooms, validate file type and size, and persist files to MinIO object storage with metadata tracking in PostgreSQL.
|
||||
The system SHALL accept multipart file uploads to incident rooms, validate file type and size, persist files to MinIO object storage with metadata tracking in PostgreSQL, AND create an associated message in the chat for context.
|
||||
|
||||
#### Scenario: Upload image to incident room
|
||||
- **WHEN** a user with OWNER or EDITOR role uploads an image file via `POST /api/rooms/{room_id}/files`
|
||||
@@ -20,11 +20,19 @@ The system SHALL accept multipart file uploads to incident rooms, validate file
|
||||
- Validate JWT token and extract user_id
|
||||
- Verify user is member of room-123 with OWNER or EDITOR role
|
||||
- Validate file MIME type is image/jpeg, image/png, or image/gif
|
||||
- Validate file size ≤ 10MB
|
||||
- Validate file size <= 10MB
|
||||
- Generate unique file_id (UUID)
|
||||
- Upload file to MinIO bucket `task-reporter-files` at path `room-123/images/{file_id}.jpg`
|
||||
- Create database record in `room_files` table
|
||||
- Return file metadata with presigned download URL (1-hour expiry)
|
||||
- Create a message with `message_type=image_ref` containing file metadata
|
||||
- Create database record in `room_files` table with `message_id` reference
|
||||
- Return file metadata with presigned download URL (1-hour expiry) and message_id
|
||||
|
||||
#### Scenario: Upload document to incident room
|
||||
- **WHEN** a user uploads a non-image file (PDF, log, etc.)
|
||||
- **THEN** the system SHALL:
|
||||
- Create a message with `message_type=file_ref`
|
||||
- Store message_id in the file record
|
||||
- Return file metadata with message_id
|
||||
|
||||
#### Scenario: Reject oversized file upload
|
||||
- **WHEN** a user attempts to upload a 15MB PDF file
|
||||
@@ -34,6 +42,7 @@ The system SHALL accept multipart file uploads to incident rooms, validate file
|
||||
- Include error message: "File size exceeds limit: 15MB > 20MB"
|
||||
- NOT upload file to MinIO
|
||||
- NOT create database record
|
||||
- NOT create chat message
|
||||
|
||||
#### Scenario: Reject unauthorized file type
|
||||
- **WHEN** a user attempts to upload an executable file (e.g., .exe, .sh, .bat)
|
||||
@@ -57,7 +66,8 @@ The system SHALL accept multipart file uploads to incident rooms, validate file
|
||||
- Validate MIME type is text/plain
|
||||
- Upload to MinIO at `room-456/logs/{file_id}.log`
|
||||
- Store metadata with file_type='log'
|
||||
- Return success response with file_id
|
||||
- Create a message with message_type='file_ref'
|
||||
- Return success response with file_id and message_id
|
||||
|
||||
### Requirement: File Download with Access Control
|
||||
The system SHALL generate time-limited presigned download URLs for files, enforcing room membership-based access control.
|
||||
@@ -264,3 +274,39 @@ The system SHALL validate file types using MIME type detection (not just file ex
|
||||
- NOT upload to MinIO
|
||||
- Log security event
|
||||
|
||||
### Requirement: File-Message Association
|
||||
The system SHALL maintain a foreign key relationship between uploaded files and their associated chat messages, enabling contextual display of files in conversations.
|
||||
|
||||
#### Scenario: Query file with associated message
|
||||
- **WHEN** a client requests file metadata via `GET /api/rooms/{room_id}/files/{file_id}`
|
||||
- **THEN** the response SHALL include:
|
||||
```json
|
||||
{
|
||||
"file_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"message_id": "msg-789",
|
||||
"filename": "defect.jpg",
|
||||
"file_type": "image",
|
||||
"download_url": "...",
|
||||
"uploaded_at": "2025-12-08T10:30:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario: Delete file cascades to message
|
||||
- **WHEN** a file is soft-deleted via `DELETE /api/rooms/{room_id}/files/{file_id}`
|
||||
- **THEN** the system SHALL also soft-delete the associated message
|
||||
- **AND** broadcast both `file_deleted` and `message_deleted` events
|
||||
|
||||
### Requirement: Image Thumbnail URLs
|
||||
The system SHALL generate presigned URLs suitable for thumbnail display, allowing frontends to efficiently render image previews.
|
||||
|
||||
#### Scenario: File metadata includes thumbnail URL
|
||||
- **WHEN** file metadata is returned for an image file
|
||||
- **THEN** the response SHALL include a `thumbnail_url` field
|
||||
- **AND** the URL SHALL be a presigned MinIO URL valid for 1 hour
|
||||
- **AND** the frontend SHALL use CSS to constrain thumbnail display size
|
||||
|
||||
#### Scenario: Non-image files have no thumbnail
|
||||
- **WHEN** file metadata is returned for a non-image file (PDF, log, etc.)
|
||||
- **THEN** the response SHALL NOT include a `thumbnail_url` field
|
||||
- **AND** the frontend SHALL display a file-type icon instead
|
||||
|
||||
|
||||
Reference in New Issue
Block a user