@@ -3,23 +3,44 @@ package handlers
33import (
44 "errors"
55 "net/http"
6+ "reflect"
67
78 "github.com/gin-gonic/gin"
89 "gorm.io/gorm"
910
11+ "github.com/Wikid82/charon/backend/internal/orthrus"
1012 "github.com/Wikid82/charon/backend/internal/services"
1113)
1214
15+ // orthrusProxyStatusResolver is satisfied by *orthrus.OrthrusServer.
16+ type orthrusProxyStatusResolver interface {
17+ GetExternalProxyStatus (agentUUID string ) (orthrus.ExternalProxyStatus , bool )
18+ }
19+
1320// OrthrusHandler handles REST requests for Orthrus agent management.
1421type OrthrusHandler struct {
15- svc * services.OrthrusService
22+ svc * services.OrthrusService
23+ proxyResolver orthrusProxyStatusResolver
1624}
1725
1826// NewOrthrusHandler creates an OrthrusHandler backed by the given service.
1927func NewOrthrusHandler (orthrsuSvc * services.OrthrusService ) * OrthrusHandler {
2028 return & OrthrusHandler {svc : orthrsuSvc }
2129}
2230
31+ // SetProxyResolver wires a live OrthrusServer so that GetProxyStatus can
32+ // return real-time external proxy state for connected agents.
33+ func (h * OrthrusHandler ) SetProxyResolver (r orthrusProxyStatusResolver ) {
34+ if r != nil {
35+ rv := reflect .ValueOf (r )
36+ if rv .Kind () == reflect .Ptr && rv .IsNil () {
37+ h .proxyResolver = nil
38+ return
39+ }
40+ }
41+ h .proxyResolver = r
42+ }
43+
2344// RegisterRoutes wires all Orthrus management routes onto the given router group.
2445func (h * OrthrusHandler ) RegisterRoutes (rg * gin.RouterGroup ) {
2546 rg .GET ("/orthrus/agents" , h .List )
@@ -29,6 +50,7 @@ func (h *OrthrusHandler) RegisterRoutes(rg *gin.RouterGroup) {
2950 rg .DELETE ("/orthrus/agents/:uuid" , h .Delete )
3051 rg .POST ("/orthrus/agents/:uuid/revoke" , h .Revoke )
3152 rg .GET ("/orthrus/agents/:uuid/snippets" , h .GetInstallSnippets )
53+ rg .GET ("/orthrus/agents/:uuid/proxy-status" , h .GetProxyStatus )
3254}
3355
3456// List returns all registered Orthrus agents.
@@ -77,10 +99,11 @@ func (h *OrthrusHandler) Get(c *gin.Context) {
7799
78100// patchAgentRequest is the payload for partially updating an agent.
79101type patchAgentRequest struct {
80- Name * string `json:"name"`
81- HecateTunnelUUID * string `json:"hecate_tunnel_uuid"`
82- DeviceID * string `json:"device_id"`
83- ResolvedAddress * string `json:"resolved_address"`
102+ Name * string `json:"name"`
103+ HecateTunnelUUID * string `json:"hecate_tunnel_uuid"`
104+ DeviceID * string `json:"device_id"`
105+ ResolvedAddress * string `json:"resolved_address"`
106+ ExternalProxyPort * int `json:"external_proxy_port"`
84107}
85108
86109// Patch applies a partial update to an Orthrus agent.
@@ -91,12 +114,12 @@ func (h *OrthrusHandler) Patch(c *gin.Context) {
91114 c .JSON (http .StatusBadRequest , gin.H {"error" : err .Error ()})
92115 return
93116 }
94- agent , err := h .svc .Patch (uuid , req .Name , req .HecateTunnelUUID , req .DeviceID , req .ResolvedAddress )
117+ agent , err := h .svc .Patch (uuid , req .Name , req .HecateTunnelUUID , req .DeviceID , req .ResolvedAddress , req . ExternalProxyPort )
95118 if err != nil {
96119 if errors .Is (err , gorm .ErrRecordNotFound ) {
97120 c .JSON (http .StatusNotFound , gin.H {"error" : "agent not found" })
98121 } else {
99- c .JSON (http .StatusInternalServerError , gin.H {"error" : err .Error ()})
122+ c .JSON (http .StatusBadRequest , gin.H {"error" : err .Error ()})
100123 }
101124 return
102125 }
@@ -150,3 +173,38 @@ func (h *OrthrusHandler) GetInstallSnippets(c *gin.Context) {
150173 }
151174 c .JSON (http .StatusOK , snippets )
152175}
176+
177+ // GetProxyStatus returns the runtime external Docker proxy state for an agent.
178+ // 404 when the agent is not found in the database. When the agent exists but
179+ // is not currently connected, agent_online is false and live fields are zero.
180+ func (h * OrthrusHandler ) GetProxyStatus (c * gin.Context ) {
181+ uuid := c .Param ("uuid" )
182+ agent , err := h .svc .Get (uuid )
183+ if err != nil {
184+ c .JSON (http .StatusNotFound , gin.H {"error" : "agent not found" })
185+ return
186+ }
187+ resp := gin.H {
188+ "agent_uuid" : agent .UUID ,
189+ "agent_online" : false ,
190+ "configured_port" : agent .ExternalProxyPort ,
191+ "active" : false ,
192+ "active_port" : 0 ,
193+ "bind_address" : "" ,
194+ "connection_string" : "" ,
195+ "error" : "" ,
196+ }
197+ if h .proxyResolver != nil {
198+ if status , ok := h .proxyResolver .GetExternalProxyStatus (uuid ); ok {
199+ resp ["agent_online" ] = true
200+ resp ["active" ] = status .Active
201+ resp ["active_port" ] = status .ActivePort
202+ resp ["bind_address" ] = status .BoundAddress
203+ resp ["connection_string" ] = status .ConnectionString
204+ if status .Error != "" {
205+ resp ["error" ] = status .Error
206+ }
207+ }
208+ }
209+ c .JSON (http .StatusOK , resp )
210+ }
0 commit comments