Major release introducing the Live Platform architecture:
- SQLite database backend with async operations and migrations (E.1)
- AES-256-GCM credential encryption with HKDF key derivation (E.1)
- OAuth Social Login with GitHub and Google (E.2)
- Site management API with encrypted credential storage (E.3)
- Per-user MCP endpoints at /u/{user_id}/{alias}/mcp (E.3)
- User API keys with bcrypt hashing (E.3)
- Auto-generated config snippets for 5 MCP clients (E.3)
- Dashboard: dark/light mode, RBAC, My Sites, Connect page
- Active background health checks
- 452 tests (up from 303), all passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
91 lines
6.3 KiB
HTML
91 lines
6.3 KiB
HTML
{% extends "dashboard/base.html" %}
|
|
|
|
{% block title %}{% if lang == 'fa' %}پروفایل{% else %}Profile{% endif %}{% endblock %}
|
|
{% block page_title %}{% if lang == 'fa' %}پروفایل{% else %}Profile{% endif %}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="max-w-2xl mx-auto space-y-6">
|
|
<!-- User Info Card -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 border border-gray-200 dark:border-gray-700">
|
|
<div class="flex items-center space-x-4 {% if lang == 'fa' %}space-x-reverse{% endif %}">
|
|
{% if user and user.avatar_url %}
|
|
<img src="{{ user.avatar_url }}" alt="{{ user.name or user.email }}" class="w-16 h-16 rounded-full border-2 border-purple-500">
|
|
{% else %}
|
|
<div class="w-16 h-16 rounded-full bg-purple-600 flex items-center justify-center text-white text-xl font-bold">
|
|
{{ (session.name or session.email or "?")[0]|upper }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div>
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white">{{ user.name or session.name or "User" }}</h2>
|
|
<p class="text-gray-500 dark:text-gray-400">{{ user.email or session.email }}</p>
|
|
{% if user %}
|
|
<div class="flex items-center mt-1 space-x-2 {% if lang == 'fa' %}space-x-reverse{% endif %}">
|
|
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium
|
|
{% if user.provider == 'github' %}bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300{% else %}bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300{% endif %}">
|
|
{% if user.provider == 'github' %}
|
|
<svg class="w-3 h-3 {% if lang == 'fa' %}ml-1{% else %}mr-1{% endif %}" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
|
{% else %}
|
|
<svg class="w-3 h-3 {% if lang == 'fa' %}ml-1{% else %}mr-1{% endif %}" fill="currentColor" viewBox="0 0 24 24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/></svg>
|
|
{% endif %}
|
|
{{ user.provider|title }}
|
|
</span>
|
|
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 dark:bg-purple-900 text-purple-700 dark:text-purple-300">
|
|
{{ user.role|title }}
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Account Details -->
|
|
{% if user %}
|
|
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 border border-gray-200 dark:border-gray-700">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
|
{% if lang == 'fa' %}جزئیات حساب{% else %}Account Details{% endif %}
|
|
</h3>
|
|
<dl class="space-y-3">
|
|
<div class="flex justify-between">
|
|
<dt class="text-gray-500 dark:text-gray-400">{% if lang == 'fa' %}شناسه کاربر{% else %}User ID{% endif %}</dt>
|
|
<dd class="text-gray-700 dark:text-gray-200 font-mono text-sm">{{ user.id[:8] }}...</dd>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<dt class="text-gray-500 dark:text-gray-400">{% if lang == 'fa' %}ارائهدهنده ورود{% else %}Login Provider{% endif %}</dt>
|
|
<dd class="text-gray-700 dark:text-gray-200">{{ user.provider|title }}</dd>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<dt class="text-gray-500 dark:text-gray-400">{% if lang == 'fa' %}تاریخ عضویت{% else %}Joined{% endif %}</dt>
|
|
<dd class="text-gray-700 dark:text-gray-200">{{ user.created_at[:10] if user.created_at else "Unknown" }}</dd>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<dt class="text-gray-500 dark:text-gray-400">{% if lang == 'fa' %}آخرین ورود{% else %}Last Login{% endif %}</dt>
|
|
<dd class="text-gray-700 dark:text-gray-200">{{ user.last_login[:10] if user.last_login else "Never" }}</dd>
|
|
</div>
|
|
</dl>
|
|
|
|
<!-- Account linking note -->
|
|
<div class="mt-4 p-3 bg-blue-50 dark:bg-blue-500/10 border border-blue-200 dark:border-blue-500/30 rounded-lg">
|
|
<p class="text-xs text-blue-700 dark:text-blue-300">
|
|
{% if lang == 'fa' %}
|
|
حساب شما به آدرس ایمیل {{ user.email }} متصل است. میتوانید با هر ارائهدهندهای که از این ایمیل استفاده میکند وارد شوید.
|
|
{% else %}
|
|
Your account is linked to {{ user.email }}. You can sign in with any provider that uses this email address.
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Logout -->
|
|
<div class="text-center">
|
|
<a href="/auth/logout" class="inline-flex items-center px-4 py-2 text-red-600 dark:text-red-400 hover:text-red-500 dark:hover:text-red-300 border border-red-200 dark:border-red-500/30 rounded-lg hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
|
|
<svg class="w-5 h-5 {% 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="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
|
|
</svg>
|
|
{% if lang == 'fa' %}خروج{% else %}Sign Out{% endif %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|