#!/usr/bin/env bash
# Codeberg Pages DNS troubleshooter
# v2

check_dns () {
	s i "Checking DNS records for $1..."
	s i "Looking for CNAME record..."
	CNAME_RESOLVE="$(dig +short "$1" cname)"
	case "$CNAME_RESOLVE" in
	"")
		s i "No CNAME record found!"
		check_dns_apex "$1"
		;;
	*".codeberg.page.")
		s k "$1 resolves to $CNAME_RESOLVE"
		DNS_TYPE="CNAME"
		check_repo "$CNAME_RESOLVE" "$1"
		;;
	*".codeberg.page"*)
		s e "$1 resolves to $CNAME_RESOLVE"
		s e "This configuration is invalid!"
		s a "The record may be malformed. On some DNS services you must add an extra dot after codeberg.page in the CNAME target"
		exit 31
		;;
	*)
		s e "$1 resolves to $CNAME_RESOLVE"
		s e "This configuration is invalid!"
		s a "Check Codeberg Pages documentation and reconfigure your domain accordingly."
		exit 3
		;;
	esac
}

check_dns_apex () {
	ERR_APEX=0
	NS_RESOLVE="$(dig +short $1 ns)"
	if [ "$NS_RESOLVE" = "" ];then
		s w "No NS records found! You should use a CNAME record if $1 is not the apex of the zone."
		NOT_APEX=1
		advice_collect "Check the Codeberg Pages documentation and reconfigure your domain using a CNAME record."
	fi
	s i "Looking for AAAA record..."
	CODEBERG_AAAA="$(dig +short codeberg.page aaaa)"
	AAAA_RESOLVE="$(dig +short "$1" aaaa)"
	case "$AAAA_RESOLVE" in
	"$CODEBERG_AAAA")
		s k "$1 resolves to $AAAA_RESOLVE"
		;;
	"")
		s w "No AAAA record found! Your site will not be reachable via IPv6!"
		advice_collect "Add AAAA record with target $CODEBERG_AAAA"
		ERR_APEX=$(($ERR_APEX+1))
		;;
	*)
		s e "$1 resolves to ${AAAA_RESOLVE//$'\n'/,}"
		s e "This configuration is invalid!"
		advice_collect "Change AAAA record target to $CODEBERG_AAAA"
		ERR_APEX=$(($ERR_APEX+3))
		;;
	esac
	s i "Looking for A record..."
	CODEBERG_A="$(dig +short codeberg.page a)"
	A_RESOLVE="$(dig +short $1 a)"
	case "$A_RESOLVE" in
	"$CODEBERG_A")
		s k "$1 resolves to $A_RESOLVE"
		;;
	"")
		s w "No A record found! Your site will not be reachable via IPv4!"
		advice_collect "Add A record with target $CODEBERG_A"
		ERR_APEX=$(($ERR_APEX+1))
		;;
	*)
		s e "$1 resolves to ${A_RESOLVE//$'\n'/,}"
		s e "This configuration is invalid!"
		advice_collect "Change A record target to $CODEBERG_A"
		ERR_APEX=$(($ERR_APEX+5))
		;;
	esac
	s i "Looking for TXT record..."
	TXT_RESOLVE="$(dig +short $1 txt | grep codeberg.page)"
	case "$TXT_RESOLVE" in
	"")
		s e "No valid Codeberg Pages TXT records found for $1!"
		s e "This configuration is invalid!"
		if [[ $NOT_APEX == 1 ]];then
			s a "Check Codeberg Pages documentation and reconfigure your domain using a CNAME record."
		else
			s a "Check Codeberg Pages documentation and reconfigure your domain accordingly."
		fi
		exit 40
		;;
	"\""*".codeberg.page\"")
		s k "$1 has TXT record $TXT_RESOLVE"
		;;
	*)
		s e "$1 has TXT record $TXT_RESOLVE"
		s e "This record appears to be malformed! Please seek help!"
		exit 49
		;;
	esac
	case $ERR_APEX in
	0)
		;;
	1)
		s w "Domain has warnings! Check previous warning messages."
		advice_dispense
		;;
	2)
		s e "This configuration is invalid! Check previous warning messages."
		advice_dispense
		exit 42
		;;
	*)
		s e "This configuration is invalid! Check previous error messages."
		if [ $NOT_APEX = 1 ];then
			s a "Check Codeberg Pages documentation and reconfigure your domain using a CNAME record."
		else
			advice_dispense
		fi
		exit $((40+$ERR_APEX))
		;;
	esac
	DNS_TYPE="TXT"
	check_repo "$TXT_RESOLVE" "$1"	
}

