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/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/check_ssh_tunnel.sh b/mount_folder.sh similarity index 74% rename from check_ssh_tunnel.sh rename to mount_folder.sh index 4277192..763d2b9 100755 --- a/check_ssh_tunnel.sh +++ b/mount_folder.sh @@ -1,46 +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/app_share" -) -SERVER1_NFS_SHARES=( - "/volume1/Aufnahme-stereo/010 Gottesdienste ARCHIV" - "/volume1/Aufnahme-stereo/013 Besondere Gottesdienste" - "/volume1/Aufnahme-stereo/014 Liedersammlung" - "/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" -) -SERVER2_NFS_SHARES=( - "/volume1/Aufnahme-stereo/010 Gottesdienste ARCHIV" -) +# 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") ############################################################################### diff --git a/search.py b/search.py index 5564899..06377be 100644 --- a/search.py +++ b/search.py @@ -1,5 +1,6 @@ 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__) @@ -10,49 +11,79 @@ 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() + allowed_basefolders = list(session['folders'].keys()) + print("Allowed base folders:", allowed_basefolders) + if not include_transcript: - # Simple search: all words must be in either relative_path or filename. 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}%"]) + # 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: 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] + else: # Advanced search: include transcript. Count transcript hits. conditions = [] params = [] + # Apply query words 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}%"]) + # 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: 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 # Remove full transcript if needed. + 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) 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 += `
-