Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 105 additions & 43 deletions python/searxng-addons/dashboard_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
1. Copy file to `/usr/local/searxng/searx/engines/dashboard_services.py`
2. Add the following configuration to your `settings.yml` file:

```yaml
\```yaml
engines:
- name: selfhosted
engine: dashboard_services
Expand All @@ -18,7 +18,7 @@
enable_http: true
enable_http2: true
weight: 0.5 # Higher priority than regular search engines
```
\```

For use with https://github.com/searxng/searxng
"""
Expand All @@ -40,8 +40,8 @@
}

# Engine configuration
engine_type = "online"
categories = ["general"]
engine_type = 'online'
categories = ['general']
disabled = False
timeout = 10.0
paging = False
Expand All @@ -50,21 +50,28 @@
# API endpoint
base_url = f"{HOMEPAGE_BASE_URL}/api/services"

# Store the current query
_current_query = ""


def request(query, params):
"""Build the request parameters for the dashboard services API."""
params["url"] = base_url
params["method"] = "GET"
params["headers"] = {
"Accept": "application/json",
"User-Agent": "SearXNG Dashboard Services Engine",
global _current_query
_current_query = query.lower() # Store for filtering in response

params['url'] = base_url
params['method'] = 'GET'
params['headers'] = {
'Accept': 'application/json',
'User-Agent': 'SearXNG Dashboard Services Engine'
}
return params


def response(resp) -> EngineResults:
def response(resp):
"""Parse the API response and return search results."""
results = EngineResults()
global _current_query
results = []

try:
# Check if response is empty
Expand All @@ -75,49 +82,95 @@ def response(resp) -> EngineResults:
# Parse JSON response
json_data = loads(resp.text)

# Get query from the original request (simplified)
query = ""
if hasattr(resp, "url") and "?" in str(resp.url):
# Try to extract query from URL params if available
pass
# Get the query for filtering
query = _current_query
if not query:
print("Dashboard Services Engine: No query available")
return results # No query, no results

# Collect all matching services with their scores
matched_services = []

# Process each group in the response
for group in json_data:
group_name = group.get("name", "Unknown Group")
group_name = group.get('name', 'Unknown Group')

# Process direct services
if "services" in group:
for service in group["services"]:
results.append(_create_service_result(service, group_name))
if 'services' in group:
for service in group['services']:
score = _calculate_match_score(service, group_name, query)
if score > 0: # Only include if there's a match
matched_services.append({
'service': service,
'group_name': group_name,
'score': score
})

# Process nested groups
if "groups" in group:
for subgroup in group["groups"]:
subgroup_name = subgroup.get("name", "Unknown Subgroup")
if "services" in subgroup:
for service in subgroup["services"]:
results.append(
_create_service_result(
service, f"{group_name} > {subgroup_name}"
)
)
if 'groups' in group:
for subgroup in group['groups']:
subgroup_name = subgroup.get('name', 'Unknown Subgroup')
if 'services' in subgroup:
for service in subgroup['services']:
score = _calculate_match_score(
service, f"{group_name} > {subgroup_name}", query)
if score > 0: # Only include if there's a match
matched_services.append({
'service': service,
'group_name': f"{group_name} > {subgroup_name}",
'score': score
})

# Sort by score (highest first)
matched_services.sort(key=lambda x: x['score'], reverse=True)

# Create results from sorted matches
for match in matched_services:
results.append(_create_service_result(
match['service'], match['group_name']))

except Exception as e:
print(f"Dashboard Services Engine Error: {e}")
print(
f"Response content: {resp.text[:200]}..."
) # Show first 200 chars for debugging
# Show first 200 chars for debugging
print(f"Response content: {resp.text[:200]}...")

return results


def _calculate_match_score(service, group_name, query):
"""Calculate a relevance score based on where the query matches."""
score = 0

# Get the values to check, converting to lowercase and handling None values
name = (service.get('name', '') or '').lower()
description = (service.get('description', '') or '').lower()
server = (service.get('server', '') or '').lower()
container = (service.get('container', '') or '').lower()
group_name = (group_name or '').lower()

# Check for matches in different fields with different weights
if query in name:
score += 10 # Highest weight for name match
if query in description:
score += 5 # Medium weight for description match
if query in server:
score += 2 # Lower weight for server/container matches
if query in container:
score += 2
if query in group_name:
score += 3 # Medium-low weight for group match

return score


def _create_service_result(service, group_name):
"""Create a search result from a service object."""
name = service.get("name", "Unknown Service")
description = service.get("description", "No description available")
href = service.get("href", "#")
server = service.get("server", "")
container = service.get("container", "")
name = service.get('name', 'Unknown Service')
description = service.get('description', 'No description available')
href = service.get('href', '#')
server = service.get('server', '')
container = service.get('container', '')
icon = service.get('icon', '')

# Simple content creation
content = description
Expand All @@ -126,9 +179,18 @@ def _create_service_result(service, group_name):
if container:
content += f" | Container: {container}"

return {
"url": href,
"title": f"{name} ({group_name})",
"content": content,
"category": "dashboard_services",
result = {
'url': href,
'title': f"{name} ({group_name})",
'content': content,
}

# Add icon if available
if icon:
if icon.startswith('http'):
result['img_src'] = icon
elif icon.startswith('/'):
# Local icon path
result['img_src'] = f"{HOMEPAGE_BASE_URL}{icon}"

return result
Loading