feat(surf): serve startup summary, shutdown report and http request log
- serve prints a startup summary on a terminal (version, environment, URL, admin, database host and name, pending migrations, plugins, drivers) and logs it as a 'startup summary' record plus 'listening on <addr>' - on SIGINT/SIGTERM serve drains, prints and logs a shutdown report and returns nil (exit 0), or the drain errors (exit 1) - the request log is on by default under diary component http with request_id; the route pattern, never the path, is logged - lagoon.PendingMigrations, conga.Worker.Stats, diary.HideOnTTY, bonfire.IsTerminal and bonfire.Details back the summary and report
This commit is contained in:
@@ -136,6 +136,7 @@ func (p *Plugin) Migrations() []*gormigrate.Migration {
|
||||
| `lagoon.Migrate` | Runs framework and plugin migrations in order. |
|
||||
| `lagoon.RollbackLast` | Rolls back the last migration of one plugin. |
|
||||
| `lagoon.Status` | Lists applied migration IDs per plugin as `lagoon.StatusRow` values. |
|
||||
| `lagoon.PendingMigrations` | Lists the migrations `lagoon.Migrate` would apply, as `lagoon.MigrationRef` values (`Plugin`, `ID`): the framework sets first, then each plugin in order, each in declaration order. A missing history table means the whole set is pending. Read-only; `serve` reports the count in its startup summary. |
|
||||
| `lagoon.HistoryTableName` | Returns the gormigrate history table for a plugin ID. |
|
||||
| `lagoon.BackendAdminMigrations` | Creates the backend user, role and admin token blacklist tables, the `backend_api_tokens` table for personal API tokens, seeds the system roles, adds a case-insensitive unique index on `backend_users.email` and adds WinterCMS's nullable jsonable `backend_users.metadata` column (TEXT; the admin subtitle lives under its `subtitle` key). The index migration refuses to run while emails differ only in case and names the clashing logins. Rolling back the metadata migration drops only that column. |
|
||||
| `lagoon.QueueMigrations` | Migrates River's schema to `lagoon.RiverSchemaVersion` and creates the `summer_jobs` job record table. |
|
||||
|
||||
@@ -201,3 +201,79 @@ func Status(gdb *gorm.DB, plugins []party.Plugin) ([]StatusRow, error) {
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// MigrationRef names one migration: the plugin (or framework set) that
|
||||
// declares it and its ID.
|
||||
type MigrationRef struct {
|
||||
Plugin string
|
||||
ID string
|
||||
}
|
||||
|
||||
// migrationSet is one history table's declared migrations.
|
||||
type migrationSet struct {
|
||||
id string
|
||||
migrations []*gormigrate.Migration
|
||||
}
|
||||
|
||||
// migrationSets lists the sets Migrate applies, in its order: the framework
|
||||
// sets (system files, backend admin, deferred bindings, job queue), then each
|
||||
// plugin's HasMigrations set in plugin order.
|
||||
func migrationSets(gdb *gorm.DB, plugins []party.Plugin) ([]migrationSet, error) {
|
||||
sqlDB, err := gdb.DB()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("lagoon: migration sets: %w", err)
|
||||
}
|
||||
sets := []migrationSet{
|
||||
{"summercms.attach", attach.Migrations},
|
||||
{"summercms.cabana", BackendAdminMigrations},
|
||||
{DeferredHistoryID, DeferredBindingMigrations},
|
||||
{QueueHistoryID, QueueMigrations(sqlDB)},
|
||||
}
|
||||
for _, p := range plugins {
|
||||
hm, ok := p.(pact.HasMigrations)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if ms := hm.Migrations(); len(ms) > 0 {
|
||||
sets = append(sets, migrationSet{p.ID(), ms})
|
||||
}
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
// PendingMigrations returns the migrations Migrate would apply: each set's
|
||||
// declared IDs that its history table does not hold, framework sets first,
|
||||
// then plugins in order, each in declaration order. A missing history table
|
||||
// means every migration of that set is pending. It only reads.
|
||||
func PendingMigrations(gdb *gorm.DB, plugins []party.Plugin) ([]MigrationRef, error) {
|
||||
if gdb == nil {
|
||||
return nil, fmt.Errorf("lagoon: gorm db is nil")
|
||||
}
|
||||
sets, err := migrationSets(gdb, plugins)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pending []MigrationRef
|
||||
for _, s := range sets {
|
||||
table, err := HistoryTableName(s.id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applied := map[string]bool{}
|
||||
if gdb.Migrator().HasTable(table) {
|
||||
var ids []string
|
||||
if err := gdb.Table(table).Pluck("id", &ids).Error; err != nil {
|
||||
return nil, fmt.Errorf("lagoon: pending %s: %w", s.id, err)
|
||||
}
|
||||
for _, id := range ids {
|
||||
applied[id] = true
|
||||
}
|
||||
}
|
||||
for _, m := range s.migrations {
|
||||
if m != nil && !applied[m.ID] {
|
||||
pending = append(pending, MigrationRef{Plugin: s.id, ID: m.ID})
|
||||
}
|
||||
}
|
||||
}
|
||||
return pending, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user