From f40bd0cd5194707e8b46f34064baefb73001a876 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Thu, 1 Jan 2026 22:27:37 +0800 Subject: [PATCH] feat(script): add usage statistics preservation across container rebuilds --- docker-build.sh | 128 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 3 deletions(-) diff --git a/docker-build.sh b/docker-build.sh index edfd5ead..fc846703 100644 --- a/docker-build.sh +++ b/docker-build.sh @@ -5,9 +5,115 @@ # This script automates the process of building and running the Docker container # with version information dynamically injected at build time. -# Exit immediately if a command exits with a non-zero status. +# Hidden feature: Preserve usage statistics across rebuilds +# Usage: ./docker-build.sh --with-usage +# First run prompts for management API key, saved to temp/stats/.api_secret + set -euo pipefail +STATS_DIR="temp/stats" +STATS_FILE="${STATS_DIR}/.usage_backup.json" +SECRET_FILE="${STATS_DIR}/.api_secret" +WITH_USAGE=false + +get_port() { + if [[ -f "config.yaml" ]]; then + grep -E "^port:" config.yaml | sed -E 's/^port: *["'"'"']?([0-9]+)["'"'"']?.*$/\1/' + else + echo "8317" + fi +} + +export_stats_api_secret() { + if [[ -f "${SECRET_FILE}" ]]; then + API_SECRET=$(cat "${SECRET_FILE}") + else + if [[ ! -d "${STATS_DIR}" ]]; then + mkdir -p "${STATS_DIR}" + fi + echo "First time using --with-usage. Management API key required." + read -r -p "Enter management key: " -s API_SECRET + echo + echo "${API_SECRET}" > "${SECRET_FILE}" + chmod 600 "${SECRET_FILE}" + fi +} + +check_container_running() { + local port + port=$(get_port) + + if ! curl -s -o /dev/null -w "%{http_code}" "http://localhost:${port}/" | grep -q "200"; then + echo "Error: cli-proxy-api service is not responding at localhost:${port}" + echo "Please start the container first or use without --with-usage flag." + exit 1 + fi +} + +export_stats() { + local port + port=$(get_port) + + if [[ ! -d "${STATS_DIR}" ]]; then + mkdir -p "${STATS_DIR}" + fi + check_container_running + echo "Exporting usage statistics..." + EXPORT_RESPONSE=$(curl -s -w "\n%{http_code}" -H "X-Management-Key: ${API_SECRET}" \ + "http://localhost:${port}/v0/management/usage/export") + HTTP_CODE=$(echo "${EXPORT_RESPONSE}" | tail -n1) + RESPONSE_BODY=$(echo "${EXPORT_RESPONSE}" | sed '$d') + + if [[ "${HTTP_CODE}" != "200" ]]; then + echo "Export failed (HTTP ${HTTP_CODE}): ${RESPONSE_BODY}" + exit 1 + fi + + echo "${RESPONSE_BODY}" > "${STATS_FILE}" + echo "Statistics exported to ${STATS_FILE}" +} + +import_stats() { + local port + port=$(get_port) + + echo "Importing usage statistics..." + IMPORT_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + -H "X-Management-Key: ${API_SECRET}" \ + -H "Content-Type: application/json" \ + -d @"${STATS_FILE}" \ + "http://localhost:${port}/v0/management/usage/import") + IMPORT_CODE=$(echo "${IMPORT_RESPONSE}" | tail -n1) + IMPORT_BODY=$(echo "${IMPORT_RESPONSE}" | sed '$d') + + if [[ "${IMPORT_CODE}" == "200" ]]; then + echo "Statistics imported successfully" + else + echo "Import failed (HTTP ${IMPORT_CODE}): ${IMPORT_BODY}" + fi + + rm -f "${STATS_FILE}" +} + +wait_for_service() { + local port + port=$(get_port) + + echo "Waiting for service to be ready..." + for i in {1..30}; do + if curl -s -o /dev/null -w "%{http_code}" "http://localhost:${port}/" | grep -q "200"; then + break + fi + sleep 1 + done + sleep 2 +} + +if [[ "${1:-}" == "--with-usage" ]]; then + WITH_USAGE=true + export_stats_api_secret +fi + # --- Step 1: Choose Environment --- echo "Please select an option:" echo "1) Run using Pre-built Image (Recommended)" @@ -18,7 +124,14 @@ read -r -p "Enter choice [1-2]: " choice case "$choice" in 1) echo "--- Running with Pre-built Image ---" + if [[ "${WITH_USAGE}" == "true" ]]; then + export_stats + fi docker compose up -d --remove-orphans --no-build + if [[ "${WITH_USAGE}" == "true" ]]; then + wait_for_service + import_stats + fi echo "Services are starting from remote image." echo "Run 'docker compose logs -f' to see the logs." ;; @@ -38,7 +151,11 @@ case "$choice" in # Build and start the services with a local-only image tag export CLI_PROXY_IMAGE="cli-proxy-api:local" - + + if [[ "${WITH_USAGE}" == "true" ]]; then + export_stats + fi + echo "Building the Docker image..." docker compose build \ --build-arg VERSION="${VERSION}" \ @@ -48,6 +165,11 @@ case "$choice" in echo "Starting the services..." docker compose up -d --remove-orphans --pull never + if [[ "${WITH_USAGE}" == "true" ]]; then + wait_for_service + import_stats + fi + echo "Build complete. Services are starting." echo "Run 'docker compose logs -f' to see the logs." ;; @@ -55,4 +177,4 @@ case "$choice" in echo "Invalid choice. Please enter 1 or 2." exit 1 ;; -esac \ No newline at end of file +esac