-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAgentBridgeController.java
More file actions
90 lines (80 loc) · 3.66 KB
/
Copy pathAgentBridgeController.java
File metadata and controls
90 lines (80 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package com.dbaagent.controller;
import com.dbaagent.service.AgentBridgeService;
import com.dbaagent.service.security.AccessControlService;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* Bootstrap endpoint for the native "Agent" chat tab. Resolves the current
* DeepSQL user to their isolated agent profile (provisioning it on first use)
* so the frontend can open a chat session against the DeepSQL Agent service.
*/
@RestController
@RequestMapping("/agent")
@RequiredArgsConstructor
public class AgentBridgeController {
private static final Logger log = LoggerFactory.getLogger(AgentBridgeController.class);
private final AccessControlService accessControlService;
private final AgentBridgeService agentBridgeService;
@Value("${security.cookie.name:auth_token}")
private String cookieName;
@PostMapping("/session")
public ResponseEntity<Map<String, Object>> session(
@RequestBody(required = false) Map<String, Object> body,
HttpServletRequest request) {
String username = accessControlService.requireCurrentUsername();
String token = extractToken(request);
String connectionId = body == null ? null : asString(body.get("connectionId"));
AgentBridgeService.ProfileBootstrap bootstrap;
try {
bootstrap = agentBridgeService.ensureProfile(username, token, connectionId);
} catch (AgentBridgeService.ProvisioningException e) {
log.warn("Agent session bootstrap failed for {}: {}", username, e.getMessage());
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(Map.of(
"error", "agent_provisioning_failed",
"message", "Could not provision the DeepSQL Agent for this user. "
+ "Check that the agent provisioner is running and reachable."
));
}
// Boot health check: probe the exact token just provisioned against this
// backend's own API before telling the UI it's safe to chat. Without
// this, an expired/misrouted token surfaces only after several failed
// tool calls deep into a conversation (W1 "fail loud, early").
Map<String, Object> response = new HashMap<>();
response.put("profile", bootstrap.profile());
response.put("username", username);
boolean mcpAuthOk = agentBridgeService.probeMcpAuth(bootstrap.token());
response.put("mcpAuthOk", mcpAuthOk);
if (!mcpAuthOk) {
response.put("mcpAuthError", "The DeepSQL Agent could not authenticate against this API with its "
+ "provisioned token. Reconnect or check the Agent runtime.");
}
return ResponseEntity.ok(response);
}
private String extractToken(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7);
}
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if (cookieName.equals(c.getName())) {
return c.getValue();
}
}
}
return "";
}
private static String asString(Object o) {
return o == null ? null : o.toString();
}
}