fix(ci): fix black/ruff failures, add CoC and PR template

- Fix Python formatting (sync no longer strips blank lines from .py files)
- Remove test_community_build.py (tests private sync module)
- Fix ruff warnings in test files
- Add CODE_OF_CONDUCT.md
- Add .github/PULL_REQUEST_TEMPLATE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-17 18:19:39 +03:30
parent c9083d86b1
commit c73e39f6e4
135 changed files with 1185 additions and 317 deletions

View File

@@ -20,6 +20,7 @@ logger = logging.getLogger(__name__)
# Singleton instance
_dashboard_auth: Optional["DashboardAuth"] = None
@dataclass
class DashboardSession:
"""Dashboard session information."""
@@ -30,6 +31,7 @@ class DashboardSession:
user_type: str # "master" or "api_key"
key_id: str | None = None # For API key sessions
class DashboardAuth:
"""
Dashboard authentication manager.
@@ -270,6 +272,7 @@ class DashboardAuth:
)
return None
def get_dashboard_auth() -> DashboardAuth:
"""Get or create the singleton DashboardAuth instance."""
global _dashboard_auth

View File

@@ -35,6 +35,7 @@ PLUGIN_DISPLAY_NAMES = {
"appwrite": "Appwrite",
}
def get_plugin_display_name(plugin_type: str) -> str:
"""Get the proper display name for a plugin type."""
if plugin_type in PLUGIN_DISPLAY_NAMES:
@@ -42,6 +43,7 @@ def get_plugin_display_name(plugin_type: str) -> str:
# Default: replace underscores with spaces and title case
return plugin_type.replace("_", " ").title()
# Register custom Jinja filter
templates.env.filters["plugin_name"] = get_plugin_display_name
@@ -155,6 +157,7 @@ DASHBOARD_TRANSLATIONS = {
},
}
def detect_language(accept_language: str | None, query_lang: str | None = None) -> str:
"""Detect language from Accept-Language header or query parameter."""
if query_lang and query_lang in DASHBOARD_TRANSLATIONS:
@@ -167,10 +170,12 @@ def detect_language(accept_language: str | None, query_lang: str | None = None)
return "en"
def get_translations(lang: str) -> dict:
"""Get translations for a language."""
return DASHBOARD_TRANSLATIONS.get(lang, DASHBOARD_TRANSLATIONS["en"])
def get_client_ip(request: Request) -> str:
"""Get client IP from request, handling proxies."""
forwarded = request.headers.get("x-forwarded-for")
@@ -178,6 +183,7 @@ def get_client_ip(request: Request) -> str:
return forwarded.split(",")[0].strip()
return request.client.host if request.client else "unknown"
async def get_dashboard_stats() -> dict:
"""Get dashboard statistics."""
stats = {
@@ -221,6 +227,7 @@ async def get_dashboard_stats() -> dict:
return stats
async def get_projects_by_type() -> dict:
"""Get projects grouped by plugin type."""
projects_by_type = {}
@@ -242,6 +249,7 @@ async def get_projects_by_type() -> dict:
return projects_by_type
async def get_recent_activity(limit: int = 10) -> list:
"""Get recent activity from audit logs."""
activity = []
@@ -270,6 +278,7 @@ async def get_recent_activity(limit: int = 10) -> list:
return activity
async def get_health_summary() -> dict:
"""Get health status summary."""
summary = {
@@ -302,8 +311,10 @@ async def get_health_summary() -> dict:
return summary
# Route handlers
async def dashboard_login_page(request: Request) -> Response:
"""Render dashboard login page."""
auth = get_dashboard_auth()
@@ -333,6 +344,7 @@ async def dashboard_login_page(request: Request) -> Response:
},
)
async def dashboard_login_submit(request: Request) -> Response:
"""Handle dashboard login form submission."""
auth = get_dashboard_auth()
@@ -406,6 +418,7 @@ async def dashboard_login_submit(request: Request) -> Response:
logger.info(f"Dashboard login successful: type={user_type}, ip={client_ip}")
return response
async def dashboard_logout(request: Request) -> Response:
"""Handle dashboard logout."""
auth = get_dashboard_auth()
@@ -429,6 +442,7 @@ async def dashboard_logout(request: Request) -> Response:
logger.info(f"Dashboard logout from {client_ip}")
return response
async def dashboard_home(request: Request) -> Response:
"""Render dashboard home page."""
auth = get_dashboard_auth()
@@ -467,6 +481,7 @@ async def dashboard_home(request: Request) -> Response:
},
)
async def dashboard_api_stats(request: Request) -> Response:
"""API endpoint for dashboard stats."""
auth = get_dashboard_auth()
@@ -488,10 +503,12 @@ async def dashboard_api_stats(request: Request) -> Response:
}
)
# ============================================================
# Projects Routes (Phase K.2)
# ============================================================
def get_cached_health_status(project_id: str) -> dict:
"""
Get cached health status for a project without performing new health check.
@@ -537,6 +554,7 @@ def get_cached_health_status(project_id: str) -> dict:
logger.warning(f"Error getting cached health for {project_id}: {e}")
return {"status": "unknown", "last_check": None, "error_rate": 0}
async def get_all_projects(
plugin_type: str | None = None,
search: str | None = None,
@@ -615,6 +633,7 @@ async def get_all_projects(
"available_plugin_types": sorted(available_plugin_types),
}
async def get_project_detail(project_id: str) -> dict | None:
"""Get detailed information about a specific project."""
try:
@@ -728,6 +747,7 @@ async def get_project_detail(project_id: str) -> dict | None:
logger.warning(f"Error getting project detail: {e}")
return None
async def dashboard_projects_list(request: Request) -> Response:
"""Render projects list page."""
auth = get_dashboard_auth()
@@ -779,6 +799,7 @@ async def dashboard_projects_list(request: Request) -> Response:
},
)
async def dashboard_project_detail(request: Request) -> Response:
"""Render project detail page."""
auth = get_dashboard_auth()
@@ -829,6 +850,7 @@ async def dashboard_project_detail(request: Request) -> Response:
},
)
async def dashboard_api_projects(request: Request) -> Response:
"""API endpoint for projects list."""
auth = get_dashboard_auth()
@@ -851,6 +873,7 @@ async def dashboard_api_projects(request: Request) -> Response:
return JSONResponse(projects_data)
async def dashboard_api_project_detail(request: Request) -> Response:
"""API endpoint for project detail."""
auth = get_dashboard_auth()
@@ -868,6 +891,7 @@ async def dashboard_api_project_detail(request: Request) -> Response:
return JSONResponse(project)
async def dashboard_project_health_check(request: Request) -> Response:
"""API endpoint to check health of a specific project."""
logger.debug(f"Health check request received: {request.url.path}")
@@ -942,10 +966,12 @@ async def dashboard_project_health_check(request: Request) -> Response:
{"status": "error", "project_id": project_id, "message": str(e)}, status_code=500
)
# =============================================================================
# K.3: API Keys Management
# =============================================================================
async def get_all_api_keys(
project_id: str | None = None,
status: str = "active",
@@ -1000,6 +1026,7 @@ async def get_all_api_keys(
"per_page": per_page,
}
async def dashboard_api_keys_list(request: Request) -> Response:
"""Render API keys list page."""
auth = get_dashboard_auth()
@@ -1057,6 +1084,7 @@ async def dashboard_api_keys_list(request: Request) -> Response:
},
)
async def dashboard_api_keys_create(request: Request) -> Response:
"""API endpoint to create a new API key."""
auth = get_dashboard_auth()
@@ -1111,6 +1139,7 @@ async def dashboard_api_keys_create(request: Request) -> Response:
logger.error(f"Error creating API key: {e}")
return JSONResponse({"error": "Failed to create key"}, status_code=500)
async def dashboard_api_keys_revoke(request: Request) -> Response:
"""API endpoint to revoke an API key."""
auth = get_dashboard_auth()
@@ -1144,6 +1173,7 @@ async def dashboard_api_keys_revoke(request: Request) -> Response:
logger.error(f"Error revoking API key: {e}")
return JSONResponse({"error": "Failed to revoke key"}, status_code=500)
async def dashboard_api_keys_delete(request: Request) -> Response:
"""API endpoint to delete an API key."""
auth = get_dashboard_auth()
@@ -1177,10 +1207,12 @@ async def dashboard_api_keys_delete(request: Request) -> Response:
logger.error(f"Error deleting API key: {e}")
return JSONResponse({"error": "Failed to delete key"}, status_code=500)
# =============================================================================
# K.4: OAuth Clients Management
# =============================================================================
async def get_oauth_clients_data() -> dict:
"""Get OAuth clients data."""
from core.oauth.client_registry import get_client_registry
@@ -1213,6 +1245,7 @@ async def get_oauth_clients_data() -> dict:
"total_count": 0,
}
async def dashboard_oauth_clients_list(request: Request) -> Response:
"""Render OAuth clients list page."""
auth = get_dashboard_auth()
@@ -1246,6 +1279,7 @@ async def dashboard_oauth_clients_list(request: Request) -> Response:
},
)
async def dashboard_oauth_clients_create(request: Request) -> Response:
"""API endpoint to create OAuth client."""
auth = get_dashboard_auth()
@@ -1295,6 +1329,7 @@ async def dashboard_oauth_clients_create(request: Request) -> Response:
logger.error(f"Error creating OAuth client: {e}")
return JSONResponse({"error": "Failed to create client"}, status_code=500)
async def dashboard_oauth_clients_delete(request: Request) -> Response:
"""API endpoint to delete OAuth client."""
auth = get_dashboard_auth()
@@ -1329,10 +1364,12 @@ async def dashboard_oauth_clients_delete(request: Request) -> Response:
logger.error(f"Error deleting OAuth client: {e}")
return JSONResponse({"error": "Failed to delete client"}, status_code=500)
# =============================================================================
# K.4: Audit Logs Management
# =============================================================================
async def get_audit_logs_data(
event_type: str | None = None,
level: str | None = None,
@@ -1450,6 +1487,7 @@ async def get_audit_logs_data(
"per_page": per_page,
}
async def dashboard_audit_logs_list(request: Request) -> Response:
"""Render audit logs list page."""
auth = get_dashboard_auth()
@@ -1514,6 +1552,7 @@ async def dashboard_audit_logs_list(request: Request) -> Response:
},
)
async def dashboard_api_audit_logs(request: Request) -> Response:
"""API endpoint for audit logs list."""
auth = get_dashboard_auth()
@@ -1545,10 +1584,12 @@ async def dashboard_api_audit_logs(request: Request) -> Response:
logger.error(f"Error getting audit logs: {e}")
return JSONResponse({"error": "Failed to get logs"}, status_code=500)
# =============================================================================
# K.5: Health Monitoring
# =============================================================================
def get_basic_health_data() -> dict:
"""Get basic health data (fast, no project checks)."""
try:
@@ -1618,6 +1659,7 @@ def get_basic_health_data() -> dict:
},
}
def get_cached_projects_health() -> dict:
"""
Get cached health status for all projects without live checks.
@@ -1679,6 +1721,7 @@ def get_cached_projects_health() -> dict:
"alerts": [], # No alerts from cached data
}
async def get_health_data(live_check: bool = True) -> dict:
"""
Get comprehensive health monitoring data.
@@ -1777,6 +1820,7 @@ async def get_health_data(live_check: bool = True) -> dict:
logger.error(f"Error getting health data: {e}")
return default_result
async def dashboard_health_page(request: Request) -> Response:
"""
Render health monitoring page.
@@ -1831,6 +1875,7 @@ async def dashboard_health_page(request: Request) -> Response:
logger.error(traceback.format_exc())
return HTMLResponse(f"<h1>Error loading health page</h1><pre>{e}</pre>", status_code=500)
async def dashboard_health_projects_partial(request: Request) -> Response:
"""HTMX endpoint for projects health data (HTML partial)."""
logger.debug("Health projects partial endpoint called")
@@ -1882,6 +1927,7 @@ async def dashboard_health_projects_partial(request: Request) -> Response:
status_code=500,
)
async def dashboard_api_health(request: Request) -> Response:
"""API endpoint for health data."""
auth = get_dashboard_auth()
@@ -1898,10 +1944,12 @@ async def dashboard_api_health(request: Request) -> Response:
logger.error(f"Error getting health data: {e}")
return JSONResponse({"error": "Failed to get health data"}, status_code=500)
# =============================================================================
# K.5: Settings Page
# =============================================================================
def get_system_config() -> dict:
"""Get system configuration for display."""
@@ -1919,6 +1967,7 @@ def get_system_config() -> dict:
"dashboard_session_expiry": os.environ.get("DASHBOARD_SESSION_EXPIRY_HOURS", "24"),
}
def get_registered_plugins() -> list:
"""Get list of registered plugins."""
plugins = []
@@ -1941,6 +1990,7 @@ def get_registered_plugins() -> list:
return plugins
def get_about_info() -> dict:
"""Get about information."""
import sys
@@ -1961,6 +2011,7 @@ def get_about_info() -> dict:
"tools_count": tools_count,
}
async def dashboard_settings_page(request: Request) -> Response:
"""Render settings page."""
auth = get_dashboard_auth()
@@ -2004,6 +2055,7 @@ async def dashboard_settings_page(request: Request) -> Response:
},
)
def register_dashboard_routes(mcp):
"""
Register dashboard routes with the MCP server.