Replace old infrastructure layer with Kratos-style data layer: - data.go: GORM connection, transaction support, Wire ProviderSet, PO models - file_repo.go: All 12 S3 operations (upload, download, multipart, presign, buckets) - folder_repo.go: GORM queries including recursive CTE for descendant files - file_meta_repo.go: CRUD + move operations for file metadata - share_repo.go: CRUD + increment download count for share links Deleted old infrastructure/database, infrastructure/repository, infrastructure/s3. Kept infrastructure/grpc for later integration.
121 lines
3.9 KiB
Go
121 lines
3.9 KiB
Go
package data
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"rag/file-system/internal/conf"
|
|
|
|
"github.com/go-kratos/kratos/v2/log"
|
|
"github.com/google/wire"
|
|
"gorm.io/driver/postgres"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Data holds the GORM database connection and provides transaction support.
|
|
type Data struct {
|
|
db *gorm.DB
|
|
log *log.Helper
|
|
}
|
|
|
|
// contextTxKey is the context key for storing the current transaction.
|
|
type contextTxKey struct{}
|
|
|
|
// NewData creates a new Data instance with GORM connected to PostgreSQL.
|
|
func NewData(c *conf.Data, logger log.Logger) (*Data, func(), error) {
|
|
helper := log.NewHelper(logger)
|
|
db, err := gorm.Open(postgres.Open(c.Database.Source), &gorm.Config{})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
sqlDB.SetMaxOpenConns(25)
|
|
sqlDB.SetMaxIdleConns(5)
|
|
|
|
if err := db.AutoMigrate(&FolderPO{}, &FileMetaPO{}, &ShareLinkPO{}); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
helper.Info("connected to PostgreSQL via GORM")
|
|
cleanup := func() {
|
|
sqlDB.Close()
|
|
}
|
|
return &Data{db: db, log: helper}, cleanup, nil
|
|
}
|
|
|
|
// DB returns the *gorm.DB for the given context. If a transaction is active
|
|
// in the context, it returns the transaction DB; otherwise the global DB.
|
|
func (d *Data) DB(ctx context.Context) *gorm.DB {
|
|
tx, ok := ctx.Value(contextTxKey{}).(*gorm.DB)
|
|
if ok {
|
|
return tx
|
|
}
|
|
return d.db.WithContext(ctx)
|
|
}
|
|
|
|
// Transaction is the interface for executing operations within a database transaction.
|
|
type Transaction interface {
|
|
InTx(ctx context.Context, fn func(ctx context.Context) error) error
|
|
}
|
|
|
|
// InTx executes fn inside a database transaction.
|
|
func (d *Data) InTx(ctx context.Context, fn func(ctx context.Context) error) error {
|
|
return d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
ctx = context.WithValue(ctx, contextTxKey{}, tx)
|
|
return fn(ctx)
|
|
})
|
|
}
|
|
|
|
// ProviderSet is the Wire provider set for the data layer.
|
|
var ProviderSet = wire.NewSet(NewData, NewFileRepo, NewFolderRepo, NewFileMetaRepo, NewShareRepo)
|
|
|
|
// --- GORM Models (Persistence Objects) ---
|
|
|
|
// FolderPO maps to the "folders" table.
|
|
type FolderPO struct {
|
|
ID string `gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
|
|
ParentID *string `gorm:"type:uuid;index:idx_folders_parent"`
|
|
Name string `gorm:"type:varchar(255);not null"`
|
|
OwnerID string `gorm:"type:varchar(36);not null;index:idx_folders_owner"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
|
}
|
|
|
|
func (FolderPO) TableName() string { return "folders" }
|
|
|
|
// FileMetaPO maps to the "files" table.
|
|
type FileMetaPO struct {
|
|
ID string `gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
|
|
FolderID string `gorm:"type:uuid;index:idx_files_folder"`
|
|
Name string `gorm:"type:varchar(255);not null"`
|
|
S3Key string `gorm:"type:varchar(512);not null;index:idx_files_s3_key"`
|
|
S3Bucket string `gorm:"type:varchar(255);not null"`
|
|
Size int64 `gorm:"default:0"`
|
|
ContentType string `gorm:"type:varchar(255);default:'application/octet-stream'"`
|
|
OwnerID string `gorm:"type:varchar(36);not null;index:idx_files_owner"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
|
}
|
|
|
|
func (FileMetaPO) TableName() string { return "files" }
|
|
|
|
// ShareLinkPO maps to the "share_links" table.
|
|
type ShareLinkPO struct {
|
|
ID string `gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
|
|
ResourceType string `gorm:"type:varchar(10);not null"`
|
|
ResourceID string `gorm:"type:uuid;not null"`
|
|
Token string `gorm:"type:varchar(32);not null;uniqueIndex:idx_share_token"`
|
|
Password *string `gorm:"type:varchar(255)"`
|
|
ExpiresAt *time.Time `gorm:"type:timestamptz"`
|
|
DownloadCount int `gorm:"default:0"`
|
|
MaxDownloads *int
|
|
CreatedBy string `gorm:"type:varchar(36);not null"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
|
}
|
|
|
|
func (ShareLinkPO) TableName() string { return "share_links" }
|