chore: auto-commit after execute-task
GSD-Unit: M001/S06/T01
This commit is contained in:
Executable
+195
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
API_BASE="${API_BASE:-http://localhost:5202/api}"
|
||||
AUTH_TOKEN="${AUTH_TOKEN:-}"
|
||||
CURL_TIMEOUT="${CURL_TIMEOUT:-15}"
|
||||
CONNECT_TIMEOUT="${CONNECT_TIMEOUT:-3}"
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
note() {
|
||||
printf '%s\n' "$*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf 'ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
json_get() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
python3 - "$file" "$path" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
file_path, path = sys.argv[1], sys.argv[2]
|
||||
with open(file_path, 'r', encoding='utf-8') as fh:
|
||||
data = json.load(fh)
|
||||
|
||||
value = data
|
||||
for part in path.split('.'):
|
||||
if isinstance(value, dict) and part in value:
|
||||
value = value[part]
|
||||
else:
|
||||
raise KeyError(path)
|
||||
|
||||
if value is None:
|
||||
print('null')
|
||||
elif isinstance(value, bool):
|
||||
print('true' if value else 'false')
|
||||
elif isinstance(value, (dict, list)):
|
||||
print(json.dumps(value, separators=(',', ':')))
|
||||
else:
|
||||
print(value)
|
||||
PY
|
||||
}
|
||||
|
||||
validate_json() {
|
||||
local file="$1"
|
||||
if ! python3 - "$file" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
with open(sys.argv[1], 'r', encoding='utf-8') as fh:
|
||||
json.load(fh)
|
||||
PY
|
||||
then
|
||||
printf 'Raw response body:\n' >&2
|
||||
cat "$file" >&2
|
||||
fail "Malformed JSON returned by API."
|
||||
fi
|
||||
}
|
||||
|
||||
request_json() {
|
||||
local name="$1"
|
||||
local url="$2"
|
||||
local body_file="$3"
|
||||
shift 3
|
||||
local -a headers=("$@")
|
||||
local err_file="$TMP_DIR/${name}.curl.err"
|
||||
local status_file="$TMP_DIR/${name}.status"
|
||||
: >"$err_file"
|
||||
|
||||
set +e
|
||||
curl -fsS \
|
||||
--connect-timeout "$CONNECT_TIMEOUT" \
|
||||
--max-time "$CURL_TIMEOUT" \
|
||||
-H 'Accept: application/json' \
|
||||
"${headers[@]}" \
|
||||
-o "$body_file" \
|
||||
-w '%{http_code}' \
|
||||
"$url" >"$status_file" 2>"$err_file"
|
||||
local curl_exit=$?
|
||||
set -e
|
||||
|
||||
if [[ $curl_exit -eq 0 ]]; then
|
||||
validate_json "$body_file"
|
||||
return 0
|
||||
fi
|
||||
local status='000'
|
||||
if [[ -s "$status_file" ]]; then
|
||||
status="$(tr -d '\r\n' <"$status_file")"
|
||||
fi
|
||||
|
||||
case "$curl_exit:$status" in
|
||||
7:*|28:*|52:*|56:*|6:*)
|
||||
printf 'curl: %s\n' "$(tr -d '\r' <"$err_file")" >&2
|
||||
fail "Cannot reach ${url}. Start the API from JobTrackerApi/ with 'dotnet run' and verify it is listening on ${API_BASE%/api}."
|
||||
;;
|
||||
22:401|22:403)
|
||||
note "${name}: auth required."
|
||||
note "Hint: export AUTH_TOKEN with an admin bearer token from POST ${API_BASE}/auth/login before rerunning this preflight."
|
||||
return 22
|
||||
;;
|
||||
22:*)
|
||||
local diag_body="$TMP_DIR/${name}.diag.body"
|
||||
local diag_status="$TMP_DIR/${name}.diag.status"
|
||||
local diag_err="$TMP_DIR/${name}.diag.err"
|
||||
curl -sS \
|
||||
--connect-timeout "$CONNECT_TIMEOUT" \
|
||||
--max-time "$CURL_TIMEOUT" \
|
||||
-H 'Accept: application/json' \
|
||||
"${headers[@]}" \
|
||||
-o "$diag_body" \
|
||||
-w '%{http_code}' \
|
||||
"$url" >"$diag_status" 2>"$diag_err" || true
|
||||
local http_status="$(tr -d '\r\n' <"$diag_status")"
|
||||
printf 'curl: %s\n' "$(tr -d '\r' <"$err_file")" >&2
|
||||
if [[ -s "$diag_body" ]]; then
|
||||
printf 'Response body:\n' >&2
|
||||
cat "$diag_body" >&2
|
||||
printf '\n' >&2
|
||||
fi
|
||||
fail "${name} probe failed with HTTP ${http_status:-unknown}."
|
||||
;;
|
||||
*)
|
||||
printf 'curl: %s\n' "$(tr -d '\r' <"$err_file")" >&2
|
||||
fail "${name} probe failed with curl exit ${curl_exit}."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
auth_config_body="$TMP_DIR/auth-config.json"
|
||||
request_json "auth config" "$API_BASE/auth/config" "$auth_config_body"
|
||||
|
||||
require_auth="$(json_get "$auth_config_body" 'requireAuth')"
|
||||
google_enabled="$(json_get "$auth_config_body" 'googleEnabled')"
|
||||
local_enabled="$(json_get "$auth_config_body" 'localEnabled')"
|
||||
allow_registration="$(json_get "$auth_config_body" 'allowRegistration')"
|
||||
|
||||
note "Preflight target: ${API_BASE}"
|
||||
note "cors.requiredOriginPair=UI http://localhost:3000 -> API http://localhost:5202/api"
|
||||
note "auth.requireAuth=${require_auth}"
|
||||
note "auth.localEnabled=${local_enabled}"
|
||||
note "auth.googleEnabled=${google_enabled}"
|
||||
note "auth.allowRegistration=${allow_registration}"
|
||||
|
||||
system_body="$TMP_DIR/admin-system.json"
|
||||
declare -a auth_headers=()
|
||||
if [[ -n "$AUTH_TOKEN" ]]; then
|
||||
auth_headers+=(-H "Authorization: Bearer ${AUTH_TOKEN}")
|
||||
fi
|
||||
|
||||
if request_json "admin system" "$API_BASE/admin/system" "$system_body" "${auth_headers[@]}"; then
|
||||
db_provider="$(json_get "$system_body" 'database.provider')"
|
||||
db_configured="$(json_get "$system_body" 'database.looksConfigured')"
|
||||
db_connect="$(json_get "$system_body" 'database.canConnect')"
|
||||
db_target="$(json_get "$system_body" 'database.target')"
|
||||
db_warning="$(json_get "$system_body" 'database.warning')"
|
||||
auth_required_runtime="$(json_get "$system_body" 'auth.required')"
|
||||
gmail_configured="$(json_get "$system_body" 'auth.gmailConfigured')"
|
||||
ai_healthy="$(json_get "$system_body" 'ai.healthy')"
|
||||
ai_model="$(json_get "$system_body" 'ai.model')"
|
||||
ai_last_error="$(json_get "$system_body" 'ai.lastError')"
|
||||
|
||||
note "db.provider=${db_provider}"
|
||||
note "db.looksConfigured=${db_configured}"
|
||||
note "db.canConnect=${db_connect}"
|
||||
note "db.target=${db_target}"
|
||||
note "db.warning=${db_warning}"
|
||||
note "auth.runtimeRequired=${auth_required_runtime}"
|
||||
note "gmailConfigured=${gmail_configured}"
|
||||
note "ai.healthy=${ai_healthy}"
|
||||
note "ai.model=${ai_model}"
|
||||
note "ai.lastError=${ai_last_error}"
|
||||
|
||||
if [[ "$db_connect" != "true" ]]; then
|
||||
fail "Database is not connectable according to /admin/system."
|
||||
fi
|
||||
|
||||
note "Preflight passed."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
note "db.provider=unknown"
|
||||
note "db.looksConfigured=unknown"
|
||||
note "db.canConnect=unknown"
|
||||
note "db.target=unknown"
|
||||
note "db.warning=admin token required to inspect database status"
|
||||
note "gmailConfigured=unknown"
|
||||
note "ai.healthy=unknown"
|
||||
note "ai.model=unknown"
|
||||
note "ai.lastError=unknown"
|
||||
note "Preflight partially passed: anonymous auth config is reachable, but admin/system requires an admin bearer token for DB/Gmail/AI details."
|
||||
Reference in New Issue
Block a user