feat(12.2-01): add deferred bindings, guarded upload store and purge

- deferred_bindings migration set under summercms.deferred with backend_user_id
- lagoon.DeferredBind/Unbind/Bindings/Forget/Slaves scoped by DeferredKey
- lagoon.PurgeDeferred with SKIP LOCKED batches and after-commit blob deletes
- attach.Store with the ported image guard, extension and MIME limits
- attach.Relation, attach.HasRelations, attach.BlobKeys, File.ThumbKey
- lagoon README and attachments docs
This commit is contained in:
Jakub Zych
2026-10-02 17:36:43 +02:00
parent 79e2a43095
commit 19f4cf8232
14 changed files with 1280 additions and 15 deletions

View File

@@ -0,0 +1,55 @@
package lagoon
import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)
// DeferredHistoryID is the history id of the deferred-binding migration set;
// its gormigrate table is summer_migrations_summercms_deferred.
const DeferredHistoryID = "summercms.deferred"
// DeferredBindingMigrations creates WinterCMS's deferred_bindings table,
// folded from winter/storm's 2013_10_01_000001_Db_Deferred_Bindings.php and
// 2021_01_19_000001_Db_Add_Pivot_Data_To_Deferred_Bindings.php. One column is
// added: backend_user_id, the backend admin who owns the binding, so a
// session key replayed by another admin matches nothing. master_type and
// slave_type hold morph type strings (see MorphType), never PHP class names
// of framework internals. Rolling the set back drops the table.
var DeferredBindingMigrations = []*gormigrate.Migration{
{
ID: "202610020001_create_deferred_bindings",
Migrate: func(tx *gorm.DB) error {
stmts := []string{
`CREATE TABLE deferred_bindings (
id SERIAL PRIMARY KEY,
master_type TEXT NOT NULL,
master_field TEXT NOT NULL,
slave_type TEXT NOT NULL,
slave_id TEXT NOT NULL,
pivot_data TEXT,
session_key TEXT NOT NULL,
is_bind BOOLEAN NOT NULL DEFAULT TRUE,
backend_user_id INTEGER NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)`,
`CREATE INDEX deferred_bindings_master_type_index ON deferred_bindings (master_type)`,
`CREATE INDEX deferred_bindings_master_field_index ON deferred_bindings (master_field)`,
`CREATE INDEX deferred_bindings_slave_type_index ON deferred_bindings (slave_type)`,
`CREATE INDEX deferred_bindings_slave_id_index ON deferred_bindings (slave_id)`,
`CREATE INDEX deferred_bindings_session_lookup_index ON deferred_bindings (session_key, backend_user_id, master_type)`,
`CREATE INDEX deferred_bindings_created_at_index ON deferred_bindings (created_at)`,
}
for _, stmt := range stmts {
if err := tx.Exec(stmt).Error; err != nil {
return err
}
}
return nil
},
Rollback: func(tx *gorm.DB) error {
return tx.Exec("DROP TABLE IF EXISTS deferred_bindings").Error
},
},
}