#!/usr/bin/env bash # ============================================================================== # OSMAP KAI Embed Token & Map Navigator CLI # ============================================================================== # Automates token management (obtain, validate, cache, revoke) and provides # access to OSMAP KAI protected embed map routes. # ============================================================================== set -e # Color definitions RED='\033[0;31m' GREEN='\033[0;32m' BLUE='\033[0;34m' CYAN='\033[0;36m' YELLOW='\033[1;33m' BOLD='\033[1m' NC='\033[0m' # No Color # Default values DEFAULT_ENV="live" DEFAULT_APP_ID="IrSIcf06QRwUCmsRCZ0JvsVibSfuPUG4" DEFAULT_APP_SECRET="Z8tPOBHGbwWu9TrIddRqkiDFDymtiPAhMNbSz6aUOOTkqdZoQYuTWXTra5m2mPQa" CONFIG_FILE=".osmap_credentials" CACHE_DIR="${HOME}/.cache/osmap" mkdir -p "$CACHE_DIR" # Route Definitions: "Route_Key|Display_Name|Path" ROUTES=( "executive|Executive Map|/executive" "rolling|Rolling Map|/rolling" "iris-jpl|IRIS JPL|/iris-jpl" "iris-jpl-v2|IRIS JPL (v2)|/v2/iris-jpl" "accelerometer|Executive Accelerometer|/executive/accelerometer" "genset|Genset Monitoring|/genset" "managerial|Managerial / Asset View|/managerial" "fatigue|Fatigue Monitoring|/fatigue" "sigap-tracker|SIGAP Tracker|/sigap-tracker" "sigap-dashboard|SIGAP Dashboard|/sigap-dashboard" "railway-cargo-system|Railway Cargo System (RCS)|/railway-cargo-system" ) # Helper: Resolve Base URL get_base_url() { local env_name="$1" case "$env_name" in live) echo "https://live.osmap.id" ;; beta) echo "https://beta.osmap.id" ;; staging) echo "https://staging.osmap.id" ;; demo) echo "https://demo.osmap.id" ;; local) echo "http://localhost:8000" ;; http://*|https://*) echo "$env_name" ;; *) echo "https://${env_name}" ;; esac } # Helper: Copy to Clipboard copy_to_clipboard() { local text="$1" if command -v pbcopy >/dev/null 2>&1; then echo -n "$text" | pbcopy return 0 elif command -v xclip >/dev/null 2>&1; then echo -n "$text" | xclip -selection clipboard return 0 elif command -v xsel >/dev/null 2>&1; then echo -n "$text" | xsel --clipboard --input return 0 fi return 1 } # Helper: Open URL in Browser open_url() { local url="$1" if command -v open >/dev/null 2>&1; then open "$url" elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$url" >/dev/null 2>&1 & elif command -v start >/dev/null 2>&1; then start "$url" else echo -e "${YELLOW}Could not detect browser launcher. Please open the URL manually.${NC}" fi } # Helper: Load Credentials load_credentials() { # 1. Check CLI args / Env vars APP_ID="${APP_ID:-$OSMAP_APP_ID}" APP_SECRET="${APP_SECRET:-$OSMAP_APP_SECRET}" # 2. Check local config file if [[ -z "$APP_ID" || -z "$APP_SECRET" ]] && [[ -f "$CONFIG_FILE" ]]; then # shellcheck disable=SC1090 source "$CONFIG_FILE" APP_ID="${APP_ID:-$OSMAP_APP_ID}" APP_SECRET="${APP_SECRET:-$OSMAP_APP_SECRET}" fi # 3. Check .env in current directory if [[ -z "$APP_ID" || -z "$APP_SECRET" ]] && [[ -f ".env" ]]; then local env_app_id local env_app_secret env_app_id=$(grep -E "^OSMAP_APP_ID=" .env | cut -d '=' -f2- | tr -d '"' | tr -d "'" || true) env_app_secret=$(grep -E "^OSMAP_APP_SECRET=" .env | cut -d '=' -f2- | tr -d '"' | tr -d "'" || true) APP_ID="${APP_ID:-$env_app_id}" APP_SECRET="${APP_SECRET:-$env_app_secret}" fi # 4. Fallback to built-in default credentials APP_ID="${APP_ID:-$DEFAULT_APP_ID}" APP_SECRET="${APP_SECRET:-$DEFAULT_APP_SECRET}" } # Helper: Safe Read from TTY when piped (e.g. curl ... | bash) read_input() { if [[ -t 0 ]]; then read "$@" elif [[ -e /dev/tty ]]; then read "$@" < /dev/tty else read "$@" fi } # Helper: Prompt Credentials if missing ensure_credentials() { load_credentials if [[ -z "$APP_ID" ]]; then echo -e "${CYAN}Please enter your OSMAP App ID:${NC}" read_input -r -p "App ID: " APP_ID fi if [[ -z "$APP_SECRET" ]]; then echo -e "${CYAN}Please enter your OSMAP App Secret:${NC}" read_input -r -s -p "App Secret: " APP_SECRET echo "" fi if [[ -z "$APP_ID" || -z "$APP_SECRET" ]]; then echo -e "${RED}Error: App ID and App Secret are required.${NC}" >&2 exit 1 fi } # Helper: Save credentials to config save_credentials() { echo "OSMAP_APP_ID=\"$APP_ID\"" > "$CONFIG_FILE" echo "OSMAP_APP_SECRET=\"$APP_SECRET\"" >> "$CONFIG_FILE" chmod 600 "$CONFIG_FILE" echo -e "${GREEN}Credentials saved to ${CONFIG_FILE}${NC}" } # Cache management get_cache_file() { local env_sanitized env_sanitized=$(echo "$BASE_URL" | sed 's/[^a-zA-Z0-9]/_/g') echo "${CACHE_DIR}/token_${env_sanitized}.json" } # Step 3 per doc: Check token if valid validate_token() { local token="$1" local endpoint="${BASE_URL}/api/apps/validate-token" local response response=$(curl -s --location "$endpoint" \ --header 'Content-Type: application/json' \ --data "{\"token\":\"$token\"}" 2>/dev/null || echo '{"success":false}') local is_valid is_valid=$(echo "$response" | jq -r '.success // false' 2>/dev/null || echo "false") if [[ "$is_valid" == "true" ]]; then return 0 else return 1 fi } # Step 2 per doc: Get token through app_id and app_secret fetch_new_token() { ensure_credentials local endpoint="${BASE_URL}/api/apps/token" echo -e "${CYAN}Requesting new embed token from ${endpoint}...${NC}" >&2 local payload payload=$(jq -n \ --arg app_id "$APP_ID" \ --arg app_secret "$APP_SECRET" \ '{app_id: $app_id, app_secret: $app_secret}') local response response=$(curl -s --location "$endpoint" \ --header 'Content-Type: application/json' \ --data "$payload") local is_success is_success=$(echo "$response" | jq -r '.success // false' 2>/dev/null || echo "false") if [[ "$is_success" != "true" ]]; then local error_msg error_msg=$(echo "$response" | jq -r '.message // "Failed to obtain token"' 2>/dev/null || echo "$response") echo -e "${RED}Authentication Error:${NC} $error_msg" >&2 exit 1 fi local access_token access_token=$(echo "$response" | jq -r '.access_token') # Save to cache local cache_file cache_file=$(get_cache_file) echo "$response" > "$cache_file" chmod 600 "$cache_file" echo "$access_token" } # Step 1-5 Flow: Get valid token (cached or fresh) get_valid_token() { local cache_file cache_file=$(get_cache_file) local token="" if [[ -f "$cache_file" ]]; then token=$(jq -r '.access_token // empty' "$cache_file" 2>/dev/null || true) fi if [[ -n "$token" ]]; then # Validate existing token if validate_token "$token"; then echo "$token" return 0 fi fi # Token missing or invalid -> fetch fresh token fetch_new_token } # Revoke Tokens revoke_tokens() { ensure_credentials local endpoint="${BASE_URL}/api/apps/revoke-tokens" local payload payload=$(jq -n \ --arg app_id "$APP_ID" \ --arg app_secret "$APP_SECRET" \ '{app_id: $app_id, app_secret: $app_secret}') echo -e "${CYAN}Revoking tokens at ${endpoint}...${NC}" local response response=$(curl -s --location "$endpoint" \ --header 'Content-Type: application/json' \ --data "$payload") echo "$response" | jq . # Clear local cache local cache_file cache_file=$(get_cache_file) rm -f "$cache_file" echo -e "${GREEN}Local cache cleared.${NC}" } # Register App register_app() { local endpoint="${BASE_URL}/api/apps/register" echo -e "${BOLD}${CYAN}=== Register New App ===${NC}" read -r -p "App Name: " app_name read -r -p "Allowed Domains (comma-separated, e.g. demo.osmap.id,localhost): " allowed_domains_raw # Convert comma-separated string to JSON array local domains_json domains_json=$(echo "$allowed_domains_raw" | jq -R 'split(",") | map(gsub("^[ \t]+|[ \t]+$"; ""))') local payload payload=$(jq -n \ --arg app_name "$app_name" \ --argjson domains "$domains_json" \ '{app_name: $app_name, allowed_domains: $domains}') echo -e "${CYAN}Sending registration request to ${endpoint}...${NC}" local response response=$(curl -s --location "$endpoint" \ --header 'Content-Type: application/json' \ --data "$payload") echo "$response" | jq . local success success=$(echo "$response" | jq -r '.success // false' 2>/dev/null || echo "false") if [[ "$success" == "true" ]]; then local new_app_id local new_app_secret new_app_id=$(echo "$response" | jq -r '.app_id // empty') new_app_secret=$(echo "$response" | jq -r '.app_secret // empty') echo "" read -r -p "Do you want to save these credentials to ${CONFIG_FILE}? (y/N): " save_confirm if [[ "$save_confirm" =~ ^[Yy]$ ]]; then APP_ID="$new_app_id" APP_SECRET="$new_app_secret" save_credentials fi fi } # Print single URL display_and_handle_url() { local name="$1" local path="$2" local token="$3" local auto_open="$4" local full_url="${BASE_URL}${path}?token=${token}" echo "" echo -e "${BOLD}${GREEN}Map:${NC} ${name}" echo -e "${BOLD}${CYAN}URL:${NC} ${full_url}" echo "" if copy_to_clipboard "$full_url"; then echo -e "${GREEN}✓ Copied URL to clipboard!${NC}" fi if [[ "$auto_open" == "true" ]]; then echo -e "${CYAN}Opening in browser...${NC}" open_url "$full_url" fi } # Print all URLs print_all_urls() { local token="$1" echo "" echo -e "${BOLD}${CYAN}=== OSMAP All Embed URLs (${TARGET_ENV}) ===${NC}" echo "" printf "%-30s | %s\n" "Map Feature" "Complete Embed URL" printf "%-30s-+-%s\n" "------------------------------" "------------------------------------------------------------" for item in "${ROUTES[@]}"; do IFS="|" read -r key name path <<< "$item" local url="${BASE_URL}${path}?token=${token}" printf "%-30s | %s\n" "$name" "$url" done echo "" } # Interactive Menu show_interactive_menu() { local token="$1" while true; do echo "" echo -e "${BOLD}${CYAN}╔════════════════════════════════════════════════════════════════╗${NC}" echo -e "${BOLD}${CYAN}║ OSMAP KAI Embed Map Navigator ║${NC}" echo -e "${BOLD}${CYAN}╠════════════════════════════════════════════════════════════════╣${NC}" echo -e "${CYAN}║ Target Environment:${NC} ${BOLD}${YELLOW}${BASE_URL}${NC}" echo -e "${BOLD}${CYAN}╚════════════════════════════════════════════════════════════════╝${NC}" echo "" local index=1 for item in "${ROUTES[@]}"; do IFS="|" read -r key name path <<< "$item" printf " ${BOLD}%2d)${NC} %-32s ${CYAN}%s${NC}\n" "$index" "$name" "$path" ((index++)) done echo "" printf " ${BOLD}%2d)${NC} %s\n" "$index" "Display All URLs Table" ((index++)) printf " ${BOLD}%2d)${NC} %s\n" "$index" "Copy Raw Token" ((index++)) printf " ${BOLD}%2d)${NC} %s\n" "$index" "Switch Environment (live / beta / local / demo / custom)" ((index++)) printf " ${BOLD} 0)${NC} %s\n" "Exit" echo "" read_input -r -p "Select an option [0-$((index-1))]: " choice if [[ "$choice" == "0" || "$choice" == "q" || "$choice" == "exit" ]]; then echo -e "${GREEN}Goodbye!${NC}" exit 0 fi if [[ "$choice" -ge 1 && "$choice" -le ${#ROUTES[@]} ]]; then local selected_item="${ROUTES[$((choice-1))]}" IFS="|" read -r key name path <<< "$selected_item" display_and_handle_url "$name" "$path" "$token" "false" read_input -r -p "Open this URL in your default browser? (Y/n): " open_choice if [[ "$open_choice" =~ ^[Nn]$ ]]; then : else open_url "${BASE_URL}${path}?token=${token}" fi elif [[ "$choice" -eq $(( ${#ROUTES[@]} + 1 )) ]]; then print_all_urls "$token" elif [[ "$choice" -eq $(( ${#ROUTES[@]} + 2 )) ]]; then copy_to_clipboard "$token" echo -e "${GREEN}✓ Access Token copied to clipboard:${NC} $token" elif [[ "$choice" -eq $(( ${#ROUTES[@]} + 3 )) ]]; then echo "" read_input -r -p "Enter environment [live, beta, staging, demo, local, or URL]: " new_env if [[ -n "$new_env" ]]; then TARGET_ENV="$new_env" BASE_URL=$(get_base_url "$TARGET_ENV") echo -e "${CYAN}Validating token for ${BASE_URL}...${NC}" token=$(get_valid_token) fi else echo -e "${RED}Invalid selection.${NC}" fi done } # Help / Usage show_help() { cat << EOF OSMAP Embed CLI Tool Usage: ./osmap-embed.sh [route|command] [options] Commands & Short-names: interactive Launch interactive selection menu (default) all Display all embed URLs with token token Print current valid token only validate Check if cached token is still valid revoke Revoke current app tokens register Register a new application Routes: executive /executive rolling /rolling iris-jpl /iris-jpl iris-jpl-v2 /v2/iris-jpl accelerometer /executive/accelerometer genset /genset managerial /managerial fatigue /fatigue sigap-tracker /sigap-tracker sigap-dashboard /sigap-dashboard railway-cargo-system /railway-cargo-system Options: -e, --env Environment: live (default), beta, staging, demo, local, or custom URL --app-id Override App ID --app-secret Override App Secret -o, --open Automatically open the generated URL in browser -c, --copy Copy URL to clipboard --save Save provided App ID / Secret to .osmap_credentials -h, --help Show this help message Examples: ./osmap-embed.sh ./osmap-embed.sh managerial --env live --open ./osmap-embed.sh all --env beta ./osmap-embed.sh token --env live EOF } # ------------------------------------------------------------------------------ # Argument Parsing # ------------------------------------------------------------------------------ TARGET_ENV="$DEFAULT_ENV" COMMAND_OR_ROUTE="" AUTO_OPEN="false" AUTO_COPY="false" DO_SAVE="false" while [[ $# -gt 0 ]]; do case "$1" in -e|--env) TARGET_ENV="$2" shift 2 ;; --app-id) APP_ID="$2" shift 2 ;; --app-secret) APP_SECRET="$2" shift 2 ;; -o|--open) AUTO_OPEN="true" shift ;; -c|--copy) AUTO_COPY="true" shift ;; --save) DO_SAVE="true" shift ;; -h|--help) show_help exit 0 ;; *) if [[ -z "$COMMAND_OR_ROUTE" ]]; then COMMAND_OR_ROUTE="$1" fi shift ;; esac done BASE_URL=$(get_base_url "$TARGET_ENV") if [[ "$DO_SAVE" == "true" && -n "$APP_ID" && -n "$APP_SECRET" ]]; then save_credentials fi # Route / Command Execution case "$COMMAND_OR_ROUTE" in register) register_app exit 0 ;; revoke) revoke_tokens exit 0 ;; validate) cache_file=$(get_cache_file) if [[ -f "$cache_file" ]]; then token=$(jq -r '.access_token // empty' "$cache_file") if [[ -n "$token" ]] && validate_token "$token"; then echo -e "${GREEN}✓ Cached token is VALID for ${BASE_URL}${NC}" echo "Token: $token" exit 0 fi fi echo -e "${RED}✗ No valid cached token found for ${BASE_URL}${NC}" exit 1 ;; token) token=$(get_valid_token) echo "$token" exit 0 ;; all) token=$(get_valid_token) print_all_urls "$token" exit 0 ;; "") token=$(get_valid_token) show_interactive_menu "$token" ;; *) # Match against routes matched=false token=$(get_valid_token) for item in "${ROUTES[@]}"; do IFS="|" read -r key name path <<< "$item" if [[ "$COMMAND_OR_ROUTE" == "$key" || "$COMMAND_OR_ROUTE" == "${path#/}" ]]; then display_and_handle_url "$name" "$path" "$token" "$AUTO_OPEN" matched=true break fi done if [[ "$matched" == "false" ]]; then echo -e "${RED}Unknown command or route:${NC} $COMMAND_OR_ROUTE" echo "Run './osmap-embed.sh --help' for available routes." exit 1 fi ;; esac