Settings fixes: - MAX_SITES_PER_USER, USER_RATE_LIMIT_PER_MIN/HR now read from DB settings table (DB > ENV > default), so dashboard/settings changes apply without restart. Sync cache refreshed on every save or delete. - /api/me reports the live DB value for max_sites_per_user. Admin improvements: - Admin users bypass per-user rate limiting entirely (role=admin or ADMIN_EMAILS). - Admin Overview now shows platform stats: registered users, new users (7d), total user sites, available tools. Plugin cleanup: - Appwrite and Directus plugins removed from the active registry (8 plugins now: WordPress, WooCommerce, WordPress Specialist, Gitea, n8n, Supabase, OpenPanel, Coolify). Plugin code is retained for future re-enabling. - Settings page plugin visibility list updated to match. Mobile onboarding: - Stepper steps on narrow viewports stack vertically with correct full border and rounded corners on each step. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
164 lines
7.3 KiB
HTML
164 lines
7.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="{{ lang|default('en') }}" {% if lang=='fa' %}dir="rtl" {% endif %}>
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{ t.login_title }} - MCP Hub</title>
|
|
<link rel="icon" type="image/svg+xml" href="/static/logo.svg">
|
|
|
|
{% include "dashboard/partials/head_assets.html" %}
|
|
|
|
<style>
|
|
.login-gradient {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
}
|
|
|
|
.card-shadow {
|
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
.input-focus:focus {
|
|
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.3);
|
|
}
|
|
|
|
.btn-primary {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
|
|
}
|
|
|
|
.animate-float {
|
|
animation: float 6s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes float {
|
|
|
|
0%,
|
|
100% {
|
|
transform: translateY(0px);
|
|
}
|
|
|
|
50% {
|
|
transform: translateY(-20px);
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body class="bg-gray-900 min-h-screen flex items-center justify-center p-4">
|
|
<!-- Background decoration -->
|
|
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
|
<div class="absolute -top-40 -right-40 w-80 h-80 bg-purple-500/20 rounded-full blur-3xl animate-float"></div>
|
|
<div class="absolute -bottom-40 -left-40 w-80 h-80 bg-blue-500/20 rounded-full blur-3xl animate-float"
|
|
style="animation-delay: -3s;"></div>
|
|
</div>
|
|
|
|
<!-- Login Card -->
|
|
<div class="relative w-full max-w-md">
|
|
<div class="card-shadow bg-gray-800 rounded-2xl p-8">
|
|
<!-- Logo -->
|
|
<div class="text-center mb-8">
|
|
<div class="mx-auto w-16 h-16 login-gradient rounded-2xl flex items-center justify-center mb-4">
|
|
<img src="/static/logo.svg" alt="MCP Hub Logo"
|
|
class="w-10 h-10 object-contain drop-shadow-[0_0_8px_rgba(255,255,255,0.8)]">
|
|
</div>
|
|
<h1 class="text-2xl font-bold text-white">MCP Hub</h1>
|
|
<p class="text-gray-400 mt-2" {% if lang=='fa' %}dir="rtl" {% endif %}>{{ t.login_subtitle }}</p>
|
|
</div>
|
|
|
|
<!-- Error Message -->
|
|
{% if error %}
|
|
<div class="mb-6 p-4 bg-red-500/20 border border-red-500/50 rounded-lg" {% if lang=='fa' %}dir="rtl" {%
|
|
endif %}>
|
|
<div class="flex items-center">
|
|
<svg class="w-5 h-5 text-red-400 {% if lang == 'fa' %}ml-2{% else %}mr-2{% endif %}" fill="none"
|
|
stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<span class="text-red-400 text-sm">
|
|
{% if error == 'rate_limit' %}
|
|
{{ t.rate_limit_error }}
|
|
{% else %}
|
|
{{ t.login_error }}
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Login Form -->
|
|
<form method="POST" action="/dashboard-legacy/login" class="space-y-6">
|
|
<input type="hidden" name="csrf_token" value="{{ request.state.csrf_token }}">
|
|
<input type="hidden" name="next" value="{{ next_url }}">
|
|
|
|
<div {% if lang=='fa' %}dir="rtl" {% endif %}>
|
|
<label for="api_key" class="block text-sm font-medium text-gray-300 mb-2">
|
|
{{ t.api_key_label }}
|
|
</label>
|
|
<input type="password" id="api_key" name="api_key" required autocomplete="off"
|
|
placeholder="{{ t.api_key_placeholder }}"
|
|
class="input-focus w-full px-4 py-3 bg-gray-700 border border-gray-600 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:border-purple-500 transition-colors"
|
|
{% if lang=='fa' %}dir="ltr" style="text-align: right;" {% endif %}>
|
|
</div>
|
|
|
|
<button type="submit"
|
|
class="btn-primary w-full py-3 px-4 text-white font-semibold rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 focus:ring-offset-gray-800">
|
|
{{ t.login_button }}
|
|
</button>
|
|
</form>
|
|
|
|
<!-- Security Note -->
|
|
<div class="mt-6 p-4 bg-blue-500/10 border border-blue-500/30 rounded-lg" {% if lang=='fa' %}dir="rtl" {%
|
|
endif %}>
|
|
<div class="flex items-start">
|
|
<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" />
|
|
</svg>
|
|
<div class="text-sm text-blue-300">
|
|
{% if lang == 'fa' %}
|
|
<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 %}
|
|
<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 %}
|
|
<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>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Language Toggle -->
|
|
<div class="mt-6 text-center">
|
|
<a href="?lang={% if lang == 'fa' %}en{% else %}fa{% endif %}{% if next_url %}&next={{ next_url }}{% endif %}"
|
|
class="text-sm text-gray-400 hover:text-white transition-colors">
|
|
{% if lang == 'fa' %}English{% else %}<span lang="fa" dir="rtl">فارسی</span>{% endif %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<p class="text-center text-gray-500 text-sm mt-6">
|
|
MCP Hub v{{ version }}
|
|
</p>
|
|
</div>
|
|
</body>
|
|
|
|
</html>
|