Skip to content

Commit ce81a5c

Browse files
committed
Update api.func
1 parent 7d7c6e4 commit ce81a5c

File tree

1 file changed

+212
-17
lines changed

1 file changed

+212
-17
lines changed

misc/api.func

Lines changed: 212 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Copyright (c) 2021-2026 community-scripts ORG
2-
# Author: michelroegl-brunner
3-
# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE
2+
# Author: michelroegl-brunner | MickLesk
3+
# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/LICENSE
44

55
# ==============================================================================
66
# API.FUNC - TELEMETRY & DIAGNOSTICS API
@@ -153,7 +153,7 @@ explain_exit_code() {
153153
126) echo "Command invoked cannot execute (permission problem?)" ;;
154154
127) echo "Command not found" ;;
155155
128) echo "Invalid argument to exit" ;;
156-
130) echo "Terminated by Ctrl+C (SIGINT)" ;;
156+
130) echo "Aborted by user (SIGINT)" ;;
157157
134) echo "Process aborted (SIGABRT - possibly Node.js heap overflow)" ;;
158158
137) echo "Killed (SIGKILL / Out of memory?)" ;;
159159
139) echo "Segmentation fault (core dumped)" ;;
@@ -233,6 +233,60 @@ explain_exit_code() {
233233
esac
234234
}
235235

