package api import ( "encoding/json" "errors" "net/http" "codeberg.org/SquidSE/ClusterCanvas/service/internal/auth" "codeberg.org/SquidSE/ClusterCanvas/service/internal/settings" ) type networkResponse struct { Network settings.NetworkSettings `json:"network"` } type putNetworkRequest struct { Network settings.NetworkSettings `json:"network"` } func networkGetHandler(configDir string) http.HandlerFunc { return func(writer http.ResponseWriter, request *http.Request) { settingsPayload, err := loadSettingsOrDefault(configDir) if err != nil { writeJSON(writer, http.StatusInternalServerError, apiErrorResponse{Error: err.Error()}) return } writeJSON(writer, http.StatusOK, networkResponse{ Network: settings.EffectiveNetwork(settingsPayload.Network), }) } } func networkPutHandler(configDir string) http.HandlerFunc { return func(writer http.ResponseWriter, request *http.Request) { if err := authorizeNetworkWrite(request); err != nil { writeJSON(writer, http.StatusForbidden, apiErrorResponse{Error: err.Error()}) return } var payload putNetworkRequest if err := json.NewDecoder(request.Body).Decode(&payload); err != nil { writeJSON(writer, http.StatusBadRequest, apiErrorResponse{Error: "invalid JSON body"}) return } if err := validateNetworkSettings(payload.Network); err != nil { writeJSON(writer, http.StatusBadRequest, apiErrorResponse{Error: err.Error()}) return } network := settings.EffectiveNetwork(payload.Network) clientIP := auth.ClientIP(request) if wouldLockOut, reason := auth.WouldLockOut(clientIP, network); wouldLockOut { message := "these settings would lock out your current IP" if reason != "" { message = message + ": " + reason } writeJSON(writer, http.StatusBadRequest, apiErrorResponse{Error: message}) return } settingsPayload, err := loadSettingsOrDefault(configDir) if err != nil { writeJSON(writer, http.StatusInternalServerError, apiErrorResponse{Error: err.Error()}) return } settingsPayload.Network = network if err := settings.SaveSettings(configDir, settingsPayload); err != nil { writeJSON(writer, http.StatusInternalServerError, apiErrorResponse{Error: err.Error()}) return } writeJSON(writer, http.StatusOK, networkResponse{Network: network}) } } func authorizeNetworkWrite(_ *http.Request) error { // TODO: integrate authz middleware. return nil } func validateNetworkSettings(network settings.NetworkSettings) error { if err := auth.ValidateListenAddress(network.ListenAddress); err != nil { return err } if err := auth.ValidateHostname(network.PublicHostname); err != nil { return err } if err := auth.ValidateAccessRules(network.Rules); err != nil { return err } switch network.AccessMode { case settings.AccessModeOpen, settings.AccessModeWhitelist, settings.AccessModeBlacklist: return nil default: return errors.New("network.access_mode must be open, whitelist, or blacklist") } }