Files
qinglong/shell/api.sh
T
whyour 5801f735bc fix: improve shell compatibility and process cleanup
Preserve literal paths and arguments, use portable command options, validate startup configuration, and retain scripts when downloads fail.

Ensure task watchdogs and process groups are cleaned up across Bash versions, with compatibility and process lifecycle regression coverage.
2026-10-15 23:20:15 +08:00

272 lines
7.8 KiB
Bash
Executable File

#!/usr/bin/env bash
create_token() {
local token_file="${dir_root}/static/build/token.js"
if [[ -f $token_file ]]; then
__ql_token__=$(node "$token_file")
else
__ql_token__=$(ts-node-transpile-only "${dir_root}/back/token.ts")
fi
}
get_token() {
local token_data expiration token_value
local currentTimeStamp=${EPOCHSECONDS:-$(date +%s)}
if [[ -f "$file_auth_token" ]] && token_data=$(jq -er \
'select((.expiration | type) == "number" and (.value | type) == "string" and (.value | length) > 0 and (.value | test("[\r\n]") | not)) | .expiration, .value' \
"$file_auth_token" 2>/dev/null); then
expiration=${token_data%%$'\n'*}
token_value=${token_data#*$'\n'}
if [[ "$expiration" =~ ^[1-9][0-9]{0,10}$ && -n "$token_value" && "$token_value" != *$'\n'* && "$token_value" != *$'\r'* ]] && \
(( currentTimeStamp < expiration )); then
__ql_token__=$token_value
return 0
fi
fi
create_token
}
# Check the cached token before every authenticated API request. A rejected
# token is refreshed and retried once; other failures are never retried.
# Arguments are curl options, including the URL, method and optional body.
ql_api_request() {
local api=""
local attempt
get_token || return $?
for attempt in 1 2; do
api=$(curl -s --noproxy "*" \
-H "Authorization: Bearer ${__ql_token__}" \
-H "Content-Type: application/json;charset=UTF-8" \
"$@" \
--compressed)
if [[ "$attempt" == 2 || "$api" == '{"code":200}' ]] ||
[[ "$(jq -r '.code' <<< "$api" 2>/dev/null)" != 401 ]]; then
break
fi
create_token || return $?
done
printf '%s' "$api"
}
# Read the status response once; preserve multiline error messages and the
# legacy code/message variables used by the shell API callers.
ql_parse_status_response() {
# Both task status and statistics endpoints normally return this exact body.
# Match the whole document; all other JSON and malformed responses still
# use jq. Keep its missing-message result for existing callers.
if [[ "$1" == '{"code":200}' ]]; then
code=200
message=null
return 0
fi
local parsed
code=""
message=""
parsed=$(jq -r '.code, .message' <<< "$1") || return $?
code=${parsed%%$'\n'*}
message=${parsed#*$'\n'}
}
add_cron_api() {
local currentTimeStamp=$(date +%s)
if [[ $# -eq 1 ]]; then
local schedule=$(echo "$1" | awk -F ":" '{print $1}')
local command=$(echo "$1" | awk -F ":" '{print $2}')
local name=$(echo "$1" | awk -F ":" '{print $3}')
local sub_id=$(echo "$1" | awk -F ":" '{print $4}')
else
local schedule="$1"
local command="$2"
local name="$3"
local sub_id="$4"
fi
if [[ ! $sub_id ]]; then
sub_id="null"
fi
local payload
payload=$(jq -cn --arg name "$name" --arg command "$command" \
--arg schedule "$schedule" --argjson sub_id "$sub_id" \
'{name: $name, command: $command, schedule: $schedule, sub_id: $sub_id}') || return $?
local api=$(ql_api_request \
"http://localhost:${ql_port}/open/crons?t=$currentTimeStamp" \
--data-raw "$payload" \
-X POST)
code=$(echo "$api" | jq -r .code)
message=$(echo "$api" | jq -r .message)
if [[ $code == 200 ]]; then
t '%s -> 添加成功' "$name"
else
t '%s -> 添加失败(%s)' "$name" "$message"
fi
}
update_cron_api() {
local currentTimeStamp=$(date +%s)
if [[ $# -eq 1 ]]; then
local schedule=$(echo "$1" | awk -F ":" '{print $1}')
local command=$(echo "$1" | awk -F ":" '{print $2}')
local name=$(echo "$1" | awk -F ":" '{print $3}')
local id=$(echo "$1" | awk -F ":" '{print $4}')
else
local schedule="$1"
local command="$2"
local name="$3"
local id="$4"
fi
local payload
payload=$(jq -cn --arg name "$name" --arg command "$command" \
--arg schedule "$schedule" --arg id "$id" \
'{name: $name, command: $command, schedule: $schedule, id: $id}') || return $?
local api=$(ql_api_request \
"http://localhost:${ql_port}/open/crons?t=$currentTimeStamp" \
-X PUT \
--data-raw "$payload")
code=$(echo "$api" | jq -r .code)
message=$(echo "$api" | jq -r .message)
if [[ $code == 200 ]]; then
t '%s -> 更新成功' "$name"
else
t '%s -> 更新失败(%s)' "$name" "$message"
fi
}
update_cron_command_api() {
local currentTimeStamp=$(date +%s)
if [[ $# -eq 1 ]]; then
local command=$(echo "$1" | awk -F ":" '{print $1}')
local id=$(echo "$1" | awk -F ":" '{print $2}')
else
local command="$1"
local id="$2"
fi
local payload
payload=$(jq -cn --arg command "$command" --arg id "$id" \
'{command: $command, id: $id}') || return $?
local api=$(ql_api_request \
"http://localhost:${ql_port}/open/crons?t=$currentTimeStamp" \
-X PUT \
--data-raw "$payload")
code=$(echo "$api" | jq -r .code)
message=$(echo "$api" | jq -r .message)
if [[ $code == 200 ]]; then
t '%s -> 更新成功' "$command"
else
t '%s -> 更新失败(%s)' "$command" "$message"
fi
}
del_cron_api() {
local ids="$1"
local currentTimeStamp=$(date +%s)
local api=$(ql_api_request \
"http://localhost:${ql_port}/open/crons?t=$currentTimeStamp" \
-X DELETE \
--data-raw "[$ids]")
code=$(echo "$api" | jq -r .code)
message=$(echo "$api" | jq -r .message)
if [[ $code == 200 ]]; then
t '成功'
else
t '失败(%s)' "$message"
fi
}
update_cron() {
local ids="$1"
local status="$2"
local pid="${3:-''}"
local logPath="$4"
local lastExecutingTime="${5:-0}"
local runningTime="${6:-0}"
local exitCode="${7:-}"
local currentTimeStamp=${EPOCHSECONDS:-$(date +%s)}
local dataRaw="{\"ids\":[$ids],\"status\":\"$status\",\"pid\":\"$pid\",\"log_path\":\"$logPath\",\"last_execution_time\":$lastExecutingTime,\"last_running_time\":$runningTime"
if [[ -n $exitCode ]]; then
dataRaw="${dataRaw},\"exit_code\":$exitCode"
fi
dataRaw="${dataRaw}}"
local api=$(ql_api_request \
"http://localhost:${ql_port}/open/crons/status?t=$currentTimeStamp" \
-X PUT \
--data-raw "$dataRaw")
ql_parse_status_response "$api" || true
if [[ $code != 200 ]]; then
if [[ ! $message ]]; then
message="$api"
fi
echo -e "${message}"
fi
}
notify_api() {
local title="$1"
local content="$2"
local currentTimeStamp=$(date +%s)
local api=$(ql_api_request \
"http://localhost:${ql_port}/open/system/notify?t=$currentTimeStamp" \
-X PUT \
--data-raw "{\"title\":\"${title//\"/\\\"}\",\"content\":\"${content//\"/\\\"}\"}")
code=$(echo "$api" | jq -r .code)
message=$(echo "$api" | jq -r .message)
if [[ $code == 200 ]]; then
t '通知发送成功🎉'
else
t '通知失败(%s)' "$message"
fi
}
find_cron_api() {
local params="$1"
local currentTimeStamp=$(date +%s)
local api=$(ql_api_request \
"http://localhost:${ql_port}/open/crons/detail?$params&t=$currentTimeStamp" \
-X GET)
data=$(echo "$api" | jq -r .data)
if [[ $data == 'null' ]]; then
echo -e ""
else
name=$(echo "$api" | jq -r .data.name)
echo -e "$name"
fi
}
update_auth_config() {
local body="$1"
local tip="$2"
local currentTimeStamp=$(date +%s)
local api=$(ql_api_request \
"http://localhost:${ql_port}/open/system/auth/reset?t=$currentTimeStamp" \
-X PUT \
--data-raw "{$body}")
code=$(echo "$api" | jq -r .code)
message=$(echo "$api" | jq -r .message)
if [[ $code == 200 ]]; then
t '%s成功🎉' "$tip"
else
t '%s失败(%s)' "$tip" "$message"
fi
}
record_cron_stat() {
local ref_id="$1"
local exit_code="${2:-0}"
local elapsed="${3:-0}"
[[ $ref_id ]] && [[ $ref_id -gt 0 ]] 2>/dev/null || return
local api=$(ql_api_request \
"http://localhost:${ql_port:-5700}/open/dashboard/record" \
-X POST \
--data-raw "{\"ref_id\":$ref_id,\"code\":$exit_code,\"elapsed\":$elapsed}")
ql_parse_status_response "$api" || true
if [[ $code != 200 ]]; then
if [[ ! $message ]]; then
message="$api"
fi
echo -e "${message}"
fi
}