#!/usr/bin/env bash # configure-searxng-safesearch.sh # Set SearXNG safe-search level and disable engines that can't enforce it. # # Usage: # ./configure-searxng-safesearch.sh strict # block explicit content # ./configure-searxng-safesearch.sh moderate # filter but not strict # ./configure-searxng-safesearch.sh none # unfiltered (default) # # What it does: # 1. Updates ~/docker/ai-stack/searxng/settings.yml # 2. Disables engines that don't honour the chosen safe-search level # 3. Updates SEARXNG_QUERY_URL in docker-compose.yml to pass &safesearch=N # 4. Restarts the SearXNG container to apply changes set -euo pipefail # ── Helpers ─────────────────────────────────────────────────────────────────── red() { printf '\e[31m%s\e[0m\n' "$*"; } grn() { printf '\e[32m%s\e[0m\n' "$*"; } blu() { printf '\e[34m%s\e[0m\n' "$*"; } die() { red "ERROR: $*"; exit 1; } ok() { grn " ✓ $*"; } info() { blu " → $*"; } LEVEL="${1:-moderate}" BASE="${BASE:-$HOME/docker/ai-stack}" SETTINGS="$BASE/searxng/settings.yml" COMPOSE="$BASE/docker-compose.yml" # ── Validate ────────────────────────────────────────────────────────────────── case "$LEVEL" in strict|moderate|none) ;; *) die "Unknown level '$LEVEL'. Use: strict, moderate, or none" ;; esac [[ -f "$SETTINGS" ]] || die "SearXNG settings not found: $SETTINGS" # ── Map level → SearXNG integer ─────────────────────────────────────────────── case "$LEVEL" in none) SAFE_INT=0 ;; moderate) SAFE_INT=1 ;; strict) SAFE_INT=2 ;; esac info "Setting safe_search = $LEVEL (${SAFE_INT})" # ── Engines that cannot enforce safe-search ─────────────────────────────────── # These are disabled for moderate/strict because they either have no # safe-search API parameter, or don't reliably honour it. # # Torrent/P2P — inherently unfiltered TORRENT_ENGINES=( "1337x" "piratebay" "nyaa" "torrentz" "kickass torrents" ) # General web / image / video engines without safe-search support NO_SAFESEARCH_ENGINES=( "mojeek" # no safe-search parameter "naver" # Korean engine, no safe-search API "baidu" # Chinese engine, no safe-search for non-CN queries "yandex" # nominally supports it but does not reliably enforce "invidious" # YouTube frontend, no filtering "piped" # YouTube frontend, no filtering "peertube" # federated video, no filtering "sepiasearch" # PeerTube index, no filtering ) ALL_SKIP=("${TORRENT_ENGINES[@]}" "${NO_SAFESEARCH_ENGINES[@]}") # ── Extract existing secret key (avoid regenerating on every run) ───────────── SECRET_KEY=$(grep -oP '(?<=secret_key: ")[^"]+' "$SETTINGS" 2>/dev/null || true) [[ -z "$SECRET_KEY" ]] && SECRET_KEY=$(openssl rand -hex 32) # ── Build engine-override block ─────────────────────────────────────────────── build_overrides() { local disabled="$1" # true or false for engine in "${ALL_SKIP[@]}"; do printf ' - name: %s\n disabled: %s\n' "$engine" "$disabled" done } if [[ "$LEVEL" == "none" ]]; then OVERRIDE_BLOCK=$(build_overrides false) else OVERRIDE_BLOCK=$(build_overrides true) fi # ── Write settings.yml ──────────────────────────────────────────────────────── cat > "$SETTINGS" << YAML use_default_settings: true general: instance_name: "Local Search" server: secret_key: "$SECRET_KEY" limiter: false search: safe_search: $SAFE_INT default_lang: "en" formats: [html, json] engines: $OVERRIDE_BLOCK YAML ok "Updated settings.yml (safe_search: $SAFE_INT)" # ── Update &safesearch= in SEARXNG_QUERY_URL inside docker-compose.yml ──────── if [[ -f "$COMPOSE" ]]; then # Strip any existing &safesearch=N, then append the current value sed -i -E \ "s|(SEARXNG_QUERY_URL=http://searxng:[0-9]+/search\?[^&\n]*)(&safesearch=[0-9])?|\1\&safesearch=${SAFE_INT}|g" \ "$COMPOSE" ok "Updated SEARXNG_QUERY_URL in docker-compose.yml (&safesearch=${SAFE_INT})" else info "docker-compose.yml not found — skipping URL update" fi # ── Restart SearXNG ─────────────────────────────────────────────────────────── if docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^searxng$'; then info "Restarting SearXNG..." docker restart searxng ok "SearXNG restarted" else info "SearXNG is not running — changes take effect on next start" fi echo grn "Safe-search level: $LEVEL" if [[ "$LEVEL" != "none" ]]; then info "Disabled ${#ALL_SKIP[@]} engines that can't enforce '${LEVEL}'" for e in "${ALL_SKIP[@]}"; do printf ' - %s\n' "$e"; done fi