package controllers import ( "context" "errors" "git.golem15.com/golem15/sm-golem-plugin/models" "git.golem15.com/golem15/summercms/modules/cabana" "git.golem15.com/golem15/summercms/modules/pact" ) // PermissionAccessSettings guards the AI models screen. const PermissionAccessSettings = "golem15.golem.access_settings" // ModelsAdminController is the AI models list and form // (golem15.golem.models). The API key is write-only: the form shows it // redacted, and an update that sends it back empty or redacted keeps the // stored key. type ModelsAdminController struct{} var ( _ pact.AdminController = ModelsAdminController{} _ pact.AdminPermissioned = ModelsAdminController{} _ pact.AdminRecordSource = ModelsAdminController{} _ pact.FormBeforeCreate = ModelsAdminController{} _ pact.FormBeforeUpdate = ModelsAdminController{} ) // ID is golem15.golem.models. func (ModelsAdminController) ID() string { return "golem15.golem.models" } // ModelName is the model's WinterCMS class name. func (ModelsAdminController) ModelName() string { return `Golem15\Golem\Models\AiModel` } // ConfigDir holds config_list.yaml and config_form.yaml. func (ModelsAdminController) ConfigDir() string { return "controllers/aimodels" } // RequiredPermissions is golem15.golem.access_settings. func (ModelsAdminController) RequiredPermissions() []string { return []string{PermissionAccessSettings} } // NewRecord returns an empty AI model. func (ModelsAdminController) NewRecord() any { return &models.AiModel{} } // redacted is how lagoon.Encrypted serializes, so how the form shows the // stored key. const redacted = "[redacted]" func keyMissing(m *models.AiModel) bool { k := m.APIKey.Reveal() return k == "" || k == redacted } // FormBeforeCreate requires an API key. func (ModelsAdminController) FormBeforeCreate(_ context.Context, model any) error { m, ok := model.(*models.AiModel) if !ok { return errors.New("golem: unexpected admin record") } if keyMissing(m) { return &cabana.ValidationError{Details: map[string]any{"api_key": []string{"The api key field is required."}}} } return nil } // FormBeforeUpdate keeps the stored key when the form sent it back empty // or redacted. func (ModelsAdminController) FormBeforeUpdate(ctx context.Context, model any) error { m, ok := model.(*models.AiModel) if !ok { return errors.New("golem: unexpected admin record") } if !keyMissing(m) { return nil } tx, ok := cabana.TxFromContext(ctx) if !ok { return errors.New("golem: no transaction for the model update") } var stored models.AiModel if err := tx.Select("api_key").Where("id = ?", m.ID).Take(&stored).Error; err != nil { return err } m.APIKey = stored.APIKey if keyMissing(m) { return &cabana.ValidationError{Details: map[string]any{"api_key": []string{"The api key field is required."}}} } return nil }