Add secure media access control with authenticated proxy endpoints
- Add MediaHandler with token-based proxy endpoints for serving media: - GET /api/media/document/:id - GET /api/media/document-image/:id - GET /api/media/completion-image/:id - Add MediaURL fields to response DTOs for documents and task completions - Add FindImageByID and FindCompletionImageByID repository methods - Preload Completions.Images in all task queries for proper media URLs - Remove public /uploads static file serving for security - Verify residence access before serving any media files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -143,3 +143,13 @@ func (r *DocumentRepository) DeleteDocumentImage(id uint) error {
|
||||
func (r *DocumentRepository) DeleteDocumentImages(documentID uint) error {
|
||||
return r.db.Where("document_id = ?", documentID).Delete(&models.DocumentImage{}).Error
|
||||
}
|
||||
|
||||
// FindImageByID finds a document image by ID
|
||||
func (r *DocumentRepository) FindImageByID(id uint) (*models.DocumentImage, error) {
|
||||
var image models.DocumentImage
|
||||
err := r.db.First(&image, id).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &image, nil
|
||||
}
|
||||
|
||||
@@ -317,6 +317,8 @@ func (r *ResidenceRepository) GetTasksForReport(residenceID uint) ([]models.Task
|
||||
Preload("Priority").
|
||||
Preload("Status").
|
||||
Preload("Completions").
|
||||
Preload("Completions.Images").
|
||||
Preload("Completions.CompletedBy").
|
||||
Where("residence_id = ?", residenceID).
|
||||
Order("due_date ASC NULLS LAST, created_at DESC").
|
||||
Find(&tasks).Error
|
||||
|
||||
@@ -31,6 +31,8 @@ func (r *TaskRepository) FindByID(id uint) (*models.Task, error) {
|
||||
Preload("Status").
|
||||
Preload("Frequency").
|
||||
Preload("Completions").
|
||||
Preload("Completions.Images").
|
||||
Preload("Completions.CompletedBy").
|
||||
First(&task, id).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -48,6 +50,8 @@ func (r *TaskRepository) FindByResidence(residenceID uint) ([]models.Task, error
|
||||
Preload("Status").
|
||||
Preload("Frequency").
|
||||
Preload("Completions").
|
||||
Preload("Completions.Images").
|
||||
Preload("Completions.CompletedBy").
|
||||
Where("residence_id = ?", residenceID).
|
||||
Order("due_date ASC NULLS LAST, created_at DESC").
|
||||
Find(&tasks).Error
|
||||
@@ -65,6 +69,8 @@ func (r *TaskRepository) FindByUser(userID uint, residenceIDs []uint) ([]models.
|
||||
Preload("Status").
|
||||
Preload("Frequency").
|
||||
Preload("Completions").
|
||||
Preload("Completions.Images").
|
||||
Preload("Completions.CompletedBy").
|
||||
Where("residence_id IN ?", residenceIDs).
|
||||
Order("due_date ASC NULLS LAST, created_at DESC").
|
||||
Find(&tasks).Error
|
||||
@@ -135,6 +141,8 @@ func (r *TaskRepository) GetKanbanData(residenceID uint, daysThreshold int) (*mo
|
||||
Preload("Status").
|
||||
Preload("Frequency").
|
||||
Preload("Completions").
|
||||
Preload("Completions.Images").
|
||||
Preload("Completions.CompletedBy").
|
||||
Where("residence_id = ? AND is_archived = ?", residenceID, false).
|
||||
Order("due_date ASC NULLS LAST, priority_id DESC, created_at DESC").
|
||||
Find(&tasks).Error
|
||||
@@ -259,6 +267,8 @@ func (r *TaskRepository) GetKanbanDataForMultipleResidences(residenceIDs []uint,
|
||||
Preload("Status").
|
||||
Preload("Frequency").
|
||||
Preload("Completions").
|
||||
Preload("Completions.Images").
|
||||
Preload("Completions.CompletedBy").
|
||||
Preload("Residence").
|
||||
Where("residence_id IN ? AND is_archived = ?", residenceIDs, false).
|
||||
Order("due_date ASC NULLS LAST, priority_id DESC, created_at DESC").
|
||||
@@ -484,6 +494,16 @@ func (r *TaskRepository) DeleteCompletionImage(id uint) error {
|
||||
return r.db.Delete(&models.TaskCompletionImage{}, id).Error
|
||||
}
|
||||
|
||||
// FindCompletionImageByID finds a completion image by ID
|
||||
func (r *TaskRepository) FindCompletionImageByID(id uint) (*models.TaskCompletionImage, error) {
|
||||
var image models.TaskCompletionImage
|
||||
err := r.db.First(&image, id).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &image, nil
|
||||
}
|
||||
|
||||
// TaskStatistics represents aggregated task statistics
|
||||
type TaskStatistics struct {
|
||||
TotalTasks int
|
||||
|
||||
Reference in New Issue
Block a user