Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
327
cloud/maplefile-backend/internal/repo/filemetadata/create.go
Normal file
327
cloud/maplefile-backend/internal/repo/filemetadata/create.go
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/repo/filemetadata/create.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
)
|
||||
|
||||
func (impl *fileMetadataRepositoryImpl) Create(file *dom_file.File) error {
|
||||
if file == nil {
|
||||
return fmt.Errorf("file cannot be nil")
|
||||
}
|
||||
|
||||
if !impl.isValidUUID(file.ID) {
|
||||
return fmt.Errorf("file ID is required")
|
||||
}
|
||||
|
||||
if !impl.isValidUUID(file.CollectionID) {
|
||||
return fmt.Errorf("collection ID is required")
|
||||
}
|
||||
|
||||
if !impl.isValidUUID(file.OwnerID) {
|
||||
return fmt.Errorf("owner ID is required")
|
||||
}
|
||||
|
||||
// Set creation timestamp if not set
|
||||
if file.CreatedAt.IsZero() {
|
||||
file.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
if file.ModifiedAt.IsZero() {
|
||||
file.ModifiedAt = file.CreatedAt
|
||||
}
|
||||
|
||||
// Ensure state is set
|
||||
if file.State == "" {
|
||||
file.State = dom_file.FileStateActive
|
||||
}
|
||||
|
||||
// Serialize encrypted file key
|
||||
encryptedKeyJSON, err := impl.serializeEncryptedFileKey(file.EncryptedFileKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize encrypted file key: %w", err)
|
||||
}
|
||||
|
||||
// Serialize tags
|
||||
tagsJSON, err := impl.serializeTags(file.Tags)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize tags: %w", err)
|
||||
}
|
||||
|
||||
batch := impl.Session.NewBatch(gocql.LoggedBatch)
|
||||
|
||||
// 1. Insert into main table
|
||||
batch.Query(`INSERT INTO maplefile.files_by_id
|
||||
(id, collection_id, owner_id, encrypted_metadata, encrypted_file_key, encryption_version,
|
||||
encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
|
||||
encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes, tags,
|
||||
created_at, created_by_user_id, modified_at, modified_by_user_id, version,
|
||||
state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.ID, file.CollectionID, file.OwnerID, file.EncryptedMetadata, encryptedKeyJSON,
|
||||
file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
|
||||
file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, tagsJSON, file.CreatedAt, file.CreatedByUserID,
|
||||
file.ModifiedAt, file.ModifiedByUserID, file.Version, file.State,
|
||||
file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 2. Insert into collection table
|
||||
batch.Query(`INSERT INTO maplefile.files_by_collection
|
||||
(collection_id, modified_at, id, owner_id, encrypted_metadata, encrypted_file_key,
|
||||
encryption_version, encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
|
||||
encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
|
||||
created_at, created_by_user_id, modified_by_user_id, version,
|
||||
state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.CollectionID, file.ModifiedAt, file.ID, file.OwnerID, file.EncryptedMetadata,
|
||||
encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
|
||||
file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, file.CreatedAt, file.CreatedByUserID,
|
||||
file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 3. Insert into owner table
|
||||
batch.Query(`INSERT INTO maplefile.files_by_owner
|
||||
(owner_id, modified_at, id, collection_id, encrypted_metadata, encrypted_file_key,
|
||||
encryption_version, encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
|
||||
encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
|
||||
created_at, created_by_user_id, modified_by_user_id, version,
|
||||
state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.OwnerID, file.ModifiedAt, file.ID, file.CollectionID, file.EncryptedMetadata,
|
||||
encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
|
||||
file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, file.CreatedAt, file.CreatedByUserID,
|
||||
file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 4. Insert into created_by table
|
||||
batch.Query(`INSERT INTO maplefile.files_by_creator
|
||||
(created_by_user_id, created_at, id, collection_id, owner_id, encrypted_metadata,
|
||||
encrypted_file_key, encryption_version, encrypted_hash, encrypted_file_object_key,
|
||||
encrypted_file_size_in_bytes, encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
|
||||
modified_at, modified_by_user_id, version, state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.CreatedByUserID, file.CreatedAt, file.ID, file.CollectionID, file.OwnerID,
|
||||
file.EncryptedMetadata, encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
|
||||
file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, file.ModifiedAt, file.ModifiedByUserID, file.Version,
|
||||
file.State, file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 5. Insert into user sync table (for owner and any collection members)
|
||||
batch.Query(`INSERT INTO maplefile.files_by_user
|
||||
(user_id, modified_at, id, collection_id, owner_id, encrypted_metadata,
|
||||
encrypted_file_key, encryption_version, encrypted_hash, encrypted_file_object_key,
|
||||
encrypted_file_size_in_bytes, encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
|
||||
tags, created_at, created_by_user_id, modified_by_user_id, version,
|
||||
state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.OwnerID, file.ModifiedAt, file.ID, file.CollectionID, file.OwnerID,
|
||||
file.EncryptedMetadata, encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
|
||||
file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, tagsJSON, file.CreatedAt, file.CreatedByUserID,
|
||||
file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 6. Insert into denormalized files_by_tag_id table for each tag
|
||||
for _, tag := range file.Tags {
|
||||
batch.Query(`INSERT INTO maplefile.files_by_tag_id
|
||||
(tag_id, file_id, collection_id, owner_id, encrypted_metadata, encrypted_file_key,
|
||||
encryption_version, encrypted_hash, encrypted_file_object_key,
|
||||
encrypted_file_size_in_bytes, encrypted_thumbnail_object_key,
|
||||
encrypted_thumbnail_size_in_bytes, tag_ids, created_at, created_by_user_id,
|
||||
modified_at, modified_by_user_id, version, state, tombstone_version, tombstone_expiry,
|
||||
created_from_ip_address, modified_from_ip_address, ip_anonymized_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
tag.ID, file.ID, file.CollectionID, file.OwnerID, file.EncryptedMetadata,
|
||||
encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
|
||||
file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes,
|
||||
file.EncryptedThumbnailObjectKey, file.EncryptedThumbnailSizeInBytes,
|
||||
tagsJSON, file.CreatedAt, file.CreatedByUserID, file.ModifiedAt,
|
||||
file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion,
|
||||
file.TombstoneExpiry,
|
||||
nil, nil, nil) // IP tracking fields not yet in domain model
|
||||
}
|
||||
|
||||
// Execute batch
|
||||
if err := impl.Session.ExecuteBatch(batch); err != nil {
|
||||
impl.Logger.Error("failed to create file",
|
||||
zap.String("file_id", file.ID.String()),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("failed to create file: %w", err)
|
||||
}
|
||||
|
||||
// Increment collection file count for active files
|
||||
if file.State == dom_file.FileStateActive {
|
||||
if err := impl.CollectionRepo.IncrementFileCount(context.Background(), file.CollectionID); err != nil {
|
||||
impl.Logger.Error("failed to increment collection file count",
|
||||
zap.String("file_id", file.ID.String()),
|
||||
zap.String("collection_id", file.CollectionID.String()),
|
||||
zap.Error(err))
|
||||
// Don't fail the entire operation if count update fails
|
||||
}
|
||||
}
|
||||
|
||||
impl.Logger.Info("file created successfully",
|
||||
zap.String("file_id", file.ID.String()),
|
||||
zap.String("collection_id", file.CollectionID.String()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (impl *fileMetadataRepositoryImpl) CreateMany(files []*dom_file.File) error {
|
||||
if len(files) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
batch := impl.Session.NewBatch(gocql.LoggedBatch)
|
||||
|
||||
for _, file := range files {
|
||||
if file == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Set timestamps if not set
|
||||
if file.CreatedAt.IsZero() {
|
||||
file.CreatedAt = time.Now()
|
||||
}
|
||||
if file.ModifiedAt.IsZero() {
|
||||
file.ModifiedAt = file.CreatedAt
|
||||
}
|
||||
if file.State == "" {
|
||||
file.State = dom_file.FileStateActive
|
||||
}
|
||||
|
||||
encryptedKeyJSON, err := impl.serializeEncryptedFileKey(file.EncryptedFileKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize encrypted file key for file %s: %w", file.ID.String(), err)
|
||||
}
|
||||
|
||||
tagsJSON, err := impl.serializeTags(file.Tags)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize tags for file %s: %w", file.ID.String(), err)
|
||||
}
|
||||
|
||||
// Add to all 5 tables (same as Create but in batch)
|
||||
batch.Query(`INSERT INTO maplefile.files_by_id
|
||||
(id, collection_id, owner_id, encrypted_metadata, encrypted_file_key, encryption_version,
|
||||
encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
|
||||
encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes, tags,
|
||||
created_at, created_by_user_id, modified_at, modified_by_user_id, version,
|
||||
state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.ID, file.CollectionID, file.OwnerID, file.EncryptedMetadata, encryptedKeyJSON,
|
||||
file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
|
||||
file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, tagsJSON, file.CreatedAt, file.CreatedByUserID,
|
||||
file.ModifiedAt, file.ModifiedByUserID, file.Version, file.State,
|
||||
file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 2. Insert into collection table
|
||||
batch.Query(`INSERT INTO maplefile.files_by_collection
|
||||
(collection_id, modified_at, id, owner_id, encrypted_metadata, encrypted_file_key,
|
||||
encryption_version, encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
|
||||
encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
|
||||
created_at, created_by_user_id, modified_by_user_id, version,
|
||||
state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.CollectionID, file.ModifiedAt, file.ID, file.OwnerID, file.EncryptedMetadata,
|
||||
encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
|
||||
file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, file.CreatedAt, file.CreatedByUserID,
|
||||
file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 3. Insert into owner table
|
||||
batch.Query(`INSERT INTO maplefile.files_by_owner
|
||||
(owner_id, modified_at, id, collection_id, encrypted_metadata, encrypted_file_key,
|
||||
encryption_version, encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
|
||||
encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
|
||||
created_at, created_by_user_id, modified_by_user_id, version,
|
||||
state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.OwnerID, file.ModifiedAt, file.ID, file.CollectionID, file.EncryptedMetadata,
|
||||
encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
|
||||
file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, file.CreatedAt, file.CreatedByUserID,
|
||||
file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 4. Insert into created_by table
|
||||
batch.Query(`INSERT INTO maplefile.files_by_creator
|
||||
(created_by_user_id, created_at, id, collection_id, owner_id, encrypted_metadata,
|
||||
encrypted_file_key, encryption_version, encrypted_hash, encrypted_file_object_key,
|
||||
encrypted_file_size_in_bytes, encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
|
||||
modified_at, modified_by_user_id, version, state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.CreatedByUserID, file.CreatedAt, file.ID, file.CollectionID, file.OwnerID,
|
||||
file.EncryptedMetadata, encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
|
||||
file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, file.ModifiedAt, file.ModifiedByUserID, file.Version,
|
||||
file.State, file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 5. Insert into user sync table (for owner and any collection members)
|
||||
batch.Query(`INSERT INTO maplefile.files_by_user
|
||||
(user_id, modified_at, id, collection_id, owner_id, encrypted_metadata,
|
||||
encrypted_file_key, encryption_version, encrypted_hash, encrypted_file_object_key,
|
||||
encrypted_file_size_in_bytes, encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
|
||||
tags, created_at, created_by_user_id, modified_by_user_id, version,
|
||||
state, tombstone_version, tombstone_expiry)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
file.OwnerID, file.ModifiedAt, file.ID, file.CollectionID, file.OwnerID,
|
||||
file.EncryptedMetadata, encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
|
||||
file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
|
||||
file.EncryptedThumbnailSizeInBytes, tagsJSON, file.CreatedAt, file.CreatedByUserID,
|
||||
file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)
|
||||
|
||||
// 6. Insert into denormalized files_by_tag_id table for each tag
|
||||
for _, tag := range file.Tags {
|
||||
batch.Query(`INSERT INTO maplefile.files_by_tag_id
|
||||
(tag_id, file_id, collection_id, owner_id, encrypted_metadata, encrypted_file_key,
|
||||
encryption_version, encrypted_hash, encrypted_file_object_key,
|
||||
encrypted_file_size_in_bytes, encrypted_thumbnail_object_key,
|
||||
encrypted_thumbnail_size_in_bytes, tag_ids, created_at, created_by_user_id,
|
||||
modified_at, modified_by_user_id, version, state, tombstone_version, tombstone_expiry,
|
||||
created_from_ip_address, modified_from_ip_address, ip_anonymized_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
tag.ID, file.ID, file.CollectionID, file.OwnerID, file.EncryptedMetadata,
|
||||
encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
|
||||
file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes,
|
||||
file.EncryptedThumbnailObjectKey, file.EncryptedThumbnailSizeInBytes,
|
||||
tagsJSON, file.CreatedAt, file.CreatedByUserID, file.ModifiedAt,
|
||||
file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion,
|
||||
file.TombstoneExpiry,
|
||||
nil, nil, nil) // IP tracking fields not yet in domain model
|
||||
}
|
||||
}
|
||||
|
||||
if err := impl.Session.ExecuteBatch(batch); err != nil {
|
||||
impl.Logger.Error("failed to create multiple files", zap.Error(err))
|
||||
return fmt.Errorf("failed to create multiple files: %w", err)
|
||||
}
|
||||
|
||||
// Increment collection file counts for active files
|
||||
// Group by collection to minimize updates
|
||||
collectionCounts := make(map[gocql.UUID]int)
|
||||
for _, file := range files {
|
||||
if file != nil && file.State == dom_file.FileStateActive {
|
||||
collectionCounts[file.CollectionID]++
|
||||
}
|
||||
}
|
||||
|
||||
for collectionID, count := range collectionCounts {
|
||||
for i := 0; i < count; i++ {
|
||||
if err := impl.CollectionRepo.IncrementFileCount(context.Background(), collectionID); err != nil {
|
||||
impl.Logger.Error("failed to increment collection file count",
|
||||
zap.String("collection_id", collectionID.String()),
|
||||
zap.Error(err))
|
||||
// Don't fail the entire operation if count update fails
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl.Logger.Info("multiple files created successfully", zap.Int("count", len(files)))
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue