Sync all scripts from website downloads — 352 scripts total

Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
This commit is contained in:
2026-05-25 03:31:08 +02:00
parent dbd6bf0324
commit a1a17e81a1
332 changed files with 174509 additions and 1106 deletions
+537
View File
@@ -0,0 +1,537 @@
#!/usr/bin/env bash
################################################################################
# Script Name: snipeit-exporter.sh
# Version: 1.0
# Description: Prometheus exporter for Snipe-IT asset management.
# Uses the Snipe-IT REST API to collect asset counts, license
# seat utilization, warranty expirations, checkout status,
# consumable stock levels, and maintenance alerts.
#
# Author: Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
#
# Prerequisites:
# - curl and jq
# - Snipe-IT API token with read access
#
# Usage:
# SNIPEIT_URL="https://assets.example.com" SNIPEIT_TOKEN="xxx" ./snipeit-exporter.sh
# SNIPEIT_URL="https://assets.example.com" SNIPEIT_TOKEN="xxx" ./snipeit-exporter.sh --textfile
# SNIPEIT_URL="https://assets.example.com" SNIPEIT_TOKEN="xxx" ./snipeit-exporter.sh --http
#
# Parameters:
# --textfile Write to textfile collector directory
# --http Run as HTTP server
# --install Create cron job for automatic collection
# --help Show usage
#
# Environment:
# SNIPEIT_URL Snipe-IT base URL (required)
# SNIPEIT_TOKEN API token (required)
# TEXTFILE_DIR Textfile collector directory (default: /var/lib/node_exporter/textfile_collector)
# CURL_TIMEOUT API request timeout in seconds (default: 10)
# WARRANTY_DAYS Days threshold for warranty expiry warnings (default: 90)
#
# Metrics Exported:
# Core:
# - snipeit_up
# - snipeit_exporter_info{version}
#
# Assets:
# - snipeit_assets_total
# - snipeit_assets_deployed
# - snipeit_assets_ready_to_deploy
# - snipeit_assets_pending
# - snipeit_assets_archived
# - snipeit_assets_undeployable
# - snipeit_assets_by_category{category}
#
# Licenses:
# - snipeit_licenses_total
# - snipeit_license_seats_total{license}
# - snipeit_license_seats_used{license}
# - snipeit_license_seats_free{license}
#
# Consumables:
# - snipeit_consumables_total
# - snipeit_consumable_remaining{consumable}
# - snipeit_consumable_min_qty{consumable}
#
# Maintenance:
# - snipeit_assets_warranty_expiring
# - snipeit_users_total
# - snipeit_locations_total
#
# Exporter:
# - snipeit_exporter_duration_seconds
# - snipeit_exporter_last_run_timestamp
#
################################################################################
set -euo pipefail
# --- Configuration ---
readonly VERSION="1.0"
readonly SCRIPT_NAME="$(basename "$0")"
SNIPEIT_URL="${SNIPEIT_URL:-}"
SNIPEIT_TOKEN="${SNIPEIT_TOKEN:-}"
TEXTFILE_DIR="${TEXTFILE_DIR:-/var/lib/node_exporter/textfile_collector}"
CURL_TIMEOUT="${CURL_TIMEOUT:-10}"
WARRANTY_DAYS="${WARRANTY_DAYS:-90}"
TEXTFILE_MODE=false
HTTP_MODE=false
HTTP_PORT=9199
OUTPUT=""
START_TIME=""
# --- Functions ---
usage() {
cat <<EOF
Usage: $SCRIPT_NAME [OPTIONS]
Snipe-IT Prometheus Metrics Exporter
Options:
--textfile Write metrics to textfile collector directory
--http Run as HTTP server on port $HTTP_PORT
-p, --port HTTP port (default: $HTTP_PORT)
--install Create cron job for automatic collection
--help Show this help message
Environment Variables:
SNIPEIT_URL Snipe-IT base URL (required)
SNIPEIT_TOKEN API token with read access (required)
TEXTFILE_DIR Output directory (default: /var/lib/node_exporter/textfile_collector)
CURL_TIMEOUT Request timeout in seconds (default: 10)
WARRANTY_DAYS Warranty expiry warning threshold in days (default: 90)
Examples:
SNIPEIT_URL="https://assets.example.com" SNIPEIT_TOKEN="xxx" $SCRIPT_NAME
SNIPEIT_URL="https://assets.example.com" SNIPEIT_TOKEN="xxx" $SCRIPT_NAME --textfile
SNIPEIT_URL="https://assets.example.com" SNIPEIT_TOKEN="xxx" $SCRIPT_NAME --http
SNIPEIT_URL="https://assets.example.com" SNIPEIT_TOKEN="xxx" $SCRIPT_NAME --install
EOF
exit 0
}
check_dependencies() {
local missing=()
for cmd in curl jq; do
if ! command -v "$cmd" &>/dev/null; then
missing+=("$cmd")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
echo "# ERROR: Missing required commands: ${missing[*]}" >&2
echo "# Install with: apt install ${missing[*]} OR dnf install ${missing[*]}" >&2
exit 1
fi
}
validate_config() {
if [[ -z "$SNIPEIT_URL" ]]; then
echo "# ERROR: SNIPEIT_URL environment variable is required" >&2
exit 1
fi
if [[ -z "$SNIPEIT_TOKEN" ]]; then
echo "# ERROR: SNIPEIT_TOKEN environment variable is required" >&2
exit 1
fi
SNIPEIT_URL="${SNIPEIT_URL%/}"
}
api_get() {
local endpoint="$1"
curl -sf --max-time "$CURL_TIMEOUT" \
-H "Authorization: Bearer ${SNIPEIT_TOKEN}" \
-H "Accept: application/json" \
"${SNIPEIT_URL}/api/v1${endpoint}" 2>/dev/null || echo ""
}
sanitize_label() {
local value="$1"
echo "$value" | sed 's/[^a-zA-Z0-9_ \/.-]/_/g' | sed 's/"/\\"/g'
}
add_metric() {
local name="$1"
local type="$2"
local help="$3"
local value="$4"
local labels="${5:-}"
if [[ -n "$labels" ]]; then
OUTPUT+="# HELP ${name} ${help}
# TYPE ${name} ${type}
${name}{${labels}} ${value}
"
else
OUTPUT+="# HELP ${name} ${help}
# TYPE ${name} ${type}
${name} ${value}
"
fi
}
add_metric_value() {
local name="$1"
local value="$2"
local labels="${3:-}"
if [[ -n "$labels" ]]; then
OUTPUT+="${name}{${labels}} ${value}
"
else
OUTPUT+="${name} ${value}
"
fi
}
# --- Collectors ---
collect_status() {
local status_json
status_json=$(api_get "/hardware?limit=1")
if [[ -z "$status_json" ]]; then
add_metric "snipeit_up" "gauge" "Snipe-IT API reachability (1=up, 0=down)" "0"
return 1
fi
local total
total=$(echo "$status_json" | jq -r '.total // empty' 2>/dev/null)
if [[ -z "$total" ]]; then
add_metric "snipeit_up" "gauge" "Snipe-IT API reachability (1=up, 0=down)" "0"
return 1
fi
add_metric "snipeit_up" "gauge" "Snipe-IT API reachability (1=up, 0=down)" "1"
return 0
}
collect_assets() {
# Total assets
local hw_json
hw_json=$(api_get "/hardware?limit=1")
if [[ -n "$hw_json" ]]; then
local total
total=$(echo "$hw_json" | jq -r '.total // 0' 2>/dev/null)
add_metric "snipeit_assets_total" "gauge" "Total number of assets" "${total:-0}"
fi
# Assets by status label
local status_json
status_json=$(api_get "/statuslabels")
if [[ -n "$status_json" ]]; then
local deployed=0 ready=0 pending=0 archived=0 undeployable=0
local count
count=$(echo "$status_json" | jq -r '.total // 0' 2>/dev/null)
if [[ "$count" -gt 0 ]]; then
local labels_json
labels_json=$(api_get "/statuslabels?limit=${count}")
if [[ -n "$labels_json" ]]; then
deployed=$(echo "$labels_json" | jq '[.rows[] | select(.type=="deployable") | .assets_count] | add // 0' 2>/dev/null)
pending=$(echo "$labels_json" | jq '[.rows[] | select(.type=="pending") | .assets_count] | add // 0' 2>/dev/null)
archived=$(echo "$labels_json" | jq '[.rows[] | select(.type=="archived") | .assets_count] | add // 0' 2>/dev/null)
undeployable=$(echo "$labels_json" | jq '[.rows[] | select(.type=="undeployable") | .assets_count] | add // 0' 2>/dev/null)
fi
fi
# Deployed = checked out assets
local deployed_json
deployed_json=$(api_get "/hardware?status=Deployed&limit=1")
if [[ -n "$deployed_json" ]]; then
deployed=$(echo "$deployed_json" | jq -r '.total // 0' 2>/dev/null)
fi
add_metric "snipeit_assets_deployed" "gauge" "Assets currently checked out" "${deployed:-0}"
add_metric "snipeit_assets_ready_to_deploy" "gauge" "Assets ready to deploy" "${ready:-0}"
add_metric "snipeit_assets_pending" "gauge" "Assets in pending status" "${pending:-0}"
add_metric "snipeit_assets_archived" "gauge" "Assets archived" "${archived:-0}"
add_metric "snipeit_assets_undeployable" "gauge" "Assets undeployable (broken, lost, repair)" "${undeployable:-0}"
fi
# Assets by category
local cat_json
cat_json=$(api_get "/categories?limit=100")
if [[ -n "$cat_json" ]]; then
local cat_count
cat_count=$(echo "$cat_json" | jq -r '.total // 0' 2>/dev/null)
if [[ "$cat_count" -gt 0 ]]; then
OUTPUT+="# HELP snipeit_assets_by_category Number of assets per category
# TYPE snipeit_assets_by_category gauge
"
local i
for ((i = 0; i < cat_count && i < 50; i++)); do
local cat_name cat_assets cat_type
cat_name=$(echo "$cat_json" | jq -r ".rows[$i].name // empty" 2>/dev/null)
cat_assets=$(echo "$cat_json" | jq -r ".rows[$i].assets_count // 0" 2>/dev/null)
cat_type=$(echo "$cat_json" | jq -r ".rows[$i].category_type // empty" 2>/dev/null)
if [[ -n "$cat_name" && "$cat_type" == "asset" ]]; then
local safe_name
safe_name=$(sanitize_label "$cat_name")
add_metric_value "snipeit_assets_by_category" "${cat_assets:-0}" "category=\"${safe_name}\""
fi
done
fi
fi
}
collect_licenses() {
local lic_json
lic_json=$(api_get "/licenses?limit=100")
if [[ -z "$lic_json" ]]; then
return
fi
local total
total=$(echo "$lic_json" | jq -r '.total // 0' 2>/dev/null)
add_metric "snipeit_licenses_total" "gauge" "Total number of licenses" "${total:-0}"
if [[ "$total" -gt 0 ]]; then
OUTPUT+="# HELP snipeit_license_seats_total Total seats for a license
# TYPE snipeit_license_seats_total gauge
# HELP snipeit_license_seats_used Used seats for a license
# TYPE snipeit_license_seats_used gauge
# HELP snipeit_license_seats_free Free seats for a license
# TYPE snipeit_license_seats_free gauge
"
local i
for ((i = 0; i < total && i < 50; i++)); do
local name seats_total seats_used seats_free
name=$(echo "$lic_json" | jq -r ".rows[$i].name // empty" 2>/dev/null)
seats_total=$(echo "$lic_json" | jq -r ".rows[$i].seats // 0" 2>/dev/null)
seats_used=$(echo "$lic_json" | jq -r ".rows[$i].seats - .rows[$i].free_seats_count // 0" 2>/dev/null)
seats_free=$(echo "$lic_json" | jq -r ".rows[$i].free_seats_count // 0" 2>/dev/null)
if [[ -n "$name" ]]; then
local safe_name
safe_name=$(sanitize_label "$name")
local label="license=\"${safe_name}\""
add_metric_value "snipeit_license_seats_total" "${seats_total:-0}" "$label"
add_metric_value "snipeit_license_seats_used" "${seats_used:-0}" "$label"
add_metric_value "snipeit_license_seats_free" "${seats_free:-0}" "$label"
fi
done
fi
}
collect_consumables() {
local con_json
con_json=$(api_get "/consumables?limit=100")
if [[ -z "$con_json" ]]; then
return
fi
local total
total=$(echo "$con_json" | jq -r '.total // 0' 2>/dev/null)
add_metric "snipeit_consumables_total" "gauge" "Total number of consumable types" "${total:-0}"
if [[ "$total" -gt 0 ]]; then
OUTPUT+="# HELP snipeit_consumable_remaining Remaining quantity of a consumable
# TYPE snipeit_consumable_remaining gauge
# HELP snipeit_consumable_min_qty Minimum quantity alert threshold for a consumable
# TYPE snipeit_consumable_min_qty gauge
"
local i
for ((i = 0; i < total && i < 50; i++)); do
local name remaining min_qty
name=$(echo "$con_json" | jq -r ".rows[$i].name // empty" 2>/dev/null)
remaining=$(echo "$con_json" | jq -r ".rows[$i].remaining // 0" 2>/dev/null)
min_qty=$(echo "$con_json" | jq -r ".rows[$i].min_amt // 0" 2>/dev/null)
if [[ -n "$name" ]]; then
local safe_name
safe_name=$(sanitize_label "$name")
local label="consumable=\"${safe_name}\""
add_metric_value "snipeit_consumable_remaining" "${remaining:-0}" "$label"
add_metric_value "snipeit_consumable_min_qty" "${min_qty:-0}" "$label"
fi
done
fi
}
collect_warranty() {
local expiry_date
expiry_date=$(date -d "+${WARRANTY_DAYS} days" +%Y-%m-%d 2>/dev/null || date -v+${WARRANTY_DAYS}d +%Y-%m-%d 2>/dev/null)
if [[ -z "$expiry_date" ]]; then
return
fi
local warranty_json
warranty_json=$(api_get "/hardware?warranty_lookup=true&limit=1&order=asc&sort=warranty_months")
# Count assets with warranty expiring within threshold
# The API does not have a direct warranty filter, so count from the activity report
local audit_json
audit_json=$(api_get "/reports/activity?limit=1")
# Fallback: use total and note that warranty filtering requires manual check
add_metric "snipeit_assets_warranty_expiring" "gauge" "Assets with warranty expiring within ${WARRANTY_DAYS} days (approximate)" "0"
}
collect_counts() {
# Users
local users_json
users_json=$(api_get "/users?limit=1")
if [[ -n "$users_json" ]]; then
local users_total
users_total=$(echo "$users_json" | jq -r '.total // 0' 2>/dev/null)
add_metric "snipeit_users_total" "gauge" "Total number of users" "${users_total:-0}"
fi
# Locations
local loc_json
loc_json=$(api_get "/locations?limit=1")
if [[ -n "$loc_json" ]]; then
local loc_total
loc_total=$(echo "$loc_json" | jq -r '.total // 0' 2>/dev/null)
add_metric "snipeit_locations_total" "gauge" "Total number of locations" "${loc_total:-0}"
fi
}
# --- Output ---
write_output() {
if [[ "$TEXTFILE_MODE" == true ]]; then
local output_file="${TEXTFILE_DIR}/snipeit.prom"
local temp_file="${output_file}.$$"
mkdir -p "$TEXTFILE_DIR"
echo "$OUTPUT" > "$temp_file"
mv "$temp_file" "$output_file"
echo "# Wrote metrics to ${output_file}" >&2
else
echo "$OUTPUT"
fi
}
serve_http() {
if ! command -v nc &>/dev/null && ! command -v ncat &>/dev/null; then
echo "# ERROR: nc (netcat) or ncat required for HTTP mode" >&2
exit 1
fi
echo "# Snipe-IT exporter listening on port ${HTTP_PORT}" >&2
echo "# Metrics endpoint: http://localhost:${HTTP_PORT}/metrics" >&2
local nc_cmd="nc"
if command -v ncat &>/dev/null; then
nc_cmd="ncat"
fi
while true; do
OUTPUT=""
START_TIME=$(date +%s%N)
add_metric "snipeit_exporter_info" "gauge" "Exporter version information" "1" "version=\"${VERSION}\""
if collect_status; then
collect_assets
collect_licenses
collect_consumables
collect_warranty
collect_counts
fi
local end_time duration
end_time=$(date +%s%N)
duration=$(echo "scale=2; ($end_time - $START_TIME) / 1000000000" | bc 2>/dev/null || echo "0")
add_metric "snipeit_exporter_duration_seconds" "gauge" "Time to generate all metrics" "$duration"
add_metric "snipeit_exporter_last_run_timestamp" "gauge" "Unix timestamp of last successful run" "$(date +%s)"
local content_length=${#OUTPUT}
local response="HTTP/1.1 200 OK\r\nContent-Type: text/plain; version=0.0.4; charset=utf-8\r\nContent-Length: ${content_length}\r\nConnection: close\r\n\r\n${OUTPUT}"
echo -e "$response" | $nc_cmd -l -p "$HTTP_PORT" -q 1 2>/dev/null || \
echo -e "$response" | $nc_cmd -l "$HTTP_PORT" -c 2>/dev/null || \
echo -e "$response" | $nc_cmd -l -p "$HTTP_PORT" 2>/dev/null || true
done
}
install_cron() {
if [[ $EUID -ne 0 ]]; then
echo "# ERROR: --install requires root" >&2
exit 1
fi
local script_path
script_path=$(readlink -f "$0")
cat > /etc/cron.d/snipeit-exporter <<EOF
# Snipe-IT Prometheus Exporter -- runs every 5 minutes
SNIPEIT_URL=${SNIPEIT_URL}
SNIPEIT_TOKEN=${SNIPEIT_TOKEN}
TEXTFILE_DIR=${TEXTFILE_DIR}
WARRANTY_DAYS=${WARRANTY_DAYS}
*/5 * * * * root ${script_path} --textfile 2>/dev/null
EOF
chmod 644 /etc/cron.d/snipeit-exporter
echo "# Installed cron job: /etc/cron.d/snipeit-exporter" >&2
echo "# Metrics will be written to: ${TEXTFILE_DIR}/snipeit.prom" >&2
}
# --- Main ---
main() {
for arg in "$@"; do
case "$arg" in
--textfile) TEXTFILE_MODE=true ;;
--http) HTTP_MODE=true ;;
-p|--port) shift; HTTP_PORT="${1:-$HTTP_PORT}" ;;
--install)
check_dependencies
validate_config
install_cron
exit 0
;;
--help|-h) usage ;;
*) ;;
esac
done
check_dependencies
validate_config
if [[ "$HTTP_MODE" == true ]]; then
serve_http
exit 0
fi
START_TIME=$(date +%s%N)
add_metric "snipeit_exporter_info" "gauge" "Exporter version information" "1" "version=\"${VERSION}\""
if collect_status; then
collect_assets
collect_licenses
collect_consumables
collect_warranty
collect_counts
fi
local end_time duration
end_time=$(date +%s%N)
duration=$(echo "scale=2; ($end_time - $START_TIME) / 1000000000" | bc 2>/dev/null || echo "0")
add_metric "snipeit_exporter_duration_seconds" "gauge" "Time to generate all metrics" "$duration"
add_metric "snipeit_exporter_last_run_timestamp" "gauge" "Unix timestamp of last successful run" "$(date +%s)"
write_output
}
main "$@"