
#!/bin/bash
##!name: 腾讯云 EdgeOne & COS 证书自动同步 (子模块)
##!desc: 被主脚本调用，监控证书变化并下发至 EdgeOne 加速节点与 COS 存储桶

set -euo pipefail

# ================= 基础配置区域 =================
ACME_DIR="/ql/data/acme"
# 作为子模块，不再独立发送邮件，将报告写入临时文件供主脚本读取
REPORT_FILE="/tmp/eo_sync_report.txt"

EMAIL_REPORT="【边缘节点与云资源同步明细】\n\n"
HAS_UPDATE=0
HAS_ERROR=0

# ================= EO 站点配置矩阵 =================
# 格式：主域名 | 腾讯云SecretId | 腾讯云SecretKey | EO站点ID(ZoneId) | 需要绑定的EO域名(多个用逗号隔开)
EO_DOMAINS_CONFIG=(
    # 填入 sedst.org 的真实 ZoneID
    "sedst.org|${TC_ID_2:-}|${TC_KEY_2:-}|**********|sedst.org"
    
    # 👇 新增：填入 cyclostrat.org 的真实 ZoneID，并绑定根域名和 test 子域名
    "cyclostrat.org|${TC_ID_2:-}|${TC_KEY_2:-}|*************|cyclostrat.org,test.cyclostrat.org"
)
# ===================================================

# ================= COS 存储桶配置矩阵 =================
# 格式：主域名 | 腾讯云SecretId | 腾讯云SecretKey | 地域(如 ap-beijing) | 存储桶名称(需带appid) | 绑定的自定义域名
COS_DOMAINS_CONFIG=(
    "machao.group|${TC_ID_1:-}|${TC_KEY_1:-}|ap-chengdu|**************|source.machao.group"
)
# ===================================================

# 准备内部 Python 脚本：EdgeOne 同步
cat > /tmp/eo_ssl_update.py << 'EOF'
import os, sys, json, time, hashlib, hmac, urllib.request, urllib.error
from datetime import datetime

def sign(key, msg):
    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()

