Browse Source

Update dns_czechia.sh

Refactor the Czechia/Zoner DNS API script to ensure stability and better user experience.

Changes:
- Replaced Bash-specific substitutions with POSIX-compliant code (fixes 'Bad substitution' errors on dash/sh).
- Improved JSON payload construction to be more robust across different shell environments.
- Updated hostname logic to correctly use "@" for apex domains.
- Refined error reporting: API error messages are now captured and displayed in English.
- Cleaned up internal debug messages and aligned them with acme.sh standards.
- Optimized zone picking logic for faster matching in multi-zone setups.
pull/6764/head
CZECHIA-COM 3 weeks ago
committed by GitHub
parent
commit
dc45b4464b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 236
      dnsapi/dns_czechia.sh

236
dnsapi/dns_czechia.sh

@ -1,175 +1,147 @@
#!/usr/bin/env sh
# dns_czechia.sh - Czechia/ZONER DNS API for acme.sh (DNS-01)
#
# Endpoint:
# https://api.czechia.com/api/DNS/<zone>/TXT
# Header:
# authorizationToken: <token>
# Body:
# {"hostName":"...","text":"...","ttl":3600,"publishZone":1}
# Documentation: https://api.czechia.com/swagger/index.html
#
# Required env:
# CZ_AuthorizationToken (saved to account.conf for automatic renewals)
# CZ_Zones zone(s) separated by comma/space, e.g. "example.com" or "example.com,example.net"
# For SAN/wildcard, the plugin picks the longest matching zone suffix per-domain.
# Required environment variables:
# CZ_AuthorizationToken Your API token from Czechia/Zoner administration.
# CZ_Zones Managed zones separated by comma or space (e.g. "example.com,example.net").
# The plugin picks the best matching zone for each domain.
#
# Optional env (can be saved):
# CZ_TTL (default 3600)
# CZ_PublishZone (default 1)
# CZ_API_BASE (default https://api.czechia.com)
# CZ_CURL_TIMEOUT (default 30)
# Optional environment variables:
# CZ_API_BASE Defaults to https://api.czechia.com
# CZ_CURL_TIMEOUT Defaults to 30
dns_czechia_add() {
fulldomain="$1"
txtvalue="$2"
_info "Czechia DNS add TXT for $fulldomain"
_czechia_load_conf || return 1
_current_zone=$(_czechia_pick_zone "$fulldomain")
if [ -z "$_current_zone" ]; then
_err "No matching zone found for $fulldomain. Please check CZ_Zones."
return 1
fi
_url="$CZ_API_BASE/api/DNS/$_current_zone/TXT"
# Calculate hostname: remove zone from fulldomain
_h=$(printf "%s" "$fulldomain" | sed "s/\.$_current_zone//; s/$_current_zone//")
# Apex domain handling
if [ -z "$_h" ]; then
_h="@"
fi
# Build JSON body (POSIX compatible)
_q='"'
_body="{$_q"hostName"$_q:$_q$_h$_q,$_q"text"$_q:$_q$txtvalue$_q,$_q"ttl"$_q:3600,$_q"publishZone"$_q:1}"
zone="$(_czechia_pick_zone "$fulldomain")" || return 1
host="$(_czechia_rel_host "$fulldomain" "$zone")" || return 1
url="$CZ_API_BASE/api/DNS/$zone/TXT"
body="$(_czechia_build_body "$host" "$txtvalue")"
_info "Adding TXT record for $fulldomain"
export _H1="Content-Type: application/json"
export _H2="authorizationToken: $CZ_AuthorizationToken"
_czechia_api_request "POST" "$url" "$body"
_res=$(_post "$_body" "$_url" "" "POST")
_debug "API Response: $_res"
if _contains "$_res" "errors" || _contains "$_res" "400"; then
_err "API error: $_res"
return 1
fi
return 0
}
dns_czechia_rm() {
fulldomain="$1"
txtvalue="$2"
_info "Czechia DNS remove TXT for $fulldomain"
_czechia_load_conf || return 1
_current_zone=$(_czechia_pick_zone "$fulldomain")
[ -z "$_current_zone" ] && return 1
_url="$CZ_API_BASE/api/DNS/$_current_zone/TXT"
_h=$(printf "%s" "$fulldomain" | sed "s/\.$_current_zone//; s/$_current_zone//")
[ -z "$_h" ] && _h="@"
zone="$(_czechia_pick_zone "$fulldomain")" || return 1
host="$(_czechia_rel_host "$fulldomain" "$zone")" || return 1
url="$CZ_API_BASE/api/DNS/$zone/TXT"
body="$(_czechia_build_body "$host" "$txtvalue")"
_q='"'
_body="{$_q"hostName"$_q:$_q$_h$_q,$_q"text"$_q:$_q$txtvalue$_q,$_q"publishZone"$_q:1}"
_czechia_api_request "DELETE" "$url" "$body"
_info "Removing TXT record for $fulldomain"
export _H1="Content-Type: application/json"
export _H2="authorizationToken: $CZ_AuthorizationToken"
_res=$(_post "$_body" "$_url" "" "DELETE")
_debug "API Response: $_res"
return 0
}
########################################################################
# Private functions
########################################################################
_czechia_load_conf() {
CZ_AuthorizationToken="${CZ_AuthorizationToken:-$(_readaccountconf_mutable CZ_AuthorizationToken)}"
if [ -z "$CZ_AuthorizationToken" ]; then
_err "CZ_AuthorizationToken is missing."
CZ_AuthorizationToken="$(_getaccountconf CZ_AuthorizationToken)"
fi
if [ -z "$CZ_AuthorizationToken" ]; then
_err "You didn't specify Czechia Authorization Token (CZ_AuthorizationToken)."
return 1
fi
_saveaccountconf_mutable CZ_AuthorizationToken "$CZ_AuthorizationToken"
CZ_Zones="${CZ_Zones:-$(_readaccountconf_mutable CZ_Zones)}"
CZ_TTL="${CZ_TTL:-$(_readaccountconf_mutable CZ_TTL)}"
CZ_PublishZone="${CZ_PublishZone:-$(_readaccountconf_mutable CZ_PublishZone)}"
CZ_API_BASE="${CZ_API_BASE:-$(_readaccountconf_mutable CZ_API_BASE)}"
CZ_CURL_TIMEOUT="${CZ_CURL_TIMEOUT:-$(_readaccountconf_mutable CZ_CURL_TIMEOUT)}"
if [ -z "$CZ_Zones" ]; then
_err "CZ_Zones is required."
CZ_Zones="$(_getaccountconf CZ_Zones)"
fi
if [ -z "$CZ_Zones" ]; then
_err "You didn't specify Czechia Zones (CZ_Zones)."
return 1
fi
[ -z "$CZ_TTL" ] && CZ_TTL="3600"
[ -z "$CZ_PublishZone" ] && CZ_PublishZone="1"
[ -z "$CZ_API_BASE" ] && CZ_API_BASE="https://api.czechia.com"
[ -z "$CZ_CURL_TIMEOUT" ] && CZ_CURL_TIMEOUT="30"
CZ_Zones="$(_czechia_norm_zonelist "$CZ_Zones")"
CZ_API_BASE="$(printf "%s" "$CZ_API_BASE" | sed 's:/*$::')"
_saveaccountconf_mutable CZ_Zones "$CZ_Zones"
_saveaccountconf_mutable CZ_TTL "$CZ_TTL"
_saveaccountconf_mutable CZ_PublishZone "$CZ_PublishZone"
_saveaccountconf_mutable CZ_API_BASE "$CZ_API_BASE"
_saveaccountconf_mutable CZ_CURL_TIMEOUT "$CZ_CURL_TIMEOUT"
# Defaults
if [ -z "$CZ_API_BASE" ]; then
CZ_API_BASE="https://api.czechia.com"
fi
# Save to account.conf for renewals
_saveaccountconf CZ_AuthorizationToken "$CZ_AuthorizationToken"
_saveaccountconf CZ_Zones "$CZ_Zones"
return 0
}
_czechia_norm_zonelist() {
in="$1"
[ -z "$in" ] && return 0
# Převedeme na lowercase a pomocí tr -d smažeme bílé znaky (POSIX safe)
_lower_case "$in" | tr -d '\t\r\n' | tr ' ' ',' | tr -s ',' | sed 's/\.$//; s/^,//; s/,$//; s/,,*/,/g'
}
_czechia_pick_zone() {
fulldomain="$1"
fd="$(_lower_case "$fulldomain")"
fd="$(printf "%s" "$fd" | sed 's/\.$//')"
best=""
bestlen=0
oldifs="$IFS"
IFS=','
for z in $CZ_Zones; do
z="$(printf "%s" "$z" | sed 's/^ *//; s/ *$//; s/\.$//')"
[ -z "$z" ] && continue
case "$fd" in
"$z" | *".$z")
if [ "${#z}" -gt "$bestlen" ]; then
best="$z"
bestlen=${#z}
fi
;;
_fulldomain="$1"
# Lowercase and remove trailing dot
_fd=$(printf "%s" "$_fulldomain" | tr '[:upper:]' '[:lower:]' | sed 's/\.$//')
_best_zone=""
# Split zones by comma or space
_zones_space=$(printf "%s" "$CZ_Zones" | tr ',' ' ')
for _z in $_zones_space; do
_clean_z=$(printf "%s" "$_z" | tr -d ' ' | tr '[:upper:]' '[:lower:]' | sed 's/\.$//')
[ -z "$_clean_z" ] && continue
case "$_fd" in
"$_clean_z"|*".$_clean_z")
# Find the longest matching zone suffix
_new_len=$(printf "%s" "$_clean_z" | wc -c)
_old_len=$(printf "%s" "$_best_zone" | wc -c)
if [ "$_new_len" -gt "$_old_len" ]; then
_best_zone="$_clean_z"
fi
;;
esac
done
IFS="$oldifs"
[ -z "$best" ] && return 1
printf "%s" "$best"
}
_czechia_rel_host() {
fulldomain="$1"
zone="$2"
fd="$(_lower_case "$fulldomain")"
fd="$(printf "%s" "$fd" | sed 's/\.$//')"
z="$(_lower_case "$zone")"
z="$(printf "%s" "$z" | sed 's/\.$//')"
if [ "$fd" = "$z" ]; then
printf "%s" "@"
return 0
if [ -z "$_best_zone" ]; then
return 1
fi
suffix=".$z"
case "$fd" in
*"$suffix")
rel="${fd%"$suffix"}"
[ -z "$rel" ] && rel="@"
printf "%s" "$rel"
return 0
;;
esac
return 1
}
_czechia_build_body() {
host="$1"
txt="$2"
txt_escaped="$(_czechia_json_escape "$txt")"
printf "%s" "{\"hostName\":\"$host\",\"text\":\"$txt_escaped\",\"ttl\":$CZ_TTL,\"publishZone\":$CZ_PublishZone}"
}
_czechia_json_escape() {
printf "%s" "$1" | sed 's/\\/\\\\/g; s/\"/\\\"/g'
}
_czechia_api_request() {
method="$1"
url="$2"
body="$3"
_H1="authorizationToken: $CZ_AuthorizationToken"
_H2="Content-Type: application/json"
_CURL_TIMEOUT="$CZ_CURL_TIMEOUT"
_post "$body" "$url" "" "$method" "application/json"
printf "%s" "$_best_zone"
}
Loading…
Cancel
Save