platform: modularize api/gui, add docs-tests-web foundation, and refresh root config

This commit is contained in:
beckline
2026-03-26 22:40:54 +03:00
parent 0e2d7f61ea
commit 6a56d734c2
562 changed files with 70151 additions and 16423 deletions

View File

@@ -0,0 +1,265 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
UPDATER="${SCRIPT_DIR}/update.sh"
ENABLED="false"
MANIFEST="${SCRIPT_DIR}/manifest.production.json"
SOURCE_POLICY=""
BIN_ROOT="/opt/selective-vpn/bin"
TARGET=""
COMPONENTS_RAW=""
ROLLOUT_STAGE="stable"
COHORT_ID=""
FORCE_ROLLOUT=0
SIGNATURE_MODE=""
MIN_INTERVAL_SEC=21600
JITTER_SEC=0
FORCE_NOW=0
DRY_RUN=0
STATE_DIR=""
LOCK_FILE=""
usage() {
cat <<'EOF'
Usage:
auto_update.sh [--enabled true|false] [--manifest PATH] [--source-policy PATH]
[--bin-root DIR] [--target OS-ARCH] [--component NAME[,NAME...]]
[--rollout-stage stable|canary|any] [--cohort-id 0..99]
[--signature-mode off|optional|required]
[--min-interval-sec N] [--jitter-sec N]
[--state-dir DIR] [--lock-file PATH]
[--force-rollout] [--force-now] [--dry-run]
Description:
Opt-in scheduler wrapper around update.sh.
Default behavior is disabled; when enabled, it enforces interval gating and lock.
Examples:
./scripts/transport-packaging/auto_update.sh --enabled true
./scripts/transport-packaging/auto_update.sh --enabled true --component singbox,phoenix --min-interval-sec 3600
EOF
}
require_cmd() {
local cmd="$1"
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "[transport-auto-update] missing required command: ${cmd}" >&2
exit 1
fi
}
bool_normalize() {
local raw
raw="$(echo "$1" | tr '[:upper:]' '[:lower:]' | xargs)"
case "$raw" in
1|true|yes|on) echo "true" ;;
0|false|no|off|"") echo "false" ;;
*)
echo "[transport-auto-update] invalid boolean value: ${1}" >&2
exit 1
;;
esac
}
int_validate_non_negative() {
local name="$1"
local value="$2"
if [[ ! "$value" =~ ^[0-9]+$ ]]; then
echo "[transport-auto-update] ${name} must be a non-negative integer" >&2
exit 1
fi
}
while [[ $# -gt 0 ]]; do
case "$1" in
--enabled)
ENABLED="${2:-}"
shift 2
;;
--manifest)
MANIFEST="${2:-}"
shift 2
;;
--source-policy)
SOURCE_POLICY="${2:-}"
shift 2
;;
--bin-root)
BIN_ROOT="${2:-}"
shift 2
;;
--target)
TARGET="${2:-}"
shift 2
;;
--component)
COMPONENTS_RAW="${2:-}"
shift 2
;;
--rollout-stage)
ROLLOUT_STAGE="${2:-}"
shift 2
;;
--cohort-id)
COHORT_ID="${2:-}"
shift 2
;;
--signature-mode)
SIGNATURE_MODE="${2:-}"
shift 2
;;
--min-interval-sec)
MIN_INTERVAL_SEC="${2:-}"
shift 2
;;
--jitter-sec)
JITTER_SEC="${2:-}"
shift 2
;;
--state-dir)
STATE_DIR="${2:-}"
shift 2
;;
--lock-file)
LOCK_FILE="${2:-}"
shift 2
;;
--force-rollout)
FORCE_ROLLOUT=1
shift
;;
--force-now)
FORCE_NOW=1
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "[transport-auto-update] unknown argument: $1" >&2
usage >&2
exit 1
;;
esac
done
ENABLED="$(bool_normalize "$ENABLED")"
int_validate_non_negative "min-interval-sec" "$MIN_INTERVAL_SEC"
int_validate_non_negative "jitter-sec" "$JITTER_SEC"
if [[ "$ENABLED" != "true" ]]; then
echo "[transport-auto-update] disabled (opt-in mode)"
exit 0
fi
if [[ ! -x "$UPDATER" ]]; then
echo "[transport-auto-update] updater not found or not executable: ${UPDATER}" >&2
exit 1
fi
if [[ ! -f "$MANIFEST" ]]; then
echo "[transport-auto-update] manifest not found: ${MANIFEST}" >&2
exit 1
fi
if [[ -n "$SOURCE_POLICY" && ! -f "$SOURCE_POLICY" ]]; then
echo "[transport-auto-update] source policy not found: ${SOURCE_POLICY}" >&2
exit 1
fi
require_cmd flock
require_cmd date
if [[ -z "$STATE_DIR" ]]; then
STATE_DIR="${BIN_ROOT}/.packaging/auto-update"
fi
mkdir -p "$STATE_DIR"
if [[ -z "$LOCK_FILE" ]]; then
LOCK_FILE="${STATE_DIR}/auto-update.lock"
fi
last_run_file="${STATE_DIR}/last_run_epoch"
last_success_file="${STATE_DIR}/last_success_epoch"
last_error_file="${STATE_DIR}/last_error"
echo "[transport-auto-update] enabled=true"
echo "[transport-auto-update] manifest=${MANIFEST}"
echo "[transport-auto-update] state_dir=${STATE_DIR}"
echo "[transport-auto-update] min_interval_sec=${MIN_INTERVAL_SEC}"
if [[ -n "$COMPONENTS_RAW" ]]; then
echo "[transport-auto-update] components=${COMPONENTS_RAW}"
fi
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
echo "[transport-auto-update] skip: another auto-update process is running"
exit 0
fi
now_epoch="$(date +%s)"
last_run_epoch=0
if [[ -f "$last_run_file" ]]; then
last_run_epoch="$(cat "$last_run_file" 2>/dev/null || echo 0)"
[[ "$last_run_epoch" =~ ^[0-9]+$ ]] || last_run_epoch=0
fi
elapsed=$((now_epoch - last_run_epoch))
if [[ "$FORCE_NOW" -ne 1 && "$elapsed" -lt "$MIN_INTERVAL_SEC" ]]; then
echo "[transport-auto-update] skip: interval gate (elapsed=${elapsed}s < ${MIN_INTERVAL_SEC}s)"
exit 0
fi
if [[ "$JITTER_SEC" -gt 0 && "$FORCE_NOW" -ne 1 ]]; then
jitter=$((RANDOM % (JITTER_SEC + 1)))
if [[ "$jitter" -gt 0 ]]; then
echo "[transport-auto-update] jitter sleep: ${jitter}s"
sleep "$jitter"
fi
fi
cmd=("$UPDATER" "--manifest" "$MANIFEST" "--bin-root" "$BIN_ROOT" "--rollout-stage" "$ROLLOUT_STAGE")
if [[ -n "$SOURCE_POLICY" ]]; then
cmd+=("--source-policy" "$SOURCE_POLICY")
fi
if [[ -n "$TARGET" ]]; then
cmd+=("--target" "$TARGET")
fi
if [[ -n "$COMPONENTS_RAW" ]]; then
cmd+=("--component" "$COMPONENTS_RAW")
fi
if [[ -n "$COHORT_ID" ]]; then
cmd+=("--cohort-id" "$COHORT_ID")
fi
if [[ -n "$SIGNATURE_MODE" ]]; then
cmd+=("--signature-mode" "$SIGNATURE_MODE")
fi
if [[ "$FORCE_ROLLOUT" -eq 1 ]]; then
cmd+=("--force-rollout")
fi
if [[ "$DRY_RUN" -eq 1 ]]; then
cmd+=("--dry-run")
fi
echo "[transport-auto-update] run: ${cmd[*]}"
if "${cmd[@]}"; then
date +%s >"$last_run_file"
date +%s >"$last_success_file"
: >"$last_error_file"
echo "[transport-auto-update] success"
exit 0
fi
rc=$?
date +%s >"$last_run_file"
{
echo "ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "exit_code=${rc}"
echo "cmd=${cmd[*]}"
} >"$last_error_file"
echo "[transport-auto-update] failed rc=${rc}" >&2
exit "$rc"