check_repo () {
	REPO_DOMAIN="${1//[\"]/}"
	REPO_DOMAIN="${REPO_DOMAIN%.}"
	s i "Resolving repository for $REPO_DOMAIN..."
	REPO_RESOLVE="${REPO_DOMAIN%.codeberg.page}"
	IFS="." read -a ARRAY_REPO_RESOLVE <<< $REPO_RESOLVE
	case ${#ARRAY_REPO_RESOLVE[@]} in
	0)
		s e "$REPO_DOMAIN is not a valid Codeberg Pages domain."
		s e "This configuration is invalid!"
		advice_dispense
		s a "Set $DNS_TYPE record using the format described in the Codeberg Pages documentation."
		exit 51
		;;
	1)
		REPO_OWNER="${ARRAY_REPO_RESOLVE[0]}"
		REPO_REPO="pages"
		;;
	2)
		REPO_OWNER="${ARRAY_REPO_RESOLVE[1]}"
		REPO_REPO="${ARRAY_REPO_RESOLVE[0]}"
		if [ "$REPO_REPO" != "pages" ];then
			REPO_BRANCH="pages"
		fi
		;;
	3)
		REPO_OWNER="${ARRAY_REPO_RESOLVE[2]}"
		REPO_REPO="${ARRAY_REPO_RESOLVE[1]}"
		REPO_BRANCH="${ARRAY_REPO_RESOLVE[0]}"
		;;
	*)
		s e "$REPO_DOMAIN contains too many tokens."
		s e "This configuration is invalid!"
		advice_dispense
		s a "Set $DNS_TYPE record using the format described in the Codeberg Pages documentation."
		exit 59
		;;
	esac
	s k "$DNS_TYPE record is OK!"
	s i "Repository: https://codeberg.org/$REPO_OWNER/$REPO_REPO"
	if [ "$REPO_BRANCH" != "" ];then
		s i "Branch: $REPO_BRANCH"
		s i "Source: https://codeberg.org/$REPO_OWNER/$REPO_REPO/src/branch/$REPO_BRANCH"
	else
		s i "Branch: \e[3mdefault\e[0m"
	fi
	s i "Checking repository..."
	REPO_DOMAINS="$(curl -s -w "\n%{http_code}" "https://codeberg.org/api/v1/repos/$REPO_OWNER/$REPO_REPO/raw/.domains?ref=$REPO_BRANCH")"
	REPO_DOMAINS_STATUS="$(tail -n1 <<< "$REPO_DOMAINS")"
	if [ "$REPO_DOMAINS_STATUS" != "200" ];then
		s e "Unable to get .domains file."
		advice_dispense
		s a "Verify the repository in the previous messages is correct and adjust $DNS_TYPE record if necessary."
		s a "Create a .domains file in the repository with a list of custom domains and subdomains you wish to use."
		exit 60
	fi
	REPO_DOMAINS_MAIN="$(head -n1 <<< "$REPO_DOMAINS")"
	if [ "$2" != "$REPO_DOMAINS_MAIN" ];then
		REPO_DOMAINS_GREP="$(echo "$REPO_DOMAINS" | grep "$2")"
		if [ "$REPO_DOMAINS_GREP" = "" ];then
			s e "$2 is not in .domains file!"
			s a "Add $2 to the .domains file in the repository."
			exit 61
		fi
		s w "$2 is not the main domain! It will be redirected to https://$REPO_DOMAINS_MAIN"
		if [ "$REPO_DOMAINS_MAIN" = "$REPO_OWNER.codeberg.page" ];then
			s w "$REPO_DOMAINS_MAIN is the default Codeberg Pages domain!"
			if [ "$REPO_REPO" != "pages" ];then
				s e "This configuration probably won't work because this is not the default pages repo!"
			fi
		else
			clean_vars_check_dns "$REPO_DOMAINS_MAIN"
		fi
	else
		s k "$2 seems to be configured correctly!"
		advice_dispense
		exit 0
	fi
}

s () {
	case $1 in
	i)
		echo -e "\e[37m[INFO]\e[0m   $2";;
	k)
		echo -e "\e[92m[OKAY]\e[0m   $2";;
	e)
		echo -e "\e[91m[ERROR]\e[0m  $2";;
	w)
		echo -e "\e[93m[WARN]\e[0m   $2";;
	a)
		echo -e "\e[95m[ADVICE]\e[0m $2";;
	ac)
		echo "\e[95m[ADVICE]\e[0m $2";;
	todo)
		echo -e "\e[90m[TODO]\e[0m   $2";;
	*)
		echo "[?$1] $2";;
	esac
}

advice_collect() {
	if [ "$ADVICE_COLLECT" != "" ];then
		ADVICE_COLLECT="$ADVICE_COLLECT\n$( s ac "$1" )"
	else
		ADVICE_COLLECT="$( s a "$1" )"
	fi
}

advice_dispense() {
	if [ "$ADVICE_COLLECT" != "" ];then
		echo -e "$ADVICE_COLLECT"
	fi
}

clean_vars() {
	CNAME_RESOLVE=
	AAAA_RESOLVE=
	A_RESOLVE=
	TXT_RESOLVE=
	NS_RESOLVE=
	DNS_TYPE=
	REPO_DOMAIN=
	REPO_OWNER=
	REPO_REPO=
	REPO_BRANCH=
	REPO_DOMAINS=
	REPO_DOMAINS_STATUS=
	REPO_DOMAINS_MAIN=
	REPO_DOMAINS_GREP=
}

clean_vars_check_dns () {
	clean_vars
	check_dns "$1"
}

if [ "$1" != "" ];then
	check_dns "$1"
else
	echo "SYNTAX: $0 <custom-domain>"
	exit 255
fi
