commit 668da38632d5b075fe8f581a2f6faff922db9559 Author: Dandy Royyan Firdaus Date: Thu Aug 27 08:44:31 2026 +0000 Upload files to "/" diff --git a/osmap-embed.sh b/osmap-embed.sh new file mode 100644 index 0000000..b998005 --- /dev/null +++ b/osmap-embed.sh @@ -0,0 +1,593 @@ +#!/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