@@ -15,6 +15,7 @@ import (
1515
1616 "gomodel/internal/aliases"
1717 "gomodel/internal/auditlog"
18+ "gomodel/internal/authkeys"
1819 "gomodel/internal/core"
1920 "gomodel/internal/executionplans"
2021 "gomodel/internal/guardrails"
@@ -27,6 +28,7 @@ type Handler struct {
2728 usageReader usage.UsageReader
2829 auditReader auditlog.Reader
2930 registry * providers.ModelRegistry
31+ authKeys * authkeys.Service
3032 aliases * aliases.Service
3133 plans * executionplans.Service
3234 guardrails * guardrails.Registry
@@ -69,6 +71,13 @@ func WithAliases(service *aliases.Service) Option {
6971 }
7072}
7173
74+ // WithAuthKeys enables managed auth key administration endpoints.
75+ func WithAuthKeys (service * authkeys.Service ) Option {
76+ return func (h * Handler ) {
77+ h .authKeys = service
78+ }
79+ }
80+
7281// WithExecutionPlans enables execution-plan administration endpoints.
7382func WithExecutionPlans (service * executionplans.Service ) Option {
7483 return func (h * Handler ) {
@@ -613,6 +622,12 @@ type createExecutionPlanRequest struct {
613622 Payload executionplans.Payload `json:"plan_payload"`
614623}
615624
625+ type createAuthKeyRequest struct {
626+ Name string `json:"name"`
627+ Description string `json:"description,omitempty"`
628+ ExpiresAt * time.Time `json:"expires_at,omitempty"`
629+ }
630+
616631func featureUnavailableError (message string ) error {
617632 return core .NewInvalidRequestErrorWithStatus (http .StatusServiceUnavailable , message , nil ).
618633 WithCode ("feature_unavailable" )
@@ -622,6 +637,10 @@ func (h *Handler) aliasesUnavailableError() error {
622637 return featureUnavailableError ("aliases feature is unavailable" )
623638}
624639
640+ func (h * Handler ) authKeysUnavailableError () error {
641+ return featureUnavailableError ("auth keys feature is unavailable" )
642+ }
643+
625644func (h * Handler ) executionPlansUnavailableError () error {
626645 return featureUnavailableError ("execution plans feature is unavailable" )
627646}
@@ -646,6 +665,92 @@ func executionPlanWriteError(err error) error {
646665 return err
647666}
648667
668+ func authKeyWriteError (err error ) error {
669+ if err == nil {
670+ return nil
671+ }
672+ if authkeys .IsValidationError (err ) {
673+ return core .NewInvalidRequestError (err .Error (), err )
674+ }
675+ return err
676+ }
677+
678+ func deactivateByID (
679+ c * echo.Context ,
680+ unavailableErr error ,
681+ idLabel string ,
682+ notFoundErr error ,
683+ notFoundMessage string ,
684+ deactivate func (context.Context , string ) error ,
685+ writeError func (error ) error ,
686+ ) error {
687+ if unavailableErr != nil {
688+ return handleError (c , unavailableErr )
689+ }
690+
691+ id := strings .TrimSpace (c .Param ("id" ))
692+ if id == "" {
693+ return handleError (c , core .NewInvalidRequestError (idLabel + " id is required" , nil ))
694+ }
695+
696+ if err := deactivate (c .Request ().Context (), id ); err != nil {
697+ if errors .Is (err , notFoundErr ) {
698+ return handleError (c , core .NewNotFoundError (notFoundMessage + id ))
699+ }
700+ return handleError (c , writeError (err ))
701+ }
702+ return c .NoContent (http .StatusNoContent )
703+ }
704+
705+ // ListAuthKeys handles GET /admin/api/v1/auth-keys
706+ func (h * Handler ) ListAuthKeys (c * echo.Context ) error {
707+ if h .authKeys == nil {
708+ return handleError (c , h .authKeysUnavailableError ())
709+ }
710+ views := h .authKeys .ListViews ()
711+ if views == nil {
712+ views = []authkeys.View {}
713+ }
714+ return c .JSON (http .StatusOK , views )
715+ }
716+
717+ // CreateAuthKey handles POST /admin/api/v1/auth-keys
718+ func (h * Handler ) CreateAuthKey (c * echo.Context ) error {
719+ if h .authKeys == nil {
720+ return handleError (c , h .authKeysUnavailableError ())
721+ }
722+
723+ var req createAuthKeyRequest
724+ if err := c .Bind (& req ); err != nil {
725+ return handleError (c , core .NewInvalidRequestError ("invalid request body: " + err .Error (), err ))
726+ }
727+
728+ issued , err := h .authKeys .Create (c .Request ().Context (), authkeys.CreateInput {
729+ Name : req .Name ,
730+ Description : req .Description ,
731+ ExpiresAt : req .ExpiresAt ,
732+ })
733+ if err != nil {
734+ return handleError (c , authKeyWriteError (err ))
735+ }
736+ if issued == nil {
737+ return c .NoContent (http .StatusNoContent )
738+ }
739+ return c .JSON (http .StatusCreated , issued )
740+ }
741+
742+ // DeactivateAuthKey handles POST /admin/api/v1/auth-keys/:id/deactivate
743+ func (h * Handler ) DeactivateAuthKey (c * echo.Context ) error {
744+ var unavailableErr error
745+ var deactivate func (context.Context , string ) error
746+ if h .authKeys == nil {
747+ unavailableErr = h .authKeysUnavailableError ()
748+ } else {
749+ deactivate = h .authKeys .Deactivate
750+ }
751+ return deactivateByID (c , unavailableErr , "auth key" , authkeys .ErrNotFound , "auth key not found: " , deactivate , authKeyWriteError )
752+ }
753+
649754// ListAliases handles GET /admin/api/v1/aliases
650755func (h * Handler ) ListAliases (c * echo.Context ) error {
651756 if h .aliases == nil {
@@ -806,22 +911,14 @@ func (h *Handler) CreateExecutionPlan(c *echo.Context) error {
806911
807912// DeactivateExecutionPlan handles POST /admin/api/v1/execution-plans/:id/deactivate
808913func (h * Handler ) DeactivateExecutionPlan (c * echo.Context ) error {
914+ var unavailableErr error
915+ var deactivate func (context.Context , string ) error
809916 if h .plans == nil {
810- return handleError (c , h .executionPlansUnavailableError ())
811- }
812-
813- id := strings .TrimSpace (c .Param ("id" ))
814- if id == "" {
815- return handleError (c , core .NewInvalidRequestError ("execution plan id is required" , nil ))
816- }
817-
818- if err := h .plans .Deactivate (c .Request ().Context (), id ); err != nil {
819- if errors .Is (err , executionplans .ErrNotFound ) {
820- return handleError (c , core .NewNotFoundError ("workflow not found: " + id ))
821- }
822- return handleError (c , executionPlanWriteError (err ))
917+ unavailableErr = h .executionPlansUnavailableError ()
918+ } else {
919+ deactivate = h .plans .Deactivate
823920 }
824- return c . NoContent ( http . StatusNoContent )
921+ return deactivateByID ( c , unavailableErr , "execution plan" , executionplans . ErrNotFound , "workflow not found: " , deactivate , executionPlanWriteError )
825922}
826923
827924func (h * Handler ) validateExecutionPlanGuardrails (payload executionplans.Payload ) error {
0 commit comments