#!/usr/bin/env bash
# VWCG-1357: exact-hash, single-file patch. No SQL, frontend build or service restart.
set -euo pipefail
umask 077
root=/workspace/wwwroot/zhct_jsr/project
target="$root/application/p/logic/MealOrder.php"
old_hash=d4805ec00a7cea47131a06d6e2a1d3258bd6b28f15938e65ebb8952fd16899e1
new_hash=089df04e521f98aca802ade3bc51e9c9ab13857894a9c7558a9b73723a5a11fd
backup_dir=/workspace/wwwroot/zhct_jsr/hotfix-backups/VWCG-1357-receipt-date
backup="$backup_dir/MealOrder.php.before"
php=/usr/local/php7.3/bin/php
staged=''
changed=0
success=0
hash_file() { sha256sum "$1" | awk '{print $1}'; }
cleanup() {
    status=$?
    trap - EXIT
    if [[ "$changed" == 1 && "$success" == 0 ]]; then
        if [[ "$(hash_file "$target")" == "$new_hash" && "$(hash_file "$backup")" == "$old_hash" ]]; then
            rollback=$(mktemp "$root/application/p/logic/.MealOrder.php.VWCG-1357-rollback.XXXXXX")
            cp -p "$backup" "$rollback"
            "$php" -l "$rollback" >/dev/null
            mv -f "$rollback" "$target"
            [[ "$(hash_file "$target")" == "$old_hash" ]] || exit 92
            printf 'ROLLBACK_RESTORED %s\n' "$old_hash"
        else
            printf 'ROLLBACK_STOPPED unexpected live or backup hash\n' >&2
            exit 93
        fi
    fi
    [[ -z "$staged" || ! -e "$staged" ]] || rm -f "$staged"
    exit "$status"
}
trap cleanup EXIT
case "$(hostname)" in
    iZ2zeawufz0876gxr3k5fwZ|iZ2ze12juwl7c3y6h920e8Z) ;;
    *) printf 'Unexpected host\n' >&2; exit 10 ;;
esac
[[ "$(readlink -f "$root")" == "$root" && -f "$target" && ! -L "$target" ]]
[[ "$(readlink -f "$target")" == "$target" ]]
grep -Fq "define('BUSINESS', 'zhct_jsr');" "$root/env.php"
for command in patch flock base64 sha256sum curl; do command -v "$command" >/dev/null; done
[[ -x "$php" ]]
current=$(hash_file "$target")
[[ "$current" == "$old_hash" || "$current" == "$new_hash" ]] || { printf 'Baseline mismatch\n' >&2; exit 11; }
[[ ! -L "$backup_dir" ]]
mkdir -p "$backup_dir"
[[ "$(readlink -f "$backup_dir")" == "$backup_dir" ]]
exec 9>"$backup_dir/deploy.lock"
flock -n 9 || { printf 'Another hotfix is running\n' >&2; exit 12; }
current=$(hash_file "$target")
[[ "$current" == "$old_hash" || "$current" == "$new_hash" ]] || exit 13
if [[ "$current" == "$new_hash" ]]; then
    [[ -f "$backup" && ! -L "$backup" && "$(hash_file "$backup")" == "$old_hash" ]]
    "$php" -l "$target"
    printf 'ALREADY_APPLIED %s\n' "$new_hash"
    success=1
    exit 0
fi
if [[ -e "$backup" || -L "$backup" ]]; then
    [[ ! -L "$backup" && "$(hash_file "$backup")" == "$old_hash" ]] || exit 14
else
    cp -p "$target" "$backup"
