From b22ff260d31f430bb289af1d0194452433410448 Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 5 Apr 2025 10:12:37 +0000 Subject: [PATCH 1/5] release search --- search.py | 10 +- static/app.css | 7 +- static/app.js | 3 + templates/search.html | 224 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 199 insertions(+), 45 deletions(-) diff --git a/search.py b/search.py index 5564899..33721e7 100644 --- a/search.py +++ b/search.py @@ -1,5 +1,6 @@ import sqlite3 from flask import Flask, render_template, request, request, jsonify +import os app = Flask(__name__) @@ -15,7 +16,6 @@ def searchcommand(): cursor = search_db.cursor() if not include_transcript: - # Simple search: all words must be in either relative_path or filename. conditions = [] params = [] for word in words: @@ -24,6 +24,7 @@ def searchcommand(): sql = "SELECT * FROM files" if conditions: sql += " WHERE " + " AND ".join(conditions) + sql += " ORDER BY hitcount DESC" cursor.execute(sql, params) raw_results = cursor.fetchall() results = [dict(row) for row in raw_results] @@ -46,13 +47,16 @@ def searchcommand(): transcript = result.get("transcript") or "" total_hits = sum(transcript.lower().count(word.lower()) for word in words) result["transcript_hits"] = total_hits - result["transcript"] = None # Remove full transcript if needed. + result["transcript"] = None results.append(result) # Sort results so files with more transcript hits are on top. results.sort(key=lambda x: x["transcript_hits"], reverse=True) + results = results[:100] return jsonify(results=results) def search(): - return render_template('search.html') + title_short = os.environ.get('TITLE_SHORT', 'Default Title') + title_long = os.environ.get('TITLE_LONG', 'Default Title') + return render_template("search.html", title_short=title_short, title_long=title_long) diff --git a/static/app.css b/static/app.css index 66219ad..c1b7c09 100644 --- a/static/app.css +++ b/static/app.css @@ -84,13 +84,13 @@ li { } /* mouse symbol for links */ -div.directory-item, li.directory-item, li.file-item, -div.directory-item a, li.directory-item a, li.file-item a { +div.directory-item, li.directory-item, li.file-item, li.link-item, +div.directory-item a, li.directory-item a, li.file-item a, li.link-item a { cursor: pointer; } /* Directory Items (in a list) */ -.directory-item { +.directory-item, .link-item { padding: 15px; } @@ -122,6 +122,7 @@ div.directory-item a, li.directory-item a, li.file-item a { /* Link Styles */ .directory-link, +.link-link, a.play-file { color: #34495e; text-decoration: none; diff --git a/static/app.js b/static/app.js index 999d7f1..20bc1d6 100644 --- a/static/app.js +++ b/static/app.js @@ -63,6 +63,9 @@ function renderContent(data) { data.directories.forEach(dir => { contentHTML += `
  • 📁 ${dir.name}
  • `; }); + if (data.breadcrumbs.length == 1) { + contentHTML += ``; + } contentHTML += ''; } } diff --git a/templates/search.html b/templates/search.html index 1bf8713..b2faf02 100644 --- a/templates/search.html +++ b/templates/search.html @@ -8,73 +8,108 @@ - Dateisuche + {{ title_short }} - - - - + + + + + + + + + + + + + + + + + + + + +
    +

    Suche

    + +
    + +
    +
    + + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    + + + +
    -
    From 149e64509c361d84510be2eb701ce56fa2c10c2b Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 5 Apr 2025 10:46:47 +0000 Subject: [PATCH 2/5] category search only in filenames --- search.py | 30 ++++++++++++++++++++++-------- templates/search.html | 7 +++---- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/search.py b/search.py index 33721e7..a62caf9 100644 --- a/search.py +++ b/search.py @@ -11,16 +11,23 @@ search_db.row_factory = sqlite3.Row def searchcommand(): query = request.form.get("query", "").strip() - include_transcript = request.form.get("includeTranscript") == "true" or request.form.get("includeTranscript") == "on" + category = request.form.get("category", "").strip() + include_transcript = request.form.get("includeTranscript") in ["true", "on"] words = [w for w in query.split() if w] cursor = search_db.cursor() - + if not include_transcript: conditions = [] params = [] + # Apply query words to relative_path and filename for word in words: conditions.append("(relative_path LIKE ? OR filename LIKE ?)") params.extend([f"%{word}%", f"%{word}%"]) + # search category in filename + if category: + conditions.append("(filename LIKE ?)") + params.extend([f"%{category}%"]) + sql = "SELECT * FROM files" if conditions: sql += " WHERE " + " AND ".join(conditions) @@ -28,30 +35,37 @@ def searchcommand(): cursor.execute(sql, params) raw_results = cursor.fetchall() results = [dict(row) for row in raw_results] + else: # Advanced search: include transcript. Count transcript hits. conditions = [] params = [] + # Apply query words only for filename and transcript for word in words: - conditions.append("(relative_path LIKE ? OR filename LIKE ? OR transcript LIKE ?)") - params.extend([f"%{word}%", f"%{word}%", f"%{word}%"]) + conditions.append("(filename LIKE ? OR transcript LIKE ?)") + params.extend([f"%{word}%", f"%{word}%"]) + # search category in filename + if category: + conditions.append("(filename LIKE ?)") + params.extend([f"%{category}%"]) + sql = "SELECT * FROM files" if conditions: sql += " WHERE " + " AND ".join(conditions) cursor.execute(sql, params) raw_results = cursor.fetchall() - + results = [] for row in raw_results: result = dict(row) transcript = result.get("transcript") or "" total_hits = sum(transcript.lower().count(word.lower()) for word in words) result["transcript_hits"] = total_hits - result["transcript"] = None + result.pop("transcript") results.append(result) - # Sort results so files with more transcript hits are on top. + # Sort so that files with more transcript hits appear first results.sort(key=lambda x: x["transcript_hits"], reverse=True) - + results = results[:100] return jsonify(results=results) diff --git a/templates/search.html b/templates/search.html index b2faf02..f69db2c 100644 --- a/templates/search.html +++ b/templates/search.html @@ -173,9 +173,6 @@ document.addEventListener('DOMContentLoaded', function() { const categoryRadio = document.querySelector('input[name="category"]:checked'); const category = categoryRadio ? categoryRadio.value : ''; - // Append the category to the search query if selected - const fullQuery = category ? query + " " + category : query; - // Prevent accidental re-selection of already selected radio buttons const radios = document.querySelectorAll('input[name="category"]'); radios.forEach(radio => { @@ -192,8 +189,10 @@ document.addEventListener('DOMContentLoaded', function() { }); // Prepare form data for the fetch request + // Send the query and the category as separate parameters. const formData = new FormData(); - formData.append('query', fullQuery); + formData.append('query', query); + formData.append('category', category); formData.append('includeTranscript', includeTranscript); fetch('/searchcommand', { From 1eb9361b8a6a526599e9b626dbd2b6d83e7c238b Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 5 Apr 2025 15:52:00 +0000 Subject: [PATCH 3/5] update paths --- check_ssh_tunnel.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/check_ssh_tunnel.sh b/check_ssh_tunnel.sh index 4277192..e0f1c0f 100755 --- a/check_ssh_tunnel.sh +++ b/check_ssh_tunnel.sh @@ -16,12 +16,14 @@ SERVER1_MOUNT_POINTS=( "/mnt/Gottesdienste Speyer" "/mnt/Besondere Gottesdienste" "/mnt/Liedersammlung" + "/mnt/Hochzeiten Speyer" "/mnt/app_share" ) SERVER1_NFS_SHARES=( "/volume1/Aufnahme-stereo/010 Gottesdienste ARCHIV" "/volume1/Aufnahme-stereo/013 Besondere Gottesdienste" "/volume1/Aufnahme-stereo/014 Liedersammlung" + "/volume1/Aufnahme-stereo/021 Hochzeiten in Speyer" "/volume1/app_share" ) @@ -35,9 +37,11 @@ SERVER2_LOCAL_PORT_BASE=3022 # Base local port for SSH tunnel (will add # Define multiple mount configurations for Server 2 SERVER2_MOUNT_POINTS=( "/mnt/Gottesdienste Schwegenheim" + "/mnt/Hochzeiten Schwegenheim" ) SERVER2_NFS_SHARES=( "/volume1/Aufnahme-stereo/010 Gottesdienste ARCHIV" + "/volume1/Aufnahme-stereo/020 Hochzeiten" ) # List of server identifiers (must match the prefix of configuration variables) From c1bc20d8768d5156423fccf3318bb5f3ed0749b4 Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 5 Apr 2025 16:01:17 +0000 Subject: [PATCH 4/5] show search results only of visible folder --- index_for_search.py | 17 ++++++++++++----- search.py | 21 +++++++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/index_for_search.py b/index_for_search.py index 0edd825..08e19d0 100644 --- a/index_for_search.py +++ b/index_for_search.py @@ -16,11 +16,12 @@ access_log_db.row_factory = sqlite3.Row def init_db(): """Initializes the database with the required schema.""" cursor = search_db.cursor() - # Create table with the new 'hitcount' column. + # Create table with the new 'hitcount' and 'basefolder' columns. cursor.execute(''' CREATE TABLE IF NOT EXISTS files ( id INTEGER PRIMARY KEY AUTOINCREMENT, relative_path TEXT, + basefolder TEXT, filename TEXT, filetype TEXT, transcript TEXT, @@ -29,12 +30,17 @@ def init_db(): ) ''') search_db.commit() - # If the table already existed, try to add the 'hitcount' column. + # If the table already existed, try to add the new columns. try: cursor.execute("ALTER TABLE files ADD COLUMN hitcount INTEGER DEFAULT 0") except sqlite3.OperationalError: # Likely the column already exists, so we ignore this error. pass + try: + cursor.execute("ALTER TABLE files ADD COLUMN basefolder TEXT") + except sqlite3.OperationalError: + # Likely the column already exists, so we ignore this error. + pass search_db.commit() def scan_dir(directory): @@ -70,13 +76,14 @@ def updatefileindex(): for config in config_data: for folder in config.get("folders", []): foldername = folder.get("foldername") + print(f"Processing folder: {foldername}") raw_folderpath = folder.get("folderpath") norm_folderpath = os.path.normpath(raw_folderpath) # Precompute the length of the base folder path (plus one for the separator) base_len = len(norm_folderpath) + 1 # Accumulate scanned file data and keys for this base folder. - scanned_files = [] # Each entry: (relative_path, filename, filetype, transcript, hitcount) + scanned_files = [] # Each entry: (relative_path, basefolder, filename, filetype, transcript, hitcount) current_keys = set() for entry in scan_dir(norm_folderpath): @@ -106,7 +113,7 @@ def updatefileindex(): # Retrieve the hit count for this file. hit_count = get_hit_count(relative_path) - scanned_files.append((relative_path, entry.name, filetype, transcript, hit_count)) + scanned_files.append((relative_path, foldername, entry.name, filetype, transcript, hit_count)) current_keys.add((relative_path, entry.name)) # Remove database entries for files under this base folder that are no longer on disk. @@ -120,7 +127,7 @@ def updatefileindex(): # Bulk write the scanned files using INSERT OR REPLACE. cursor.executemany( - "INSERT OR REPLACE INTO files (relative_path, filename, filetype, transcript, hitcount) VALUES (?, ?, ?, ?, ?)", + "INSERT OR REPLACE INTO files (relative_path, basefolder, filename, filetype, transcript, hitcount) VALUES (?, ?, ?, ?, ?, ?)", scanned_files ) diff --git a/search.py b/search.py index a62caf9..06377be 100644 --- a/search.py +++ b/search.py @@ -1,5 +1,5 @@ import sqlite3 -from flask import Flask, render_template, request, request, jsonify +from flask import Flask, render_template, request, request, jsonify, session import os app = Flask(__name__) @@ -15,6 +15,9 @@ def searchcommand(): include_transcript = request.form.get("includeTranscript") in ["true", "on"] words = [w for w in query.split() if w] cursor = search_db.cursor() + + allowed_basefolders = list(session['folders'].keys()) + print("Allowed base folders:", allowed_basefolders) if not include_transcript: conditions = [] @@ -23,10 +26,15 @@ def searchcommand(): for word in words: conditions.append("(relative_path LIKE ? OR filename LIKE ?)") params.extend([f"%{word}%", f"%{word}%"]) - # search category in filename + # Search category in filename if category: conditions.append("(filename LIKE ?)") params.extend([f"%{category}%"]) + # Only include rows where basefolder is in allowed_basefolders + if allowed_basefolders: + placeholders = ",".join("?" for _ in allowed_basefolders) + conditions.append(f"basefolder IN ({placeholders})") + params.extend(allowed_basefolders) sql = "SELECT * FROM files" if conditions: @@ -40,14 +48,19 @@ def searchcommand(): # Advanced search: include transcript. Count transcript hits. conditions = [] params = [] - # Apply query words only for filename and transcript + # Apply query words for filename and transcript for word in words: conditions.append("(filename LIKE ? OR transcript LIKE ?)") params.extend([f"%{word}%", f"%{word}%"]) - # search category in filename + # Search category in filename if category: conditions.append("(filename LIKE ?)") params.extend([f"%{category}%"]) + # Only include rows where basefolder is in allowed_basefolders + if allowed_basefolders: + placeholders = ",".join("?" for _ in allowed_basefolders) + conditions.append(f"basefolder IN ({placeholders})") + params.extend(allowed_basefolders) sql = "SELECT * FROM files" if conditions: From 9b2c79b039fa6c860f4db13c62a68391fbed62da Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 5 Apr 2025 16:15:37 +0000 Subject: [PATCH 5/5] config into config files --- .gitignore | 3 +- auth.py | 2 +- folder_config.json.example.json | 13 ------ check_ssh_tunnel.sh => mount_folder.sh | 64 ++++++++++---------------- 4 files changed, 28 insertions(+), 54 deletions(-) delete mode 100644 folder_config.json.example.json rename check_ssh_tunnel.sh => mount_folder.sh (72%) diff --git a/.gitignore b/.gitignore index 5ecf119..d125949 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,6 @@ /search.db /access_log.db /access_log.db.bak -/folder_config.json +/folder_permission_config.json +/folder_mount_config.json /.env \ No newline at end of file diff --git a/auth.py b/auth.py index 182746c..3fa9ee6 100644 --- a/auth.py +++ b/auth.py @@ -14,7 +14,7 @@ def require_secret(f): def decorated_function(*args, **kwargs): global folder_config if not folder_config: - with open('folder_config.json') as file: + with open('folder_permission_config.json') as file: folder_config = json.load(file) def is_valid(config_item, provided_secret): diff --git a/folder_config.json.example.json b/folder_config.json.example.json deleted file mode 100644 index 39256f1..0000000 --- a/folder_config.json.example.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - { - "secret" : "dev_key_f83745ft0g5rg3", - "validity" : "31.12.2030", - "folders": [ - { - "foldername": "My Folder", - "folderpath": "\\\\path\\if\\using\\windows" - } - ] - - } -] \ No newline at end of file diff --git a/check_ssh_tunnel.sh b/mount_folder.sh similarity index 72% rename from check_ssh_tunnel.sh rename to mount_folder.sh index e0f1c0f..763d2b9 100755 --- a/check_ssh_tunnel.sh +++ b/mount_folder.sh @@ -1,50 +1,36 @@ #!/bin/bash ############################################################################### -# Configuration Section +# Configuration Section (Loaded from config.json) ############################################################################### -# Server 1 Configuration -SERVER1_SSH_USER="root" -SERVER1_SSH_SERVER="bethaus-speyer.de" -SERVER1_SSH_SERVER_PORT=1122 # Remote SSH server port for Server 1 -SERVER1_REMOTE_NFS_PORT=2049 # Remote NFS server port (usually 2049) -SERVER1_LOCAL_PORT_BASE=2022 # Base local port for SSH tunnel (will add index offset) -# Define multiple mount configurations for Server 1 -# Each index corresponds to a mount point and NFS share. -SERVER1_MOUNT_POINTS=( - "/mnt/Gottesdienste Speyer" - "/mnt/Besondere Gottesdienste" - "/mnt/Liedersammlung" - "/mnt/Hochzeiten Speyer" - "/mnt/app_share" -) -SERVER1_NFS_SHARES=( - "/volume1/Aufnahme-stereo/010 Gottesdienste ARCHIV" - "/volume1/Aufnahme-stereo/013 Besondere Gottesdienste" - "/volume1/Aufnahme-stereo/014 Liedersammlung" - "/volume1/Aufnahme-stereo/021 Hochzeiten in Speyer" - "/volume1/app_share" -) +CONFIG_FILE="folder_mount_config.json" -# Server 2 Configuration -SERVER2_SSH_USER="root" -SERVER2_SSH_SERVER="bethaus-schwegenheim.de" -SERVER2_SSH_SERVER_PORT=1122 # Remote SSH server port for Server 2 -SERVER2_REMOTE_NFS_PORT=2049 # Remote NFS server port -SERVER2_LOCAL_PORT_BASE=3022 # Base local port for SSH tunnel (will add index offset) +# Ensure jq is installed before proceeding. +if ! command -v jq >/dev/null 2>&1; then + echo "[ERROR] 'jq' is not installed. Please install jq and try again." + exit 1 +fi -# Define multiple mount configurations for Server 2 -SERVER2_MOUNT_POINTS=( - "/mnt/Gottesdienste Schwegenheim" - "/mnt/Hochzeiten Schwegenheim" -) -SERVER2_NFS_SHARES=( - "/volume1/Aufnahme-stereo/010 Gottesdienste ARCHIV" - "/volume1/Aufnahme-stereo/020 Hochzeiten" -) +# Load Server 1 configuration +SERVER1_SSH_USER=$(jq -r '.SERVER1.SSH_USER' "$CONFIG_FILE") +SERVER1_SSH_SERVER=$(jq -r '.SERVER1.SSH_SERVER' "$CONFIG_FILE") +SERVER1_SSH_SERVER_PORT=$(jq -r '.SERVER1.SSH_SERVER_PORT' "$CONFIG_FILE") +SERVER1_REMOTE_NFS_PORT=$(jq -r '.SERVER1.REMOTE_NFS_PORT' "$CONFIG_FILE") +SERVER1_LOCAL_PORT_BASE=$(jq -r '.SERVER1.LOCAL_PORT_BASE' "$CONFIG_FILE") +readarray -t SERVER1_MOUNT_POINTS < <(jq -r '.SERVER1.MOUNT_POINTS[]' "$CONFIG_FILE") +readarray -t SERVER1_NFS_SHARES < <(jq -r '.SERVER1.NFS_SHARES[]' "$CONFIG_FILE") -# List of server identifiers (must match the prefix of configuration variables) +# Load Server 2 configuration +SERVER2_SSH_USER=$(jq -r '.SERVER2.SSH_USER' "$CONFIG_FILE") +SERVER2_SSH_SERVER=$(jq -r '.SERVER2.SSH_SERVER' "$CONFIG_FILE") +SERVER2_SSH_SERVER_PORT=$(jq -r '.SERVER2.SSH_SERVER_PORT' "$CONFIG_FILE") +SERVER2_REMOTE_NFS_PORT=$(jq -r '.SERVER2.REMOTE_NFS_PORT' "$CONFIG_FILE") +SERVER2_LOCAL_PORT_BASE=$(jq -r '.SERVER2.LOCAL_PORT_BASE' "$CONFIG_FILE") +readarray -t SERVER2_MOUNT_POINTS < <(jq -r '.SERVER2.MOUNT_POINTS[]' "$CONFIG_FILE") +readarray -t SERVER2_NFS_SHARES < <(jq -r '.SERVER2.NFS_SHARES[]' "$CONFIG_FILE") + +# Define list of server identifiers (must match JSON keys) SERVERS=("SERVER1" "SERVER2") ###############################################################################