j
This commit is contained in:
@@ -69,6 +69,17 @@ logger = logging.getLogger(__name__)
|
||||
STORAGE_PATH: Optional[Path] = None
|
||||
API_KEY: Optional[str] = None # API key for authentication (None = no auth required)
|
||||
|
||||
# Cache for database connection to prevent "database is locked" on high frequency requests
|
||||
_DB_CACHE: Dict[str, Any] = {}
|
||||
|
||||
def get_db(path: Path):
|
||||
from API.folder import LocalLibrarySearchOptimizer
|
||||
p_str = str(path)
|
||||
if p_str not in _DB_CACHE:
|
||||
_DB_CACHE[p_str] = LocalLibrarySearchOptimizer(path)
|
||||
_DB_CACHE[p_str].__enter__()
|
||||
return _DB_CACHE[p_str]
|
||||
|
||||
# Try importing Flask - will be used in main() only
|
||||
try:
|
||||
from flask import Flask, request, jsonify
|
||||
@@ -199,25 +210,34 @@ def create_app():
|
||||
# ========================================================================
|
||||
|
||||
@app.route("/health", methods=["GET"])
|
||||
@require_auth()
|
||||
def health():
|
||||
"""Check server health and storage availability."""
|
||||
# Check auth manually to allow discovery even if locked
|
||||
authed = True
|
||||
if API_KEY:
|
||||
provided_key = request.headers.get("X-API-Key") or request.args.get("api_key")
|
||||
if not provided_key or provided_key != API_KEY:
|
||||
authed = False
|
||||
|
||||
status = {
|
||||
"status": "ok",
|
||||
"service": "remote_storage",
|
||||
"name": os.environ.get("MM_SERVER_NAME", "Remote Storage"),
|
||||
"storage_configured": STORAGE_PATH is not None,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"locked": not authed and API_KEY is not None
|
||||
}
|
||||
|
||||
# If not authed but API_KEY is required, return minimal info for discovery
|
||||
if not authed and API_KEY:
|
||||
return jsonify(status), 200
|
||||
|
||||
if STORAGE_PATH:
|
||||
status["storage_path"] = str(STORAGE_PATH)
|
||||
status["storage_exists"] = STORAGE_PATH.exists()
|
||||
try:
|
||||
from API.folder import API_folder_store
|
||||
|
||||
with API_folder_store(STORAGE_PATH) as db:
|
||||
status["database_accessible"] = True
|
||||
search_db = get_db(STORAGE_PATH)
|
||||
status["database_accessible"] = True
|
||||
except Exception as e:
|
||||
status["database_accessible"] = False
|
||||
status["database_error"] = str(e)
|
||||
@@ -233,8 +253,6 @@ def create_app():
|
||||
@require_storage()
|
||||
def search_files():
|
||||
"""Search for files by name or tag."""
|
||||
from API.folder import LocalLibrarySearchOptimizer, API_folder_store
|
||||
|
||||
query = request.args.get("q", "")
|
||||
limit = request.args.get("limit", 100, type=int)
|
||||
|
||||
@@ -242,30 +260,32 @@ def create_app():
|
||||
db_query = query if query and query != "*" else ""
|
||||
|
||||
try:
|
||||
with LocalLibrarySearchOptimizer(STORAGE_PATH) as search_db:
|
||||
results = search_db.search_by_name(db_query, limit)
|
||||
tag_results = search_db.search_by_tag(db_query, limit)
|
||||
all_results_dict = {
|
||||
r["hash"]: r
|
||||
for r in (results + tag_results)
|
||||
}
|
||||
search_db = get_db(STORAGE_PATH)
|
||||
results = search_db.search_by_name(db_query, limit)
|
||||
tag_results = search_db.search_by_tag(db_query, limit)
|
||||
all_results_dict = {
|
||||
r["hash"]: r
|
||||
for r in (results + tag_results)
|
||||
}
|
||||
|
||||
# Fetch tags for each result to support title extraction on client
|
||||
with API_folder_store(STORAGE_PATH) as db:
|
||||
for res in all_results_dict.values():
|
||||
if res.get("file_path"):
|
||||
res["tag"] = db.get_tags(Path(res["file_path"]))
|
||||
# Fetch tags for each result to support title extraction on client
|
||||
if search_db.db:
|
||||
for res in all_results_dict.values():
|
||||
file_hash = res.get("hash")
|
||||
if file_hash:
|
||||
tags = search_db.db.get_tags(file_hash)
|
||||
res["tag"] = tags
|
||||
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"query": query,
|
||||
"count": len(all_results_dict),
|
||||
"files": list(all_results_dict.values()),
|
||||
}
|
||||
),
|
||||
200,
|
||||
)
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"query": query,
|
||||
"count": len(all_results_dict),
|
||||
"files": list(all_results_dict.values()),
|
||||
}
|
||||
),
|
||||
200,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Search error: {e}", exc_info=True)
|
||||
return jsonify({"error": f"Search failed: {str(e)}"}), 500
|
||||
@@ -275,30 +295,32 @@ def create_app():
|
||||
@require_storage()
|
||||
def get_file_metadata(file_hash: str):
|
||||
"""Get metadata for a specific file by hash."""
|
||||
from API.folder import API_folder_store
|
||||
|
||||
try:
|
||||
with API_folder_store(STORAGE_PATH) as db:
|
||||
file_path = db.search_hash(file_hash)
|
||||
search_db = get_db(STORAGE_PATH)
|
||||
db = search_db.db
|
||||
if not db:
|
||||
return jsonify({"error": "Database unavailable"}), 500
|
||||
|
||||
file_path = db.search_hash(file_hash)
|
||||
|
||||
if not file_path or not file_path.exists():
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
if not file_path or not file_path.exists():
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
|
||||
metadata = db.get_metadata(file_path)
|
||||
tags = db.get_tags(file_path)
|
||||
metadata = db.get_metadata(file_hash)
|
||||
tags = db.get_tags(file_hash) # Use hash string
|
||||
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"hash": file_hash,
|
||||
"path": str(file_path),
|
||||
"size": file_path.stat().st_size,
|
||||
"metadata": metadata,
|
||||
"tag": tags,
|
||||
}
|
||||
),
|
||||
200,
|
||||
)
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"hash": file_hash,
|
||||
"path": str(file_path),
|
||||
"size": file_path.stat().st_size,
|
||||
"metadata": metadata,
|
||||
"tag": tags,
|
||||
}
|
||||
),
|
||||
200,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Get metadata error: {e}", exc_info=True)
|
||||
return jsonify({"error": f"Failed to get metadata: {str(e)}"}), 500
|
||||
|
||||
Reference in New Issue
Block a user