* feat: add plugin system * fix db docker build * fix hammerhead readme, add strava subscription news to docs * fixes and sdk improvements * fix: reduce Meilisearch load, debounce federation sync (#1012) * optimize meili trail index * several fixes --------- Co-authored-by: Flomp <Flomp@users.noreply.github.com> * Bump svelte from 5.55.5 to 5.56.0 in /docs (#1032) Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.55.5 to 5.56.0. - [Release notes](https://github.com/sveltejs/svelte/releases) - [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md) - [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.56.0/packages/svelte) --- updated-dependencies: - dependency-name: svelte dependency-version: 5.56.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Flomp <Flomp@users.noreply.github.com> * Release v0.19.2 (#1035) * chore: release v0.19.2 * add changelog --------- Co-authored-by: Flomp <26000991+Flomp@users.noreply.github.com> Co-authored-by: Christian Beutel <> * speed up plugin sync and several small fixes * concepts for security improvements and process stability * improve concept * security concept implemented * remove insecure TLS * worker concept implemented * fixes and cleanup * fixes * docu * mermaid, namings * WASM plugin host improvements, plugin logging * fix db migration * Improve plugin config and category mapping UI * fixes * further fixes * remove manual test sync * fix db migration and strava mapping * type added, UI improvements * fix plugin card toggle clickable area * optimize synch status card layout * plugin type 'trails' instead of 'integration' * session auth validation in UI * fix komoot date and waypoints * improve category mapping * fix send to hammerhead: trail name * plugin setup error handling improved * fix review findings * re-mapping added * rename remote_category --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Flomp <Flomp@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Flomp <26000991+Flomp@users.noreply.github.com>
263 lines
6.8 KiB
Go
263 lines
6.8 KiB
Go
package migrations
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/pocketbase/pocketbase/core"
|
|
m "github.com/pocketbase/pocketbase/migrations"
|
|
)
|
|
|
|
const providerBackupColumn1780000006 = "provider_backup_1780000006"
|
|
const userPluginIndex1780000006 = "CREATE INDEX `idx_trail_external_reference_user_plugin_id` ON `trail_external_reference` (`user`, `plugin_id`)"
|
|
|
|
func init() {
|
|
m.Register(func(app core.App) error {
|
|
if err := backupProviderColumn1780000006(app); err != nil {
|
|
return err
|
|
}
|
|
|
|
collection, err := app.FindCollectionByNameOrId("trail_external_reference")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Drop+re-add (with a new id) blanks the provider column, so the
|
|
// provider-scoped unique indexes must not be rebuilt until the values
|
|
// have been restored, otherwise a cross-provider external_id clash would
|
|
// fail index creation and abort the migration.
|
|
removedIndexes := stripProviderIndexes1780000006(collection)
|
|
|
|
collection.Fields.RemoveByName("provider")
|
|
if err := collection.Fields.AddMarshaledJSONAt(3, []byte(`{
|
|
"autogeneratePattern": "",
|
|
"hidden": false,
|
|
"id": "text420001002",
|
|
"max": 128,
|
|
"min": 1,
|
|
"name": "provider",
|
|
"pattern": "^[a-z0-9][a-z0-9_-]*$",
|
|
"presentable": false,
|
|
"primaryKey": false,
|
|
"required": true,
|
|
"system": false,
|
|
"type": "text"
|
|
}`)); err != nil {
|
|
return err
|
|
}
|
|
if collection.Fields.GetByName("plugin_id") == nil {
|
|
if err := collection.Fields.AddMarshaledJSONAt(4, []byte(`{
|
|
"autogeneratePattern": "",
|
|
"hidden": false,
|
|
"id": "textpluginref",
|
|
"max": 64,
|
|
"min": 0,
|
|
"name": "plugin_id",
|
|
"pattern": "^[a-z0-9][a-z0-9_-]*$",
|
|
"presentable": false,
|
|
"primaryKey": false,
|
|
"required": false,
|
|
"system": false,
|
|
"type": "text"
|
|
}`)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if collection.Fields.GetByName("provider_category") == nil {
|
|
if err := collection.Fields.AddMarshaledJSONAt(6, []byte(`{
|
|
"autogeneratePattern": "",
|
|
"hidden": false,
|
|
"id": "txtrmtecat01",
|
|
"max": 255,
|
|
"min": 0,
|
|
"name": "provider_category",
|
|
"pattern": "",
|
|
"presentable": false,
|
|
"primaryKey": false,
|
|
"required": false,
|
|
"system": false,
|
|
"type": "text"
|
|
}`)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if collection.Fields.GetByName("provider_category_checked_at") == nil {
|
|
if err := collection.Fields.AddMarshaledJSONAt(7, []byte(`{
|
|
"hidden": false,
|
|
"id": "datermtecat1",
|
|
"max": "",
|
|
"min": "",
|
|
"name": "provider_category_checked_at",
|
|
"presentable": false,
|
|
"required": false,
|
|
"system": false,
|
|
"type": "date"
|
|
}`)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := app.Save(collection); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := restoreProviderColumn1780000006(app); err != nil {
|
|
return err
|
|
}
|
|
|
|
collection.Indexes = append(collection.Indexes, removedIndexes...)
|
|
if !hasIndex1780000006(collection, userPluginIndex1780000006) {
|
|
collection.Indexes = append(collection.Indexes, userPluginIndex1780000006)
|
|
}
|
|
if err := app.Save(collection); err != nil {
|
|
return err
|
|
}
|
|
|
|
refs, err := app.FindAllRecords("trail_external_reference")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, ref := range refs {
|
|
if ref.GetString("plugin_id") != "" {
|
|
continue
|
|
}
|
|
ref.Set("plugin_id", ref.GetString("provider"))
|
|
if err := app.Save(ref); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}, func(app core.App) error {
|
|
if err := backupProviderColumn1780000006(app); err != nil {
|
|
return err
|
|
}
|
|
|
|
collection, err := app.FindCollectionByNameOrId("trail_external_reference")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
removedIndexes := stripProviderIndexes1780000006(collection)
|
|
removeIndex1780000006(collection, userPluginIndex1780000006)
|
|
|
|
collection.Fields.RemoveByName("provider_category_checked_at")
|
|
collection.Fields.RemoveByName("provider_category")
|
|
collection.Fields.RemoveByName("plugin_id")
|
|
collection.Fields.RemoveByName("provider")
|
|
if err := collection.Fields.AddMarshaledJSONAt(3, []byte(`{
|
|
"hidden": false,
|
|
"id": "select420001002",
|
|
"maxSelect": 1,
|
|
"name": "provider",
|
|
"presentable": false,
|
|
"required": true,
|
|
"system": false,
|
|
"type": "select",
|
|
"values": [
|
|
"strava",
|
|
"komoot",
|
|
"hammerhead"
|
|
]
|
|
}`)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := app.Save(collection); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := restoreProviderColumn1780000006(app); err != nil {
|
|
return err
|
|
}
|
|
|
|
collection.Indexes = append(collection.Indexes, removedIndexes...)
|
|
return app.Save(collection)
|
|
})
|
|
}
|
|
|
|
// stripProviderIndexes1780000006 removes the provider-scoped indexes from the
|
|
// collection metadata and returns them so they can be re-added once the
|
|
// provider values have been restored. PocketBase rebuilds indexes from the
|
|
// collection metadata on every save; leaving the provider indexes in place
|
|
// while the column is transiently empty risks a unique-constraint failure.
|
|
func stripProviderIndexes1780000006(collection *core.Collection) []string {
|
|
kept := make([]string, 0, len(collection.Indexes))
|
|
removed := make([]string, 0)
|
|
for _, idx := range collection.Indexes {
|
|
if strings.Contains(idx, "`provider`") {
|
|
removed = append(removed, idx)
|
|
continue
|
|
}
|
|
kept = append(kept, idx)
|
|
}
|
|
collection.Indexes = kept
|
|
return removed
|
|
}
|
|
|
|
func hasIndex1780000006(collection *core.Collection, index string) bool {
|
|
for _, existing := range collection.Indexes {
|
|
if existing == index {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func removeIndex1780000006(collection *core.Collection, index string) {
|
|
indexes := collection.Indexes[:0]
|
|
for _, existing := range collection.Indexes {
|
|
if existing == index {
|
|
continue
|
|
}
|
|
indexes = append(indexes, existing)
|
|
}
|
|
collection.Indexes = indexes
|
|
}
|
|
|
|
func backupProviderColumn1780000006(app core.App) error {
|
|
exists, err := columnExists1780000006(app, providerBackupColumn1780000006)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if exists {
|
|
return nil
|
|
}
|
|
|
|
if _, err := app.DB().
|
|
NewQuery("ALTER TABLE trail_external_reference ADD COLUMN " + providerBackupColumn1780000006 + " TEXT DEFAULT '' NOT NULL").
|
|
Execute(); err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = app.DB().
|
|
NewQuery("UPDATE trail_external_reference SET " + providerBackupColumn1780000006 + " = provider").
|
|
Execute()
|
|
return err
|
|
}
|
|
|
|
func restoreProviderColumn1780000006(app core.App) error {
|
|
if _, err := app.DB().
|
|
NewQuery("UPDATE trail_external_reference SET provider = " + providerBackupColumn1780000006).
|
|
Execute(); err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err := app.DB().DropColumn("trail_external_reference", providerBackupColumn1780000006).Execute()
|
|
return err
|
|
}
|
|
|
|
func columnExists1780000006(app core.App, column string) (bool, error) {
|
|
columns, err := app.TableColumns("trail_external_reference")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
for _, existing := range columns {
|
|
if existing == column {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|