新增管理員頁面、Word PDF 預覽、租賃日期欄位、SSL docker-compose
- 新增 /admin.html:上傳/刪除範本,HTTP Basic Auth 保護 - Word 預覽改用 LibreOffice PDF 轉換,帶入表單參數即時顯示 - 新增租賃開始/結束年月日、租期年數佔位符支援 - 預覽 loading 遮罩,修正 hidden 被 CSS display:flex 覆蓋的問題 - 左右欄 UI 重構,右欄固定顯示 Word 預覽 - 新增 docker-compose.yml + nginx SSL reverse proxy - admin 密碼改由 ADMIN_PASSWORD 環境變數設定 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
114
docker/nginx/entrypoint.sh
Normal file
114
docker/nginx/entrypoint.sh
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
NGINX_PORT="${NGINX_PORT:-3001}"
|
||||
NGINX_SERVER_NAME="${NGINX_SERVER_NAME:-_}"
|
||||
SSL_CERT_DIR="${SSL_CERT_DIR:-/etc/nginx/certs}"
|
||||
SSL_CERT_FILE_NAME="${SSL_CERT_FILE_NAME:-RSA-cert.pem}"
|
||||
SSL_CHAIN_FILE_NAME="${SSL_CHAIN_FILE_NAME:-RSA-chain.pem}"
|
||||
SSL_KEY_FILE_NAME="${SSL_KEY_FILE_NAME:-RSA-privkey.pem}"
|
||||
UPSTREAM_HOST="${UPSTREAM_HOST:-rental-contract-pdf}"
|
||||
UPSTREAM_PORT="${UPSTREAM_PORT:-3001}"
|
||||
|
||||
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}"
|
||||
Reference in New Issue
Block a user