diff --git a/configure-searxng-safesearch.sh b/configure-searxng-safesearch.sh new file mode 100755 index 0000000..90f77da --- /dev/null +++ b/configure-searxng-safesearch.sh @@ -0,0 +1,136 @@ +#!/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 diff --git a/laptop_full_setup.sh b/laptop_full_setup.sh index 490c766..a1a1033 100755 --- a/laptop_full_setup.sh +++ b/laptop_full_setup.sh @@ -780,7 +780,10 @@ ${OLLAMA_VOLUME_LINE} - ENABLE_OPENAI_API=true - ENABLE_RAG_WEB_SEARCH=true - RAG_WEB_SEARCH_ENGINE=searxng - - SEARXNG_QUERY_URL=http://searxng:8080/search?q=&format=json + - SEARXNG_QUERY_URL=http://searxng:8080/search?q=&format=json&safesearch=0 + - RAG_WEB_SEARCH_RESULT_COUNT=5 + - RAG_WEB_SEARCH_CONCURRENT_REQUESTS=10 + - ENABLE_TOOL_SERVERS=true - WEBUI_AUTH=true - WEBUI_URL=${WEBUI_URL:-} depends_on: diff --git a/local-ai-setup.sh b/local-ai-setup.sh index 92d6a45..1a5363c 100755 --- a/local-ai-setup.sh +++ b/local-ai-setup.sh @@ -553,9 +553,11 @@ services: - ENABLE_OPENAI_API=true - ENABLE_RAG_WEB_SEARCH=true - RAG_WEB_SEARCH_ENGINE=searxng - - SEARXNG_QUERY_URL=http://searxng:8080/search?q=&format=json - - WEBUI_AUTH=true + - SEARXNG_QUERY_URL=http://searxng:8080/search?q=&format=json&safesearch=0 + - RAG_WEB_SEARCH_RESULT_COUNT=5 + - RAG_WEB_SEARCH_CONCURRENT_REQUESTS=10 - ENABLE_TOOL_SERVERS=true + - WEBUI_AUTH=true depends_on: ollama: {condition: service_healthy}