mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-01-09 10:07:17 +02:00
155 lines
3.6 KiB
Go
155 lines
3.6 KiB
Go
package migratecmd
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/pocketbase/dbx"
|
|
"github.com/pocketbase/pocketbase/core"
|
|
"github.com/pocketbase/pocketbase/daos"
|
|
"github.com/pocketbase/pocketbase/models"
|
|
"github.com/pocketbase/pocketbase/tools/migrate"
|
|
)
|
|
|
|
const collectionsCacheKey = "migratecmd_collections"
|
|
|
|
// onCollectionChange handles the automigration snapshot generation on
|
|
// collection change event (create/update/delete).
|
|
func (p *plugin) afterCollectionChange() func(*core.ModelEvent) error {
|
|
return func(e *core.ModelEvent) error {
|
|
if e.Model.TableName() != "_collections" {
|
|
return nil // not a collection
|
|
}
|
|
|
|
// @todo replace with the OldModel when added to the ModelEvent
|
|
oldCollections, err := p.getCachedCollections()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
old := oldCollections[e.Model.GetId()]
|
|
|
|
new, err := p.app.Dao().FindCollectionByNameOrId(e.Model.GetId())
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
return err
|
|
}
|
|
|
|
var template string
|
|
var templateErr error
|
|
if p.options.TemplateLang == TemplateLangJS {
|
|
template, templateErr = p.jsDiffTemplate(new, old)
|
|
} else {
|
|
template, templateErr = p.goDiffTemplate(new, old)
|
|
}
|
|
if templateErr != nil {
|
|
if errors.Is(templateErr, emptyTemplateErr) {
|
|
return nil // no changes
|
|
}
|
|
return fmt.Errorf("failed to resolve template: %w", templateErr)
|
|
}
|
|
|
|
var action string
|
|
switch {
|
|
case new == nil:
|
|
action = "deleted_" + old.Name
|
|
case old == nil:
|
|
action = "created_" + new.Name
|
|
default:
|
|
action = "updated_" + old.Name
|
|
}
|
|
|
|
appliedTime := time.Now().Unix()
|
|
name := fmt.Sprintf("%d_%s.%s", appliedTime, action, p.options.TemplateLang)
|
|
filePath := filepath.Join(p.options.Dir, name)
|
|
|
|
return p.app.Dao().RunInTransaction(func(txDao *daos.Dao) error {
|
|
// insert the migration entry
|
|
_, err := txDao.DB().Insert(migrate.DefaultMigrationsTable, dbx.Params{
|
|
"file": name,
|
|
"applied": appliedTime,
|
|
}).Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// ensure that the local migrations dir exist
|
|
if err := os.MkdirAll(p.options.Dir, os.ModePerm); err != nil {
|
|
return fmt.Errorf("failed to create migration dir: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(filePath, []byte(template), 0644); err != nil {
|
|
return fmt.Errorf("failed to save automigrate file: %w", err)
|
|
}
|
|
|
|
p.updateSingleCachedCollection(new, old)
|
|
|
|
return nil
|
|
})
|
|
}
|
|
}
|
|
|
|
func (p *plugin) updateSingleCachedCollection(new, old *models.Collection) {
|
|
cached, _ := p.app.Cache().Get(collectionsCacheKey).(map[string]*models.Collection)
|
|
|
|
switch {
|
|
case new == nil:
|
|
delete(cached, old.Id)
|
|
default:
|
|
cached[new.Id] = new
|
|
}
|
|
|
|
p.app.Cache().Set(collectionsCacheKey, cached)
|
|
}
|
|
|
|
func (p *plugin) refreshCachedCollections() error {
|
|
if p.app.Dao() == nil {
|
|
return errors.New("app is not initialized yet")
|
|
}
|
|
|
|
var collections []*models.Collection
|
|
if err := p.app.Dao().CollectionQuery().All(&collections); err != nil {
|
|
return err
|
|
}
|
|
|
|
cached := map[string]*models.Collection{}
|
|
for _, c := range collections {
|
|
cached[c.Id] = c
|
|
}
|
|
|
|
p.app.Cache().Set(collectionsCacheKey, cached)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *plugin) getCachedCollections() (map[string]*models.Collection, error) {
|
|
if !p.app.Cache().Has(collectionsCacheKey) {
|
|
if err := p.refreshCachedCollections(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
result, _ := p.app.Cache().Get(collectionsCacheKey).(map[string]*models.Collection)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (p *plugin) hasCustomMigrations() bool {
|
|
files, err := os.ReadDir(p.options.Dir)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
for _, f := range files {
|
|
if f.IsDir() {
|
|
continue
|
|
}
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|