fi
[[ "$(hash_file "$backup")" == "$old_hash" ]]
staged=$(mktemp "$root/application/p/logic/.MealOrder.php.VWCG-1357.XXXXXX")
cp -p "$target" "$staged"
base64 --decode <<'JSR_PATCH_BASE64' | patch --batch --forward --fuzz=0 --no-backup-if-mismatch --reject-file=- "$staged"
ZGlmZiAtLWdpdCBhL2FwcGxpY2F0aW9uL3AvbG9naWMvTWVhbE9yZGVyLnBocCBiL2FwcGxpY2F0aW9uL3AvbG9naWMvTWVhbE9yZGVyLnBocAppbmRleCA4MzZiZWI2ODQuLjdjMWQ5ZWU4OCAxMDA2NDQKLS0tIGEvYXBwbGljYXRpb24vcC9sb2dpYy9NZWFsT3JkZXIucGhwCisrKyBiL2FwcGxpY2F0aW9uL3AvbG9naWMvTWVhbE9yZGVyLnBocApAQCAtNDA5NSw3ICs0MDk1LDcgQEAgY2xhc3MgTWVhbE9yZGVyIGV4dGVuZHMgQmFzZVNlcnZpY2UKICAgICAgICAgICAgIHRocm93RXJyb3IoJ+ivt+S8oOWFpeiuouWNlUlEJyk7CiAgICAgICAgIH0KICAgICAgICAgJG9yZGVyID0gJHRoaXMtPm1vZGVsLT53aGVyZShbJ2lkJyA9PiAkb3JkZXJJZCwgJ2lzX2RlbGV0ZScgPT4gMF0pCi0gICAgICAgICAgICAtPmZpZWxkKFsnaWQnLCAnb3JkZXJfbm8nLCAncmVzdGF1cmFudF9pZCcsICdlcXVpcG1lbnRfY29kZScsICdwYXlfdGltZScsICdiaW5kX3RpbWUnLCAndXNlcl9tb2JpbGUnLCAndXNlcl9uYW1lJywgJ3RvdGFsX3ByaWNlJywgJ3BheV9wcmljZScsICdkaXNjb3VudF9hbW91bnQnLCAnbWFuYWdlbWVudF9mZWUnLCAncmVtYXJrJywgJ3BhY2thZ2VfZmVlJywgJ3Jlc3RhdXJhbnRfbWV0aG9kJywgJ2xvY2tlcl9jb2RlJywgJ2xvY2tlcl9hZGRyZXNzJywgJ2xvY2tlcl9zdGF0dXMnLCAncGF5X3R5cGUnLCAncHJpbnRfc3RhdHVzJ10pCisgICAgICAgICAgICAtPmZpZWxkKFsnaWQnLCAnb3JkZXJfbm8nLCAncmVzdGF1cmFudF9pZCcsICdlcXVpcG1lbnRfY29kZScsICdwYXlfdGltZScsICdiaW5kX3RpbWUnLCAnc291cmNlJywgJ21lYWxfZGF0ZScsICd0YWtlX3RpbWVfc3RhcnQnLCAndGFrZV90aW1lX2VuZCcsICd1c2VyX21vYmlsZScsICd1c2VyX25hbWUnLCAndG90YWxfcHJpY2UnLCAncGF5X3ByaWNlJywgJ2Rpc2NvdW50X2Ftb3VudCcsICdtYW5hZ2VtZW50X2ZlZScsICdyZW1hcmsnLCAncGFja2FnZV9mZWUnLCAncmVzdGF1cmFudF9tZXRob2QnLCAnbG9ja2VyX2NvZGUnLCAnbG9ja2VyX2FkZHJlc3MnLCAnbG9ja2VyX3N0YXR1cycsICdwYXlfdHlwZScsICdwcmludF9zdGF0dXMnXSkKICAgICAgICAgICAgIC0+ZmluZCgpOwogICAgICAgICBpZiAoZW1wdHkoJG9yZGVyKSkgewogICAgICAgICAgICAgdGhyb3dFcnJvcign6K6i5Y2V5LiN5a2Y5ZyoJyk7CkBAIC00MzExLDcgKzQzMTEsNyBAQCBjbGFzcyBNZWFsT3JkZXIgZXh0ZW5kcyBCYXNlU2VydmljZQogICAgICAgICAkb3JkZXJObyA9IChzdHJpbmcpKCRvcmRlclsnb3JkZXJfbm8nXSA/PyAnJyk7CiAgICAgICAgICR0YWtlTWV0aG9kID0gJHRoaXMtPmZvcm1hdFJlc3RhdXJhbnRNZXRob2QoKGludCkoJG9yZGVyWydyZXN0YXVyYW50X21ldGhvZCddID8/IDApKTsKICAgICAgICAgJHBheVR5cGUgPSAkdGhpcy0+Zm9ybWF0UGF5VHlwZSgoaW50KSgkb3JkZXJbJ3BheV90eXBlJ10gPz8gMCkpOwotICAgICAgICAkYmluZFRpbWUgPSAkdGhpcy0+Zm9ybWF0UmVjZWlwdFRpbWUoKHN0cmluZykoJG9yZGVyWydiaW5kX3RpbWUnXSA/PyAnJykpOworICAgICAgICAkcGlja3VwVGltZSA9ICR0aGlzLT5mb3JtYXRSZWNlaXB0UGlja3VwVGltZSgkb3JkZXIpOwogICAgICAgICAkcGF5VGltZSA9ICR0aGlzLT5mb3JtYXRSZWNlaXB0VGltZSgoc3RyaW5nKSgkb3JkZXJbJ3BheV90aW1lJ10gPz8gJycpKTsKICAgICAgICAgJHBhY2thZ2VGZWUgPSBoZWxwZXI6Om51bWJlcjIoKHN0cmluZykoJG9yZGVyWydwYWNrYWdlX2ZlZSddID8/ICcwJykpOwogICAgICAgICAkZGlzY291bnRBbW91bnQgPSBoZWxwZXI6Om51bWJlcjIoKHN0cmluZykoJG9yZGVyWydkaXNjb3VudF9hbW91bnQnXSA/PyAnMCcpKTsKQEAgLTQzNDYsNyArNDM0NiwxMyBAQCBjbGFzcyBNZWFsT3JkZXIgZXh0ZW5kcyBCYXNlU2VydmljZQogICAgICAgICAkbGluZXNbXSA9ICc8TD48QlI+PC9MPic7CiAgICAgICAgICRsaW5lc1tdID0gJzxMPicgLiAkdGhpcy0+Zm9ybWF0VHdvQ29sdW1ucygn5pSv5LuY5pa55byPOicsICRwYXlUeXBlKSAuICc8QlI+PC9MPic7CiAgICAgICAgIGlmICgkZXF1aXBtZW50VHlwZSAhPT0gc2VsZjo6T1JERVJfTUFDSElORV9FUVVJUE1FTlRfVFlQRSkgewotICAgICAgICAgICAgJGxpbmVzW10gPSAnPEw+JyAuICR0aGlzLT5mb3JtYXRUd29Db2x1bW5zKCflj5bppJDml7bpl7Q6JywgJGJpbmRUaW1lKSAuICc8QlI+PC9MPic7CisgICAgICAgICAgICBpZiAoJHRoaXMtPmdldFByaW50YWJsZVdpZHRoKCflj5bppJDml7bpl7Q6JyAuICRwaWNrdXBUaW1lKSA8IDMyKSB7CisgICAgICAgICAgICAgICAgJGxpbmVzW10gPSAnPEw+JyAuICR0aGlzLT5mb3JtYXRUd29Db2x1bW5zKCflj5bppJDml7bpl7Q6JywgJHBpY2t1cFRpbWUpIC4gJzxCUj48L0w+JzsKKyAgICAgICAgICAgIH0gZWxzZSB7CisgICAgICAgICAgICAgICAgZm9yZWFjaCAoJHRoaXMtPnNwbGl0QnlXaWR0aCgn5Y+W6aSQ5pe26Ze0OiAnIC4gJHBpY2t1cFRpbWUsIDMyKSBhcyAkZmllbGRMaW5lKSB7CisgICAgICAgICAgICAgICAgICAgICRsaW5lc1tdID0gJzxMPicgLiAkZmllbGRMaW5lIC4gJzxCUj48L0w+JzsKKyAgICAgICAgICAgICAgICB9CisgICAgICAgICAgICB9CiAgICAgICAgIH0KICAgICAgICAgJGxpbmVzW10gPSAnPEw+JyAuICR0aGlzLT5mb3JtYXRUd29Db2x1bW5zKCforqLppJDml7bpl7Q6JywgJHBheVRpbWUpIC4gJzxCUj48L0w+JzsKICAgICAgICAgJGxpbmVzW10gPSAnPEw+JyAuICR0aGlzLT5mb3JtYXRUd29Db2x1bW5zKCforqLljZXnvJblj7c6JywgJG9yZGVyTm8pIC4gJzxCUj48L0w+JzsKQEAgLTQ1OTYsNiArNDYwMiwyNCBAQCBjbGFzcyBNZWFsT3JkZXIgZXh0ZW5kcyBCYXNlU2VydmljZQogICAgICAgICByZXR1cm4gJy0nOwogICAgIH0KIAorICAgIHByaXZhdGUgZnVuY3Rpb24gZm9ybWF0UmVjZWlwdFBpY2t1cFRpbWUoYXJyYXkgJG9yZGVyKTogc3RyaW5nCisgICAgeworICAgICAgICAvLyDpnZ7nur/kuIrorqLppJDkv53nlZnljp/lj5bppJDml7bpl7Tlj6PlvoTvvJvpooTnuqbljZXkvb/nlKjorqLljZXkv53lrZjnmoTml6XmnJ/lkozml7bmrrXjgIIKKyAgICAgICAgaWYgKChpbnQpKCRvcmRlclsnc291cmNlJ10gPz8gMCkgIT09IE9yZGVyU291cmNlRW51bTo6T1JERVJGT09EKSB7CisgICAgICAgICAgICByZXR1cm4gJHRoaXMtPmZvcm1hdFJlY2VpcHRUaW1lKChzdHJpbmcpKCRvcmRlclsnYmluZF90aW1lJ10gPz8gJycpKTsKKyAgICAgICAgfQorICAgICAgICAkbWVhbERhdGUgPSB0cmltKChzdHJpbmcpKCRvcmRlclsnbWVhbF9kYXRlJ10gPz8gJycpKTsKKyAgICAgICAgaWYgKCFwcmVnX21hdGNoKCcvXihcZHs0fSktKFxkezJ9KS0oXGR7Mn0pJC8nLCAkbWVhbERhdGUsICRwYXJ0cykKKyAgICAgICAgICAgIHx8ICFjaGVja2RhdGUoKGludCkkcGFydHNbMl0sIChpbnQpJHBhcnRzWzNdLCAoaW50KSRwYXJ0c1sxXSkKKyAgICAgICAgICAgIHx8ICRtZWFsRGF0ZSA9PT0gJzIwMDAtMDEtMDEnKSB7CisgICAgICAgICAgICByZXR1cm4gJy0nOworICAgICAgICB9CisgICAgICAgICRzdGFydCA9IHRyaW0oKHN0cmluZykoJG9yZGVyWyd0YWtlX3RpbWVfc3RhcnQnXSA/PyAnJykpOworICAgICAgICAkZW5kID0gdHJpbSgoc3RyaW5nKSgkb3JkZXJbJ3Rha2VfdGltZV9lbmQnXSA/PyAnJykpOworICAgICAgICAvLyDkuI7lkI7lj7Dor6bmg4XkuIDoh7TvvJrml7bmrrXkuKTnq6/pvZDlpIfmiY3lsZXnpLrvvIznpoHmraLlm57pgIDmiJDkuIvljZXlvZPlpKnjgIIKKyAgICAgICAgcmV0dXJuICRzdGFydCAhPT0gJycgJiYgJGVuZCAhPT0gJycgPyAkbWVhbERhdGUgLiAnICcgLiAkc3RhcnQgLiAnIC0gJyAuICRlbmQgOiAkbWVhbERhdGU7CisgICAgfQorCiAgICAgcHJpdmF0ZSBmdW5jdGlvbiBmb3JtYXRSZWNlaXB0VGltZShzdHJpbmcgJHRpbWUpOiBzdHJpbmcKICAgICB7CiAgICAgICAgIGlmICgkdGltZSA9PT0gJycgfHwgJHRpbWUgPT09ICcyMDAwLTAxLTAxIDAwOjAwOjAwJykgewo=
JSR_PATCH_BASE64
[[ "$(hash_file "$staged")" == "$new_hash" ]] || exit 15
"$php" -l "$staged"
[[ "$(hash_file "$target")" == "$old_hash" ]] || exit 16
# Same-directory rename keeps readers from seeing a partially written PHP file.
mv -f "$staged" "$target"
changed=1
[[ "$(hash_file "$target")" == "$new_hash" ]] || exit 17
"$php" -l "$target"
http=$(curl --max-time 8 --silent --output /dev/null --write-out '%{http_code}' -H 'Host: jsr.yyangpt.cn' http://127.0.0.1/)
# Both nodes returned 301 during preflight; 200 also represents a reachable entry.
[[ "$http" == 301 || "$http" == 200 ]] || { printf 'HTTP_CHECK_FAILED %s\n' "$http" >&2; exit 18; }
for path in /dist/ /p/mealOrder/detail; do
    code=$(curl --max-time 8 --silent --output /dev/null --write-out '%{http_code}' -H 'Host: jsr.yyangpt.cn' "http://127.0.0.1$path")
    [[ "$code" == 200 ]] || { printf 'ENTRY_CHECK_FAILED %s %s\n' "$path" "$code" >&2; exit 19; }
    printf 'ENTRY_OK %s %s\n' "$path" "$code"
done
printf 'HOTFIX_APPLIED host=%s sha256=%s http=%s\n' "$(hostname)" "$new_hash" "$http"
printf 'BACKUP_READY %s %s\n' "$backup" "$(hash_file "$backup")"
success=1
