{#
# Copyright (c) 2023-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.
#}

# DO NOT EDIT THIS FILE -- OPNsense auto-generated file

{% set generalSettings = helpers.getNodeByTag('Pischem.caddy.general') %}

# Global Options
{
    {#
    #   Section: Global Log Settings
    #   Purpose: Sets up global log settings. The time format and unix socket make Caddy compatible
    #            with the syslog-ng instance running on the OPNsense.
    #}
    log {
        {% if generalSettings.LogAccessPlain|default("0") == "0" %}
        {% for reverse in helpers.toList('Pischem.caddy.reverseproxy.reverse') %}
        {% if reverse.enabled|default("0") == "1" and reverse.AccessLog|default("0") == "1" %}
        include http.log.access.{{ reverse['@uuid'] }}
        {% endif %}
        {% endfor %}
        {% endif %}
        output net unixgram//var/caddy/var/run/log {
        }
        format json {
            time_format rfc3339
        }
        {% if generalSettings.LogLevel %}
        level {{ generalSettings.LogLevel }}
        {% endif %}
    }

    {#
    #   Section: Global Trusted Proxy and Credential Logging
    #   Purpose: The trusted proxy section is important when using CDNs so that headers are trusted.
    #            Credential logging is useful for troubleshooting basic auth.
    #}
    {% set accessListUuid = generalSettings.accesslist %}
    {% set logCredentials = generalSettings.LogCredentials %}

    {% set hasAccessList = false %}
    {% set hasLogCredentials = false %}

    {% if accessListUuid %}
        {% set accessList = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', accessListUuid) | first %}
        {% if accessList %}
            {% set hasAccessList = true %}
        {% endif %}
    {% endif %}

    {% if logCredentials == '1' %}
        {% set hasLogCredentials = true %}
    {% endif %}

    {% if hasAccessList or hasLogCredentials %}
    servers {
        {% if hasAccessList %}
        trusted_proxies static {{ accessList.clientIps.split(',') | join(' ') }}
        {% endif %}
        {% if hasLogCredentials %}
        log_credentials
        {% endif %}
    }
    {% endif %}

    {#
    #   Section: Dynamic DNS Global Configuration
    #   Purpose: Sets up global configuration for Dynamic DNS. Caddy needs to be compiled with
    #            https://github.com/mholt/caddy-dynamicdns and https://github.com/caddy-dns. Otherwise the
    #            generated Caddyfile won't run. Each DNS Provider that is added below has to be compiled in.
    #            Some Providers don't support setting A and AAAA-Records, like acmedns.
    #            Most need specific configurations. Since only one provider can be used at the same time,
    #            they all share the same fields for configuration.
    #   Parameters:
    #   - @param dnsProvider (string): Specifies the DNS provider for DDNS updates.
    #   - @param dnsApiKey (string): The API key for authenticating with the DNS provider.
    #   - @param dnsSecretApiKey (string): A secret API key or token for additional authentication security.
    #   - @param dnsOptionalField1 to 4 (string): Optional configuration field for the DNS provider.
    #   - @param dynDnsSimpleHttp (string): URL for a simple HTTP-based service to discover the server's public IP.
    #   - @param dynDnsInterface (string): Network interface(s) to use for IP discovery.
    #   - @param dynDnsCheckInterval (integer): Interval in minutes to check for IP changes.
    #   - @param dynDnsIpVersions (string): The IP version(s) (IPv4, IPv6) for the DDNS update.
    #   - @param dynDnsTTL (integer): Time-To-Live for the DNS records, in hours.
    #   - @param dynDnsDomains (list): Domains and subdomains list for which DDNS updates are enabled.
    #}
    {% set dnsProvider = helpers.toList('Pischem.caddy.general.TlsDnsProvider') | first %}
    {% set dnsApiKey = generalSettings.TlsDnsApiKey %}
    {% set dnsSecretApiKey = generalSettings.TlsDnsSecretApiKey %}
    {% set dnsOptionalField1 = generalSettings.TlsDnsOptionalField1 %}
    {% set dnsOptionalField2 = generalSettings.TlsDnsOptionalField2 %}
    {% set dnsOptionalField3 = generalSettings.TlsDnsOptionalField3 %}
    {% set dnsOptionalField4 = generalSettings.TlsDnsOptionalField4 %}
    {% set dynDnsSimpleHttp = generalSettings.DynDnsSimpleHttp %}
    {% set dynDnsInterface = generalSettings.DynDnsInterface %}
    {% set dynDnsCheckInterval = generalSettings.DynDnsCheckInterval %}
    {% set dynDnsIpVersions = generalSettings.DynDnsIpVersions %}
    {% set dynDnsTTL = generalSettings.DynDnsTTL %}
    {% set dynDnsDomains = [] %}

    {% for reverse in helpers.toList('Pischem.caddy.reverseproxy.reverse') %}
        {% if reverse.enabled|default("0") == "1" and reverse.DynDns|default("0") == "1" %}
            {% set cleanedDomain = reverse.FromDomain | replace("*.","") %}
            {% if reverse.FromDomain.startswith("*.") %}
                {% do dynDnsDomains.append(cleanedDomain + " *") %}
            {% else %}
                {% do dynDnsDomains.append(cleanedDomain + " @") %}
            {% endif %}
        {% endif %}

        {% for subdomain in helpers.toList('Pischem.caddy.reverseproxy.subdomain') %}
            {% if subdomain.enabled|default("0") == "1" and subdomain.DynDns|default("0") == "1" and subdomain.reverse == reverse['@uuid'] %}
                {% set fullSubdomain = subdomain.FromDomain %}
                {% set baseDomain = fullSubdomain.split('.')[1:] | join('.') %}
                {% set subDomainPart = fullSubdomain.split('.')[0] %}
                {% set subdomainEntry = baseDomain + " " + subDomainPart %}
                {% do dynDnsDomains.append(subdomainEntry) %}
            {% endif %}
        {% endfor %}
    {% endfor %}

    {% if dnsProvider and dnsProvider != "acmedns" and dynDnsDomains|length > 0 %}
        dynamic_dns {
            {% if dnsProvider in ['porkbun', 'desec', 'route53', 'googleclouddns', 'azure', 'ovh', 'namecheap', 'powerdns', 'ddnss', 'linode', 'tencentcloud', 'dinahosting', 'hexonet', 'mailinabox', 'netcup', 'rfc2136'] %}
                provider {{ dnsProvider }} {
                    {% if dnsProvider == 'porkbun' %}
                        {% if dnsApiKey %}api_key {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}api_secret_key {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'desec' %}
                        {% if dnsApiKey %}token {{ dnsApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'route53' %}
                        {% if dnsApiKey %}access_key_id {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}secret_access_key {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}max_retries {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}aws_profile {{ dnsOptionalField2 }}
                        {% endif %}
                        {% if dnsOptionalField3 %}region {{ dnsOptionalField3 }}
                        {% endif %}
                        {% if dnsOptionalField4 %}token {{ dnsOptionalField4 }}
                        {% endif %}
                    {% elif dnsProvider == 'googleclouddns' %}
                        {% if dnsApiKey %}gcp_project {{ dnsApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'azure' %}
                        {% if dnsApiKey %}tenant_id {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}client_id {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}client_secret {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}subscription_id {{ dnsOptionalField2 }}
                        {% endif %}
                        {% if dnsOptionalField3 %}resource_group_name {{ dnsOptionalField3 }}
                        {% endif %}
                    {% elif dnsProvider == 'ovh' %}
                        {% if dnsApiKey %}endpoint {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}application_key {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}application_secret {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}consumer_key {{ dnsOptionalField2 }}
                        {% endif %}
                    {% elif dnsProvider == 'namecheap' %}
                        {% if dnsApiKey %}api_key {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}user {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}api_endpoint {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}client_ip {{ dnsOptionalField2 }}
                        {% endif %}
                    {% elif dnsProvider == 'powerdns' %}
                        {% if dnsApiKey %}server_url {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}api_token {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'ddnss' %}
                        {% if dnsApiKey %}api_token {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}username {{ dnsSecretApiKey }}
                        {% endif %}
                        password {{ dnsOptionalField1 }}
                    {% elif dnsProvider == 'linode' %}
                        {% if dnsApiKey %}api_token {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}api_url {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}api_version {{ dnsOptionalField1 }}
                        {% endif %}
                    {% elif dnsProvider == 'tencentcloud' %}
                        {% if dnsApiKey %}secret_id {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}secret_key {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'dinahosting' %}
                        {% if dnsApiKey %}username {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}password {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'hexonet' %}
                        {% if dnsApiKey %}username {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}password {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'mailinabox' %}
                        {% if dnsApiKey %}api_url {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}email_address {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}password {{ dnsOptionalField1 }}
                        {% endif %}
                    {% elif dnsProvider == 'netcup' %}
                        {% if dnsApiKey %}customer_number {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}api_key {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}api_password {{ dnsOptionalField1 }}
                        {% endif %}
                    {% elif dnsProvider == 'rfc2136' %}
                        {% if dnsApiKey %}key_name {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}key_alg {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}key {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}server {{ dnsOptionalField2 }}
                        {% endif %}
                    {% endif %}
                }
            {% else %}
                provider {{ dnsProvider }} {{ dnsApiKey }}
            {% endif %}
            domains {
                {% for domain in dynDnsDomains %}
                {{ domain }}
                {% endfor %}
            }
        {% if dynDnsSimpleHttp %}
        ip_source simple_http {{ dynDnsSimpleHttp }}
        {% endif %}
        {% if dynDnsInterface %}
            {% set physicalInterfaceNames = [] %}
            {% for intfName in dynDnsInterface.split(',') %}
                {% do physicalInterfaceNames.append(helpers.physical_interface(intfName)) %}
            {% endfor %}
            ip_source interface {{ physicalInterfaceNames | join(',') }}
        {% endif %}
        {% if dynDnsCheckInterval %}
        check_interval {{ dynDnsCheckInterval }}m
        {% endif %}
        {% if dynDnsIpVersions %}
        versions {{ dynDnsIpVersions }}
        {% endif %}
        {% if dynDnsTTL %}
        ttl {{ dynDnsTTL }}h
        {% endif %}
    }
    {% endif %}

    {#
    #   Section: ACME Email, Auto HTTPS selection and global import statement
    #   Purpose: The ACME email is optional for receiving certificate notices.
    #            Auto HTTPS is optional, the default is on (which means the section is empty).
    #            The import statement is for user specific configuration out of scope of this template.
    #}
    {% set emailValue = helpers.toList('Pischem.caddy.general.TlsEmail') | first %}
    {% if emailValue %}
    email {{ emailValue }}
    {% endif %}
    {% set autoHttpsValue = helpers.toList('Pischem.caddy.general.TlsAutoHttps') | first %}
    {% if autoHttpsValue %}
    auto_https {{ autoHttpsValue }}
    {% endif %}
    {#
    # Important: Grace Period influences how fast the server can finish reloads with open connections, by forcing termination.
    # Default of Caddy is to wait for all connections to close before allowing reload, meaning the higher the value, the longer applies take.
    #}
    grace_period {{ generalSettings.GracePeriod }}s
    import /usr/local/etc/caddy/caddy.d/*.global
}

# Reverse Proxy Configuration

{#
#   Section: HTTP-01 Challenge Redirection
#   Purpose: A small premade reverse_proxy section
#            that can redirect the HTTP-01 challenge to a different webserver.
#}
{% for reverse in helpers.toList('Pischem.caddy.reverseproxy.reverse') %}
    {% if reverse.enabled|default("0") == "1" and reverse.AcmePassthrough %}
        # HTTP-01 challenge redirection for domain: "{{ reverse['@uuid'] }}"
        http://{{ reverse.FromDomain|default("") }} {
            handle /.well-known/acme-challenge/* {
                reverse_proxy {{ reverse.AcmePassthrough }}
            }
            handle {
                redir https://{host}{uri} 308
            }
        }
    {% endif %}
{% endfor %}
{# Process redirection for subdomains afterwards, since a redirection of a wildcard domain has to match before them. #}
{% for subdomain in helpers.toList('Pischem.caddy.reverseproxy.subdomain') %}
    {% if subdomain.enabled|default("0") == "1" and subdomain.AcmePassthrough %}
        # HTTP-01 challenge redirection for subdomain: "{{ subdomain['@uuid'] }}"
        http://{{ subdomain.FromDomain|default("") }} {
            handle /.well-known/acme-challenge/* {
                reverse_proxy {{ subdomain.AcmePassthrough }}
            }
            handle {
                redir https://{host}{uri} 308
            }
        }
    {% endif %}
{% endfor %}

{#
#   Macro: tls_configuration
#   Purpose: Configures TLS settings based on the DNS provider, API keys, and optional fields.
#            Sets up the Caddyfile to update TXT Records with the chosen DNS Provider and receive
#            certificates with the DNS-01 challenge. Refer to Dynamic DNS section for more details.
#   Parameters:
#   - @param dnsProvider (string): The DNS provider used for the DNS challenge.
#   - @param dnsApiKey (string): API key for the DNS provider, essential for authentication.
#   - @param customCert (string, optional): The config extracted name of a certificate.
#   - @param dnsChallenge (boolean): Indicates if a DNS challenge is used for certificate authentication.
#   - @param dnsSecretApiKey (string, optional): A secret API key or token for additional security, depending on the provider.
#   - @param TlsDnsOptionalField1 to 4 (string, optional): Additional fields for specific DNS provider configurations.
#}
{% macro tls_configuration(dnsProvider, dnsApiKey, customCert, dnsChallenge, dnsSecretApiKey, TlsDnsOptionalField1, TlsDnsOptionalField2, TlsDnsOptionalField3, TlsDnsOptionalField4) %}
    {% if dnsChallenge == "1" and dnsProvider %}
        {% if dnsProvider in ['duckdns', 'porkbun', 'desec', 'route53', 'acmedns', 'googleclouddns', 'azure', 'ovh', 'namecheap', 'powerdns', 'ddnss', 'linode', 'tencentcloud', 'dinahosting', 'hexonet', 'mailinabox', 'netcup', 'rfc2136'] %}
            tls {
                dns {{ dnsProvider }} {
                    {% if dnsProvider == 'duckdns' %}
                        {% if dnsApiKey %}api_token {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}override_domain {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'porkbun' %}
                        {% if dnsApiKey %}api_key {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}api_secret_key {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'desec' %}
                        {% if dnsApiKey %}token {{ dnsApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'route53' %}
                        {% if dnsApiKey %}access_key_id {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}secret_access_key {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}max_retries {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}aws_profile {{ dnsOptionalField2 }}
                        {% endif %}
                        {% if dnsOptionalField3 %}region {{ dnsOptionalField3 }}
                        {% endif %}
                        {% if dnsOptionalField4 %}token {{ dnsOptionalField4 }}
                        {% endif %}
                    {% elif dnsProvider == 'acmedns' %}
                        {% if dnsApiKey %}username {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}password {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}subdomain {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}server_url {{ dnsOptionalField2 }}
                        {% endif %}
                    {% elif dnsProvider == 'googleclouddns' %}
                        {% if dnsApiKey %}gcp_project {{ dnsApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'azure' %}
                        {% if dnsApiKey %}tenant_id {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}client_id {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}client_secret {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}subscription_id {{ dnsOptionalField2 }}
                        {% endif %}
                        {% if dnsOptionalField3 %}resource_group_name {{ dnsOptionalField3 }}
                        {% endif %}
                    {% elif dnsProvider == 'ovh' %}
                        {% if dnsApiKey %}endpoint {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}application_key {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}application_secret {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}consumer_key {{ dnsOptionalField2 }}
                        {% endif %}
                    {% elif dnsProvider == 'namecheap' %}
                        {% if dnsApiKey %}api_key {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}user {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}api_endpoint {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}client_ip {{ dnsOptionalField2 }}
                        {% endif %}
                    {% elif dnsProvider == 'powerdns' %}
                        {% if dnsApiKey %}server_url {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}api_token {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'ddnss' %}
                        {% if dnsApiKey %}api_token {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}username {{ dnsSecretApiKey }}
                        {% endif %}
                        password {{ dnsOptionalField1 }}
                    {% elif dnsProvider == 'linode' %}
                        {% if dnsApiKey %}api_token {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}api_url {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}api_version {{ dnsOptionalField1 }}
                        {% endif %}
                    {% elif dnsProvider == 'tencentcloud' %}
                        {% if dnsApiKey %}secret_id {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}secret_key {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'dinahosting' %}
                        {% if dnsApiKey %}username {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}password {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'hexonet' %}
                        {% if dnsApiKey %}username {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}password {{ dnsSecretApiKey }}
                        {% endif %}
                    {% elif dnsProvider == 'mailinabox' %}
                        {% if dnsApiKey %}api_url {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}email_address {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}password {{ dnsOptionalField1 }}
                        {% endif %}
                    {% elif dnsProvider == 'netcup' %}
                        {% if dnsApiKey %}customer_number {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}api_key {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}api_password {{ dnsOptionalField1 }}
                        {% endif %}
                    {% elif dnsProvider == 'rfc2136' %}
                        {% if dnsApiKey %}key_name {{ dnsApiKey }}
                        {% endif %}
                        {% if dnsSecretApiKey %}key_alg {{ dnsSecretApiKey }}
                        {% endif %}
                        {% if dnsOptionalField1 %}key {{ dnsOptionalField1 }}
                        {% endif %}
                        {% if dnsOptionalField2 %}server {{ dnsOptionalField2 }}
                        {% endif %}
                    {% endif %}
                }
            }
        {% else %}
            tls {
                dns {{ dnsProvider }} {{ dnsApiKey }}
            }
        {% endif %}
    {% endif %}
    {% if customCert %}
        tls /var/db/caddy/data/caddy/certificates/temp/{{ customCert }}.pem /var/db/caddy/data/caddy/certificates/temp/{{ customCert }}.key
    {% endif %}
{% endmacro %}

{#
#   Macro: header_manipulation
#   Purpose: Customizes HTTP headers for requests or responses; to add, remove, or modify headers.
#            It uses a 'handle' object that specifies which headers to manipulate based on their @UUIDs.
#            Each handle can have multiple of these HTTP headers assigned.
#   Parameters:
#   @param handle (@object):
#       - @uuid (@string)
#       - HeaderUpDown (string): Determines the direction of the header.
#       - HeaderType (string): Specifies the name of the header.
#       - HeaderValue (string, optional): The new value to set for the header, if any.
#       - HeaderReplace (string, optional): Specifies a value to replace in the header.
#}
{% macro header_manipulation(handle) %}
    {% if handle.header %}
        {% for header_uuid in handle.header.split(',') %}
            {% set header = helpers.toList('Pischem.caddy.reverseproxy.header') | selectattr('@uuid', 'equalto', header_uuid) | first %}
            {# Generate directive only if HeaderUpDown and HeaderType are present #}
            {% if header.HeaderUpDown and header.HeaderType %}
                {# Prepare variables, making HeaderValue and HeaderReplace optional #}
                {% set header_value = header.HeaderValue | default('') %}
                {% set header_replace = header.HeaderReplace | default('') %}
                {# Adjust output formatting based on the presence and style of HeaderValue #}
                {% if header.HeaderReplace and header.HeaderValue %}
                    {% if header_value.startswith('{') %}
                        {{ header.HeaderUpDown }} {{ header.HeaderType }} {{ header_value }} "{{ header_replace }}"
                    {% else %}
                        {{ header.HeaderUpDown }} {{ header.HeaderType }} "{{ header_value }}" "{{ header_replace }}"
                    {% endif %}
                {% elif header.HeaderValue %}
                    {% if header_value.startswith('{') %}
                        {{ header.HeaderUpDown }} {{ header.HeaderType }} {{ header_value }}
                    {% else %}
                        {{ header.HeaderUpDown }} {{ header.HeaderType }} "{{ header_value }}"
                    {% endif %}
                {% else %}
                    {{ header.HeaderUpDown }} {{ header.HeaderType }}
                {% endif %}
            {% endif %}
        {% endfor %}
    {% endif %}
{% endmacro %}

{#
#   Macro: reverse_proxy_configuration
#   Purpose: Sets up the handle with the reverse proxy configurations. The TLS Settings are generated here for the Upstream.
#   Integrated Macros: header_manipulation
#   Parameters:
#   @param handle (@object):
#       - @uuid (@string)
#       - HandleType (string): Specifies the handling strategy.
#       - HandlePath (string, optional): The path the handle should match on.
#       - ToDomain (string): Target domain for the reverse proxy.
#       - ToPort (string, optional): Target port on the ToDomain.
#       - ToPath (string, optional): Destination path on the ToDomain.
#       - HttpTls (boolean, optional): Enable TLS for the connection.
#       - HttpNtlm (boolean, optional): Enable NTLM authentication for the connection.
#       - HttpTlsInsecureSkipVerify (boolean, optional): If true, the server's SSL certificate is not verified.
#       - HttpTlsTrustedCaCerts (string, optional): The config extracted name of a CA certificate.
#       - HttpTlsServerName (string, optional): Specifies the server name for the TLS handshake.
#       - PassiveHealthFailDuration (integer, optional): Enables passive health checks when set > 0.
#}
{% macro reverse_proxy_configuration(handle) %}
    {{ handle.HandleType }} {{ handle.HandlePath|default("") }} {
        {% if handle.ToPath|default("") != "" %}
        rewrite * {{ handle.ToPath }}{uri}
        {% endif %}
        reverse_proxy {% for domain in handle.ToDomain.split(',') %}
            {# For each domain/IP, append the port if it's specified, followed by a space #}
            {{- domain -}}{% if handle.ToPort %}:{{ handle.ToPort }}{% endif %}{% if not loop.last %} {% endif %}
        {% endfor %}{
            {{ header_manipulation(handle) }}
            {% if handle.PassiveHealthFailDuration|default("") %}
            fail_duration {{ handle.PassiveHealthFailDuration }}s
            {% endif %}
            {% if handle.HttpTls|default("0") == "1" or handle.HttpTlsInsecureSkipVerify|default("0") == "1" or handle.HttpTlsTrustedCaCerts|default("") != "" or handle.HttpTlsServerName|default("") != "" %}
                {% if handle.HttpNtlm|default("0") == "1" %}
                transport http_ntlm {
                    {% if handle.HttpTls|default("0") == "1" %}
                    tls
                    {% endif %}
                    {% if handle.HttpTlsInsecureSkipVerify|default("0") == "1" %}
                    tls_insecure_skip_verify
                    {% endif %}
                    {% if handle.HttpTlsTrustedCaCerts %}
                    tls_trusted_ca_certs /var/db/caddy/data/caddy/certificates/temp/{{ handle.HttpTlsTrustedCaCerts }}.pem
                    {% endif %}
                    {% if handle.HttpTlsServerName %}
                    tls_server_name {{ handle.HttpTlsServerName }}
                    {% endif %}
                }
                {% else %}
                transport http {
                    {% if handle.HttpTls|default("0") == "1" %}
                    tls
                    {% endif %}
                    {% if handle.HttpTlsInsecureSkipVerify|default("0") == "1" %}
                    tls_insecure_skip_verify
                    {% endif %}
                    {% if handle.HttpTlsTrustedCaCerts %}
                    tls_trusted_ca_certs /var/db/caddy/data/caddy/certificates/temp/{{ handle.HttpTlsTrustedCaCerts }}.pem
                    {% endif %}
                    {% if handle.HttpTlsServerName %}
                    tls_server_name {{ handle.HttpTlsServerName }}
                    {% endif %}
                }
                {% endif %}
            {% endif %}
        }
    }
{% endmacro %}

{#
#   Macro: access_list_configuration
#   Purpose: Defines access lists based on client IP addresses. The standard logic is "allow these IP addresses, deny all others."
#            A handle with an @ matcher is created that will put the reverse_proxy_configuration inside. That means, the traffic will
#            only get to the reverse proxy, when the access list matches. Invert is also possible, to explicitely deny IPs.
#            The assembly is handled by the "Section: Reverse Proxy Configurations".
#   Parameters:
#   @param accesslist (@object):
#       - @uuid (@string)
#       - clientIps (@string): A comma-separated list of client IP addresses
#       - invert (@boolean): A flag that inverts the logic of the access list
#}
{% macro access_list_configuration(accesslist, invert) %}
    {% set client_ips = accesslist.clientIps.split(',') %}
    {% set client_ips_space_separated = client_ips | join(' ') %}
    @{{ accesslist['@uuid'] }} {
        {{ 'not' if invert else '' }} client_ip {{ client_ips_space_separated }}
    }
{% endmacro %}

{#
#   Macro: basicauth_configuration
#   Purpose: Implements basic authentication with a username and password for access.
#   Parameters:
#   @param basicauth_uuids (@string): A comma-separated list of UUIDs, each UUID corresponding to
#                                     a specific user credentials (username and password).
#       - @uuid (@string)
#       - basicauthuser (@string): The username required for authentication.
#       - basicauthpass (@string): The password associated with the username.
#}
{% macro basicauth_configuration(basicauth_uuids) %}
    {% if basicauth_uuids %}
    basicauth {
        {% for uuid in basicauth_uuids.split(',') %}
            {% set basicauth = helpers.toList('Pischem.caddy.reverseproxy.basicauth') | selectattr('@uuid', 'equalto', uuid) | first %}
            {% if basicauth %}
                {{ basicauth.basicauthuser }} {{ basicauth.basicauthpass }}
            {% endif %}
        {% endfor %}
    }
    {% endif %}
{% endmacro %}

{#
#   Section: Reverse Proxy Configurations
#   Purpose: Assembles reverse proxy configurations using predefined macros.
#            This is the main logic of the whole template, handle with care.
#   Macros Used:
#   - tls_configuration
#   - basicauth_configuration
#   - access_list_configuration
#   - reverse_proxy_configuration
#   - indirect: header_manipulation
#   Important Details:
#   - Order of Path specific Handles - Prioritizes order of specific path handles over catch-all handles.
#   - Order of Wildcard Domains and Subdomains: Handles for wildcard domains come after all subdomains.
#}
{% for reverse in helpers.toList('Pischem.caddy.reverseproxy.reverse') %}
{% if reverse.enabled|default("0") == "1" %}
# Reverse Proxy Domain: "{{ reverse['@uuid'] }}"
{{ reverse.FromDomain|default("") }}{% if reverse.FromPort %}:{{ reverse.FromPort }}{% endif %} {
    {% if reverse.AccessLog|default("0") == "1" %}
    {% if generalSettings.LogAccessPlain|default("0") == "0" %}
    log {{ reverse['@uuid'] }}
    {% else %}
    log {
        output file /var/log/caddy/access/{{ reverse['@uuid'] }}.log {
            roll_keep_for {{ generalSettings.LogAccessPlainKeep|default("10") }}d
        }
    }
    {% endif %}
    {% endif %}
    {% set customCert = reverse.CustomCertificate|default("") %}
    {% set dnsChallenge = reverse.DnsChallenge|default("0") %}
    {{ tls_configuration(dnsProvider, dnsApiKey, customCert, dnsChallenge, dnsSecretApiKey, TlsDnsOptionalField1, TlsDnsOptionalField2, TlsDnsOptionalField3, TlsDnsOptionalField4) }}

    {% if not reverse.accesslist %}
        {% set basicauth_uuids = reverse.basicauth %}
        {{ basicauth_configuration(basicauth_uuids) }}
    {% endif %}

    {% for subdomain in helpers.toList('Pischem.caddy.reverseproxy.subdomain') %}
    {% if subdomain.enabled|default("0") == "1" and subdomain.reverse == reverse['@uuid'] %}
    @{{ subdomain['@uuid'] }} {
        host {{ subdomain.FromDomain }}{% if subdomain.FromPort %}:{{ subdomain.FromPort }}{% endif %}
    }
    handle @{{ subdomain['@uuid'] }} {

        {% if not subdomain.accesslist %}
            {% set subdomain_basicauth_uuids = subdomain.basicauth %}
            {{ basicauth_configuration(subdomain_basicauth_uuids) }}
        {% endif %}

        {% if subdomain.accesslist %}
        {% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', subdomain.accesslist) | first %}
        {{ access_list_configuration(accesslist, accesslist.accesslistInvert|default("0") == "1") }}
        handle @{{ accesslist['@uuid'] }} {

            {% set subdomain_basicauth_uuids = subdomain.basicauth %}
            {{ basicauth_configuration(subdomain_basicauth_uuids) }}

            {% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %}
            {% for handle in subdomain_handles %}
            {% if handle.enabled|default("0") == "1" and handle.HandlePath %}
                {{ reverse_proxy_configuration(handle) }}
            {% endif %}
            {% endfor %}
            {% for handle in subdomain_handles %}
            {% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
                {{ reverse_proxy_configuration(handle) }}
            {% endif %}
            {% endfor %}
        }
        {% else %}
        {% set subdomain_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('subdomain', 'equalto', subdomain['@uuid']) | list %}
        {% for handle in subdomain_handles %}
        {% if handle.enabled|default("0") == "1" and handle.HandlePath %}
            {{ reverse_proxy_configuration(handle) }}
        {% endif %}
        {% endfor %}
        {% for handle in subdomain_handles %}
        {% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
            {{ reverse_proxy_configuration(handle) }}
        {% endif %}
        {% endfor %}
        {% endif %}

        {% if subdomain.accesslist %}
            {% if accesslist.HttpResponseCode or accesslist.HttpResponseMessage %}
            respond {{ '"' + accesslist.HttpResponseMessage|default('') + '"' if accesslist.HttpResponseMessage else '' }} {{ accesslist.HttpResponseCode|default(403) }}
            {% elif Pischem.caddy.general.abort|default("0") == "1" %}
            abort
            {% endif %}
        {% else %}
            {% if Pischem.caddy.general.abort|default("0") == "1" %}
            abort
            {% endif %}
        {% endif %}
    }
    {% endif %}
    {% endfor %}

    {% if reverse.accesslist %}
    {% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', reverse.accesslist) | first %}
    {{ access_list_configuration(accesslist, accesslist.accesslistInvert|default("0") == "1") }}
    handle @{{ accesslist['@uuid'] }} {

        {% set basicauth_uuids = reverse.basicauth %}
        {{ basicauth_configuration(basicauth_uuids) }}

        {% set wildcard_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %}
        {% for handle in wildcard_handles %}
        {% if handle.enabled|default("0") == "1" and handle.HandlePath %}
            {{ reverse_proxy_configuration(handle) }}
        {% endif %}
        {% endfor %}
        {% for handle in wildcard_handles %}
        {% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
            {{ reverse_proxy_configuration(handle) }}
        {% endif %}
        {% endfor %}
    }
    {% else %}
    {% set wildcard_handles = helpers.toList('Pischem.caddy.reverseproxy.handle') | selectattr('reverse', 'equalto', reverse['@uuid']) | selectattr('subdomain', 'undefined') | list %}
    {% for handle in wildcard_handles %}
    {% if handle.enabled|default("0") == "1" and handle.HandlePath %}
        {{ reverse_proxy_configuration(handle) }}
    {% endif %}
    {% endfor %}
    {% for handle in wildcard_handles %}
    {% if handle.enabled|default("0") == "1" and not handle.HandlePath %}
        {{ reverse_proxy_configuration(handle) }}
    {% endif %}
    {% endfor %}
    {% endif %}

    {% set accesslist = helpers.toList('Pischem.caddy.reverseproxy.accesslist') | selectattr('@uuid', 'equalto', reverse.accesslist) | first %}
    {% if accesslist %}
        {% if accesslist.HttpResponseCode or accesslist.HttpResponseMessage %}
        respond {{ '"' + accesslist.HttpResponseMessage|default('') + '"' if accesslist.HttpResponseMessage else '' }} {{ accesslist.HttpResponseCode|default(403) }}
        {% elif Pischem.caddy.general.abort|default("0") == "1" %}
        abort
        {% endif %}
    {% else %}
        {% if Pischem.caddy.general.abort|default("0") == "1" %}
        abort
        {% endif %}
    {% endif %}
}
{% endif %}
{% endfor %}

import /usr/local/etc/caddy/caddy.d/*.conf