236+
# ------------------------------------------------------------------------------
237+
# json_escape()
238+
#
239+
# - Escapes a string for safe JSON embedding
240+
# - Handles backslashes, quotes, newlines, tabs, and carriage returns
241+
# ------------------------------------------------------------------------------
242+
json_escape() {
243+
local s="$1"
244+
s=${s//\\/\\\\}
245+
s=${s//"/\\"/}
246+
s=${s//$'\n'/\\n}
247+
s=${s//$'\r'/}
248+
s=${s//$'\t'/\\t}
249+
echo "$s"
250+
}
251+
252+
# ------------------------------------------------------------------------------
253+
# get_error_text()
254+
#
255+
# - Returns last 20 lines of the active log (INSTALL_LOG or BUILD_LOG)
256+
# - Falls back to combined log or BUILD_LOG if primary is not accessible
257+
# - Handles container paths that don't exist on the host
258+
# ------------------------------------------------------------------------------
259+
get_error_text() {
260+
local logfile=""
261+
if declare -f get_active_logfile >/dev/null 2>&1; then
262+
logfile=$(get_active_logfile)
263+
elif [[ -n "${INSTALL_LOG:-}" ]]; then
264+
logfile="$INSTALL_LOG"
265+
elif [[ -n "${BUILD_LOG:-}" ]]; then
266+
logfile="$BUILD_LOG"
267+
fi
268+
269+
# If logfile is inside container (e.g. /root/.install-*), try the host copy
270+
if [[ -n "$logfile" && ! -s "$logfile" ]]; then
271+
# Try combined log: /tmp/<app>-<CTID>-<SESSION_ID>.log
272+
if [[ -n "${CTID:-}" && -n "${SESSION_ID:-}" ]]; then
273+
local combined_log="/tmp/${NSAPP:-lxc}-${CTID}-${SESSION_ID}.log"
274+
if [[ -s "$combined_log" ]]; then
275+
logfile="$combined_log"
276+
fi
277+
fi
278+
fi
279+
280+
# Also try BUILD_LOG as fallback if primary log is empty/missing
281+
if [[ -z "$logfile" || ! -s "$logfile" ]] && [[ -n "${BUILD_LOG:-}" && -s "${BUILD_LOG}" ]]; then
282+
logfile="$BUILD_LOG"
283+
fi
284+
285+
if [[ -n "$logfile" && -s "$logfile" ]]; then
286+
tail -n 20 "$logfile" 2>/dev/null | sed 's/\r$//'
287+
fi
288+
}
289+
236290
# ==============================================================================
237291
# SECTION 2: TELEMETRY FUNCTIONS
238292
# ==============================================================================
@@ -353,6 +407,9 @@ detect_ram() {
353407
# - Never blocks or fails script execution
354408
# ------------------------------------------------------------------------------
355409
post_to_api() {
410+
# Prevent duplicate submissions (post_to_api is called from multiple places)
411+
[[ "${POST_TO_API_DONE:-}" == "true" ]] && return 0
412+
356413
# Silent fail - telemetry should never break scripts
357414
command -v curl &>/dev/null || {
358415
[[ "${DEV_MODE:-}" == "true" ]] && echo "[DEBUG] curl not found, skipping" >&2
@@ -382,15 +439,17 @@ post_to_api() {
382439
detect_gpu
383440
fi
384441
local gpu_vendor="${GPU_VENDOR:-unknown}"
385-
local gpu_model="${GPU_MODEL:-}"
442+
local gpu_model
443+
gpu_model=$(json_escape "${GPU_MODEL:-}")
386444
local gpu_passthrough="${GPU_PASSTHROUGH:-unknown}"
387445

388446
# Detect CPU if not already set
389447
if [[ -z "${CPU_VENDOR:-}" ]]; then
390448
detect_cpu
391449
fi
392450
local cpu_vendor="${CPU_VENDOR:-unknown}"
393-
local cpu_model="${CPU_MODEL:-}"
451+
local cpu_model
452+
cpu_model=$(json_escape "${CPU_MODEL:-}")
394453

395454
# Detect RAM if not already set
396455
if [[ -z "${RAM_SPEED:-}" ]]; then
@@ -440,6 +499,8 @@ EOF
440499
-H "Content-Type: application/json" \
441500
-d "$JSON_PAYLOAD" &>/dev/null || true
442501
fi
502+
503+
POST_TO_API_DONE=true
443504
}
444505

445506
# ------------------------------------------------------------------------------
@@ -451,6 +512,7 @@ EOF
451512
# * ct_type=2 (VM instead of LXC)
452513
# * type="vm"
453514
# * Disk size without 'G' suffix
515+
# - Includes hardware detection: CPU, GPU, RAM speed
454516
# - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set
455517
# - Never blocks or fails script execution
456518
# ------------------------------------------------------------------------------
@@ -473,6 +535,29 @@ post_to_api_vm() {
473535
pve_version=$(pveversion 2>/dev/null | awk -F'[/ ]' '{print $2}') || true
474536
fi
475537

538+
# Detect GPU if not already set
539+
if [[ -z "${GPU_VENDOR:-}" ]]; then
540+
detect_gpu
541+
fi
542+
local gpu_vendor="${GPU_VENDOR:-unknown}"
543+
local gpu_model
544+
gpu_model=$(json_escape "${GPU_MODEL:-}")
545+
local gpu_passthrough="${GPU_PASSTHROUGH:-unknown}"
546+
547+
# Detect CPU if not already set
548+
if [[ -z "${CPU_VENDOR:-}" ]]; then
549+
detect_cpu
550+
fi
551+
local cpu_vendor="${CPU_VENDOR:-unknown}"
552+
local cpu_model
553+
cpu_model=$(json_escape "${CPU_MODEL:-}")
554+
555+
# Detect RAM if not already set
556+
if [[ -z "${RAM_SPEED:-}" ]]; then
557+
detect_ram
558+
fi
559+
local ram_speed="${RAM_SPEED:-}"
560+
476561
# Remove 'G' suffix from disk size
477562
local DISK_SIZE_API="${DISK_SIZE%G}"
478563

@@ -492,6 +577,12 @@ post_to_api_vm() {
492577
"os_version": "${var_version:-}",
493578
"pve_version": "${pve_version}",
494579
"method": "${METHOD:-default}",
580+
"cpu_vendor": "${cpu_vendor}",
581+
"cpu_model": "${cpu_model}",
582+
"gpu_vendor": "${gpu_vendor}",
583+
"gpu_model": "${gpu_model}",
584+
"gpu_passthrough": "${gpu_passthrough}",
585+
"ram_speed": "${ram_speed}",
495586
"repo_source": "${REPO_SOURCE}"
496587
}
497588
EOF
@@ -522,9 +613,12 @@ post_update_to_api() {
522613
# Silent fail - telemetry should never break scripts
523614
command -v curl &>/dev/null || return 0
524615

525-
# Prevent duplicate submissions
616+
# Support "force" mode (3rd arg) to bypass duplicate check for retries after cleanup
617+
local force="${3:-}"
526618
POST_UPDATE_DONE=${POST_UPDATE_DONE:-false}
527-
[[ "$POST_UPDATE_DONE" == "true" ]] && return 0
619+
if [[ "$POST_UPDATE_DONE" == "true" && "$force" != "force" ]]; then
620+
return 0
621+
fi
528622

529623
[[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0
530624
[[ -z "${RANDOM_UUID:-}" ]] && return 0
@@ -535,12 +629,14 @@ post_update_to_api() {
535629

536630
# Get GPU info (if detected)
537631
local gpu_vendor="${GPU_VENDOR:-unknown}"
538-
local gpu_model="${GPU_MODEL:-}"
632+
local gpu_model
633+
gpu_model=$(json_escape "${GPU_MODEL:-}")
539634
local gpu_passthrough="${GPU_PASSTHROUGH:-unknown}"
540635

541636
# Get CPU info (if detected)
542637
local cpu_vendor="${CPU_VENDOR:-unknown}"
543-
local cpu_model="${CPU_MODEL:-}"
638+
local cpu_model
639+
cpu_model=$(json_escape "${CPU_MODEL:-}")
544640

545641
# Get RAM info (if detected)
546642
local ram_speed="${RAM_SPEED:-}"
@@ -562,13 +658,21 @@ post_update_to_api() {
562658
esac
563659

564660
# For failed/unknown status, resolve exit code and error description
661+
local short_error=""
565662
if [[ "$pb_status" == "failed" ]] || [[ "$pb_status" == "unknown" ]]; then
566663
if [[ "$raw_exit_code" =~ ^[0-9]+$ ]]; then
567664
exit_code="$raw_exit_code"
568665
else
569666
exit_code=1
570667
fi
571-
error=$(explain_exit_code "$exit_code")
668+
local error_text=""
669+
error_text=$(get_error_text)
670+
if [[ -n "$error_text" ]]; then
671+
error=$(json_escape "$error_text")
672+
else
673+
error=$(json_escape "$(explain_exit_code "$exit_code")")
674+
fi
675+
short_error=$(json_escape "$(explain_exit_code "$exit_code")")
572676
error_category=$(categorize_error "$exit_code")
573677
[[ -z "$error" ]] && error="Unknown error"
574678
fi
@@ -585,8 +689,9 @@ post_update_to_api() {
585689
pve_version=$(pveversion 2>/dev/null | awk -F'[/ ]' '{print $2}') || true
586690
fi
587691

588-
# Full payload including all fields - allows record creation if initial call failed
589-
# The Go service will find the record by random_id and PATCH, or create if not found
692+
local http_code=""
693+
694+
# ── Attempt 1: Full payload with complete error text ──
590695
local JSON_PAYLOAD
591696
JSON_PAYLOAD=$(
592697
cat <<EOF
@@ -618,11 +723,80 @@ post_update_to_api() {
618723
EOF
619724
)
620725

621-
# Fire-and-forget: never block, never fail
726+
http_code=$(curl -sS -w "%{http_code}" -m "${TELEMETRY_TIMEOUT}" -X POST "${TELEMETRY_URL}" \
727+
-H "Content-Type: application/json" \
728+
-d "$JSON_PAYLOAD" -o /dev/null 2>/dev/null) || http_code="000"
729+
730+
if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then
731+
POST_UPDATE_DONE=true
732+
return 0
733+
fi
734+
735+
# ── Attempt 2: Short error text (no full log) ──
736+
sleep 1
737+
local RETRY_PAYLOAD
738+
RETRY_PAYLOAD=$(
739+
cat <<EOF
740+
{
741+
"random_id": "${RANDOM_UUID}",
742+
"type": "${TELEMETRY_TYPE:-lxc}",
743+
"nsapp": "${NSAPP:-unknown}",
744+
"status": "${pb_status}",
745+
"ct_type": ${CT_TYPE:-1},
746+
"disk_size": ${DISK_SIZE:-0},
747+
"core_count": ${CORE_COUNT:-0},
748+
"ram_size": ${RAM_SIZE:-0},
749+
"os_type": "${var_os:-}",
750+
"os_version": "${var_version:-}",
751+
"pve_version": "${pve_version}",
752+
"method": "${METHOD:-default}",
753+
"exit_code": ${exit_code},
754+
"error": "${short_error}",
755+
"error_category": "${error_category}",
756+
"install_duration": ${duration},
757+
"cpu_vendor": "${cpu_vendor}",
758+
"cpu_model": "${cpu_model}",
759+
"gpu_vendor": "${gpu_vendor}",
760+
"gpu_model": "${gpu_model}",
761+
"gpu_passthrough": "${gpu_passthrough}",
762+
"ram_speed": "${ram_speed}",
763+
"repo_source": "${REPO_SOURCE}"
764+
}
765+
EOF
766+
)
767+
768+
http_code=$(curl -sS -w "%{http_code}" -m "${TELEMETRY_TIMEOUT}" -X POST "${TELEMETRY_URL}" \
769+
-H "Content-Type: application/json" \
770+
-d "$RETRY_PAYLOAD" -o /dev/null 2>/dev/null) || http_code="000"
771+
772+
if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then
773+
POST_UPDATE_DONE=true
774+
return 0
775+
fi
776+
777+
# ── Attempt 3: Minimal payload (bare minimum to set status) ──
778+
sleep 2
779+
local MINIMAL_PAYLOAD
780+
MINIMAL_PAYLOAD=$(
781+
cat <<EOF
782+
{
783+
"random_id": "${RANDOM_UUID}",
784+
"type": "${TELEMETRY_TYPE:-lxc}",
785+
"nsapp": "${NSAPP:-unknown}",
786+
"status": "${pb_status}",
787+
"exit_code": ${exit_code},
788+
"error": "${short_error}",
789+
"error_category": "${error_category}",
790+
"install_duration": ${duration}
791+
}
792+
EOF
793+
)
794+
622795
curl -sS -w "%{http_code}" -m "${TELEMETRY_TIMEOUT}" -X POST "${TELEMETRY_URL}" \
623796
-H "Content-Type: application/json" \
624-
-d "$JSON_PAYLOAD" -o /dev/null 2>&1 || true
797+
-d "$MINIMAL_PAYLOAD" -o /dev/null 2>/dev/null || true
625798

799+
# Tried 3 times - mark as done regardless to prevent infinite loops
626800
POST_UPDATE_DONE=true
627801
}
628802

@@ -658,6 +832,9 @@ categorize_error() {
658832
# Configuration errors
659833
203 | 204 | 205 | 206 | 207 | 208) echo "config" ;;
660834

835+
# Aborted by user
836+
130) echo "aborted" ;;
837+
661838
# Resource errors (OOM, etc)
662839
137 | 134) echo "resource" ;;
663840

@@ -722,7 +899,13 @@ post_tool_to_api() {
722899

723900
if [[ "$status" == "failed" ]]; then
724901
[[ ! "$exit_code" =~ ^[0-9]+$ ]] && exit_code=1
725-
error=$(explain_exit_code "$exit_code")
902+
local error_text=""
903+
error_text=$(get_error_text)
904+
if [[ -n "$error_text" ]]; then
905+
error=$(json_escape "$error_text")
906+
else
907+
error=$(json_escape "$(explain_exit_code "$exit_code")")
908+
fi
726909
error_category=$(categorize_error "$exit_code")
727910
fi
728911

@@ -783,7 +966,13 @@ post_addon_to_api() {
783966

784967
if [[ "$status" == "failed" ]]; then
785968
[[ ! "$exit_code" =~ ^[0-9]+$ ]] && exit_code=1
786-
error=$(explain_exit_code "$exit_code")
969+
local error_text=""
970+
error_text=$(get_error_text)
971+
if [[ -n "$error_text" ]]; then
972+
error=$(json_escape "$error_text")
973+
else
974+
error=$(json_escape "$(explain_exit_code "$exit_code")")
975+
fi
787976
error_category=$(categorize_error "$exit_code")
788977
fi
789978

@@ -876,7 +1065,13 @@ post_update_to_api_extended() {
8761065
else
8771066
exit_code=1
8781067
fi
879-
error=$(explain_exit_code "$exit_code")
1068+
local error_text=""
1069+
error_text=$(get_error_text)
1070+
if [[ -n "$error_text" ]]; then
1071+
error=$(json_escape "$error_text")
1072+
else
1073+
error=$(json_escape "$(explain_exit_code "$exit_code")")
1074+
fi
8801075
error_category=$(categorize_error "$exit_code")
8811076
[[ -z "$error" ]] && error="Unknown error"
8821077
fi

0 commit comments

Comments
 (0)