fix(ux): improve new user onboarding experience
- Show full temporary API key in logs (not masked) so users can log in - Redirect root URL / to /dashboard (was 404) - Add setup guidance and docs link to login page - Make footer version dynamic (read from pyproject.toml) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,11 +27,14 @@ class AuthManager:
|
|||||||
if not self.master_api_key:
|
if not self.master_api_key:
|
||||||
# Generate a random key if not provided (dev mode)
|
# Generate a random key if not provided (dev mode)
|
||||||
self.master_api_key = secrets.token_urlsafe(32)
|
self.master_api_key = secrets.token_urlsafe(32)
|
||||||
|
self._is_temporary_key = True
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"No MASTER_API_KEY environment variable found. "
|
"No MASTER_API_KEY environment variable found. "
|
||||||
f"Generated temporary key: {self.master_api_key[:8]}***{self.master_api_key[-4:]} "
|
f"Generated temporary key: {self.master_api_key} "
|
||||||
"(set MASTER_API_KEY in .env for production use)"
|
"(set MASTER_API_KEY in .env for production use)"
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
self._is_temporary_key = False
|
||||||
|
|
||||||
# Project-specific keys (future feature)
|
# Project-specific keys (future feature)
|
||||||
self.project_keys = {}
|
self.project_keys = {}
|
||||||
|
|||||||
@@ -334,6 +334,7 @@ async def dashboard_login_page(request: Request) -> Response:
|
|||||||
"t": t,
|
"t": t,
|
||||||
"error": error,
|
"error": error,
|
||||||
"next_url": next_url,
|
"next_url": next_url,
|
||||||
|
"version": _get_project_version(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1984,6 +1985,19 @@ def get_registered_plugins() -> list:
|
|||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
|
|
||||||
|
def _get_project_version() -> str:
|
||||||
|
"""Read version from pyproject.toml."""
|
||||||
|
try:
|
||||||
|
toml_path = os.path.join(os.path.dirname(os.path.dirname(TEMPLATES_DIR)), "pyproject.toml")
|
||||||
|
with open(toml_path) as f:
|
||||||
|
for line in f:
|
||||||
|
if line.strip().startswith("version"):
|
||||||
|
return line.split("=")[1].strip().strip('"').strip("'")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return "3.0.0"
|
||||||
|
|
||||||
|
|
||||||
def get_about_info() -> dict:
|
def get_about_info() -> dict:
|
||||||
"""Get about information."""
|
"""Get about information."""
|
||||||
import sys
|
import sys
|
||||||
@@ -1998,7 +2012,7 @@ def get_about_info() -> dict:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"version": "1.0.0",
|
"version": _get_project_version(),
|
||||||
"mcp_version": "2024-11-05",
|
"mcp_version": "2024-11-05",
|
||||||
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
||||||
"tools_count": tools_count,
|
"tools_count": tools_count,
|
||||||
|
|||||||
@@ -108,15 +108,22 @@
|
|||||||
<svg class="w-5 h-5 text-blue-400 {% if lang == 'fa' %}ml-2{% else %}mr-2{% endif %} mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5 text-blue-400 {% if lang == 'fa' %}ml-2{% else %}mr-2{% endif %} mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
</svg>
|
</svg>
|
||||||
<p class="text-sm text-blue-300">
|
<div class="text-sm text-blue-300">
|
||||||
{% if lang == 'fa' %}
|
{% if lang == 'fa' %}
|
||||||
برای دسترسی به داشبورد به Master API Key یا یک API Key با scope=admin نیاز دارید.
|
<p>برای دسترسی به داشبورد به Master API Key نیاز دارید.</p>
|
||||||
|
<p class="mt-2 text-blue-400">کلید API خود را در متغیر محیطی <code class="bg-gray-700 px-1 rounded">MASTER_API_KEY</code> تنظیم کنید.</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
You need a Master API Key or an API Key with scope=admin to access the dashboard.
|
<p>Enter your <code class="bg-gray-700 px-1 rounded">MASTER_API_KEY</code> to access the dashboard.</p>
|
||||||
|
<p class="mt-2 text-blue-400">Don't have one? Set it in your <code class="bg-gray-700 px-1 rounded">.env</code> file, or check the server logs for the temporary key.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<p class="mt-2">
|
||||||
|
<a href="https://github.com/airano-ir/mcphub#quick-start" target="_blank" class="text-purple-400 hover:text-purple-300 underline">
|
||||||
|
{% if lang == 'fa' %}راهنمای شروع{% else %}Setup Guide{% endif %}
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Language Toggle -->
|
<!-- Language Toggle -->
|
||||||
<div class="mt-6 text-center">
|
<div class="mt-6 text-center">
|
||||||
@@ -131,7 +138,7 @@
|
|||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<p class="text-center text-gray-500 text-sm mt-6">
|
<p class="text-center text-gray-500 text-sm mt-6">
|
||||||
MCP Hub v3.0.0
|
MCP Hub v{{ version|default('3.0.0') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
13
server.py
13
server.py
@@ -274,7 +274,10 @@ logger.info("=" * 60)
|
|||||||
logger.info("MCP Hub Server - Initialized")
|
logger.info("MCP Hub Server - Initialized")
|
||||||
logger.info("=" * 60)
|
logger.info("=" * 60)
|
||||||
_mk = auth_manager.get_master_key()
|
_mk = auth_manager.get_master_key()
|
||||||
logger.info(f"Master API Key: {_mk[:8]}***{_mk[-4:]}")
|
if auth_manager._is_temporary_key:
|
||||||
|
logger.info(f"Master API Key (temporary): {_mk}")
|
||||||
|
else:
|
||||||
|
logger.info(f"Master API Key: {_mk[:8]}***{_mk[-4:]}")
|
||||||
logger.info(f"Discovered {len(project_manager.projects)} per-site project instances (legacy)")
|
logger.info(f"Discovered {len(project_manager.projects)} per-site project instances (legacy)")
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Discovered {site_manager.get_count()} unique sites across {len(plugin_types)} plugin types"
|
f"Discovered {site_manager.get_count()} unique sites across {len(plugin_types)} plugin types"
|
||||||
@@ -4182,12 +4185,18 @@ def create_multi_endpoint_app(transport: str = "streamable-http"):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Error during lifespan cleanup for {name}: {e}")
|
logger.warning(f"Error during lifespan cleanup for {name}: {e}")
|
||||||
|
|
||||||
|
# Root redirect: / → /dashboard (so users don't see 404)
|
||||||
|
async def root_redirect(request):
|
||||||
|
return RedirectResponse(url="/dashboard", status_code=302)
|
||||||
|
|
||||||
# Build routes
|
# Build routes
|
||||||
# Note: Order matters! More specific routes first
|
# Note: Order matters! More specific routes first
|
||||||
routes = [
|
routes = [
|
||||||
# Health check
|
# Health check
|
||||||
Route("/health", health_check, methods=["GET"]),
|
Route("/health", health_check, methods=["GET"]),
|
||||||
# Dashboard routes (Phase K.1)
|
# Root redirect
|
||||||
|
Route("/", root_redirect, methods=["GET"]),
|
||||||
|
# Dashboard routes
|
||||||
Route("/dashboard/login", dashboard_login_page, methods=["GET"]),
|
Route("/dashboard/login", dashboard_login_page, methods=["GET"]),
|
||||||
Route("/dashboard/login", dashboard_login_submit, methods=["POST"]),
|
Route("/dashboard/login", dashboard_login_submit, methods=["POST"]),
|
||||||
Route("/dashboard/logout", dashboard_logout, methods=["GET", "POST"]),
|
Route("/dashboard/logout", dashboard_logout, methods=["GET", "POST"]),
|
||||||
|
|||||||
Reference in New Issue
Block a user