#!/usr/bin/env bash # Publish the admin build without restarting Go or changing BaoTa/Nginx routes. set -euo pipefail umask 0022 archive=$(realpath -- "${1:?Usage: baota-publish-admin.sh /tmp/build.zip SHA256}") expected=${2:?Missing archive SHA256} [[ $EUID -eq 0 && $archive == /tmp/*.zip && -f $archive ]] [[ $expected =~ ^[a-fA-F0-9]{64}$ ]] [[ $(sha256sum "$archive" | cut -d ' ' -f 1) == "${expected,,}" ]] root=/www/wwwroot/im.bchongw.com releases=/www/wwwroot/xingyu-admin/releases entry=$root/admin [[ -L $entry ]] previous=$(readlink -f -- "$entry") [[ $previous == "$releases/"* && -f $previous/index.html ]] [[ -f /www/server/panel/vhost/nginx/go_xingyu_im.conf ]] /www/server/nginx/sbin/nginx -t # Accept only relative archive paths. The ZIP is built locally and SHA-256 # checked above; reject path traversal before extracting into a new version. while IFS= read -r item; do case "$item" in /*|*\\*|../*|*/../*|*/..|..|*:*) exit 1 ;; esac done < <(unzip -Z1 "$archive") version=$(date +%Y%m%d-%H%M%S) release=$releases/$version next=$root/admin.next-$version [[ ! -e $next && ! -L $next ]] mkdir -m 0755 -- "$release" unzip -q "$archive" -d "$release" [[ -f $release/index.html && -f $release/_app.config.js ]] grep -q '/admin/jse/' "$release/index.html" grep -q '"/admin/v1"' "$release/_app.config.js" # Keep the previous hashed chunks reachable for tabs opened before deployment. # Never overwrite files in the new build or copy the old HTML/runtime config. for directory in js jse css; do [[ -d $release/$directory && -d $previous/$directory ]] cp -an -- "$previous/$directory/." "$release/$directory/" done chown -R www:www -- "$release" find "$release" -type d -exec chmod 0755 {} + find "$release" -type f -exec chmod 0644 {} + switched=false rollback() { if [[ $switched == true ]]; then ln -s -- "$previous" "$next" mv -Tf -- "$next" "$entry" printf 'ROLLED_BACK=%s\n' "$previous" >&2 fi } trap rollback ERR ln -s -- "$release" "$next" mv -Tf -- "$next" "$entry" switched=true cmp -s "$release/index.html" <(curl -fsS --max-time 20 "https://im.bchongw.com/admin/?release=$version") cmp -s "$release/_app.config.js" <(curl -fsS --max-time 20 "https://im.bchongw.com/admin/_app.config.js?release=$version") curl -fsS --max-time 20 https://im.bchongw.com/healthz curl -fsS --max-time 20 -o /dev/null https://im.bchongw.com/app/ trap - ERR printf '\nADMIN_PUBLISHED=%s\nPREVIOUS_RELEASE=%s\nARCHIVE_SHA256=%s\n' "$release" "$previous" "${expected,,}"