package calendar import ( "context" "errors" "time" "github.com/jackc/pgx/v5" ) var ErrAttachmentNotFound = errors.New("attachment not found") type Attachment struct { ID string `json:"id"` CalendarItemID string `json:"calendar_item_id"` OriginalFilename string `json:"original_filename"` StoredFilename string `json:"stored_filename"` MimeType string `json:"mime_type"` SizeBytes int64 `json:"size_bytes"` StorageDriver string `json:"storage_driver"` StoragePath string `json:"-"` CreatedBy *string `json:"created_by"` CreatedAt time.Time `json:"created_at"` } type CreateAttachmentInput struct { CalendarItemID string OriginalFilename string StoredFilename string MimeType string SizeBytes int64 StorageDriver string StoragePath string CreatedBy string } func (s Store) CreateAttachment(ctx context.Context, input CreateAttachmentInput) (Attachment, error) { row := s.db.QueryRow(ctx, ` INSERT INTO calendar_item_attachments ( calendar_item_id, original_filename, stored_filename, mime_type, size_bytes, storage_driver, storage_path, created_by ) VALUES ($1::uuid, $2, $3, $4, $5, $6, $7, NULLIF($8, '')::uuid) RETURNING id::text, calendar_item_id::text, original_filename, stored_filename, mime_type, size_bytes, storage_driver, storage_path, created_by::text, created_at `, input.CalendarItemID, input.OriginalFilename, input.StoredFilename, input.MimeType, input.SizeBytes, input.StorageDriver, input.StoragePath, input.CreatedBy) return scanAttachment(row) } func (s Store) ListAttachments(ctx context.Context, calendarItemID string, viewerUserID string) ([]Attachment, error) { rows, err := s.db.Query(ctx, ` SELECT calendar_item_attachments.id::text, calendar_item_attachments.calendar_item_id::text, calendar_item_attachments.original_filename, calendar_item_attachments.stored_filename, calendar_item_attachments.mime_type, calendar_item_attachments.size_bytes, calendar_item_attachments.storage_driver, calendar_item_attachments.storage_path, calendar_item_attachments.created_by::text, calendar_item_attachments.created_at FROM calendar_item_attachments INNER JOIN calendar_items ON calendar_items.id = calendar_item_attachments.calendar_item_id WHERE calendar_item_attachments.calendar_item_id = $1::uuid AND ( $2 = '' OR EXISTS ( SELECT 1 FROM client_users WHERE client_users.client_id = calendar_items.client_id AND client_users.user_id = $2::uuid ) ) ORDER BY calendar_item_attachments.created_at DESC `, calendarItemID, viewerUserID) if err != nil { return nil, err } defer rows.Close() attachments := []Attachment{} for rows.Next() { attachment, err := scanAttachment(rows) if err != nil { return nil, err } attachments = append(attachments, attachment) } return attachments, rows.Err() } func (s Store) FindAttachment(ctx context.Context, id string, viewerUserID string) (Attachment, error) { row := s.db.QueryRow(ctx, ` SELECT calendar_item_attachments.id::text, calendar_item_attachments.calendar_item_id::text, calendar_item_attachments.original_filename, calendar_item_attachments.stored_filename, calendar_item_attachments.mime_type, calendar_item_attachments.size_bytes, calendar_item_attachments.storage_driver, calendar_item_attachments.storage_path, calendar_item_attachments.created_by::text, calendar_item_attachments.created_at FROM calendar_item_attachments INNER JOIN calendar_items ON calendar_items.id = calendar_item_attachments.calendar_item_id WHERE calendar_item_attachments.id = $1::uuid AND ( $2 = '' OR EXISTS ( SELECT 1 FROM client_users WHERE client_users.client_id = calendar_items.client_id AND client_users.user_id = $2::uuid ) ) `, id, viewerUserID) return scanAttachment(row) } func (s Store) DeleteAttachment(ctx context.Context, id string) (Attachment, error) { row := s.db.QueryRow(ctx, ` DELETE FROM calendar_item_attachments WHERE id = $1::uuid RETURNING id::text, calendar_item_id::text, original_filename, stored_filename, mime_type, size_bytes, storage_driver, storage_path, created_by::text, created_at `, id) return scanAttachment(row) } type attachmentScanner interface { Scan(dest ...any) error } func scanAttachment(row attachmentScanner) (Attachment, error) { var attachment Attachment err := row.Scan( &attachment.ID, &attachment.CalendarItemID, &attachment.OriginalFilename, &attachment.StoredFilename, &attachment.MimeType, &attachment.SizeBytes, &attachment.StorageDriver, &attachment.StoragePath, &attachment.CreatedBy, &attachment.CreatedAt, ) if errors.Is(err, pgx.ErrNoRows) { return Attachment{}, ErrAttachmentNotFound } if err != nil { return Attachment{}, err } return attachment, nil }