release: v3.3.0 — platform hardening & admin unification (Track F.1–F.8)

Plugin visibility control, UI/UX fixes, unified admin panel,
database-backed settings, and security hardening.

Highlights:
- ENABLED_PLUGINS env var for plugin visibility (F.1)
- Admin designation via ADMIN_EMAILS (F.4a)
- Master key scope control (F.4b)
- Panel unification + settings from UI (F.4c)
- exec() removal, shell injection fix, bcrypt migration (F.8)
- 5 new test suites

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 09:55:49 +02:00
parent f6dbbeaab0
commit 9b70b259bb
47 changed files with 2122 additions and 366 deletions

View File

@@ -12,7 +12,7 @@
{# ══════════════════════════════════════════════════════ #}
<!-- Admin Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Projects Card -->
<div class="card-hover bg-white dark:bg-gray-800 rounded-xl p-6 border border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
@@ -94,6 +94,42 @@
</svg>
</a>
</div>
<!-- Users Card -->
<div class="card-hover bg-white dark:bg-gray-800 rounded-xl p-6 border border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">{% if lang == 'fa' %}کاربران{% else %}Total Users{% endif %}</p>
<p class="text-3xl font-bold text-gray-900 dark:text-white mt-1">{{ stats.users_count|default(0) }}</p>
</div>
<div class="w-12 h-12 bg-cyan-500/20 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
</svg>
</div>
</div>
<p class="mt-4 text-sm text-gray-500 dark:text-gray-400">
{% if lang == 'fa' %}کاربران ثبت‌نام شده{% else %}Registered users{% endif %}
</p>
</div>
<!-- User Sites Card -->
<div class="card-hover bg-white dark:bg-gray-800 rounded-xl p-6 border border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">{% if lang == 'fa' %}سایت‌های کاربران{% else %}User Sites{% endif %}</p>
<p class="text-3xl font-bold text-gray-900 dark:text-white mt-1">{{ stats.user_sites_count|default(0) }}</p>
</div>
<div class="w-12 h-12 bg-teal-500/20 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/>
</svg>
</div>
</div>
<p class="mt-4 text-sm text-gray-500 dark:text-gray-400">
{% if lang == 'fa' %}سایت‌های متصل شده{% else %}Connected sites{% endif %}
</p>
</div>
</div>
<!-- Admin Main Content Grid -->
@@ -252,33 +288,6 @@
</div>
</div>
<!-- Support / Donate -->
<div class="bg-gradient-to-br from-primary-900/40 to-gray-800 rounded-xl border border-primary-700/30">
<div class="p-6 text-center">
<div class="w-10 h-10 bg-primary-500/20 rounded-full flex items-center justify-center mx-auto mb-3">
<svg class="w-5 h-5 text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
</svg>
</div>
<h3 class="text-sm font-semibold text-white mb-1">
{% if lang == 'fa' %}حمایت از پروژه{% else %}Support MCP Hub{% endif %}
</h3>
<p class="text-xs text-gray-400 mb-4">
{% if lang == 'fa' %}با حمایت مالی به توسعه این پروژه کمک کنید{% else %}Help us keep this project alive and growing{% endif %}
</p>
<a
href="https://nowpayments.io/donation/airano"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-primary-600 hover:bg-primary-500 rounded-lg transition-colors"
>
<svg class="w-4 h-4 {% 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 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
{% if lang == 'fa' %}حمایت با رمزارز{% else %}Donate with Crypto{% endif %}
</a>
</div>
</div>
</div>
</div>
@@ -288,7 +297,7 @@
{# ══════════════════════════════════════════════════════ #}
<!-- User Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- My Sites Card -->
<div class="card-hover bg-white dark:bg-gray-800 rounded-xl p-6 border border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
@@ -349,24 +358,6 @@
</a>
</div>
<!-- Available Tools Card -->
<div class="card-hover bg-white dark:bg-gray-800 rounded-xl p-6 border border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ t.get('available_tools', 'Available Tools') }}</p>
<p class="text-3xl font-bold text-gray-900 dark:text-white mt-1">{{ stats.tools_count|default(0) }}</p>
</div>
<div class="w-12 h-12 bg-orange-500/20 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
</div>
</div>
<p class="mt-4 text-sm text-gray-500 dark:text-gray-400">
{% if lang == 'fa' %}ابزارهای MCP قابل استفاده{% else %}MCP tools available{% endif %}
</p>
</div>
</div>
<!-- User Site Status -->
@@ -439,33 +430,6 @@
</div>
</div>
<!-- Support / Donate (User Dashboard) -->
<div class="bg-gradient-to-br from-primary-900/40 to-gray-800 rounded-xl border border-primary-700/30">
<div class="p-6 text-center">
<div class="w-10 h-10 bg-primary-500/20 rounded-full flex items-center justify-center mx-auto mb-3">
<svg class="w-5 h-5 text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
</svg>
</div>
<h3 class="text-sm font-semibold text-white mb-1">
{% if lang == 'fa' %}حمایت از پروژه{% else %}Support MCP Hub{% endif %}
</h3>
<p class="text-xs text-gray-400 mb-4">
{% if lang == 'fa' %}با حمایت مالی به توسعه این پروژه کمک کنید{% else %}Help us keep this project alive and growing{% endif %}
</p>
<a
href="https://nowpayments.io/donation/airano"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-primary-600 hover:bg-primary-500 rounded-lg transition-colors"
>
<svg class="w-4 h-4 {% 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 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
{% if lang == 'fa' %}حمایت با رمزارز{% else %}Donate with Crypto{% endif %}
</a>
</div>
</div>
{% endif %}
</div>