Skip to content

Commit 10ab630

Browse files
0xmadmichiosw
authored andcommitted
feat(web): backend service integration
- [x] Add create server method - [x] Add remove server method
1 parent fd9dc6c commit 10ab630

File tree

7 files changed

+77
-99
lines changed

7 files changed

+77
-99
lines changed

internal/web/domain/server.go

Lines changed: 0 additions & 7 deletions
This file was deleted.

internal/web/handlers/api.go

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ func (h *APIHandlers) RegisterRoutes(mux *http.ServeMux /* or other router type
5050
// For GET requests on server details, tools
5151
mux.HandleFunc("GET /api/mcp/servers/", h.routeServerGetRequests)
5252

53-
// For PUT requests (update server)
54-
mux.HandleFunc("PUT /api/mcp/servers/", h.routeServerPutRequests)
53+
// For update requests (update server)
54+
mux.HandleFunc("POST /api/mcp/servers/update/", h.routeServerUpdateRequests)
5555

56-
// For DELETE requests (remove server)
57-
mux.HandleFunc("DELETE /api/mcp/servers/", h.routeServerDeleteRequests)
56+
// For delete requests (remove server)
57+
mux.HandleFunc("POST /api/mcp/servers/remove/", h.routeServerDeleteRequests)
5858

5959
// For POST requests (rediscover tools)
6060
mux.HandleFunc("POST /api/mcp/servers/", h.routeServerPostRequests)
@@ -67,9 +67,6 @@ func (h *APIHandlers) RegisterRoutes(mux *http.ServeMux /* or other router type
6767
mux.HandleFunc("GET /api/docs/openapi.yaml", h.ServeOpenAPISpec)
6868
mux.HandleFunc("GET /api/docs", h.ServeSwaggerUI)
6969
mux.HandleFunc("GET /api/docs/", h.ServeSwaggerUI)
70-
71-
// Root handler - REMOVED to avoid conflict with redirect in app.go
72-
// mux.HandleFunc("/", h.rootHandler)
7370
}
7471

7572
// HealthCheck handles GET /api/health
@@ -82,7 +79,7 @@ func (h *APIHandlers) HealthCheck(w http.ResponseWriter, _ *http.Request) {
8279
// routeServerGetRequests handles GET requests for server-specific paths and routes to appropriate handlers
8380
func (h *APIHandlers) routeServerGetRequests(w http.ResponseWriter, r *http.Request) {
8481
// Extract server ID and subpath
85-
serverID, subpath, err := parseServerPath(r.URL.Path)
82+
serverID, subpath, err := parseServerPath(r.URL.Path, "/api/mcp/servers/")
8683
if err != nil {
8784
http.Error(w, err.Error(), http.StatusBadRequest)
8885
return
@@ -104,7 +101,7 @@ func (h *APIHandlers) routeServerGetRequests(w http.ResponseWriter, r *http.Requ
104101
// routeServerPostRequests handles POST requests for server-specific paths and routes to appropriate handlers
105102
func (h *APIHandlers) routeServerPostRequests(w http.ResponseWriter, r *http.Request) {
106103
// Extract server ID and subpath
107-
serverID, subpath, err := parseServerPath(r.URL.Path)
104+
serverID, subpath, err := parseServerPath(r.URL.Path, "/api/mcp/servers/")
108105
if err != nil {
109106
http.Error(w, err.Error(), http.StatusBadRequest)
110107
return
@@ -123,20 +120,20 @@ func (h *APIHandlers) routeServerPostRequests(w http.ResponseWriter, r *http.Req
123120
}
124121
}
125122

126-
// routeServerPutRequests handles PUT requests for server-specific paths
127-
func (h *APIHandlers) routeServerPutRequests(w http.ResponseWriter, r *http.Request) {
128-
serverID, subpath, err := parseServerPath(r.URL.Path)
123+
// routeServerUpdateRequests handles PUT requests for server-specific paths
124+
func (h *APIHandlers) routeServerUpdateRequests(w http.ResponseWriter, r *http.Request) {
125+
serverID, subpath, err := parseServerPath(r.URL.Path, "/api/mcp/servers/update/")
129126
if err != nil {
130127
http.Error(w, err.Error(), http.StatusBadRequest)
131128
return
132129
}
133130

134131
switch subpath {
135132
case "":
136-
// PUT /api/mcp/servers/:id - Update server name/url
133+
// POST /api/mcp/servers/update/:id - Update server name/url
137134
h.UpdateMCPServer(w, r, serverID)
138135
case "status":
139-
// PUT /api/mcp/servers/:id/status - Update server connection status
136+
// POST /api/mcp/servers/update/:id/status - Update server connection status
140137
h.UpdateMCPServerStatus(w, r, serverID)
141138
default:
142139
http.NotFound(w, r)
@@ -145,15 +142,15 @@ func (h *APIHandlers) routeServerPutRequests(w http.ResponseWriter, r *http.Requ
145142

146143
// routeServerDeleteRequests handles DELETE requests for server-specific paths (Recommended structure)
147144
func (h *APIHandlers) routeServerDeleteRequests(w http.ResponseWriter, r *http.Request) {
148-
serverID, subpath, err := parseServerPath(r.URL.Path)
145+
serverID, subpath, err := parseServerPath(r.URL.Path, "/api/mcp/servers/remove/")
149146
if err != nil {
150147
http.Error(w, err.Error(), http.StatusBadRequest)
151148
return
152149
}
153150

154151
switch subpath {
155152
case "":
156-
// DELETE /api/mcp/servers/:id - Remove server
153+
// POST /api/mcp/servers/remove/:id - Remove server
157154
h.RemoveMCPServer(w, r, serverID)
158155
default:
159156
http.NotFound(w, r)
@@ -162,9 +159,9 @@ func (h *APIHandlers) routeServerDeleteRequests(w http.ResponseWriter, r *http.R
162159

163160
// parseServerPath extracts the server ID and subpath from a URL path
164161
// Returns serverID, subpath, error
165-
func parseServerPath(path string) (int64, string, error) {
166-
// Extract path after /api/mcp/servers/
167-
path = path[len("/api/mcp/servers/"):]
162+
func parseServerPath(path string, prefix string) (int64, string, error) {
163+
// Extract path after prefix
164+
path = path[len(prefix):]
168165

169166
// Split the remaining path
170167
parts := splitPath(path)
@@ -189,42 +186,37 @@ func parseServerPath(path string) (int64, string, error) {
189186

190187
// --- MCP Server Handlers ---
191188

192-
type addServerRequest struct {
193-
Name string `json:"name"`
194-
URL string `json:"url"`
195-
}
196-
197189
// AddMCPServer handles POST /api/mcp/servers
198190
func (h *APIHandlers) AddMCPServer(w http.ResponseWriter, r *http.Request) {
199-
var req addServerRequest
200-
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
201-
http.Error(w, fmt.Sprintf("Invalid request body: %v", err), http.StatusBadRequest)
191+
// Parse form data
192+
if err := r.ParseForm(); err != nil {
193+
h.logger.Error().Err(err).Msg("Failed to parse form data")
194+
http.Redirect(w, r, "/error?error=Form parse error", http.StatusSeeOther)
202195
return
203196
}
204197

205-
if req.Name == "" || req.URL == "" {
206-
http.Error(w, "Server name and URL are required", http.StatusBadRequest)
198+
name := r.FormValue("name")
199+
url := r.FormValue("url")
200+
201+
if name == "" || url == "" {
202+
http.Redirect(w, r, "/error?error=Missing name or url", http.StatusSeeOther)
207203
return
208204
}
209205

210-
newServer, err := h.backendService.AddMCPServer(req.Name, req.URL)
206+
_, err := h.backendService.AddMCPServer(name, url)
211207
if err != nil {
212208
h.logger.Error().Err(err).Msg("Error adding MCP server via API")
213-
// TODO: Check error type for user-friendly messages (e.g., duplicate URL)
214-
if err.Error() == fmt.Sprintf("MCP server with URL '%s' already exists", req.URL) {
215-
http.Error(w, err.Error(), http.StatusConflict) // 409 Conflict
209+
210+
if err.Error() == fmt.Sprintf("MCP server with URL '%s' already exists", url) {
211+
http.Redirect(w, r, "/error?error=Server already exists", http.StatusSeeOther)
216212
return
217213
}
218-
http.Error(w, fmt.Sprintf("Failed to add server: %v", err), http.StatusInternalServerError)
214+
215+
http.Redirect(w, r, "/error?error=Internal server error", http.StatusSeeOther)
219216
return
220217
}
221218

222-
w.Header().Set("Content-Type", "application/json")
223-
w.WriteHeader(http.StatusCreated) // 201 Created
224-
if err := json.NewEncoder(w).Encode(newServer); err != nil {
225-
h.logger.Error().Err(err).Msg("Error encoding added server response")
226-
// Header already sent, can't change status code
227-
}
219+
http.Redirect(w, r, "/success", http.StatusSeeOther)
228220
}
229221

230222
// ListMCPServers handles GET /api/mcp/servers - Returns full JSON
@@ -345,22 +337,22 @@ func (h *APIHandlers) UpdateMCPServer(w http.ResponseWriter, r *http.Request, se
345337
}
346338
}
347339

348-
// RemoveMCPServer handles DELETE /api/mcp/servers/:id
340+
// RemoveMCPServer handles POST /api/mcp/servers/:id
349341
func (h *APIHandlers) RemoveMCPServer(w http.ResponseWriter, r *http.Request, serverID int64) {
350-
// ID is now parsed in routeServerDeleteRequests
351342
err := h.backendService.RemoveMCPServer(serverID)
343+
352344
if err != nil {
353345
h.logger.Error().Err(err).Int64("serverId", serverID).Msg("Error removing MCP server via API")
354-
// Check if error is "not found" vs other internal errors
355-
if strings.Contains(err.Error(), "not found") { // Basic check
356-
http.Error(w, err.Error(), http.StatusNotFound)
346+
347+
if strings.Contains(err.Error(), "not found") {
348+
http.Redirect(w, r, "/error?error=Server not found", http.StatusSeeOther)
357349
} else {
358-
http.Error(w, "Failed to remove server", http.StatusInternalServerError)
350+
http.Redirect(w, r, "/error?error=Failed to remove server", http.StatusSeeOther)
359351
}
360352
return
361353
}
362354

363-
w.WriteHeader(http.StatusNoContent) // 204 No Content on successful delete
355+
http.Redirect(w, r, "/success", http.StatusSeeOther)
364356
}
365357

366358
// --- Tool Management Handlers ---

internal/web/handlers/ui.go

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@ import (
55
"fmt"
66
"net/http"
77

8+
"github.com/co-browser/agent-browser/internal/backend"
89
"github.com/co-browser/agent-browser/internal/log"
910
"github.com/co-browser/agent-browser/internal/web/templates"
1011
)
1112

1213
// UIHandler holds dependencies for UI handlers.
1314
type UIHandler struct {
14-
log log.Logger
15+
backendService backend.Service
16+
log log.Logger
1517
}
1618

1719
// NewUIHandler creates a handler for serving the main UI page.
1820
// It now requires a logger.
19-
func NewUIHandler(logger log.Logger) http.Handler {
20-
h := &UIHandler{log: logger}
21+
func NewUIHandler(bs backend.Service, logger log.Logger) http.Handler {
22+
h := &UIHandler{backendService: bs, log: logger}
2123
mux := http.NewServeMux()
2224

2325
mux.HandleFunc("/", h.serveIndex)
@@ -30,24 +32,17 @@ func NewUIHandler(logger log.Logger) http.Handler {
3032

3133
// serveIndex handles requests for the main UI page.
3234
func (h *UIHandler) serveIndex(w http.ResponseWriter, r *http.Request) {
35+
// Call backend service directly to get the list of servers
36+
servers, listServersError := h.backendService.ListMCPServers()
37+
38+
if listServersError != nil {
39+
h.log.Error().Err(listServersError).Msg("failed to fetch MCP server list")
40+
http.Error(w, "failed to load server list", http.StatusInternalServerError)
41+
return
42+
}
43+
3344
err := templates.IndexPage(templates.IndexPageProps{
34-
Servers: []domain.ServerItem{
35-
{
36-
ID: "1",
37-
Name: "Server Alpha",
38-
IP: "192.168.1.10",
39-
},
40-
{
41-
ID: "2",
42-
Name: "Server Beta",
43-
IP: "192.168.1.11",
44-
},
45-
{
46-
ID: "3",
47-
Name: "Server Charlie",
48-
IP: "192.168.1.12",
49-
},
50-
},
45+
Servers: servers,
5146
}).Render(r.Context(), w)
5247

5348
if err != nil {

internal/web/server.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,15 @@ import (
1212
)
1313

1414
// NewMux creates the main HTTP ServeMux.
15-
func NewMux(params struct {
16-
fx.In
17-
18-
UIHandler http.Handler `name:"uiHandler"`
19-
APIHandler http.Handler `name:"apiHandler"`
20-
}) *http.ServeMux {
15+
func NewMux(
16+
UIHandler http.Handler,
17+
APIHandlers http.Handler,
18+
) *http.ServeMux {
2119
mux := http.NewServeMux()
2220
// Define routes here
2321
mux.Handle("/metrics", promhttp.Handler())
24-
mux.Handle("/", params.UIHandler)
25-
mux.Handle("/api/", params.APIHandler)
22+
mux.Handle("/", UIHandler)
23+
mux.Handle("/api/", APIHandlers)
2624

2725
return mux
2826
}

internal/web/templates/blocks/add_server_form.templ

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ css addServerFormButton() {
4343
templ AddServerForm() {
4444
<section class={ addServerForm() } data-testid="add-server-form">
4545
<h2>Add New Server</h2>
46-
<form method="POST" action="/api/create">
46+
<form method="POST" action="/api/mcp/servers">
4747
<div class={ addServerFormField() }>
4848
<label class={ addServerFormFieldLabel() } for="name">
4949
Server Name:
@@ -53,22 +53,20 @@ templ AddServerForm() {
5353
id="name"
5454
type="text"
5555
name="name"
56-
pattern="^[a-zA-Z0-9]([a-zA-Z0-9-_]{0,62}[a-zA-Z0-9])?$"
5756
placeholder="Enter server name"
5857
required
5958
/>
6059
</div>
6160
<div class={ addServerFormField() }>
62-
<label class={ addServerFormFieldLabel() } for="ip">
63-
IP Address:
61+
<label class={ addServerFormFieldLabel() } for="url">
62+
URL Address:
6463
</label>
6564
<input
6665
class={ addServerFormFieldInput() }
67-
id="ip"
66+
id="url"
6867
type="text"
69-
pattern="^((25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})$"
70-
name="ip"
71-
placeholder="Enter ip address"
68+
name="url"
69+
placeholder="Enter URL address"
7270
required
7371
/>
7472
</div>

internal/web/templates/blocks/servers.templ

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package blocks
22

33
import (
4-
"github.com/co-browser/agent-browser/internal/web/domain"
4+
"fmt"
5+
6+
"github.com/co-browser/agent-browser/internal/backend/models"
57
"github.com/co-browser/agent-browser/internal/web/templates/components"
68
)
79

@@ -44,7 +46,7 @@ css serverListItemName() {
4446
font-weight: bold;
4547
}
4648

47-
css serverListItemIP() {
49+
css serverListItemURL() {
4850
display: block;
4951
font-size: 0.9em;
5052
font-style: italic;
@@ -64,7 +66,7 @@ css serverListItemRemove() {
6466
border-radius: 4px;
6567
}
6668

67-
templ Servers(servers []domain.ServerItem) {
69+
templ Servers(servers []models.MCPServer) {
6870
<section class={ serverSection() }>
6971
<div class={ serverSectionHeader() }>
7072
<h2 class={ serverTitle() }>MCP servers</h2>
@@ -78,14 +80,14 @@ templ Servers(servers []domain.ServerItem) {
7880
</section>
7981
}
8082

81-
templ Server(server domain.ServerItem) {
83+
templ Server(server models.MCPServer) {
8284
<li class={ serverListItem() }>
8385
<div class={ serverListItemWrapper() }>
8486
<strong class={ serverListItemName() }>{ server.Name }</strong>
85-
<div class={ serverListItemIP() }>{ server.IP }</div>
87+
<div class={ serverListItemURL() }>{ server.URL }</div>
8688
</div>
87-
<form method="POST" action="/api/remove">
88-
<input type="hidden" name="id" value={ server.ID }/>
89+
<form method="POST" action={ templ.SafeURL(fmt.Sprintf("/api/mcp/servers/remove/%d", server.ID)) }>
90+
<input type="hidden" name="id" value={ fmt.Sprintf("%d", server.ID) }/>
8991
@components.Button(components.ButtonProps{Label: "Remove", Type: "submit", Class: serverListItemRemove()})
9092
</form>
9193
</li>

internal/web/templates/components/link.templ

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package components
22

33
type LinkProps struct {
4-
Name string
5-
Href templ.SafeURL
4+
Name string
5+
Href templ.SafeURL
66
Class templ.CSSClass
77
}
88

0 commit comments

Comments
 (0)