- 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>
156 lines
4.8 KiB
Go
156 lines
4.8 KiB
Go
package repositories
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/treytartt/casera-api/internal/models"
|
|
)
|
|
|
|
// DocumentRepository handles database operations for documents
|
|
type DocumentRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
// NewDocumentRepository creates a new document repository
|
|
func NewDocumentRepository(db *gorm.DB) *DocumentRepository {
|
|
return &DocumentRepository{db: db}
|
|
}
|
|
|
|
// FindByID finds a document by ID with preloaded relations
|
|
func (r *DocumentRepository) FindByID(id uint) (*models.Document, error) {
|
|
var document models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Task").
|
|
Preload("Images").
|
|
Where("id = ? AND is_active = ?", id, true).
|
|
First(&document).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &document, nil
|
|
}
|
|
|
|
// FindByResidence finds all documents for a residence
|
|
func (r *DocumentRepository) FindByResidence(residenceID uint) ([]models.Document, error) {
|
|
var documents []models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Images").
|
|
Where("residence_id = ? AND is_active = ?", residenceID, true).
|
|
Order("created_at DESC").
|
|
Find(&documents).Error
|
|
return documents, err
|
|
}
|
|
|
|
// FindByUser finds all documents accessible to a user
|
|
func (r *DocumentRepository) FindByUser(residenceIDs []uint) ([]models.Document, error) {
|
|
var documents []models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Residence").
|
|
Preload("Images").
|
|
Where("residence_id IN ? AND is_active = ?", residenceIDs, true).
|
|
Order("created_at DESC").
|
|
Find(&documents).Error
|
|
return documents, err
|
|
}
|
|
|
|
// FindWarranties finds all warranty documents
|
|
func (r *DocumentRepository) FindWarranties(residenceIDs []uint) ([]models.Document, error) {
|
|
var documents []models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Residence").
|
|
Preload("Images").
|
|
Where("residence_id IN ? AND is_active = ? AND document_type = ?",
|
|
residenceIDs, true, models.DocumentTypeWarranty).
|
|
Order("expiry_date ASC NULLS LAST").
|
|
Find(&documents).Error
|
|
return documents, err
|
|
}
|
|
|
|
// FindExpiringWarranties finds warranties expiring within the specified days
|
|
func (r *DocumentRepository) FindExpiringWarranties(residenceIDs []uint, days int) ([]models.Document, error) {
|
|
threshold := time.Now().UTC().AddDate(0, 0, days)
|
|
now := time.Now().UTC()
|
|
|
|
var documents []models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Residence").
|
|
Preload("Images").
|
|
Where("residence_id IN ? AND is_active = ? AND document_type = ? AND expiry_date > ? AND expiry_date <= ?",
|
|
residenceIDs, true, models.DocumentTypeWarranty, now, threshold).
|
|
Order("expiry_date ASC").
|
|
Find(&documents).Error
|
|
return documents, err
|
|
}
|
|
|
|
// Create creates a new document
|
|
func (r *DocumentRepository) Create(document *models.Document) error {
|
|
return r.db.Create(document).Error
|
|
}
|
|
|
|
// Update updates a document
|
|
func (r *DocumentRepository) Update(document *models.Document) error {
|
|
return r.db.Save(document).Error
|
|
}
|
|
|
|
// Delete soft-deletes a document
|
|
func (r *DocumentRepository) Delete(id uint) error {
|
|
return r.db.Model(&models.Document{}).
|
|
Where("id = ?", id).
|
|
Update("is_active", false).Error
|
|
}
|
|
|
|
// Activate activates a document
|
|
func (r *DocumentRepository) Activate(id uint) error {
|
|
return r.db.Model(&models.Document{}).
|
|
Where("id = ?", id).
|
|
Update("is_active", true).Error
|
|
}
|
|
|
|
// Deactivate deactivates a document
|
|
func (r *DocumentRepository) Deactivate(id uint) error {
|
|
return r.db.Model(&models.Document{}).
|
|
Where("id = ?", id).
|
|
Update("is_active", false).Error
|
|
}
|
|
|
|
// CountByResidence counts documents in a residence
|
|
func (r *DocumentRepository) CountByResidence(residenceID uint) (int64, error) {
|
|
var count int64
|
|
err := r.db.Model(&models.Document{}).
|
|
Where("residence_id = ? AND is_active = ?", residenceID, true).
|
|
Count(&count).Error
|
|
return count, err
|
|
}
|
|
|
|
// FindByIDIncludingInactive finds a document by ID including inactive ones
|
|
func (r *DocumentRepository) FindByIDIncludingInactive(id uint, document *models.Document) error {
|
|
return r.db.Preload("CreatedBy").Preload("Images").First(document, id).Error
|
|
}
|
|
|
|
// CreateDocumentImage creates a new document image
|
|
func (r *DocumentRepository) CreateDocumentImage(image *models.DocumentImage) error {
|
|
return r.db.Create(image).Error
|
|
}
|
|
|
|
// DeleteDocumentImage deletes a document image
|
|
func (r *DocumentRepository) DeleteDocumentImage(id uint) error {
|
|
return r.db.Delete(&models.DocumentImage{}, id).Error
|
|
}
|
|
|
|
// DeleteDocumentImages deletes all images for a document
|
|
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
|
|
}
|