The Files module provides tenant-scoped file asset management with upload, download, soft-delete, and pagination. Files integrate with Tickets and Chat modules as attachment providers.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/files | POST | Upload file (multipart form) |
/api/files/{id} | GET | Download file by asset ID |
/api/files/{id} | DELETE | Soft-delete file |
/api/files | GET | Paginated file listing |
FileAsset entity
public class FileAssetEntity : TenantSoftDeleteEntityBase{ public string FileName { get; set; } public string ContentType { get; set; } public long FileSizeBytes { get; set; } public string StoragePath { get; set; } public string? Description { get; set; }}Architecture
FileEndpoints (Minimal API) │ ▼FileAssetService │ ├── IFileAssetRepository → SqlSugarFileAssetRepository ├── IFileStorage → LocalFileStorage └── IFileEventPublisher → CapFileEventPublisherUpload flow
- Client sends
POST /api/fileswith multipart form data FileAssetServicereads the stream viaIFileStorage.SaveAsync()- Metadata persisted in
FileAssetEntityvia SqlSugar IFileEventPublisherpublishes file.uploaded integration event- Returns the file asset ID and metadata
Cross-module attachments
Files serve as the attachment backbone for other modules:
| Module | Attachment Service | Purpose |
|---|---|---|
| Tickets | FileAssetTicketAttachmentAccessService | Attach files to tickets |
| Chat | FileAssetChatAttachmentAccessService | Attach files to chat messages |
Both implement module-specific access patterns:
public interface ITicketAttachmentAccessService{ Task<FileAssetEntity?> GetAttachmentAsync(Guid attachmentId, CancellationToken ct);}
public interface IChatAttachmentAccessService{ Task<FileAssetEntity?> GetAttachmentAsync(Guid attachmentId, CancellationToken ct);}Storage configuration
{ "Storage": { "BasePath": "./storage/files" }}Files are stored at {basePath}/{year}/{month}/{guid}.{extension}.
See also
- Storage building block — IFileStorage abstraction