package filemanager import ( "strings" "time" "gorm.io/gorm" "wucher/internal/shared/pkg/uuidv7" ) const ( FileStatusUploaded = "uploaded" FileStatusProcessing = "processing" FileStatusValidated = "validated" FileStatusFailed = "failed" FileStatusReady = "ready" FileStatusTrashed = "trashed" FileLifecycleStatusTemp = "temp" FileLifecycleStatusDraft = "draft" FileLifecycleStatusActive = "active" FileLifecycleStatusFinal = "final" FileLifecycleStatusOrphanPending = "orphan_pending" FileLifecycleStatusDeleted = "deleted" ) type File struct { ID []byte `gorm:"type:binary(16);primaryKey;column:id"` FolderID []byte `gorm:"type:binary(16);not null;column:folder_id;index:idx_file_files_folder_active,priority:1;index:uq_file_files_folder_name_slot,priority:1"` Name string `gorm:"type:varchar(255);not null;column:name"` NameNormalized string `gorm:"type:varchar(255);not null;column:name_normalized;index:uq_file_files_folder_name_slot,priority:2"` Extension string `gorm:"type:varchar(32);column:extension"` SizeBytes int64 `gorm:"type:bigint unsigned;not null;default:0;column:size_bytes"` MimeType string `gorm:"type:varchar(255);not null;column:mime_type"` Bucket string `gorm:"type:varchar(128);not null;column:bucket"` ObjectKey string `gorm:"type:varchar(512);not null;column:object_key;uniqueIndex:uq_file_files_object_key"` ObjectVersion string `gorm:"type:varchar(128);column:object_version"` ETag string `gorm:"type:varchar(128);column:etag"` ThumbnailMimeType *string `gorm:"type:varchar(255);column:thumbnail_mime_type"` ThumbnailSizeBytes int64 `gorm:"type:bigint unsigned;column:thumbnail_size_bytes"` ThumbnailBucket *string `gorm:"type:varchar(128);column:thumbnail_bucket"` ThumbnailObjectKey *string `gorm:"type:varchar(512);column:thumbnail_object_key;uniqueIndex:uq_file_files_thumbnail_object_key"` ThumbnailObjectVersion *string `gorm:"type:varchar(128);column:thumbnail_object_version"` ThumbnailETag *string `gorm:"type:varchar(128);column:thumbnail_etag"` PreviewMimeType *string `gorm:"type:varchar(255);column:preview_mime_type"` PreviewSizeBytes int64 `gorm:"type:bigint unsigned;column:preview_size_bytes"` PreviewBucket *string `gorm:"type:varchar(128);column:preview_bucket"` PreviewObjectKey *string `gorm:"type:varchar(512);column:preview_object_key;uniqueIndex:uq_file_files_preview_object_key"` PreviewObjectVersion *string `gorm:"type:varchar(128);column:preview_object_version"` PreviewETag *string `gorm:"type:varchar(128);column:preview_etag"` ChecksumSHA256 string `gorm:"type:char(64);column:checksum_sha256"` IsTemplate bool `gorm:"column:is_template;not null;default:false"` TemplateUUID []byte `gorm:"type:binary(16);column:template_uuid;index:idx_file_files_template_takeover,priority:1"` TakeoverID []byte `gorm:"type:binary(16);column:takeover_id;index:idx_file_files_template_takeover,priority:2"` Status string `gorm:"type:varchar(32);not null;default:'uploaded';column:status;index:idx_file_files_status_updated,priority:1"` ProcessingStartedAt *time.Time `gorm:"column:processing_started_at"` ProcessedAt *time.Time `gorm:"column:processed_at"` ValidatedAt *time.Time `gorm:"column:validated_at"` FailedAt *time.Time `gorm:"column:failed_at"` FailureReason string `gorm:"type:text;column:failure_reason"` UploadError string `gorm:"type:text;column:upload_error"` NameSlot string `gorm:"type:char(36);not null;default:'live';column:name_slot;index:uq_file_files_folder_name_slot,priority:3"` CreatedAt time.Time `gorm:"column:created_at"` CreatedBy []byte `gorm:"type:binary(16);column:created_by"` UpdatedAt time.Time `gorm:"column:updated_at;index:idx_file_files_status_updated,priority:2"` UpdatedBy []byte `gorm:"type:binary(16);column:updated_by"` DeletedAt *time.Time `gorm:"column:deleted_at;index:idx_file_files_folder_active,priority:2"` DeletedBy []byte `gorm:"type:binary(16);column:deleted_by"` TrashedAt *time.Time `gorm:"column:trashed_at"` PurgeAt *time.Time `gorm:"column:purge_at;index:idx_file_files_purge_at"` LifecycleStatus string `gorm:"type:varchar(32);not null;default:'temp';column:lifecycle_status;index:idx_file_files_lifecycle_status_orphaned,priority:1"` AttachedAt *time.Time `gorm:"column:attached_at"` OrphanedAt *time.Time `gorm:"column:orphaned_at;index:idx_file_files_lifecycle_status_orphaned,priority:2"` LifecycleDeletedAt *time.Time `gorm:"column:lifecycle_deleted_at"` WOPILockID string `gorm:"type:varchar(128);column:wopi_lock_id"` WOPILockJTI string `gorm:"type:varchar(128);column:wopi_lock_jti"` WOPILockedAt *time.Time `gorm:"column:wopi_locked_at"` WOPILockExpiresAt *time.Time `gorm:"column:wopi_lock_expires_at"` Folder *Folder `gorm:"foreignKey:FolderID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT"` } func (File) TableName() string { return "file_files" } func (f *File) BeforeCreate(tx *gorm.DB) error { if len(f.ID) == 0 { f.ID = uuidv7.MustBytes() } return nil } func NormalizeFileStatus(status string) string { s := strings.ToLower(strings.TrimSpace(status)) switch s { case "pending": return FileStatusUploaded case "uploading": return FileStatusProcessing default: return s } } func IsKnownFileStatus(status string) bool { switch NormalizeFileStatus(status) { case FileStatusUploaded, FileStatusProcessing, FileStatusValidated, FileStatusFailed, FileStatusReady, FileStatusTrashed: return true default: return false } } func CanTransitionFileStatus(fromStatus, toStatus string) bool { from := NormalizeFileStatus(fromStatus) to := NormalizeFileStatus(toStatus) switch from { case FileStatusUploaded: return to == FileStatusProcessing || to == FileStatusFailed case FileStatusProcessing: return to == FileStatusValidated || to == FileStatusFailed case FileStatusValidated: return to == FileStatusReady || to == FileStatusFailed case FileStatusReady: return to == FileStatusTrashed default: return false } } func NormalizeFileLifecycleStatus(status string) string { s := strings.ToLower(strings.TrimSpace(status)) switch s { case "": return FileLifecycleStatusTemp default: return s } } func IsKnownFileLifecycleStatus(status string) bool { switch NormalizeFileLifecycleStatus(status) { case FileLifecycleStatusTemp, FileLifecycleStatusDraft, FileLifecycleStatusActive, FileLifecycleStatusFinal, FileLifecycleStatusOrphanPending, FileLifecycleStatusDeleted: return true default: return false } }