Files
Algis DumbrisandClaude Opus 4.6 667b7a4c2e feat: file attachments and thread visibility (009-attachments-threads)
Web UI: paperclip button for file upload (images, PDFs, text), inline
attachment cards with file icon/name/size, image thumbnails with
fullscreen overlay, attachment display in thread panel.

Threads: always-visible reply count badges on messages, clickable to
open thread panel. reply_count and attachments enriched in all API
responses via batch queries.

MCP: attachments parameter on send_message tool, updated tool
descriptions for threading and attachment workflow guidance.

Backend: file type validation (allowlist), AttachmentLinker interface
to avoid circular deps, GetReplyCounts batch query, EnrichMessages
method on MessagingService.

Admin CLI: synapbus attachments backup/restore with tar.gz archives,
dedup-safe restore.

24 new test cases across 4 packages. All 24 test packages pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:36:21 +02:00

64 lines
1.8 KiB
Go

package attachments
import (
"errors"
"io"
"time"
)
// MaxFileSize is the maximum allowed upload size (50 MB).
const MaxFileSize = 50 * 1024 * 1024 // 50 MB
// Sentinel errors.
var (
ErrNotFound = errors.New("attachment not found")
ErrFileTooLarge = errors.New("file exceeds maximum size of 50MB")
ErrEmptyFile = errors.New("empty file not allowed")
ErrFileMissing = errors.New("attachment file missing from disk")
ErrUnsupportedType = errors.New("unsupported file type: only images (jpg, png, gif, webp, svg), PDFs, and text files are allowed")
)
// Attachment represents the metadata for a stored file.
type Attachment struct {
ID int64 `json:"id"`
Hash string `json:"hash"`
OriginalFilename string `json:"original_filename"`
Size int64 `json:"size"`
MIMEType string `json:"mime_type"`
MessageID *int64 `json:"message_id,omitempty"`
UploadedBy string `json:"uploaded_by"`
CreatedAt time.Time `json:"created_at"`
}
// UploadRequest contains the parameters for uploading an attachment.
type UploadRequest struct {
Content io.Reader
Filename string
MIMEType string
MessageID *int64
UploadedBy string
}
// UploadResult contains the result of a successful upload.
type UploadResult struct {
Hash string `json:"hash"`
Size int64 `json:"size"`
MIMEType string `json:"mime_type"`
Filename string `json:"original_filename"`
}
// DownloadResult contains the result of a successful download.
type DownloadResult struct {
Content io.ReadCloser
Hash string
Filename string
MIMEType string
Size int64
}
// GCResult contains the result of a garbage collection run.
type GCResult struct {
FilesRemoved int `json:"files_removed"`
BytesReclaimed int64 `json:"bytes_reclaimed"`
}