diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Api/DiagnosticsController.php b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Api/DiagnosticsController.php
new file mode 100644
index 000000000..d0d711f60
--- /dev/null
+++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/Api/DiagnosticsController.php
@@ -0,0 +1,92 @@
+configdRun('caddy config');
+
+ // Decode JSON to PHP array
+ $responseArray = json_decode($response, true);
+
+ // Since errors are handled by the caddy_diagnostics script and returned as json, check for an error key in the response
+ if (isset($responseArray['error'])) {
+ return ["status" => "failed", "message" => $responseArray['message']];
+ }
+
+ // Prepare the response array
+ $response = ['status' => 'success', 'content' => $responseArray];
+ // Set the content type
+ $this->response->setContentType('application/json', 'UTF-8');
+ // Encode and set the content
+ $this->response->setContent(json_encode($response, JSON_PRETTY_PRINT));
+
+ return $this->response;
+ }
+
+ /**
+ * Fetch and display the contents of the Caddyfile as JSON.
+ */
+ public function caddyfileAction()
+ {
+ $backend = new Backend();
+ $response = $backend->configdRun('caddy caddyfile');
+
+ // Decode JSON to PHP array
+ $responseArray = json_decode($response, true);
+
+ if (isset($responseArray['error'])) {
+ // Handle the error
+ return ["status" => "failed", "message" => $responseArray['message']];
+ }
+
+ // Assuming the response structure is like { "content": "actual Caddyfile content here" }
+ if (!isset($responseArray['content'])) {
+ return ["status" => "failed", "message" => "Caddyfile content not found"];
+ }
+
+ // Return the response as an array which gets automatically encoded to JSON
+ return ["status" => "success", "content" => $responseArray['content']];
+ }
+}
diff --git a/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/DiagnosticsController.php b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/DiagnosticsController.php
new file mode 100644
index 000000000..55e7bbcc6
--- /dev/null
+++ b/www/caddy/src/opnsense/mvc/app/controllers/OPNsense/Caddy/DiagnosticsController.php
@@ -0,0 +1,41 @@
+view->pick('OPNsense/Caddy/diagnostics');
+ }
+}
diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/ACL/ACL.xml b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/ACL/ACL.xml
index c98f84a7e..cea3a9cc5 100644
--- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/ACL/ACL.xml
+++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/ACL/ACL.xml
@@ -15,4 +15,12 @@
api/caddy/reverse_proxy/*
+
+ Services: Caddy Web Server: Diagnostics
+ Allow access to Caddy Diagnostics
+
+ ui/caddy/diagnostics/*
+ api/caddy/diagnostics/*
+
+
diff --git a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Menu/Menu.xml b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Menu/Menu.xml
index 60886d550..70d146ffe 100644
--- a/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Menu/Menu.xml
+++ b/www/caddy/src/opnsense/mvc/app/models/OPNsense/Caddy/Menu/Menu.xml
@@ -3,7 +3,8 @@
-
+
+
diff --git a/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/diagnostics.volt b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/diagnostics.volt
new file mode 100644
index 000000000..b42a37741
--- /dev/null
+++ b/www/caddy/src/opnsense/mvc/app/views/OPNsense/Caddy/diagnostics.volt
@@ -0,0 +1,194 @@
+{#
+ # Copyright (c) 2024 Cedrik Pischem
+ # All rights reserved.
+ #
+ # Redistribution and use in source and binary forms, with or without modification,
+ # are permitted provided that the following conditions are met:
+ #
+ # 1. Redistributions of source code must retain the above copyright notice,
+ # this list of conditions and the following disclaimer.
+ #
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
+ # this list of conditions and the following disclaimer in the documentation
+ # and/or other materials provided with the distribution.
+ #
+ # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ # AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ # POSSIBILITY OF SUCH DAMAGE.
+ #}
+
+
+
+
+
+
+
{{ lang._("This is the generated configuration located at %sCaddyfile%s. It's the main configuration file to get support with. The validation button triggers a manual check for any configuration errors, which is the same check that is triggered by the Apply buttons automatically.") | format('/usr/local/etc/caddy/', '') }}
+
+
+
+
+
+
+
+
+
+
{{ lang._("Shows the running Caddy configuration located in %sautosave.json%s. It is automatically adapted from the Caddyfile and also includes any custom imported configurations from %scaddy.d%s.") | format('/var/db/caddy/config/caddy/', '', '/usr/local/etc/caddy/', '') }}
+
+
+
+
+
diff --git a/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_diagnostics.py b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_diagnostics.py
new file mode 100755
index 000000000..ec1e2d13f
--- /dev/null
+++ b/www/caddy/src/opnsense/scripts/OPNsense/Caddy/caddy_diagnostics.py
@@ -0,0 +1,86 @@
+#!/usr/local/bin/python3
+
+#
+# Copyright (c) 2024 Cedrik Pischem
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+import sys
+import json
+
+# Function to show the Caddy configuration from a JSON file
+def show_caddy_config():
+ config_path = "/var/db/caddy/config/caddy/autosave.json"
+
+ try:
+ # Open and read the JSON configuration file directly into a Python dictionary
+ with open(config_path, "r") as file:
+ config_data = json.load(file) # Load the JSON directly
+
+ # Print the JSON to stdout using json.dumps to ensure it's a JSON string and nicely formatted
+ print(json.dumps(config_data)) # Output the JSON directly
+ # Output error details in JSON format so that the API can consume them
+ except FileNotFoundError:
+ print(json.dumps({"error": "File not found", "message": "Caddy autosave.json configuration file not found"}))
+ except json.JSONDecodeError:
+ print(json.dumps({"error": "Invalid JSON", "message": "Error decoding the Caddy autosave.json, the file is not valid JSON"}))
+ except Exception as e:
+ print(json.dumps({"error": "General Error", "message": str(e)}))
+
+def show_caddyfile():
+ caddyfile_path = "/usr/local/etc/caddy/Caddyfile"
+
+ try:
+ with open(caddyfile_path, "r") as file:
+ caddyfile_data = file.read()
+ # Output the Caddyfile data directly as a JSON object with a generic key like "content"
+ print(json.dumps({"content": caddyfile_data}))
+ except FileNotFoundError:
+ print(json.dumps({"error": "Caddyfile not found", "message": "Caddyfile not found"}))
+ except Exception as e:
+ print(json.dumps({"error": "General Error", "message": str(e)}))
+
+# Action handler
+def perform_action(action):
+ actions = {
+ "config": show_caddy_config,
+ "caddyfile": show_caddyfile
+ # Additional actions can be added here in the same format.
+ }
+
+ action_func = actions.get(action)
+ if action_func:
+ action_func()
+ else:
+ # Output error details in JSON format if action is unknown
+ print(json.dumps({"error": "Unknown Action", "message": f"Unknown action: {action}"}))
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ action = sys.argv[1]
+ perform_action(action)
+ else:
+ # Output error details in JSON format if no action is specified
+ print(json.dumps({"error": "No Action Specified", "message": "No action specified"}))
+
diff --git a/www/caddy/src/opnsense/service/conf/actions.d/actions_caddy.conf b/www/caddy/src/opnsense/service/conf/actions.d/actions_caddy.conf
index cd5f1b2f6..0d768ce42 100644
--- a/www/caddy/src/opnsense/service/conf/actions.d/actions_caddy.conf
+++ b/www/caddy/src/opnsense/service/conf/actions.d/actions_caddy.conf
@@ -35,3 +35,15 @@ command:/usr/local/sbin/pluginctl -s caddy status
parameters:
type:script_output
message:Request Caddy status
+
+[config]
+command:/usr/local/opnsense/scripts/OPNsense/Caddy/caddy_diagnostics.py config
+parameters:
+type:script_output
+message:Request Caddy JSON configuration
+
+[caddyfile]
+command:/usr/local/opnsense/scripts/OPNsense/Caddy/caddy_diagnostics.py caddyfile
+parameters:
+type:script_output
+message:Request Caddyfile