Docker/Voltaserve/api/repo/snapshot_repo.go
2024-04-17 20:22:30 +02:00

346 lines
7.8 KiB
Go

package repo
import (
"encoding/json"
"errors"
"log"
"time"
"voltaserve/errorpkg"
"voltaserve/infra"
"voltaserve/model"
"gorm.io/datatypes"
"gorm.io/gorm"
)
type SnapshotUpdateOptions struct {
Original *model.S3Object
Preview *model.S3Object
Text *model.S3Object
Thumbnail *model.Thumbnail
Status string
}
type SnapshotRepo interface {
Find(id string) (model.Snapshot, error)
Save(snapshot model.Snapshot) error
Update(id string, opts SnapshotUpdateOptions) error
MapWithFile(id string, fileID string) error
DeleteMappingsForFile(fileID string) error
FindAllDangling() ([]model.Snapshot, error)
DeleteAllDangling() error
GetLatestVersionForFile(fileID string) (int64, error)
}
func NewSnapshotRepo() SnapshotRepo {
return newSnapshotRepo()
}
func NewSnapshot() model.Snapshot {
return &snapshotEntity{}
}
type snapshotEntity struct {
ID string `json:"id" gorm:"column:id;size:36"`
Version int64 `json:"version" gorm:"column:version"`
Original datatypes.JSON `json:"original,omitempty" gorm:"column:original"`
Preview datatypes.JSON `json:"preview,omitempty" gorm:"column:preview"`
Text datatypes.JSON `json:"text,omitempty" gorm:"column:text"`
Thumbnail datatypes.JSON `json:"thumbnail,omitempty" gorm:"column:thumbnail"`
Status string `json:"status,omitempty" gorm:"column,status"`
CreateTime string `json:"createTime" gorm:"column:create_time"`
UpdateTime *string `json:"updateTime,omitempty" gorm:"column:update_time"`
}
func (*snapshotEntity) TableName() string {
return "snapshot"
}
func (s *snapshotEntity) BeforeCreate(*gorm.DB) (err error) {
s.CreateTime = time.Now().UTC().Format(time.RFC3339)
return nil
}
func (s *snapshotEntity) BeforeSave(*gorm.DB) (err error) {
timeNow := time.Now().UTC().Format(time.RFC3339)
s.UpdateTime = &timeNow
return nil
}
func (s *snapshotEntity) GetID() string {
return s.ID
}
func (s *snapshotEntity) GetVersion() int64 {
return s.Version
}
func (s *snapshotEntity) GetOriginal() *model.S3Object {
if s.Original.String() == "" {
return nil
}
var res = model.S3Object{}
if err := json.Unmarshal([]byte(s.Original.String()), &res); err != nil {
log.Fatal(err)
return nil
}
return &res
}
func (s *snapshotEntity) GetPreview() *model.S3Object {
if s.Preview.String() == "" {
return nil
}
var res = model.S3Object{}
if err := json.Unmarshal([]byte(s.Preview.String()), &res); err != nil {
log.Fatal(err)
return nil
}
return &res
}
func (s *snapshotEntity) GetText() *model.S3Object {
if s.Text.String() == "" {
return nil
}
var res = model.S3Object{}
if err := json.Unmarshal([]byte(s.Text.String()), &res); err != nil {
log.Fatal(err)
return nil
}
return &res
}
func (s *snapshotEntity) GetThumbnail() *model.Thumbnail {
if s.Thumbnail.String() == "" {
return nil
}
var res = model.Thumbnail{}
if err := json.Unmarshal([]byte(s.Thumbnail.String()), &res); err != nil {
log.Fatal(err)
return nil
}
return &res
}
func (s *snapshotEntity) GetStatus() string {
return s.Status
}
func (s *snapshotEntity) SetID(id string) {
s.ID = id
}
func (s *snapshotEntity) SetVersion(version int64) {
s.Version = version
}
func (s *snapshotEntity) SetOriginal(m *model.S3Object) {
if m == nil {
s.Original = nil
} else {
b, err := json.Marshal(m)
if err != nil {
log.Fatal(err)
return
}
if err := s.Original.UnmarshalJSON(b); err != nil {
log.Fatal(err)
}
}
}
func (s *snapshotEntity) SetPreview(m *model.S3Object) {
if m == nil {
s.Preview = nil
} else {
b, err := json.Marshal(m)
if err != nil {
log.Fatal(err)
return
}
if err := s.Preview.UnmarshalJSON(b); err != nil {
log.Fatal(err)
}
}
}
func (s *snapshotEntity) SetText(m *model.S3Object) {
if m == nil {
s.Text = nil
} else {
b, err := json.Marshal(m)
if err != nil {
log.Fatal(err)
return
}
if err := s.Text.UnmarshalJSON(b); err != nil {
log.Fatal(err)
}
}
}
func (s *snapshotEntity) SetThumbnail(m *model.Thumbnail) {
if m == nil {
s.Thumbnail = nil
} else {
b, err := json.Marshal(m)
if err != nil {
log.Fatal(err)
return
}
if err := s.Thumbnail.UnmarshalJSON(b); err != nil {
log.Fatal(err)
}
}
}
func (s *snapshotEntity) SetStatus(status string) {
s.Status = status
}
func (s *snapshotEntity) HasOriginal() bool {
return s.Original != nil
}
func (s *snapshotEntity) HasPreview() bool {
return s.Preview != nil
}
func (s *snapshotEntity) HasText() bool {
return s.Text != nil
}
func (s *snapshotEntity) HasThumbnail() bool {
return s.Thumbnail != nil
}
func (s *snapshotEntity) GetCreateTime() string {
return s.CreateTime
}
func (s *snapshotEntity) GetUpdateTime() *string {
return s.UpdateTime
}
type snapshotRepo struct {
db *gorm.DB
}
func newSnapshotRepo() *snapshotRepo {
return &snapshotRepo{
db: infra.GetDb(),
}
}
func (repo *snapshotRepo) find(id string) (*snapshotEntity, error) {
var res snapshotEntity
if db := repo.db.Where("id = ?", id).First(&res); db.Error != nil {
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
return nil, errorpkg.NewSnapshotNotFoundError(db.Error)
} else {
return nil, errorpkg.NewInternalServerError(db.Error)
}
}
return &res, nil
}
func (repo *snapshotRepo) Find(id string) (model.Snapshot, error) {
res, err := repo.find(id)
if err != nil {
return nil, err
}
return res, nil
}
func (repo *snapshotRepo) Save(snapshot model.Snapshot) error {
if db := repo.db.Save(snapshot); db.Error != nil {
return db.Error
}
return nil
}
func (repo *snapshotRepo) Update(id string, opts SnapshotUpdateOptions) error {
snapshot, err := repo.find(id)
if err != nil {
return err
}
if opts.Thumbnail != nil {
snapshot.SetThumbnail(opts.Thumbnail)
}
if opts.Original != nil {
snapshot.SetOriginal(opts.Original)
}
if opts.Preview != nil {
snapshot.SetPreview(opts.Preview)
}
if opts.Text != nil {
snapshot.SetText(opts.Text)
}
if opts.Status != "" {
snapshot.SetStatus(opts.Status)
}
if db := repo.db.Save(&snapshot); db.Error != nil {
return db.Error
}
return nil
}
func (repo *snapshotRepo) MapWithFile(id string, fileID string) error {
if db := repo.db.Exec("INSERT INTO snapshot_file (snapshot_id, file_id) VALUES (?, ?)", id, fileID); db.Error != nil {
return db.Error
}
return nil
}
func (repo *snapshotRepo) DeleteMappingsForFile(fileID string) error {
if db := repo.db.Exec("DELETE FROM snapshot_file WHERE file_id = ?", fileID); db.Error != nil {
return db.Error
}
return nil
}
func (repo *snapshotRepo) findAllForFile(fileID string) ([]*snapshotEntity, error) {
var res []*snapshotEntity
db := repo.db.
Raw("SELECT * FROM snapshot s LEFT JOIN snapshot_file sf ON s.id = sf.snapshot_id WHERE sf.file_id = ? ORDER BY s.version", fileID).
Scan(&res)
if db.Error != nil {
return nil, db.Error
}
return res, nil
}
func (repo *snapshotRepo) FindAllDangling() ([]model.Snapshot, error) {
var snapshots []*snapshotEntity
db := repo.db.Raw("SELECT * FROM snapshot s LEFT JOIN snapshot_file sf ON s.id = sf.snapshot_id WHERE sf.snapshot_id IS NULL").Scan(&snapshots)
if db.Error != nil {
return nil, db.Error
}
var res []model.Snapshot
for _, s := range snapshots {
res = append(res, s)
}
return res, nil
}
func (repo *snapshotRepo) DeleteAllDangling() error {
if db := repo.db.Exec("DELETE FROM snapshot WHERE id IN (SELECT s.id FROM (SELECT * FROM snapshot) s LEFT JOIN snapshot_file sf ON s.id = sf.snapshot_id WHERE sf.snapshot_id IS NULL)"); db.Error != nil {
return db.Error
}
return nil
}
func (repo *snapshotRepo) GetLatestVersionForFile(fileID string) (int64, error) {
type Result struct {
Result int64
}
var res Result
if db := repo.db.
Raw("SELECT coalesce(max(s.version), 0) + 1 result FROM snapshot s LEFT JOIN snapshot_file map ON s.id = map.snapshot_id WHERE map.file_id = ?", fileID).
Scan(&res); db.Error != nil {
return 0, db.Error
}
return res.Result, nil
}