feat(db): add storage backend metadata columns
Add storage_backend columns to track which backend (minio or filesystem) holds each file, enabling dual-running during migration. Migration 017_file_storage_metadata.sql: - item_files.storage_backend TEXT NOT NULL DEFAULT 'minio' - revisions.file_storage_backend TEXT NOT NULL DEFAULT 'minio' DB repository changes: - Revision struct: add FileStorageBackend field - ItemFile struct: add StorageBackend field - All INSERT queries include the new columns - All SELECT queries read them (COALESCE for pre-migration compat) - CreateRevisionFromExisting copies the backend from source revision - Default to 'minio' when field is empty (backward compat) Existing rows default to 'minio'. New uploads will write 'filesystem' when the filesystem backend is active. Closes #128
This commit is contained in:
@@ -35,11 +35,12 @@ type Revision struct {
|
||||
ItemID string
|
||||
RevisionNumber int
|
||||
Properties map[string]any
|
||||
FileKey *string
|
||||
FileVersion *string
|
||||
FileChecksum *string
|
||||
FileSize *int64
|
||||
ThumbnailKey *string
|
||||
FileKey *string
|
||||
FileVersion *string
|
||||
FileChecksum *string
|
||||
FileSize *int64
|
||||
FileStorageBackend string // "minio" or "filesystem"
|
||||
ThumbnailKey *string
|
||||
CreatedAt time.Time
|
||||
CreatedBy *string
|
||||
Comment *string
|
||||
@@ -306,16 +307,20 @@ func (r *ItemRepository) CreateRevision(ctx context.Context, rev *Revision) erro
|
||||
return fmt.Errorf("marshaling properties: %w", err)
|
||||
}
|
||||
|
||||
if rev.FileStorageBackend == "" {
|
||||
rev.FileStorageBackend = "minio"
|
||||
}
|
||||
|
||||
err = r.db.pool.QueryRow(ctx, `
|
||||
INSERT INTO revisions (
|
||||
item_id, revision_number, properties, file_key, file_version,
|
||||
file_checksum, file_size, thumbnail_key, created_by, comment
|
||||
file_checksum, file_size, file_storage_backend, thumbnail_key, created_by, comment
|
||||
)
|
||||
SELECT $1, current_revision + 1, $2, $3, $4, $5, $6, $7, $8, $9
|
||||
SELECT $1, current_revision + 1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
||||
FROM items WHERE id = $1
|
||||
RETURNING id, revision_number, created_at
|
||||
`, rev.ItemID, propsJSON, rev.FileKey, rev.FileVersion,
|
||||
rev.FileChecksum, rev.FileSize, rev.ThumbnailKey, rev.CreatedBy, rev.Comment,
|
||||
rev.FileChecksum, rev.FileSize, rev.FileStorageBackend, rev.ThumbnailKey, rev.CreatedBy, rev.Comment,
|
||||
).Scan(&rev.ID, &rev.RevisionNumber, &rev.CreatedAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting revision: %w", err)
|
||||
@@ -342,7 +347,8 @@ func (r *ItemRepository) GetRevisions(ctx context.Context, itemID string) ([]*Re
|
||||
if hasStatusColumn {
|
||||
rows, err = r.db.pool.Query(ctx, `
|
||||
SELECT id, item_id, revision_number, properties, file_key, file_version,
|
||||
file_checksum, file_size, thumbnail_key, created_at, created_by, comment,
|
||||
file_checksum, file_size, COALESCE(file_storage_backend, 'minio'),
|
||||
thumbnail_key, created_at, created_by, comment,
|
||||
COALESCE(status, 'draft') as status, COALESCE(labels, ARRAY[]::TEXT[]) as labels
|
||||
FROM revisions
|
||||
WHERE item_id = $1
|
||||
@@ -369,7 +375,8 @@ func (r *ItemRepository) GetRevisions(ctx context.Context, itemID string) ([]*Re
|
||||
if hasStatusColumn {
|
||||
err = rows.Scan(
|
||||
&rev.ID, &rev.ItemID, &rev.RevisionNumber, &propsJSON, &rev.FileKey, &rev.FileVersion,
|
||||
&rev.FileChecksum, &rev.FileSize, &rev.ThumbnailKey, &rev.CreatedAt, &rev.CreatedBy, &rev.Comment,
|
||||
&rev.FileChecksum, &rev.FileSize, &rev.FileStorageBackend,
|
||||
&rev.ThumbnailKey, &rev.CreatedAt, &rev.CreatedBy, &rev.Comment,
|
||||
&rev.Status, &rev.Labels,
|
||||
)
|
||||
} else {
|
||||
@@ -379,6 +386,7 @@ func (r *ItemRepository) GetRevisions(ctx context.Context, itemID string) ([]*Re
|
||||
)
|
||||
rev.Status = "draft"
|
||||
rev.Labels = []string{}
|
||||
rev.FileStorageBackend = "minio"
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("scanning revision: %w", err)
|
||||
@@ -412,13 +420,15 @@ func (r *ItemRepository) GetRevision(ctx context.Context, itemID string, revisio
|
||||
if hasStatusColumn {
|
||||
err = r.db.pool.QueryRow(ctx, `
|
||||
SELECT id, item_id, revision_number, properties, file_key, file_version,
|
||||
file_checksum, file_size, thumbnail_key, created_at, created_by, comment,
|
||||
file_checksum, file_size, COALESCE(file_storage_backend, 'minio'),
|
||||
thumbnail_key, created_at, created_by, comment,
|
||||
COALESCE(status, 'draft') as status, COALESCE(labels, ARRAY[]::TEXT[]) as labels
|
||||
FROM revisions
|
||||
WHERE item_id = $1 AND revision_number = $2
|
||||
`, itemID, revisionNumber).Scan(
|
||||
&rev.ID, &rev.ItemID, &rev.RevisionNumber, &propsJSON, &rev.FileKey, &rev.FileVersion,
|
||||
&rev.FileChecksum, &rev.FileSize, &rev.ThumbnailKey, &rev.CreatedAt, &rev.CreatedBy, &rev.Comment,
|
||||
&rev.FileChecksum, &rev.FileSize, &rev.FileStorageBackend,
|
||||
&rev.ThumbnailKey, &rev.CreatedAt, &rev.CreatedBy, &rev.Comment,
|
||||
&rev.Status, &rev.Labels,
|
||||
)
|
||||
} else {
|
||||
@@ -433,6 +443,7 @@ func (r *ItemRepository) GetRevision(ctx context.Context, itemID string, revisio
|
||||
)
|
||||
rev.Status = "draft"
|
||||
rev.Labels = []string{}
|
||||
rev.FileStorageBackend = "minio"
|
||||
}
|
||||
|
||||
if err == pgx.ErrNoRows {
|
||||
@@ -606,15 +617,16 @@ func (r *ItemRepository) CreateRevisionFromExisting(ctx context.Context, itemID
|
||||
|
||||
// Create new revision with copied properties (and optionally file reference)
|
||||
newRev := &Revision{
|
||||
ItemID: itemID,
|
||||
Properties: source.Properties,
|
||||
FileKey: source.FileKey,
|
||||
FileVersion: source.FileVersion,
|
||||
FileChecksum: source.FileChecksum,
|
||||
FileSize: source.FileSize,
|
||||
ThumbnailKey: source.ThumbnailKey,
|
||||
CreatedBy: createdBy,
|
||||
Comment: &comment,
|
||||
ItemID: itemID,
|
||||
Properties: source.Properties,
|
||||
FileKey: source.FileKey,
|
||||
FileVersion: source.FileVersion,
|
||||
FileChecksum: source.FileChecksum,
|
||||
FileSize: source.FileSize,
|
||||
FileStorageBackend: source.FileStorageBackend,
|
||||
ThumbnailKey: source.ThumbnailKey,
|
||||
CreatedBy: createdBy,
|
||||
Comment: &comment,
|
||||
}
|
||||
|
||||
// Insert the new revision
|
||||
@@ -626,13 +638,13 @@ func (r *ItemRepository) CreateRevisionFromExisting(ctx context.Context, itemID
|
||||
err = r.db.pool.QueryRow(ctx, `
|
||||
INSERT INTO revisions (
|
||||
item_id, revision_number, properties, file_key, file_version,
|
||||
file_checksum, file_size, thumbnail_key, created_by, comment, status
|
||||
file_checksum, file_size, file_storage_backend, thumbnail_key, created_by, comment, status
|
||||
)
|
||||
SELECT $1, current_revision + 1, $2, $3, $4, $5, $6, $7, $8, $9, 'draft'
|
||||
SELECT $1, current_revision + 1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'draft'
|
||||
FROM items WHERE id = $1
|
||||
RETURNING id, revision_number, created_at
|
||||
`, newRev.ItemID, propsJSON, newRev.FileKey, newRev.FileVersion,
|
||||
newRev.FileChecksum, newRev.FileSize, newRev.ThumbnailKey, newRev.CreatedBy, newRev.Comment,
|
||||
newRev.FileChecksum, newRev.FileSize, newRev.FileStorageBackend, newRev.ThumbnailKey, newRev.CreatedBy, newRev.Comment,
|
||||
).Scan(&newRev.ID, &newRev.RevisionNumber, &newRev.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inserting revision: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user