package modules import ( "context" "github.com/jackc/pgx/v5/pgxpool" "github.com/kindredsystems/silo/internal/config" ) // LoadState applies module state from config YAML and database overrides. // // Precedence (highest wins): // 1. Database module_state table // 2. YAML modules.* toggles // 3. Backward-compat YAML fields (auth.enabled, odoo.enabled) // 4. Module defaults (set by NewRegistry) func LoadState(r *Registry, cfg *config.Config, pool *pgxpool.Pool) error { // Step 1: Apply backward-compat top-level YAML fields. // auth.enabled and odoo.enabled existed before the modules section. // Only apply if the new modules.* section doesn't override them. if cfg.Modules.Auth == nil { r.setEnabledUnchecked(Auth, cfg.Auth.Enabled) } if cfg.Modules.Odoo == nil { r.setEnabledUnchecked(Odoo, cfg.Odoo.Enabled) } // Step 2: Apply explicit modules.* YAML toggles (override defaults + compat). applyToggle(r, Auth, cfg.Modules.Auth) applyToggle(r, Projects, cfg.Modules.Projects) applyToggle(r, Audit, cfg.Modules.Audit) applyToggle(r, Odoo, cfg.Modules.Odoo) applyToggle(r, FreeCAD, cfg.Modules.FreeCAD) applyToggle(r, Jobs, cfg.Modules.Jobs) applyToggle(r, DAG, cfg.Modules.DAG) // Step 3: Apply database overrides (highest precedence). if pool != nil { if err := loadFromDB(r, pool); err != nil { return err } } // Step 4: Validate the final state. return r.ValidateDependencies() } // applyToggle sets a module's state from a YAML ModuleToggle if present. func applyToggle(r *Registry, id string, toggle *config.ModuleToggle) { if toggle == nil || toggle.Enabled == nil { return } r.setEnabledUnchecked(id, *toggle.Enabled) } // setEnabledUnchecked sets module state without dependency validation. // Used during loading when the full state is being assembled incrementally. func (r *Registry) setEnabledUnchecked(id string, enabled bool) { r.mu.Lock() defer r.mu.Unlock() if m, ok := r.modules[id]; ok && !m.Required { m.enabled = enabled } } // loadFromDB reads module_state rows and applies them to the registry. func loadFromDB(r *Registry, pool *pgxpool.Pool) error { rows, err := pool.Query(context.Background(), `SELECT module_id, enabled FROM module_state`) if err != nil { return err } defer rows.Close() for rows.Next() { var id string var enabled bool if err := rows.Scan(&id, &enabled); err != nil { return err } r.setEnabledUnchecked(id, enabled) } return rows.Err() }