package app import ( "net/http" "time" ) func handleTransportClientProvisionAction(w http.ResponseWriter, r *http.Request, id string) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } ifaceID, ok := resolveTransportClientIfaceID(id) if !ok { writeJSON(w, http.StatusNotFound, TransportClientLifecycleResponse{ OK: false, Message: "not found", Code: "TRANSPORT_CLIENT_NOT_FOUND", }) return } status := http.StatusOK resp := TransportClientLifecycleResponse{} withTransportIfaceLock(ifaceID, func() { status, resp = executeTransportClientProvisionActionLocked(id) }) writeJSON(w, status, resp) } func executeTransportClientProvisionActionLocked(id string) (int, TransportClientLifecycleResponse) { now := time.Now().UTC() transportMu.Lock() st := loadTransportClientsState() idx := findTransportClientIndex(st.Items, id) if idx < 0 { transportMu.Unlock() return http.StatusNotFound, TransportClientLifecycleResponse{ OK: false, Message: "not found", Code: "TRANSPORT_CLIENT_NOT_FOUND", } } it := st.Items[idx] prev := it.Status ifaces, err := syncTransportInterfacesWithClientsLocked(st.Items) if err != nil { transportMu.Unlock() return http.StatusOK, TransportClientLifecycleResponse{ OK: false, Message: "interfaces sync failed: " + err.Error(), Code: "TRANSPORT_INTERFACES_SAVE_FAILED", ClientID: id, Kind: it.Kind, Action: "provision", StatusBefore: prev, StatusAfter: it.Status, Health: it.Health, Runtime: transportRuntimeSnapshot(it, now), } } if bound, changed := applyTransportIfaceBinding(it, ifaces, now); changed { it = bound } transportMu.Unlock() backend := selectTransportBackend(it) provision := backend.Provision(it) transportMu.Lock() defer transportMu.Unlock() st = loadTransportClientsState() idx = findTransportClientIndex(st.Items, id) if idx < 0 { return http.StatusNotFound, TransportClientLifecycleResponse{ OK: false, Message: "not found", Code: "TRANSPORT_CLIENT_NOT_FOUND", } } it = st.Items[idx] ifaces, err = syncTransportInterfacesWithClientsLocked(st.Items) if err != nil { return http.StatusOK, TransportClientLifecycleResponse{ OK: false, Message: "interfaces sync failed: " + err.Error(), Code: "TRANSPORT_INTERFACES_SAVE_FAILED", ClientID: id, Kind: it.Kind, Action: "provision", StatusBefore: prev, StatusAfter: it.Status, Health: it.Health, Runtime: transportRuntimeSnapshot(it, now), } } if bound, changed := applyTransportIfaceBinding(it, ifaces, now); changed { it = bound } applyTransportProvisionResult(&it, now, backend.ID(), provision) st.Items[idx] = it if err := saveTransportClientsState(st); err != nil { return http.StatusOK, TransportClientLifecycleResponse{ OK: false, Message: "save failed: " + err.Error(), Code: "TRANSPORT_CLIENT_SAVE_FAILED", ClientID: id, Kind: it.Kind, Action: "provision", ExitCode: provision.ExitCode, Stdout: provision.Stdout, Stderr: provision.Stderr, } } events.push("transport_client_provisioned", map[string]any{ "id": id, "ok": provision.OK, "msg": provision.Message, }) return http.StatusOK, TransportClientLifecycleResponse{ OK: provision.OK, Message: provision.Message, Code: provision.Code, ExitCode: provision.ExitCode, Stdout: provision.Stdout, Stderr: provision.Stderr, ClientID: id, Kind: it.Kind, Action: "provision", StatusBefore: prev, StatusAfter: it.Status, Health: it.Health, Runtime: transportRuntimeSnapshot(it, now), } }