Files
badminton-scoreboard/docker/nginx/entrypoint.sh
T
JianMiauandClaude Fable 5 1bc42f5bf6 功能:SSL 憑證過期偵測與提醒、歷史戰績改為分頁載入
根本原因:
1. 憑證過期時瀏覽器只回 Failed to fetch,前端無法辨識原因,使用者誤以為是 DB 連不上(本次 7/1 憑證過期即是如此)。
2. 歷史戰績一次撈出全部資料(400+ 筆、逾 100KB),筆數持續成長會越來越慢。

影響:所有 API 的連線錯誤訊息、歷史戰績頁載入速度、Docker 憑證掛載路徑與檔名。

修法:
- 後端讀取 cert.pem 到期日(60 秒快取),由 /api/version 與 /api/health 回傳 certNotAfter
- 前端把到期日存進 localStorage;fetch 失敗時依離線、憑證已過期、其他連線問題分別顯示明確訊息
- 憑證到期前 7 天於頁面跳出「SSL 憑證提醒」通知,可手動關閉
- /api/history 支援 page/pageSize(預設 20 筆、上限 100),回傳 total 與 totalPages
- 歷史戰績頁加入分頁控制列(上一頁/頁碼含省略號/下一頁/總筆數),刪除後自動重新載入並修正超出範圍的頁碼
- 憑證掛載目錄改為 /volume1/docker/certs,檔名改為 cert.pem / chain.pem / privkey.pem,app 容器同步掛載以讀取到期日

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:44:44 +08:00

115 lines
2.6 KiB
Bash

#!/bin/sh
set -eu
NGINX_PORT="${NGINX_PORT:-3501}"
NGINX_SERVER_NAME="${NGINX_SERVER_NAME:-_}"
SSL_CERT_DIR="${SSL_CERT_DIR:-/etc/nginx/certs}"
SSL_CERT_FILE_NAME="${SSL_CERT_FILE_NAME:-cert.pem}"
SSL_CHAIN_FILE_NAME="${SSL_CHAIN_FILE_NAME:-chain.pem}"
SSL_KEY_FILE_NAME="${SSL_KEY_FILE_NAME:-privkey.pem}"
UPSTREAM_HOST="${UPSTREAM_HOST:-badminton-scoreboard}"
UPSTREAM_PORT="${UPSTREAM_PORT:-8788}"
GENERATED_DIR="/etc/nginx/generated"
GENERATED_CERT_PATH="${GENERATED_DIR}/fullchain.pem"
GENERATED_KEY_PATH="${GENERATED_DIR}/privkey.pem"
mkdir -p "${GENERATED_DIR}"
build_cert_bundle() {
cert_path="${SSL_CERT_DIR}/${SSL_CERT_FILE_NAME}"
chain_path="${SSL_CERT_DIR}/${SSL_CHAIN_FILE_NAME}"
key_path="${SSL_CERT_DIR}/${SSL_KEY_FILE_NAME}"
if [ ! -f "${cert_path}" ]; then
echo "Missing certificate file: ${cert_path}" >&2
exit 1
fi
if [ ! -f "${chain_path}" ]; then
echo "Missing chain file: ${chain_path}" >&2
exit 1
fi
if [ ! -f "${key_path}" ]; then
echo "Missing key file: ${key_path}" >&2
exit 1
fi
normalize_pem_file "${cert_path}" > "${GENERATED_CERT_PATH}"
normalize_pem_file "${chain_path}" >> "${GENERATED_CERT_PATH}"
cp "${key_path}" "${GENERATED_KEY_PATH}"
}
normalize_pem_file() {
pem_path="$1"
awk '
{
sub(/\r$/, "")
print
has_content = 1
}
END {
if (has_content) {
print ""
}
}
' "${pem_path}"
}
write_nginx_config() {
cat > /etc/nginx/conf.d/default.conf <<EOF
server {
listen ${NGINX_PORT} ssl;
server_name ${NGINX_SERVER_NAME};
ssl_certificate ${GENERATED_CERT_PATH};
ssl_certificate_key ${GENERATED_KEY_PATH};
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
client_max_body_size 20m;
location / {
proxy_pass http://${UPSTREAM_HOST}:${UPSTREAM_PORT};
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
}
}
EOF
}
watch_cert_updates() {
while inotifywait -qq -e close_write,create,delete,move "${SSL_CERT_DIR}"; do
echo "Certificate files changed, reloading nginx..."
build_cert_bundle
nginx -s reload
done
}
build_cert_bundle
write_nginx_config
nginx -t
watch_cert_updates &
WATCHER_PID=$!
cleanup() {
kill "${WATCHER_PID}" 2>/dev/null || true
}
trap cleanup INT TERM
nginx -g 'daemon off;' &
NGINX_PID=$!
wait "${NGINX_PID}"