def tencent_api_call(secret_id, secret_key, service, version, action, payload_dict, region=""):
    endpoint = f"{service}.tencentcloudapi.com"
    payload = json.dumps(payload_dict)
    timestamp = int(time.time())
    date = datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d")
    
    canonical_headers = f"content-type:application/json\nhost:{endpoint}\n"
    signed_headers = "content-type;host"
    hashed_request_payload = hashlib.sha256(payload.encode("utf-8")).hexdigest()
    canonical_request = f"POST\n/\n\n{canonical_headers}\n{signed_headers}\n{hashed_request_payload}"
    
    credential_scope = f"{date}/{service}/tc3_request"
    hashed_canonical_request = hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()
    string_to_sign = f"TC3-HMAC-SHA256\n{timestamp}\n{credential_scope}\n{hashed_canonical_request}"
    
    secret_date = sign(("TC3" + secret_key).encode("utf-8"), date)
    secret_service = sign(secret_date, service)
    secret_signing = sign(secret_service, "tc3_request")
    signature = hmac.new(secret_signing, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
    
    authorization = f"TC3-HMAC-SHA256 Credential={secret_id}/{credential_scope}, SignedHeaders={signed_headers}, Signature={signature}"
    headers = {
        "Authorization": authorization, "Content-Type": "application/json", 
        "Host": endpoint, "X-TC-Action": action, 
        "X-TC-Version": version, "X-TC-Timestamp": str(timestamp)
    }
    if region:
        headers["X-TC-Region"] = region
    
    req = urllib.request.Request(f"https://{endpoint}", data=payload.encode("utf-8"), headers=headers)
    try:
        with urllib.request.urlopen(req) as response:
            res = json.loads(response.read().decode("utf-8"))
            if "Error" in res.get("Response", {}): raise Exception(res["Response"]["Error"]["Message"])
            return res["Response"]
    except urllib.error.HTTPError as e:
        raise Exception(e.read().decode("utf-8"))

def main():
    secret_id, secret_key = os.environ.get("Tencent_SecretId"), os.environ.get("Tencent_SecretKey")
    zone_id, hosts_str = os.environ.get("EO_ZONE_ID"), os.environ.get("EO_HOSTS")
    cert_path, key_path = os.environ.get("CERT_PATH"), os.environ.get("KEY_PATH")

    if not all([secret_id, secret_key, zone_id, hosts_str, cert_path, key_path]):
        sys.exit(1)

    hosts = [h.strip() for h in hosts_str.split(',') if h.strip()]
    with open(cert_path, 'r') as f: cert_content = f.read()
    with open(key_path, 'r') as f: key_content = f.read()

    print(f"   [API] 正在将新证书上传至腾讯云 SSL 托管中心...")
    ssl_payload = {
        "CertificatePublicKey": cert_content, "CertificatePrivateKey": key_content, 
        "CertificateType": "SVR", "Alias": f"AutoDeploy_EO_{hosts[0]}_{int(time.time())}"
    }
    try:
        cert_id = tencent_api_call(secret_id, secret_key, "ssl", "2019-12-05", "UploadCertificate", ssl_payload)["CertificateId"]
    except Exception as e:
        print(f"❌ SSL 上传失败: {e}"); sys.exit(1)

    print(f"   [API] 正在将证书下发至 EdgeOne 节点: {hosts} ...")
    teo_payload = {"ZoneId": zone_id, "Hosts": hosts, "Mode": "sslcert", "ServerCertInfo": [{"CertId": cert_id}]}
    try:
        tencent_api_call(secret_id, secret_key, "teo", "2022-09-01", "ModifyHostsCertificate", teo_payload)
        print("   [API] EdgeOne 边缘节点证书绑定成功！")
    except Exception as e:
        print(f"❌ EdgeOne 绑定失败: {e}"); sys.exit(1)

if __name__ == "__main__":
    main()
EOF

# 准备内部 Python 脚本：COS 存储桶同步
cat > /tmp/cos_ssl_update.py << 'EOF'
import os, sys, json, time, hashlib, hmac, urllib.request, urllib.error
from datetime import datetime

def sign(key, msg):
    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()

def tencent_api_call(secret_id, secret_key, service, version, action, payload_dict, region=""):
    endpoint = f"{service}.tencentcloudapi.com"
    payload = json.dumps(payload_dict)
    timestamp = int(time.time())
    date = datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d")
    canonical_headers = f"content-type:application/json\nhost:{endpoint}\n"
    signed_headers = "content-type;host"
    hashed_request_payload = hashlib.sha256(payload.encode("utf-8")).hexdigest()
    canonical_request = f"POST\n/\n\n{canonical_headers}\n{signed_headers}\n{hashed_request_payload}"
    credential_scope = f"{date}/{service}/tc3_request"
    hashed_canonical_request = hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()
    string_to_sign = f"TC3-HMAC-SHA256\n{timestamp}\n{credential_scope}\n{hashed_canonical_request}"
    secret_date = sign(("TC3" + secret_key).encode("utf-8"), date)
    secret_service = sign(secret_date, service)
    secret_signing = sign(secret_service, "tc3_request")
    signature = hmac.new(secret_signing, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
    
    headers = {
        "Authorization": f"TC3-HMAC-SHA256 Credential={secret_id}/{credential_scope}, SignedHeaders={signed_headers}, Signature={signature}", 
        "Content-Type": "application/json", 
        "Host": endpoint, 
        "X-TC-Action": action, 
        "X-TC-Version": version, 
        "X-TC-Timestamp": str(timestamp)
    }
    
    if region:
        headers["X-TC-Region"] = region
    
    req = urllib.request.Request(f"https://{endpoint}", data=payload.encode("utf-8"), headers=headers)
    try:
        with urllib.request.urlopen(req) as response:
            res = json.loads(response.read().decode("utf-8"))
            if "Error" in res.get("Response", {}): raise Exception(res["Response"]["Error"]["Message"])
            return res["Response"]
    except urllib.error.HTTPError as e:
        raise Exception(e.read().decode("utf-8"))

def main():
    secret_id, secret_key = os.environ.get("Tencent_SecretId"), os.environ.get("Tencent_SecretKey")
    region, bucket, domain = os.environ.get("COS_REGION"), os.environ.get("COS_BUCKET"), os.environ.get("COS_DOMAIN")
    cert_path, key_path = os.environ.get("CERT_PATH"), os.environ.get("KEY_PATH")

    if not all([secret_id, secret_key, region, bucket, domain, cert_path, key_path]):
        sys.exit(1)

    with open(cert_path, 'r') as f: cert_content = f.read()
    with open(key_path, 'r') as f: key_content = f.read()

    print(f"   [API] 正在将新证书上传至腾讯云 SSL 托管中心(为COS预备)...")
    ssl_payload = {
        "CertificatePublicKey": cert_content, "CertificatePrivateKey": key_content, 
        "CertificateType": "SVR", "Alias": f"AutoDeploy_COS_{domain}_{int(time.time())}"
    }
    try:
        cert_id = tencent_api_call(secret_id, secret_key, "ssl", "2019-12-05", "UploadCertificate", ssl_payload)["CertificateId"]
    except Exception as e:
        print(f"❌ SSL 上传失败: {e}"); sys.exit(1)

    print(f"   [API] 正在将证书下发至 COS 存储桶: {bucket} (绑定域名: {domain}) ...")
    deploy_payload = {
        "CertificateId": cert_id,
        "ResourceType": "cos",
        "InstanceIdList": [f"{region}|{bucket}|{domain}"]
    }
    try:
        tencent_api_call(secret_id, secret_key, "ssl", "2019-12-05", "DeployCertificateInstance", deploy_payload, region)
        print("   [API] COS 存储桶证书部署任务触发成功！(云端后台将异步绑定)")
    except Exception as e:
        print(f"❌ COS 部署失败: {e}"); sys.exit(1)

if __name__ == "__main__":
    main()
EOF

# ================= 核心业务逻辑：1. EdgeOne 处理 =================

for config in "${EO_DOMAINS_CONFIG[@]}"; do
    IFS='|' read -r DOMAIN TC_ID TC_KEY EO_ZONE_ID EO_HOSTS <<< "$config"
    
    if [ -z "$EO_ZONE_ID" ]; then continue; fi

    LOCAL_SSL_DIR="/tmp/eo_ssl_$DOMAIN"
    mkdir -p "$LOCAL_SSL_DIR"
    bash "$ACME_DIR/acme.sh" --install-cert --home "$ACME_DIR" -d "$DOMAIN" --key-file "$LOCAL_SSL_DIR/$DOMAIN.key.pem" --fullchain-file "$LOCAL_SSL_DIR/$DOMAIN.pem" >/dev/null 2>&1 || true
    if [ ! -f "$LOCAL_SSL_DIR/$DOMAIN.pem" ]; then continue; fi

    MD5_FILE="/ql/data/eo_cert_md5_$DOMAIN.txt"
    CURRENT_MD5=$(md5sum "$LOCAL_SSL_DIR/$DOMAIN.pem" | awk '{print $1}')
    OLD_MD5=""
    [ -f "$MD5_FILE" ] && OLD_MD5=$(cat "$MD5_FILE")

    if [ "$CURRENT_MD5" == "$OLD_MD5" ]; then
        echo "✅ [$DOMAIN] 证书 MD5 未变化，跳过 EdgeOne 同步。"
        EMAIL_REPORT="${EMAIL_REPORT}✅ [${DOMAIN}]: 证书指纹未变化，跳过 EdgeOne 同步。\n"
        continue
    fi

    echo "⚡ [$DOMAIN] 检测到新证书！开始执行 EdgeOne 节点同步..."
    HAS_UPDATE=1
    
    export Tencent_SecretId="$TC_ID"
    export Tencent_SecretKey="$TC_KEY"
    export EO_ZONE_ID="$EO_ZONE_ID"
    export EO_HOSTS="$EO_HOSTS"
    export CERT_PATH="$LOCAL_SSL_DIR/$DOMAIN.pem"
    export KEY_PATH="$LOCAL_SSL_DIR/$DOMAIN.key.pem"
    
    if python3 /tmp/eo_ssl_update.py; then
        echo "🎉 [$DOMAIN] EdgeOne 同步成功！"
        EMAIL_REPORT="${EMAIL_REPORT}🎉 [${DOMAIN}]: 新证书已成功上传并绑定至 EdgeOne 节点 (${EO_HOSTS})！\n"
        echo "$CURRENT_MD5" > "$MD5_FILE"
    else
        echo "❌ [$DOMAIN] EdgeOne 同步失败！"
        EMAIL_REPORT="${EMAIL_REPORT}❌ [${DOMAIN}]: EdgeOne 节点同步失败，请检查日志。\n"
        HAS_ERROR=1
    fi
done

# ================= 核心业务逻辑：2. COS 存储桶处理 =================

for config in "${COS_DOMAINS_CONFIG[@]}"; do
    IFS='|' read -r DOMAIN TC_ID TC_KEY COS_REGION COS_BUCKET COS_DOMAIN <<< "$config"
    
    if [ -z "$COS_BUCKET" ]; then continue; fi

    LOCAL_SSL_DIR="/tmp/cos_ssl_$DOMAIN"
    mkdir -p "$LOCAL_SSL_DIR"
    bash "$ACME_DIR/acme.sh" --install-cert --home "$ACME_DIR" -d "$DOMAIN" --key-file "$LOCAL_SSL_DIR/$DOMAIN.key.pem" --fullchain-file "$LOCAL_SSL_DIR/$DOMAIN.pem" >/dev/null 2>&1 || true
    if [ ! -f "$LOCAL_SSL_DIR/$DOMAIN.pem" ]; then continue; fi

    MD5_FILE="/ql/data/cos_cert_md5_$DOMAIN.txt"
    CURRENT_MD5=$(md5sum "$LOCAL_SSL_DIR/$DOMAIN.pem" | awk '{print $1}')
    OLD_MD5=""
    [ -f "$MD5_FILE" ] && OLD_MD5=$(cat "$MD5_FILE")

    if [ "$CURRENT_MD5" == "$OLD_MD5" ]; then
        echo "✅ [$DOMAIN] 证书 MD5 未变化，跳过 COS 同步。"
        EMAIL_REPORT="${EMAIL_REPORT}✅ [${DOMAIN}]: 证书指纹未变化，跳过 COS 存储桶同步。\n"
        continue
    fi

    echo "⚡ [$DOMAIN] 检测到新证书！开始执行 COS 存储桶同步..."
    HAS_UPDATE=1
    
    export Tencent_SecretId="$TC_ID"
    export Tencent_SecretKey="$TC_KEY"
    export COS_REGION="$COS_REGION"
    export COS_BUCKET="$COS_BUCKET"
    export COS_DOMAIN="$COS_DOMAIN"
    export CERT_PATH="$LOCAL_SSL_DIR/$DOMAIN.pem"
    export KEY_PATH="$LOCAL_SSL_DIR/$DOMAIN.key.pem"
    
    if python3 /tmp/cos_ssl_update.py; then
        echo "🎉 [$DOMAIN] COS 同步成功！"
        EMAIL_REPORT="${EMAIL_REPORT}🎉 [${DOMAIN}]: 新证书已成功部署至 COS 存储桶 (${COS_BUCKET}) 的自定义域名 (${COS_DOMAIN})！\n"
        echo "$CURRENT_MD5" > "$MD5_FILE"
    else
        echo "❌ [$DOMAIN] COS 同步失败！"
        EMAIL_REPORT="${EMAIL_REPORT}❌ [${DOMAIN}]: COS 存储桶同步失败，请检查日志。\n"
        HAS_ERROR=1
    fi
done

# ================= 清理与收尾 =================
rm -f /tmp/eo_ssl_update.py /tmp/cos_ssl_update.py

# 将合并报告输出到指定文件，供主脚本读取
echo -e "$EMAIL_REPORT" > "$REPORT_FILE"

# 返回状态码给主脚本
if [ $HAS_ERROR -eq 1 ]; then
    exit 1
else
    exit 0
fi