From e5e357a37dd70ae34a806bdbe93a17ec01fa7f84 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 03:18:27 +0000 Subject: [PATCH 1/5] Enable Open WebUI authentication (WEBUI_AUTH=true) Disabling auth is unsafe when Open WebUI is exposed via a public FQDN. Enabling it allows proper login/signup with credentials. https://claude.ai/code/session_012gDnantBmFTWZGCiKyjazx --- laptop_full_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/laptop_full_setup.sh b/laptop_full_setup.sh index 6046dd7..490c766 100755 --- a/laptop_full_setup.sh +++ b/laptop_full_setup.sh @@ -781,7 +781,7 @@ ${OLLAMA_VOLUME_LINE} - ENABLE_RAG_WEB_SEARCH=true - RAG_WEB_SEARCH_ENGINE=searxng - SEARXNG_QUERY_URL=http://searxng:8080/search?q=&format=json - - WEBUI_AUTH=false + - WEBUI_AUTH=true - WEBUI_URL=${WEBUI_URL:-} depends_on: ollama: From cccf3afc4f2170d2e4a359f80cbf24507580120e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 03:41:11 +0000 Subject: [PATCH 2/5] Enable tool servers and auth in Open WebUI config - WEBUI_AUTH=true: require login (auth was disabled by default) - ENABLE_TOOL_SERVERS=true: unlocks the MCP/OpenAPI tool server UI https://claude.ai/code/session_012gDnantBmFTWZGCiKyjazx --- local-ai-setup.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/local-ai-setup.sh b/local-ai-setup.sh index 263be2c..92d6a45 100755 --- a/local-ai-setup.sh +++ b/local-ai-setup.sh @@ -554,7 +554,8 @@ services: - ENABLE_RAG_WEB_SEARCH=true - RAG_WEB_SEARCH_ENGINE=searxng - SEARXNG_QUERY_URL=http://searxng:8080/search?q=&format=json - - WEBUI_AUTH=false + - WEBUI_AUTH=true + - ENABLE_TOOL_SERVERS=true depends_on: ollama: {condition: service_healthy} From bab3f113a25a433d84aa8bd999c34a96001d287e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 04:38:03 +0000 Subject: [PATCH 3/5] add SearXNG safe-search script and improve OWUI search env vars - New configure-searxng-safesearch.sh: set strict/moderate/none level, disable engines that can't enforce the chosen level (torrent sites, mojeek, baidu, yandex, nvidious/piped/peertube video frontends), updates &safesearch= in SEARXNG_QUERY_URL, and restarts searxng - Add RAG_WEB_SEARCH_RESULT_COUNT=5 and RAG_WEB_SEARCH_CONCURRENT_REQUESTS=10 to Open WebUI env in both setup scripts - Add &safesearch=0 to SEARXNG_QUERY_URL so the script can find/replace it - Sync ENABLE_TOOL_SERVERS=true into laptop_full_setup.sh (was missing) https://claude.ai/code/session_012gDnantBmFTWZGCiKyjazx --- configure-searxng-safesearch.sh | 136 ++++++++++++++++++++++++++++++++ laptop_full_setup.sh | 5 +- local-ai-setup.sh | 6 +- 3 files changed, 144 insertions(+), 3 deletions(-) create mode 100755 configure-searxng-safesearch.sh 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} From 9bcef897194932ab5cd4c6ff38549762197cbf55 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 04:49:50 +0000 Subject: [PATCH 4/5] add interactive SearXNG safe-search prompts and category/engine controls Setup (both scripts): - Q6 asks safe-search level: none / moderate / strict - Then asks which categories to disable: videos images news science social - Then asks which engines to disable by name (duckduckgo, bing, etc.) - Choices flow into SEARXNG_QUERY_URL &safesearch=N in docker-compose.yml - SearXNG summary line added to laptop_full_setup.sh install plan configure-searxng-safesearch.sh: - Full rewrite: --disable-categories, --disable-engines, --enable-engines - Category maps: videos / images / news / science / social engine lists - --enable-engines overrides auto-disables (e.g. keep yandex on strict) - Preserves existing secret key on update - Creates settings.yml from scratch if missing (safe for setup use) - Help flag (-h/--help) https://claude.ai/code/session_012gDnantBmFTWZGCiKyjazx --- configure-searxng-safesearch.sh | 237 +++++++++++++++++++++----------- laptop_full_setup.sh | 60 ++++++-- local-ai-setup.sh | 45 ++++-- 3 files changed, 233 insertions(+), 109 deletions(-) diff --git a/configure-searxng-safesearch.sh b/configure-searxng-safesearch.sh index 90f77da..c7bcd52 100755 --- a/configure-searxng-safesearch.sh +++ b/configure-searxng-safesearch.sh @@ -1,17 +1,21 @@ #!/usr/bin/env bash # configure-searxng-safesearch.sh -# Set SearXNG safe-search level and disable engines that can't enforce it. +# Set SearXNG safe-search level; optionally disable categories or engines. # # 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) +# ./configure-searxng-safesearch.sh [strict|moderate|none] [OPTIONS] # -# 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 +# Options: +# --disable-categories cat1,cat2 videos images news science social +# --disable-engines eng1,eng2 e.g. duckduckgo,bing,yandex +# --enable-engines eng1,eng2 re-enable engines auto-disabled by level +# +# Examples: +# ./configure-searxng-safesearch.sh strict +# ./configure-searxng-safesearch.sh moderate --disable-categories videos,images +# ./configure-searxng-safesearch.sh none --disable-engines bing,duckduckgo +# ./configure-searxng-safesearch.sh strict --disable-categories videos \ +# --disable-engines bing --enable-engines yandex set -euo pipefail @@ -19,104 +23,177 @@ set -euo pipefail red() { printf '\e[31m%s\e[0m\n' "$*"; } grn() { printf '\e[32m%s\e[0m\n' "$*"; } blu() { printf '\e[34m%s\e[0m\n' "$*"; } +yel() { printf '\e[33m%s\e[0m\n' "$*"; } die() { red "ERROR: $*"; exit 1; } ok() { grn " ✓ $*"; } info() { blu " → $*"; } +warn() { yel " ! $*"; } -LEVEL="${1:-moderate}" +# ── Defaults ────────────────────────────────────────────────────────────────── +LEVEL="moderate" +DISABLE_CATS="" +DISABLE_ENGINES_EXTRA="" +ENABLE_ENGINES_EXTRA="" BASE="${BASE:-$HOME/docker/ai-stack}" + +# ── Parse arguments ─────────────────────────────────────────────────────────── +while [[ $# -gt 0 ]]; do + case "$1" in + strict|moderate|none) LEVEL="$1"; shift ;; + --disable-categories) DISABLE_CATS="$2"; shift 2 ;; + --disable-engines) DISABLE_ENGINES_EXTRA="$2"; shift 2 ;; + --enable-engines) ENABLE_ENGINES_EXTRA="$2"; shift 2 ;; + --base) BASE="$2"; shift 2 ;; + -h|--help) + sed -n '2,20p' "$0" | sed 's/^# \?//' + exit 0 + ;; + *) die "Unknown argument: $1 (run with --help)" ;; + esac +done + 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 +# Ensure settings directory exists +mkdir -p "$(dirname "$SETTINGS")" -[[ -f "$SETTINGS" ]] || die "SearXNG settings not found: $SETTINGS" - -# ── Map level → SearXNG integer ─────────────────────────────────────────────── +# ── Level → integer ─────────────────────────────────────────────────────────── case "$LEVEL" in none) SAFE_INT=0 ;; moderate) SAFE_INT=1 ;; strict) SAFE_INT=2 ;; esac -info "Setting safe_search = $LEVEL (${SAFE_INT})" +info "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 +# ── Engines with no safe-search support ─────────────────────────────────────── +# Auto-disabled when level is moderate or strict. 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 + # Torrent / P2P — no filtering possible + "1337x" "piratebay" "nyaa" "torrentz" "kickass torrents" + # Web engines without safe-search API + "mojeek" "naver" "baidu" + # Yandex: parameter exists but not reliably enforced for non-Russian queries + "yandex" + # Video frontends — no safe-search passthrough + "invidious" "piped" "peertube" "sepiasearch" ) -ALL_SKIP=("${TORRENT_ENGINES[@]}" "${NO_SAFESEARCH_ENGINES[@]}") +# ── Category → engine lists ─────────────────────────────────────────────────── +VIDEOS_ENGINES=( + "youtube" "invidious" "piped" "peertube" "sepiasearch" + "dailymotion" "vimeo" + "bing videos" "duckduckgo videos" "google videos" +) +IMAGES_ENGINES=( + "google images" "bing images" "duckduckgo images" + "brave images" "qwant images" + "flickr" "unsplash" "imgur" "deviantart" "openverse" +) +NEWS_ENGINES=( + "google news" "bing news" "duckduckgo news" "brave news" "qwant news" +) +SCIENCE_ENGINES=( + "arxiv" "semantic scholar" "pubmed" "crossref" "base" +) +SOCIAL_ENGINES=( + "reddit" "lemmy" "mastodon" +) -# ── Extract existing secret key (avoid regenerating on every run) ───────────── +# ── Build disable/enable maps ───────────────────────────────────────────────── +declare -A DISABLE_MAP # engine → 1 +declare -A ENABLE_MAP # engine → 1 (overrides everything) + +# Parse --enable-engines +if [[ -n "$ENABLE_ENGINES_EXTRA" ]]; then + IFS=',' read -ra _engs <<< "$ENABLE_ENGINES_EXTRA" + for e in "${_engs[@]}"; do + e="${e#"${e%%[![:space:]]*}"}"; e="${e%"${e##*[![:space:]]}"}" # trim + [[ -n "$e" ]] && ENABLE_MAP["$e"]=1 + done +fi + +# Helper: add to DISABLE_MAP unless explicitly re-enabled +mark_disabled() { + local eng="$1" + [[ -n "${ENABLE_MAP[$eng]+x}" ]] && return # user said keep it + DISABLE_MAP["$eng"]=1 +} + +# Auto-disable no-safesearch engines for moderate/strict +if [[ "$LEVEL" != "none" ]]; then + for eng in "${NO_SAFESEARCH_ENGINES[@]}"; do + mark_disabled "$eng" + done +fi + +# Category disables +if [[ -n "$DISABLE_CATS" ]]; then + IFS=',' read -ra _cats <<< "$DISABLE_CATS" + for cat in "${_cats[@]}"; do + cat="${cat#"${cat%%[![:space:]]*}"}"; cat="${cat%"${cat##*[![:space:]]}"}" + cat="${cat,,}" + case "$cat" in + videos) for e in "${VIDEOS_ENGINES[@]}"; do mark_disabled "$e"; done ;; + images) for e in "${IMAGES_ENGINES[@]}"; do mark_disabled "$e"; done ;; + news) for e in "${NEWS_ENGINES[@]}"; do mark_disabled "$e"; done ;; + science) for e in "${SCIENCE_ENGINES[@]}"; do mark_disabled "$e"; done ;; + social) for e in "${SOCIAL_ENGINES[@]}"; do mark_disabled "$e"; done ;; + "") ;; + *) warn "Unknown category '$cat' — valid: videos images news science social" ;; + esac + done +fi + +# Extra engine disables +if [[ -n "$DISABLE_ENGINES_EXTRA" ]]; then + IFS=',' read -ra _engs <<< "$DISABLE_ENGINES_EXTRA" + for e in "${_engs[@]}"; do + e="${e#"${e%%[![:space:]]*}"}"; e="${e%"${e##*[![:space:]]}"}" + [[ -n "$e" ]] && mark_disabled "$e" + done +fi + +# ── Preserve existing secret key ───────────────────────────────────────────── 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 +# ── Build engine override block ─────────────────────────────────────────────── +ENGINE_BLOCK="" +for eng in "${!DISABLE_MAP[@]}"; do + ENGINE_BLOCK+=" - name: ${eng}\n disabled: true\n" +done +for eng in "${!ENABLE_MAP[@]}"; do + ENGINE_BLOCK+=" - name: ${eng}\n disabled: false\n" +done # ── 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 +{ + printf 'use_default_settings: true\n' + printf 'general:\n instance_name: "Local Search"\n' + printf 'server:\n secret_key: "%s"\n limiter: false\n' "$SECRET_KEY" + printf 'search:\n safe_search: %d\n default_lang: "en"\n formats: [html, json]\n' "$SAFE_INT" + if [[ -n "$ENGINE_BLOCK" ]]; then + printf 'engines:\n' + printf '%b' "$ENGINE_BLOCK" + fi +} > "$SETTINGS" ok "Updated settings.yml (safe_search: $SAFE_INT)" +if [[ ${#DISABLE_MAP[@]} -gt 0 ]]; then + info "Disabled (${#DISABLE_MAP[@]}): $(printf '%s, ' "${!DISABLE_MAP[@]}" | sed 's/, $//')" +fi +if [[ ${#ENABLE_MAP[@]} -gt 0 ]]; then + info "Re-enabled: $(printf '%s, ' "${!ENABLE_MAP[@]}" | sed 's/, $//')" +fi # ── 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" \ + "s|(SEARXNG_QUERY_URL=http://searxng:[0-9]+/search\?[^&[:space:]]*)(&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" + ok "Updated SEARXNG_QUERY_URL (&safesearch=${SAFE_INT})" fi # ── Restart SearXNG ─────────────────────────────────────────────────────────── @@ -125,12 +202,10 @@ if docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^searxng$'; then docker restart searxng ok "SearXNG restarted" else - info "SearXNG is not running — changes take effect on next start" + info "SearXNG 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 +grn "Done — safe search: $LEVEL" +[[ "$LEVEL" != "none" ]] && \ + info "Engines skipped (can't enforce '$LEVEL'): ${#DISABLE_MAP[@]} total" diff --git a/laptop_full_setup.sh b/laptop_full_setup.sh index a1a1033..befc92d 100755 --- a/laptop_full_setup.sh +++ b/laptop_full_setup.sh @@ -493,6 +493,37 @@ if $INSTALL_AI; then [[ "${DO_PULL,,}" != "n" ]] && PULL_MODELS=true fi +# ── Q6: SearXNG safe-search ─────────────────────────────────────────────────── +SEARXNG_SAFE_LEVEL="none" +SEARXNG_SAFE_INT=0 +SEARXNG_DISABLE_CATS="" +SEARXNG_DISABLE_ENGINES="" + +if $INSTALL_AI && $SVC_SEARXNG; then + echo "" + echo -e " ${BOLD}[6/6] SearXNG safe-search${NC}" + echo " 0) None — all results, no filtering (default)" + echo " 1) Moderate — filter explicit content" + echo " 2) Strict — block all explicit content" + echo "" + read -rp " Choice [0]: " _SAFE_PICK + case "${_SAFE_PICK:-0}" in + 1) SEARXNG_SAFE_LEVEL="moderate"; SEARXNG_SAFE_INT=1 ;; + 2) SEARXNG_SAFE_LEVEL="strict"; SEARXNG_SAFE_INT=2 ;; + *) SEARXNG_SAFE_LEVEL="none"; SEARXNG_SAFE_INT=0 ;; + esac + + echo "" + echo " Disable search categories? (Enter to keep all)" + echo " Options: videos images news science social" + read -rp " Categories (space-separated, Enter to skip): " SEARXNG_DISABLE_CATS + + echo "" + echo " Disable specific engines? (Enter to keep defaults)" + echo " Web: google bing duckduckgo brave startpage qwant yahoo" + read -rp " Engines (space-separated, Enter to skip): " SEARXNG_DISABLE_ENGINES +fi + # ── Summary ─────────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" @@ -521,6 +552,12 @@ if $INSTALL_AI; then fi fi $SVC_KIWIX && echo " ✓ Kiwix ZIMs → $KIWIX_DIR" +if $INSTALL_AI && $SVC_SEARXNG; then + _sx="safe_search: $SEARXNG_SAFE_LEVEL" + [[ -n "$SEARXNG_DISABLE_CATS" ]] && _sx+=" | disabled cats: $SEARXNG_DISABLE_CATS" + [[ -n "$SEARXNG_DISABLE_ENGINES" ]] && _sx+=" | disabled engines: $SEARXNG_DISABLE_ENGINES" + echo " ✓ SearXNG → $_sx" +fi $PULL_MODELS && echo " ✓ Pull models : $EMBED_MODEL + $FAST_MODEL + $CHAT_MODEL + $CODE_MODEL${REASON_MODEL:+ + $REASON_MODEL}" [[ "$ZIM_CHOICE" == "1" ]] && echo " ✓ Download all ZIMs in background (~130GB)" [[ "$ZIM_CHOICE" == "2" ]] && echo " ✓ Select ZIMs to download (prompted after stack starts)" @@ -674,23 +711,16 @@ ok "mcp_requirements.txt" fi # INSTALL_AI # ============================================================================= -if $INSTALL_AI; then +if $INSTALL_AI && $SVC_SEARXNG; then section "SearXNG Config" # ============================================================================= -write_if_new "$BASE/searxng/settings.yml" << SEARXNG -use_default_settings: true -general: - instance_name: "Local Search" -server: - secret_key: "$(openssl rand -hex 32)" - limiter: false -search: - safe_search: 0 - default_lang: "en" - formats: [html, json] -SEARXNG +mkdir -p "$BASE/searxng" +_SXARGS=("$SEARXNG_SAFE_LEVEL") +[[ -n "$SEARXNG_DISABLE_CATS" ]] && _SXARGS+=(--disable-categories "${SEARXNG_DISABLE_CATS// /,}") +[[ -n "$SEARXNG_DISABLE_ENGINES" ]] && _SXARGS+=(--disable-engines "${SEARXNG_DISABLE_ENGINES// /,}") +BASE="$BASE" bash "$SCRIPT_DIR/configure-searxng-safesearch.sh" "${_SXARGS[@]}" -fi # INSTALL_AI +fi # INSTALL_AI && SVC_SEARXNG # ============================================================================= section ".env File" @@ -780,7 +810,7 @@ ${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&safesearch=0 + - SEARXNG_QUERY_URL=http://searxng:8080/search?q=&format=json&safesearch=${SEARXNG_SAFE_INT} - RAG_WEB_SEARCH_RESULT_COUNT=5 - RAG_WEB_SEARCH_CONCURRENT_REQUESTS=10 - ENABLE_TOOL_SERVERS=true diff --git a/local-ai-setup.sh b/local-ai-setup.sh index 1a5363c..91b5b46 100755 --- a/local-ai-setup.sh +++ b/local-ai-setup.sh @@ -14,6 +14,7 @@ FORCE=false; NO_PULL=false for a in "$@"; do [[ "$a" == "--force" ]] && FORCE=true; [[ "$a" == "--no-pull" ]] && NO_PULL=true; done BASE="$HOME/docker/ai-stack" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LOCAL_IP=$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'src \K\S+' || hostname -I | awk '{print $1}') [[ -z "$LOCAL_IP" ]] && read -rp "Enter LAN IP: " LOCAL_IP @@ -43,6 +44,31 @@ info "Base : $BASE" info "IP : $LOCAL_IP" info "GPU : ${VRAM_GB}GB VRAM → $TIER" +# ── SearXNG safe-search ─────────────────────────────────────────────────────── +echo "" +echo " SearXNG safe-search level:" +echo " 0) None — all results, no filtering (default)" +echo " 1) Moderate — filter explicit content" +echo " 2) Strict — block all explicit content" +echo "" +read -rp " Choice [0]: " _SAFE_PICK +case "${_SAFE_PICK:-0}" in + 1) SEARXNG_SAFE_LEVEL="moderate"; SEARXNG_SAFE_INT=1 ;; + 2) SEARXNG_SAFE_LEVEL="strict"; SEARXNG_SAFE_INT=2 ;; + *) SEARXNG_SAFE_LEVEL="none"; SEARXNG_SAFE_INT=0 ;; +esac + +echo "" +echo " Disable search categories? (Enter to keep all)" +echo " Options: videos images news science social" +read -rp " Categories (space-separated, Enter to skip): " SEARXNG_DISABLE_CATS + +echo "" +echo " Disable specific engines? (Enter to keep defaults)" +echo " Web: google bing duckduckgo brave startpage qwant yahoo" +read -rp " Engines (space-separated, Enter to skip): " SEARXNG_DISABLE_ENGINES +echo "" + write_if_new() { local dest="$1"; local body; body=$(cat) if [[ ! -f "$dest" ]] || $FORCE; then @@ -481,18 +507,11 @@ ok "requirements.txt + mcp_requirements.txt" # ============================================================================= section "SearXNG Config" # ============================================================================= -write_if_new "$BASE/searxng/settings.yml" << SEARXNG -use_default_settings: true -general: - instance_name: "Local Search" -server: - secret_key: "$(openssl rand -hex 32)" - limiter: false -search: - safe_search: 0 - default_lang: "en" - formats: [html, json] -SEARXNG +mkdir -p "$BASE/searxng" +_SXARGS=("$SEARXNG_SAFE_LEVEL") +[[ -n "$SEARXNG_DISABLE_CATS" ]] && _SXARGS+=(--disable-categories "${SEARXNG_DISABLE_CATS// /,}") +[[ -n "$SEARXNG_DISABLE_ENGINES" ]] && _SXARGS+=(--disable-engines "${SEARXNG_DISABLE_ENGINES// /,}") +BASE="$BASE" bash "$SCRIPT_DIR/configure-searxng-safesearch.sh" "${_SXARGS[@]}" # ============================================================================= section ".env (tokens — never overwritten)" @@ -553,7 +572,7 @@ 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&safesearch=0 + - SEARXNG_QUERY_URL=http://searxng:8080/search?q=&format=json&safesearch=${SEARXNG_SAFE_INT} - RAG_WEB_SEARCH_RESULT_COUNT=5 - RAG_WEB_SEARCH_CONCURRENT_REQUESTS=10 - ENABLE_TOOL_SERVERS=true From e6eaec4bd4e6621846c9f0b4a9bcff353a239970 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 05:01:50 +0000 Subject: [PATCH 5/5] replace category/engine text prompts with whiptail checklists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For both setup scripts and configure-searxng-safesearch.sh: - Five whiptail checklist menus: Web · Images · Videos · News · Science - Pre-populated ON/OFF based on the chosen safe-search level: none → all engines ON moderate/strict → no-safe-search engines (mojeek, yandex, baidu, naver, invidious, piped, peertube, sepiasearch, flickr, imgur, deviantart, dailymotion, vimeo) default OFF - User can toggle any engine independently before confirming - ESC on any menu restores defaults for that category (keeps them unchanged) - If user re-enables an auto-disabled engine, --enable-engines is passed to configure script to override the auto-disable - Text fallback for non-interactive/headless installs - Summary shows count of disabled engines instead of raw list https://claude.ai/code/session_012gDnantBmFTWZGCiKyjazx --- laptop_full_setup.sh | 149 +++++++++++++++++++++++++++++++++++++++---- local-ai-setup.sh | 138 +++++++++++++++++++++++++++++++++++---- 2 files changed, 262 insertions(+), 25 deletions(-) diff --git a/laptop_full_setup.sh b/laptop_full_setup.sh index befc92d..6840215 100755 --- a/laptop_full_setup.sh +++ b/laptop_full_setup.sh @@ -493,11 +493,11 @@ if $INSTALL_AI; then [[ "${DO_PULL,,}" != "n" ]] && PULL_MODELS=true fi -# ── Q6: SearXNG safe-search ─────────────────────────────────────────────────── +# ── Q6: SearXNG safe-search & engine selection ──────────────────────────────── SEARXNG_SAFE_LEVEL="none" SEARXNG_SAFE_INT=0 -SEARXNG_DISABLE_CATS="" SEARXNG_DISABLE_ENGINES="" +SEARXNG_ENABLE_ENGINES="" if $INSTALL_AI && $SVC_SEARXNG; then echo "" @@ -513,15 +513,134 @@ if $INSTALL_AI && $SVC_SEARXNG; then *) SEARXNG_SAFE_LEVEL="none"; SEARXNG_SAFE_INT=0 ;; esac - echo "" - echo " Disable search categories? (Enter to keep all)" - echo " Options: videos images news science social" - read -rp " Categories (space-separated, Enter to skip): " SEARXNG_DISABLE_CATS + # Default ON/OFF for an engine given the chosen safe-search level + _sxst() { + [[ "$SEARXNG_SAFE_LEVEL" == "none" ]] && echo ON && return + case "$1" in + mojeek|yandex|baidu|naver|invidious|piped|peertube|sepiasearch|\ + dailymotion|vimeo|imgur|deviantart|flickr) echo OFF ;; + *) echo ON ;; + esac + } - echo "" - echo " Disable specific engines? (Enter to keep defaults)" - echo " Web: google bing duckduckgo brave startpage qwant yahoo" - read -rp " Engines (space-separated, Enter to skip): " SEARXNG_DISABLE_ENGINES + # Whiptail checklist — prints selected engine names (one per line). + # ESC returns the engines that were pre-checked ON (keeps defaults unchanged). + # Args: "Title" name "Desc" ON|OFF [name "Desc" ON|OFF ...] + _sxmenu() { + local _t="$1"; shift + local -a _n=() _s=() _a=(); local _i=1 + while [[ $# -ge 3 ]]; do + _n+=("$1"); _s+=("$3") + _a+=("$_i" "$(printf '%-22s %s' "$1" "$2")" "$3") + _i=$(( _i + 1 )); shift 3 + done + local _h=$(( ${#_n[@]} + 9 > 24 ? 24 : ${#_n[@]} + 9 )) + local _lh=$(( ${#_n[@]} < 14 ? ${#_n[@]} : 14 )) + local _o _j + if ! _o=$(whiptail --backtitle "SearXNG — $SEARXNG_SAFE_LEVEL" \ + --title "$_t" --checklist \ + "SPACE = toggle ENTER = confirm ESC = use defaults" \ + "$_h" 72 "$_lh" "${_a[@]}" 3>&1 1>&2 2>&3 2>/dev/null); then + for _j in "${!_n[@]}"; do + [[ "${_s[$_j]}" == "ON" ]] && printf '%s\n' "${_n[$_j]}" + done + return + fi + for _id in $_o; do _id="${_id//\"/}"; printf '%s\n' "${_n[$(( _id-1 ))]}"; done + } + + # Engines auto-disabled by configure script for moderate/strict. + # If user re-enables one in the menu we pass --enable-engines to override. + _SX_AUTOFF=(mojeek yandex baidu naver invidious piped peertube sepiasearch) + + if command -v whiptail &>/dev/null && [[ -t 0 ]]; then + _SX_W=$(_sxmenu "Web Search Engines" \ + "google" "Google" ON \ + "bing" "Microsoft Bing" ON \ + "duckduckgo" "DuckDuckGo" ON \ + "brave" "Brave Search" ON \ + "startpage" "Startpage (Google proxy)" ON \ + "qwant" "Qwant" ON \ + "yahoo" "Yahoo" ON \ + "ecosia" "Ecosia" ON \ + "mojeek" "Mojeek [no safe-search]" "$(_sxst mojeek)" \ + "yandex" "Yandex [unreliable filter]" "$(_sxst yandex)" \ + "baidu" "Baidu [no safe-search]" "$(_sxst baidu)" \ + "naver" "Naver [no safe-search]" "$(_sxst naver)") + + _SX_I=$(_sxmenu "Image Search Engines" \ + "google images" "Google Images" ON \ + "bing images" "Bing Images" ON \ + "duckduckgo images" "DuckDuckGo Images" ON \ + "brave images" "Brave Images" ON \ + "qwant images" "Qwant Images" ON \ + "openverse" "Openverse (open license)" ON \ + "unsplash" "Unsplash (stock photos)" ON \ + "flickr" "Flickr [no safe-search]" "$(_sxst flickr)" \ + "imgur" "Imgur [no safe-search]" "$(_sxst imgur)" \ + "deviantart" "DeviantArt [no filter]" "$(_sxst deviantart)") + + _SX_V=$(_sxmenu "Video Search Engines" \ + "youtube" "YouTube (via Google)" ON \ + "bing videos" "Bing Videos" ON \ + "duckduckgo videos" "DuckDuckGo Videos" ON \ + "google videos" "Google Videos" ON \ + "invidious" "Invidious [no filter]" "$(_sxst invidious)" \ + "piped" "Piped [no filter]" "$(_sxst piped)" \ + "peertube" "PeerTube [no filter]" "$(_sxst peertube)" \ + "sepiasearch" "SepiaSearch [no filter]" "$(_sxst sepiasearch)" \ + "dailymotion" "Dailymotion [no filter]" "$(_sxst dailymotion)" \ + "vimeo" "Vimeo [no filter]" "$(_sxst vimeo)") + + _SX_N=$(_sxmenu "News Search Engines" \ + "google news" "Google News" ON \ + "bing news" "Bing News" ON \ + "duckduckgo news" "DuckDuckGo News" ON \ + "brave news" "Brave News" ON \ + "qwant news" "Qwant News" ON) + + _SX_S=$(_sxmenu "Science / Academic" \ + "arxiv" "arXiv (preprints)" ON \ + "semantic scholar" "Semantic Scholar" ON \ + "pubmed" "PubMed (medical)" ON \ + "crossref" "Crossref (DOI/papers)" ON \ + "base" "BASE (open access)" ON) + + _SX_ALL=( + "google" "bing" "duckduckgo" "brave" "startpage" "qwant" "yahoo" "ecosia" + "mojeek" "yandex" "baidu" "naver" + "google images" "bing images" "duckduckgo images" "brave images" "qwant images" + "openverse" "unsplash" "flickr" "imgur" "deviantart" + "youtube" "bing videos" "duckduckgo videos" "google videos" + "invidious" "piped" "peertube" "sepiasearch" "dailymotion" "vimeo" + "google news" "bing news" "duckduckgo news" "brave news" "qwant news" + "arxiv" "semantic scholar" "pubmed" "crossref" "base" + ) + _SX_SEL=$(printf '%s\n' "$_SX_W" "$_SX_I" "$_SX_V" "$_SX_N" "$_SX_S") + + for _e in "${_SX_ALL[@]}"; do + if printf '%s\n' "$_SX_SEL" | grep -qxF "$_e"; then + # Selected — if auto-disabled by level, pass --enable-engines to override + for _ao in "${_SX_AUTOFF[@]}"; do + if [[ "$_e" == "$_ao" ]]; then + SEARXNG_ENABLE_ENGINES+="${SEARXNG_ENABLE_ENGINES:+,}${_e}" + break + fi + done + else + SEARXNG_DISABLE_ENGINES+="${SEARXNG_DISABLE_ENGINES:+,}${_e}" + fi + done + else + # Fallback: no whiptail / non-interactive terminal + echo "" + echo " (whiptail not available — text entry)" + echo " Web: google bing duckduckgo brave startpage qwant yahoo" + echo " Imgs: google_images bing_images duckduckgo_images flickr imgur" + echo " Vids: youtube invidious piped peertube dailymotion vimeo" + read -rp " Engines to disable (space-separated, Enter for defaults): " _ENGS + SEARXNG_DISABLE_ENGINES="${_ENGS// /,}" + fi fi # ── Summary ─────────────────────────────────────────────────────────────────── @@ -554,8 +673,10 @@ fi $SVC_KIWIX && echo " ✓ Kiwix ZIMs → $KIWIX_DIR" if $INSTALL_AI && $SVC_SEARXNG; then _sx="safe_search: $SEARXNG_SAFE_LEVEL" - [[ -n "$SEARXNG_DISABLE_CATS" ]] && _sx+=" | disabled cats: $SEARXNG_DISABLE_CATS" - [[ -n "$SEARXNG_DISABLE_ENGINES" ]] && _sx+=" | disabled engines: $SEARXNG_DISABLE_ENGINES" + if [[ -n "$SEARXNG_DISABLE_ENGINES" ]]; then + _ndis=$(tr ',' '\n' <<< "$SEARXNG_DISABLE_ENGINES" | grep -c .) + _sx+=" | ${_ndis} engines disabled" + fi echo " ✓ SearXNG → $_sx" fi $PULL_MODELS && echo " ✓ Pull models : $EMBED_MODEL + $FAST_MODEL + $CHAT_MODEL + $CODE_MODEL${REASON_MODEL:+ + $REASON_MODEL}" @@ -716,8 +837,8 @@ section "SearXNG Config" # ============================================================================= mkdir -p "$BASE/searxng" _SXARGS=("$SEARXNG_SAFE_LEVEL") -[[ -n "$SEARXNG_DISABLE_CATS" ]] && _SXARGS+=(--disable-categories "${SEARXNG_DISABLE_CATS// /,}") -[[ -n "$SEARXNG_DISABLE_ENGINES" ]] && _SXARGS+=(--disable-engines "${SEARXNG_DISABLE_ENGINES// /,}") +[[ -n "$SEARXNG_DISABLE_ENGINES" ]] && _SXARGS+=(--disable-engines "$SEARXNG_DISABLE_ENGINES") +[[ -n "$SEARXNG_ENABLE_ENGINES" ]] && _SXARGS+=(--enable-engines "$SEARXNG_ENABLE_ENGINES") BASE="$BASE" bash "$SCRIPT_DIR/configure-searxng-safesearch.sh" "${_SXARGS[@]}" fi # INSTALL_AI && SVC_SEARXNG diff --git a/local-ai-setup.sh b/local-ai-setup.sh index 91b5b46..f161ee9 100755 --- a/local-ai-setup.sh +++ b/local-ai-setup.sh @@ -44,7 +44,9 @@ info "Base : $BASE" info "IP : $LOCAL_IP" info "GPU : ${VRAM_GB}GB VRAM → $TIER" -# ── SearXNG safe-search ─────────────────────────────────────────────────────── +# ── SearXNG safe-search & engine selection ──────────────────────────────────── +SEARXNG_DISABLE_ENGINES="" +SEARXNG_ENABLE_ENGINES="" echo "" echo " SearXNG safe-search level:" echo " 0) None — all results, no filtering (default)" @@ -58,15 +60,129 @@ case "${_SAFE_PICK:-0}" in *) SEARXNG_SAFE_LEVEL="none"; SEARXNG_SAFE_INT=0 ;; esac -echo "" -echo " Disable search categories? (Enter to keep all)" -echo " Options: videos images news science social" -read -rp " Categories (space-separated, Enter to skip): " SEARXNG_DISABLE_CATS +# Default ON/OFF for an engine given the chosen safe-search level +_sxst() { + [[ "$SEARXNG_SAFE_LEVEL" == "none" ]] && echo ON && return + case "$1" in + mojeek|yandex|baidu|naver|invidious|piped|peertube|sepiasearch|\ + dailymotion|vimeo|imgur|deviantart|flickr) echo OFF ;; + *) echo ON ;; + esac +} -echo "" -echo " Disable specific engines? (Enter to keep defaults)" -echo " Web: google bing duckduckgo brave startpage qwant yahoo" -read -rp " Engines (space-separated, Enter to skip): " SEARXNG_DISABLE_ENGINES +# Whiptail checklist — prints selected engine names (one per line). +# ESC returns the engines that were pre-checked ON (keeps defaults unchanged). +_sxmenu() { + local _t="$1"; shift + local -a _n=() _s=() _a=(); local _i=1 + while [[ $# -ge 3 ]]; do + _n+=("$1"); _s+=("$3") + _a+=("$_i" "$(printf '%-22s %s' "$1" "$2")" "$3") + _i=$(( _i + 1 )); shift 3 + done + local _h=$(( ${#_n[@]} + 9 > 24 ? 24 : ${#_n[@]} + 9 )) + local _lh=$(( ${#_n[@]} < 14 ? ${#_n[@]} : 14 )) + local _o _j + if ! _o=$(whiptail --backtitle "SearXNG — $SEARXNG_SAFE_LEVEL" \ + --title "$_t" --checklist \ + "SPACE = toggle ENTER = confirm ESC = use defaults" \ + "$_h" 72 "$_lh" "${_a[@]}" 3>&1 1>&2 2>&3 2>/dev/null); then + for _j in "${!_n[@]}"; do + [[ "${_s[$_j]}" == "ON" ]] && printf '%s\n' "${_n[$_j]}" + done + return + fi + for _id in $_o; do _id="${_id//\"/}"; printf '%s\n' "${_n[$(( _id-1 ))]}"; done +} + +_SX_AUTOFF=(mojeek yandex baidu naver invidious piped peertube sepiasearch) + +if command -v whiptail &>/dev/null && [[ -t 0 ]]; then + _SX_W=$(_sxmenu "Web Search Engines" \ + "google" "Google" ON \ + "bing" "Microsoft Bing" ON \ + "duckduckgo" "DuckDuckGo" ON \ + "brave" "Brave Search" ON \ + "startpage" "Startpage (Google proxy)" ON \ + "qwant" "Qwant" ON \ + "yahoo" "Yahoo" ON \ + "ecosia" "Ecosia" ON \ + "mojeek" "Mojeek [no safe-search]" "$(_sxst mojeek)" \ + "yandex" "Yandex [unreliable filter]" "$(_sxst yandex)" \ + "baidu" "Baidu [no safe-search]" "$(_sxst baidu)" \ + "naver" "Naver [no safe-search]" "$(_sxst naver)") + + _SX_I=$(_sxmenu "Image Search Engines" \ + "google images" "Google Images" ON \ + "bing images" "Bing Images" ON \ + "duckduckgo images" "DuckDuckGo Images" ON \ + "brave images" "Brave Images" ON \ + "qwant images" "Qwant Images" ON \ + "openverse" "Openverse (open license)" ON \ + "unsplash" "Unsplash (stock photos)" ON \ + "flickr" "Flickr [no safe-search]" "$(_sxst flickr)" \ + "imgur" "Imgur [no safe-search]" "$(_sxst imgur)" \ + "deviantart" "DeviantArt [no filter]" "$(_sxst deviantart)") + + _SX_V=$(_sxmenu "Video Search Engines" \ + "youtube" "YouTube (via Google)" ON \ + "bing videos" "Bing Videos" ON \ + "duckduckgo videos" "DuckDuckGo Videos" ON \ + "google videos" "Google Videos" ON \ + "invidious" "Invidious [no filter]" "$(_sxst invidious)" \ + "piped" "Piped [no filter]" "$(_sxst piped)" \ + "peertube" "PeerTube [no filter]" "$(_sxst peertube)" \ + "sepiasearch" "SepiaSearch [no filter]" "$(_sxst sepiasearch)" \ + "dailymotion" "Dailymotion [no filter]" "$(_sxst dailymotion)" \ + "vimeo" "Vimeo [no filter]" "$(_sxst vimeo)") + + _SX_N=$(_sxmenu "News Search Engines" \ + "google news" "Google News" ON \ + "bing news" "Bing News" ON \ + "duckduckgo news" "DuckDuckGo News" ON \ + "brave news" "Brave News" ON \ + "qwant news" "Qwant News" ON) + + _SX_S=$(_sxmenu "Science / Academic" \ + "arxiv" "arXiv (preprints)" ON \ + "semantic scholar" "Semantic Scholar" ON \ + "pubmed" "PubMed (medical)" ON \ + "crossref" "Crossref (DOI/papers)" ON \ + "base" "BASE (open access)" ON) + + _SX_ALL=( + "google" "bing" "duckduckgo" "brave" "startpage" "qwant" "yahoo" "ecosia" + "mojeek" "yandex" "baidu" "naver" + "google images" "bing images" "duckduckgo images" "brave images" "qwant images" + "openverse" "unsplash" "flickr" "imgur" "deviantart" + "youtube" "bing videos" "duckduckgo videos" "google videos" + "invidious" "piped" "peertube" "sepiasearch" "dailymotion" "vimeo" + "google news" "bing news" "duckduckgo news" "brave news" "qwant news" + "arxiv" "semantic scholar" "pubmed" "crossref" "base" + ) + _SX_SEL=$(printf '%s\n' "$_SX_W" "$_SX_I" "$_SX_V" "$_SX_N" "$_SX_S") + + for _e in "${_SX_ALL[@]}"; do + if printf '%s\n' "$_SX_SEL" | grep -qxF "$_e"; then + for _ao in "${_SX_AUTOFF[@]}"; do + if [[ "$_e" == "$_ao" ]]; then + SEARXNG_ENABLE_ENGINES+="${SEARXNG_ENABLE_ENGINES:+,}${_e}" + break + fi + done + else + SEARXNG_DISABLE_ENGINES+="${SEARXNG_DISABLE_ENGINES:+,}${_e}" + fi + done +else + echo "" + echo " (whiptail not available — text entry)" + echo " Web: google bing duckduckgo brave startpage qwant yahoo" + echo " Imgs: google_images bing_images duckduckgo_images flickr imgur" + echo " Vids: youtube invidious piped peertube dailymotion vimeo" + read -rp " Engines to disable (space-separated, Enter for defaults): " _ENGS + SEARXNG_DISABLE_ENGINES="${_ENGS// /,}" +fi echo "" write_if_new() { @@ -509,8 +625,8 @@ section "SearXNG Config" # ============================================================================= mkdir -p "$BASE/searxng" _SXARGS=("$SEARXNG_SAFE_LEVEL") -[[ -n "$SEARXNG_DISABLE_CATS" ]] && _SXARGS+=(--disable-categories "${SEARXNG_DISABLE_CATS// /,}") -[[ -n "$SEARXNG_DISABLE_ENGINES" ]] && _SXARGS+=(--disable-engines "${SEARXNG_DISABLE_ENGINES// /,}") +[[ -n "$SEARXNG_DISABLE_ENGINES" ]] && _SXARGS+=(--disable-engines "$SEARXNG_DISABLE_ENGINES") +[[ -n "$SEARXNG_ENABLE_ENGINES" ]] && _SXARGS+=(--enable-engines "$SEARXNG_ENABLE_ENGINES") BASE="$BASE" bash "$SCRIPT_DIR/configure-searxng-safesearch.sh" "${_SXARGS[@]}" # =============================================================================