Repository: caddyserver/caddy (language: go)

Issue:
feature request: API to get the adapted config
As far as I understand it, to load a Caddyfile you have two options:
* use the command `caddy adapt` with a Caddyfile, get the JSON, then give that to the `/load` admin API
* use the `/load` admin API with `text/caddyfile` and it adapts and loads.

I would like to be able to do something like this:
* use a `/adapt`  API with `text/caddyfile` which adapts and returns the json, I then do any other transformations, and then post that to `/load`.

My use case: I have a bunch of Caddyfile configs, the syntax is nice, but I also would like to use https://github.com/mholt/caddy-l4 -- which doesn't yet have Caddyfile support.

So, I'd adapt the Caddyfiles to json, merge in some custom handlers for layer4, and then load that.

pros:
* nice endpoint
* means i dont need a caddy for the server and a caddy on a configuration service
* the code is basically already done

cons:
* why is this webserver spending cpu cycles doing something that doesn't need to be ran on the server

It didn't seem so hard to make so I copied some lines from `caddyconfig/load.go` and published it as an extension: https://github.com/adamburgess/caddy-admin-adapt


Discussion / hints:
That seems like a reasonable thing to add. I don't see why not 👍 feel free to make a pull request

I think we'll want to make the endpoint also support passing the adapter name like `/adapt/json5` because I don't think all the adapters have defined mime types. It can definitely also handle `text/caddyfile` though if not in the URL.
Hm, yeah, I guess that would be useful in that case.

> why is this webserver spending cpu cycles doing something that doesn't need to be ran on the server

Run it locally then, instead of on a production server. Should get the same result if your server build is identical.

Pull requests welcome, if you want to bundle this in the standard distribution.
@adamburgess Why did you close this? (It did remind me that I need to work on this though.)

Fix patch:
diff --git a/caddyconfig/load.go b/caddyconfig/load.go
index 7a390d0b520..78aaba2b7d6 100644
--- a/caddyconfig/load.go
+++ b/caddyconfig/load.go
@@ -58,6 +58,10 @@ func (al adminLoad) Routes() []caddy.AdminRoute {
 			Pattern: "/load",
 			Handler: caddy.AdminHandlerFunc(al.handleLoad),
 		},
+		{
+			Pattern: "/adapt",
+			Handler: caddy.AdminHandlerFunc(al.handleAdapt),
+		},
 	}
 }
 
@@ -122,7 +126,48 @@ func (adminLoad) handleLoad(w http.ResponseWriter, r *http.Request) error {
 	return nil
 }
 
-// adaptByContentType adapts body to Caddy JSON using the adapter specified by contenType.
+// handleAdapt adapts the given Caddy config to JSON and responds with the result.
+func (adminLoad) handleAdapt(w http.ResponseWriter, r *http.Request) error {
+	if r.Method != http.MethodPost {
+		return caddy.APIError{
+			HTTPStatus: http.StatusMethodNotAllowed,
+			Err:        fmt.Errorf("method not allowed"),
+		}
+	}
+
+	buf := bufPool.Get().(*bytes.Buffer)
+	buf.Reset()
+	defer bufPool.Put(buf)
+
+	_, err := io.Copy(buf, r.Body)
+	if err != nil {
+		return caddy.APIError{
+			HTTPStatus: http.StatusBadRequest,
+			Err:        fmt.Errorf("reading request body: %v", err),
+		}
+	}
+
+	result, warnings, err := adaptByContentType(r.Header.Get("Content-Type"), buf.Bytes())
+	if err != nil {
+		return caddy.APIError{
+			HTTPStatus: http.StatusBadRequest,
+			Err:        err,
+		}
+	}
+
+	out := struct {
+		Warnings []Warning       `json:"warnings,omitempty"`
+		Result   json.RawMessage `json:"result"`
+	}{
+		Warnings: warnings,
+		Result:   result,
+	}
+
+	w.Header().Set("Content-Type", "application/json")
+	return json.NewEncoder(w).Encode(out)
+}
+
+// adaptByContentType adapts body to Caddy JSON using the adapter specified by contentType.
 // If contentType is empty or ends with "/json", the input will be returned, as a no-op.
 func adaptByContentType(contentType string, body []byte) ([]byte, []Warning, error) {
 	// assume JSON as the default


Test patch: