feat(httpapi): return replica docker install commands on speaker create
quality / commitlint (push) Skipped
quality / changes (push) Successful in 8s
quality / docker-check (push) Skipped
quality / openapi (push) Failing after 21s
quality / web (push) Successful in 55s
quality / go (push) Successful in 1m2s
quality / bird2 (push) Successful in 16s
CD / quality (push) Failing after 2m49s
CD / publish (push) Skipped

После создания реплики 201 отдаёт agent_secret, node_token и install.docker_commands (bird2 + agent + Traefik DNS-01). UI показывает шаг установки вместо закрытия диалога, чтобы секрет больше не терялся.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-08-21 13:51:06 +07:00
co-authored by Cursor
parent c5148ac4a0
commit 3723ba7ed1
11 changed files with 891 additions and 112 deletions
+36 -2
View File
@@ -1129,11 +1129,25 @@ func (s *Server) handlePostSpeaker(w http.ResponseWriter, r *http.Request) {
if !ok || !s.requirePerm(w, a, "bgp:network:write") {
return
}
var body store.Speaker
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
var req speakerCreateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeProblem(w, http.StatusBadRequest, "Bad Request", "invalid json")
return
}
metaStr, err := metaJSONRawToString(req.MetaJSON)
if err != nil {
writeProblem(w, http.StatusBadRequest, "Bad Request", "meta_json must be a JSON object or string")
return
}
role := strings.TrimSpace(req.Role)
if role == "" {
role = "replica"
}
body := store.Speaker{
Role: role,
Endpoint: strings.TrimSpace(req.Endpoint),
MetaJSON: metaStr,
}
if err := normalizeSpeakerCreate(&body); err != nil {
writeStoreErr(w, err)
return
@@ -1148,6 +1162,26 @@ func (s *Server) handlePostSpeaker(w http.ResponseWriter, r *http.Request) {
if meta := store.ParseSpeakerMeta(x.MetaJSON); meta.AgentSecret != "" {
resp["agent_secret"] = meta.AgentSecret
}
if strings.ToLower(strings.TrimSpace(x.Role)) != "master" {
createdKey, kerr := s.store.CreateAPIKey(a.TenantID, &store.APIKeyCreate{
Name: "speaker:" + x.ID,
Role: "node",
})
if kerr != nil {
_ = s.store.DeleteSpeaker(a.TenantID, x.ID)
writeStoreErr(w, kerr)
return
}
if err := s.keyResolver.Reload(s.store); err != nil {
writeProblem(w, http.StatusInternalServerError, "Internal Server Error", "failed to reload api keys")
return
}
s.recordCRUDAudit(r, a, "bgp.api_key.create", "Created API key "+createdKey.Name, createdKey.ID, map[string]any{"api_key_id": createdKey.ID, "role": createdKey.Role, "speaker_id": x.ID})
resp["node_token"] = createdKey.Token
s.attachReplicaInstall(resp, x, createdKey.Token, req, r)
}
writeJSON(w, http.StatusCreated, resp)
}