added migration to normalize the system collection and field ids
This commit is contained in:
parent
b3d88349d7
commit
9e70c77736
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,3 +1,15 @@
|
||||||
|
## v0.23.0-rc10
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> **This is a prerelease intended for test and experimental purposes only!**
|
||||||
|
|
||||||
|
- Restore the CRC32 checksum autogeneration for the collection/field ids in order to maintain deterministic default identifier value and minimize conflicts between custom migrations and full collections snapshots.
|
||||||
|
_There is a system migration that will attempt to normalize existing system collections ids, but if you already migrated to v0.23.0-rc and have generated a full collections snapshot migration, you have to delete it and regenerate a new one._
|
||||||
|
|
||||||
|
- Change the behavior of the default generated collections snapshot migration to act as "extend" instead of "replace" to prevent accidental data deletion.
|
||||||
|
_I think this would be rare but if you want the old behaviour you can edit the generated snapshot file and replace the second argument (`deleteMissing`) of `App.ImportCollection/App.ImportCollectionsByMarshaledJSON` from `false` to `true`._
|
||||||
|
|
||||||
|
|
||||||
## v0.23.0-rc9
|
## v0.23.0-rc9
|
||||||
|
|
||||||
> [!CAUTION]
|
> [!CAUTION]
|
||||||
|
|
|
@ -2,6 +2,7 @@ package core_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -9,6 +10,7 @@ import (
|
||||||
|
|
||||||
_ "unsafe"
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
"github.com/pocketbase/pocketbase/tests"
|
"github.com/pocketbase/pocketbase/tests"
|
||||||
"github.com/pocketbase/pocketbase/tools/logger"
|
"github.com/pocketbase/pocketbase/tools/logger"
|
||||||
|
@ -351,6 +353,12 @@ func TestBaseAppRefreshSettingsLoggerMinLevelEnabled(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// silence query logs
|
||||||
|
app.DB().(*dbx.DB).ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {}
|
||||||
|
app.DB().(*dbx.DB).QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {}
|
||||||
|
app.NonconcurrentDB().(*dbx.DB).ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {}
|
||||||
|
app.NonconcurrentDB().(*dbx.DB).QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {}
|
||||||
|
|
||||||
handler, ok := app.Logger().Handler().(*logger.BatchHandler)
|
handler, ok := app.Logger().Handler().(*logger.BatchHandler)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("Expected BatchHandler, got %v", app.Logger().Handler())
|
t.Fatalf("Expected BatchHandler, got %v", app.Logger().Handler())
|
||||||
|
|
|
@ -488,12 +488,13 @@ func (m *Collection) UnmarshalJSON(b []byte) error {
|
||||||
minimal := &struct {
|
minimal := &struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Id string `json:"id"`
|
||||||
}{}
|
}{}
|
||||||
if err := json.Unmarshal(b, minimal); err != nil {
|
if err := json.Unmarshal(b, minimal); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
blank := NewCollection(minimal.Type, minimal.Name)
|
blank := NewCollection(minimal.Type, minimal.Name, minimal.Id)
|
||||||
*m = *blank
|
*m = *blank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,13 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
"github.com/pocketbase/pocketbase/tools/list"
|
"github.com/pocketbase/pocketbase/tools/list"
|
||||||
"github.com/pocketbase/pocketbase/tools/logger"
|
"github.com/pocketbase/pocketbase/tools/logger"
|
||||||
)
|
)
|
||||||
|
@ -51,6 +54,12 @@ func TestBaseAppLoggerLevelDevPrint(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// silence query logs
|
||||||
|
app.DB().(*dbx.DB).ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {}
|
||||||
|
app.DB().(*dbx.DB).QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {}
|
||||||
|
app.NonconcurrentDB().(*dbx.DB).ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {}
|
||||||
|
app.NonconcurrentDB().(*dbx.DB).QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {}
|
||||||
|
|
||||||
app.Settings().Logs.MinLevel = testLogLevel
|
app.Settings().Logs.MinLevel = testLogLevel
|
||||||
if err := app.Save(app.Settings()); err != nil {
|
if err := app.Save(app.Settings()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -118,7 +118,7 @@ func createParamsTable(txApp core.App) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMFAsCollection(txApp core.App) error {
|
func createMFAsCollection(txApp core.App) error {
|
||||||
col := core.NewBaseCollection(core.CollectionNameMFAs, "_pbc"+core.CollectionNameMFAs)
|
col := core.NewBaseCollection(core.CollectionNameMFAs)
|
||||||
col.System = true
|
col.System = true
|
||||||
|
|
||||||
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
||||||
|
@ -157,7 +157,7 @@ func createMFAsCollection(txApp core.App) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createOTPsCollection(txApp core.App) error {
|
func createOTPsCollection(txApp core.App) error {
|
||||||
col := core.NewBaseCollection(core.CollectionNameOTPs, "_pbc"+core.CollectionNameOTPs)
|
col := core.NewBaseCollection(core.CollectionNameOTPs)
|
||||||
col.System = true
|
col.System = true
|
||||||
|
|
||||||
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
||||||
|
@ -198,7 +198,7 @@ func createOTPsCollection(txApp core.App) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAuthOriginsCollection(txApp core.App) error {
|
func createAuthOriginsCollection(txApp core.App) error {
|
||||||
col := core.NewBaseCollection(core.CollectionNameAuthOrigins, "_pbc"+core.CollectionNameAuthOrigins)
|
col := core.NewBaseCollection(core.CollectionNameAuthOrigins)
|
||||||
col.System = true
|
col.System = true
|
||||||
|
|
||||||
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
||||||
|
@ -238,7 +238,7 @@ func createAuthOriginsCollection(txApp core.App) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createExternalAuthsCollection(txApp core.App) error {
|
func createExternalAuthsCollection(txApp core.App) error {
|
||||||
col := core.NewBaseCollection(core.CollectionNameExternalAuths, "_pbc"+core.CollectionNameExternalAuths)
|
col := core.NewBaseCollection(core.CollectionNameExternalAuths)
|
||||||
col.System = true
|
col.System = true
|
||||||
|
|
||||||
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
||||||
|
@ -284,7 +284,7 @@ func createExternalAuthsCollection(txApp core.App) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSuperusersCollection(txApp core.App) error {
|
func createSuperusersCollection(txApp core.App) error {
|
||||||
superusers := core.NewAuthCollection(core.CollectionNameSuperusers, "_pbc"+core.CollectionNameSuperusers)
|
superusers := core.NewAuthCollection(core.CollectionNameSuperusers)
|
||||||
superusers.System = true
|
superusers.System = true
|
||||||
superusers.Fields.Add(&core.EmailField{
|
superusers.Fields.Add(&core.EmailField{
|
||||||
Name: "email",
|
Name: "email",
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hash/crc32"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// note: this migration will be deleted in future version
|
||||||
|
|
||||||
|
func collectionIdChecksum(c *core.Collection) string {
|
||||||
|
return "pbc_" + strconv.Itoa(int(crc32.ChecksumIEEE([]byte(c.Type+c.Name))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldIdChecksum(f core.Field) string {
|
||||||
|
return f.Type() + strconv.Itoa(int(crc32.ChecksumIEEE([]byte(f.GetName()))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize system collection and field ids
|
||||||
|
func init() {
|
||||||
|
core.SystemMigrations.Register(func(txApp core.App) error {
|
||||||
|
collections := []*core.Collection{}
|
||||||
|
err := txApp.CollectionQuery().
|
||||||
|
AndWhere(dbx.In(
|
||||||
|
"name",
|
||||||
|
core.CollectionNameMFAs,
|
||||||
|
core.CollectionNameOTPs,
|
||||||
|
core.CollectionNameExternalAuths,
|
||||||
|
core.CollectionNameAuthOrigins,
|
||||||
|
core.CollectionNameSuperusers,
|
||||||
|
)).
|
||||||
|
All(&collections)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range collections {
|
||||||
|
var needUpdate bool
|
||||||
|
|
||||||
|
references, err := txApp.FindCollectionReferences(c, c.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authOrigins, err := txApp.FindAllAuthOriginsByCollection(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mfas, err := txApp.FindAllMFAsByCollection(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
otps, err := txApp.FindAllOTPsByCollection(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
originalId := c.Id
|
||||||
|
|
||||||
|
// normalize collection id
|
||||||
|
if checksum := collectionIdChecksum(c); c.Id != checksum {
|
||||||
|
c.Id = checksum
|
||||||
|
needUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize system fields
|
||||||
|
for _, f := range c.Fields {
|
||||||
|
if !f.GetSystem() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if checksum := fieldIdChecksum(f); f.GetId() != checksum {
|
||||||
|
f.SetId(checksum)
|
||||||
|
needUpdate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !needUpdate {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rawExport, err := c.DBExport(txApp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = txApp.DB().Update("_collections", rawExport, dbx.HashExp{"id": originalId}).Execute()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update collection references
|
||||||
|
for refCollection, fields := range references {
|
||||||
|
for _, f := range fields {
|
||||||
|
relationField, ok := f.(*core.RelationField)
|
||||||
|
if !ok || relationField.CollectionId == originalId {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
relationField.CollectionId = c.Id
|
||||||
|
}
|
||||||
|
if err = txApp.Save(refCollection); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update mfas references
|
||||||
|
for _, item := range mfas {
|
||||||
|
item.SetCollectionRef(c.Id)
|
||||||
|
if err = txApp.Save(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update otps references
|
||||||
|
for _, item := range otps {
|
||||||
|
item.SetCollectionRef(c.Id)
|
||||||
|
if err = txApp.Save(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update authOrigins references
|
||||||
|
for _, item := range authOrigins {
|
||||||
|
item.SetCollectionRef(c.Id)
|
||||||
|
if err = txApp.Save(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, nil)
|
||||||
|
}
|
|
@ -62,7 +62,7 @@ func (p *plugin) jsSnapshotTemplate(collections []*core.Collection) (string, err
|
||||||
const template = jsTypesDirective + `migrate((app) => {
|
const template = jsTypesDirective + `migrate((app) => {
|
||||||
const snapshot = %s;
|
const snapshot = %s;
|
||||||
|
|
||||||
return app.importCollections(snapshot, true);
|
return app.importCollections(snapshot, false);
|
||||||
}, (app) => {
|
}, (app) => {
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
|
@ -348,7 +348,7 @@ func init() {
|
||||||
m.Register(func(app core.App) error {
|
m.Register(func(app core.App) error {
|
||||||
jsonData := ` + "`%s`" + `
|
jsonData := ` + "`%s`" + `
|
||||||
|
|
||||||
return app.ImportCollectionsByMarshaledJSON([]byte(jsonData), true)
|
return app.ImportCollectionsByMarshaledJSON([]byte(jsonData), false)
|
||||||
}, func(app core.App) error {
|
}, func(app core.App) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
Binary file not shown.
2
ui/.env
2
ui/.env
|
@ -10,4 +10,4 @@ PB_DOCS_URL = "https://pocketbase.io/docs/"
|
||||||
PB_JS_SDK_URL = "https://github.com/pocketbase/js-sdk"
|
PB_JS_SDK_URL = "https://github.com/pocketbase/js-sdk"
|
||||||
PB_DART_SDK_URL = "https://github.com/pocketbase/dart-sdk"
|
PB_DART_SDK_URL = "https://github.com/pocketbase/dart-sdk"
|
||||||
PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases"
|
PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases"
|
||||||
PB_VERSION = "v0.23.0-rc9"
|
PB_VERSION = "v0.23.0-rc10"
|
||||||
|
|
Loading…
Reference in New Issue