2026-05-01 16:22:02 +00:00
#!/usr/bin/env bash
# nexredirect — CLI wrapper for CoreX NexRedirect on the server
set -euo pipefail
INSTALL_DIR="${NEXREDIRECT_DIR:-/opt/corex-nexredirect}"
DATA_DIR="${NEXREDIRECT_DATA_DIR:-/var/lib/corex-nexredirect}"
SERVICE_USER="nexredirect"
SERVICE="corex-nexredirect.service"
DB="$DATA_DIR/nexredirect.db"
require_root() {
if [[ $EUID -ne 0 ]]; then
echo "Bitte mit sudo ausführen: sudo nexredirect $*" >&2
exit 1
fi
}
cmd_help() {
cat <<EOF
nexredirect — CoreX NexRedirect CLI
status Service-Status
start Service starten
stop Service stoppen
restart Service neu starten
logs [-n N] Logs streamen (default: -f)
2026-05-01 16:47:50 +00:00
update [tag] Auf neueste Version (oder bestimmten Tag); skip wenn schon aktuell
update -f [tag] Update erzwingen auch wenn Version gleich
2026-05-01 16:22:02 +00:00
version Aktuelle + neueste Version (GitHub)
caddy reload Caddyfile neu generieren + reload
caddy show Aktuellen Caddyfile anzeigen
db SQLite-Shell auf der Datenbank öffnen
domains Aktive Domains listen
hits [N] Letzte N Hits (default 20)
tokens API-Tokens auflisten
backup [PATH] DB + Caddyfile sichern (default: /tmp/...)
uninstall Service + Files entfernen (DB bleibt)
help Diese Hilfe
EOF
}
cmd_status() { systemctl status "$SERVICE" --no-pager; }
cmd_start() { require_root start; systemctl start "$SERVICE"; }
cmd_stop() { require_root stop; systemctl stop "$SERVICE"; }
cmd_restart(){ require_root restart; systemctl restart "$SERVICE"; }
cmd_logs() {
local args=("-fu" "$SERVICE")
if [[ "${1:-}" == "-n" && -n "${2:-}" ]]; then args=("-n" "$2" "-u" "$SERVICE" "--no-pager"); fi
journalctl "${args[@]}"
}
cmd_update() {
require_root update
2026-05-01 16:47:50 +00:00
local force=0
local target="${1:-}"
if [[ "$target" == "-f" || "$target" == "--force" ]]; then force=1; target="${2:-}"; fi
if [[ $force -eq 0 ]]; then
local current latest
current=$(grep -m1 '"version"' "$INSTALL_DIR/package.json" 2>/dev/null | sed -E 's/.*"version": *"([^"]+)".*/v\1/')
if [[ -n "$target" ]]; then
latest="$target"
else
latest=$(curl -fsSL https://api.github.com/repos/CoreXManagement/CoreX-NexRedirect/releases/latest 2>/dev/null \
| grep -m1 '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')
fi
if [[ -n "$current" && "$current" == "$latest" ]]; then
echo "Bereits auf $current — nichts zu tun. Mit --force trotzdem ausführen."
return 0
fi
fi
"$INSTALL_DIR/scripts/update.sh" "$target"
2026-05-01 16:22:02 +00:00
}
cmd_version() {
if [[ -f "$INSTALL_DIR/package.json" ]]; then
local current
current=$(grep -m1 '"version"' "$INSTALL_DIR/package.json" | sed -E 's/.*"version": *"([^"]+)".*/\1/')
echo "current: v$current"
fi
echo -n "latest: "
curl -fsSL https://api.github.com/repos/CoreXManagement/CoreX-NexRedirect/releases/latest 2>/dev/null \
| grep -m1 '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/' || echo "(check failed)"
}
cmd_caddy() {
case "${1:-}" in
reload)
require_root "caddy reload"
curl -fsS -X POST -H "Content-Type: text/caddyfile" --data-binary @/etc/caddy/Caddyfile http://localhost:2019/load \
&& echo "Caddy reloaded" \
|| systemctl reload caddy
;;
show|config|"")
cat /etc/caddy/Caddyfile
;;
*)
echo "Usage: nexredirect caddy [reload|show]" >&2
exit 1
;;
esac
}
2026-05-01 16:56:42 +00:00
ensure_sqlite() {
if command -v sqlite3 >/dev/null 2>&1; then return 0; fi
if [[ $EUID -eq 0 ]]; then
echo "==> sqlite3 wird installiert..."
apt-get install -y -qq sqlite3 >/dev/null 2>&1 && return 0
fi
echo "sqlite3 nicht installiert. Bitte ausführen: sudo apt install -y sqlite3" >&2
return 1
}
2026-05-01 16:22:02 +00:00
cmd_db() {
2026-05-01 16:56:42 +00:00
ensure_sqlite || exit 1
2026-05-01 16:22:02 +00:00
sqlite3 -header -column "$DB"
}
cmd_domains() {
2026-05-01 16:56:42 +00:00
ensure_sqlite || exit 1
2026-05-01 16:22:02 +00:00
sqlite3 -header -column "$DB" \
"SELECT id, domain, status, redirect_code AS code, COALESCE(target_url, (SELECT target_url FROM domain_groups g WHERE g.id = d.group_id), '—') AS target FROM domains d ORDER BY created_at DESC;"
}
cmd_hits() {
2026-05-01 16:56:42 +00:00
ensure_sqlite || exit 1
2026-05-01 16:22:02 +00:00
local n="${1:-20}"
sqlite3 -header -column "$DB" \
"SELECT datetime(ts/1000,'unixepoch','localtime') AS time, (SELECT domain FROM domains WHERE id = h.domain_id) AS domain, country, substr(path,1,40) AS path FROM hits h ORDER BY ts DESC LIMIT $n;"
}
cmd_tokens() {
2026-05-01 16:56:42 +00:00
ensure_sqlite || exit 1
2026-05-01 16:22:02 +00:00
sqlite3 -header -column "$DB" \
"SELECT id, name, scopes, datetime(created_at/1000,'unixepoch','localtime') AS created, CASE WHEN revoked_at IS NULL THEN 'active' ELSE 'revoked' END AS status FROM api_tokens ORDER BY id DESC;"
}
cmd_backup() {
local target="${1:-/tmp/nexredirect-backup-$(date +%F-%H%M).tar.gz}"
require_root backup
tar -czf "$target" \
-C / \
"${DATA_DIR#/}/nexredirect.db" \
"${DATA_DIR#/}/nexredirect.db-wal" 2>/dev/null \
"${DATA_DIR#/}/nexredirect.db-shm" 2>/dev/null \
etc/caddy/Caddyfile 2>/dev/null || true
echo "Backup → $target"
ls -lh "$target"
}
cmd_uninstall() {
require_root uninstall
read -rp "Wirklich deinstallieren? (DB bleibt erhalten) [y/N] " ans
[[ "$ans" =~ ^[yY]$ ]] || { echo "Abgebrochen."; exit 0; }
systemctl disable --now "$SERVICE" 2>/dev/null || true
rm -f "/etc/systemd/system/$SERVICE" /etc/sudoers.d/corex-nexredirect /usr/local/bin/nexredirect
systemctl daemon-reload
rm -rf "$INSTALL_DIR"
echo "Entfernt. DB bleibt unter $DATA_DIR."
}
case "${1:-help}" in
status) shift; cmd_status "$@" ;;
start) shift; cmd_start "$@" ;;
stop) shift; cmd_stop "$@" ;;
restart) shift; cmd_restart "$@" ;;
logs) shift; cmd_logs "$@" ;;
update) shift; cmd_update "$@" ;;
version) shift; cmd_version "$@" ;;
caddy) shift; cmd_caddy "$@" ;;
db) shift; cmd_db "$@" ;;
domains) shift; cmd_domains "$@" ;;
hits) shift; cmd_hits "$@" ;;
tokens) shift; cmd_tokens "$@" ;;
backup) shift; cmd_backup "$@" ;;
uninstall) shift; cmd_uninstall "$@" ;;
help|--help|-h) cmd_help ;;
*) echo "Unbekannter Befehl: $1"; echo; cmd_help; exit 1 ;;
esac