diff --git a/README.md b/README.md
index f0f501f..358700f 100644
--- a/README.md
+++ b/README.md
@@ -277,6 +277,12 @@ GitHub Actions workflows in `.github/workflows/`:
## 📋 Fix / changelog (this fork)
+### v2.6.3
+* **Client connection domain (AWG / WireGuard)** — set a DNS name per server (or per AWG/WG protocol) instead of IP in client configs (`Endpoint = domain:port`). Survives server IP changes — update the A-record only. UI on server list, server page, and when adding a server.
+* **Legacy `data.json` → PostgreSQL** — import normalizes old backups (UUIDs, duplicate usernames/tokens, string `server_info`); failed auto-import no longer bricks panel startup; JSON restore returns clear errors.
+* **Dokploy / Traefik 404** — `traefik.http.services.amnezia_panel.loadbalancer.server.port=5000` in compose (Traefik must route to container port **5000**).
+* **Support the project** — `/support` page for all users (SBP / card / crypto placeholders); admin configures requisites in **Settings** (no popups).
+
### v2.6.2
* **Panel backup → PostgreSQL dump** — Settings backup downloads `.sql` via `pg_dump`; restore accepts `.sql` or legacy `data.json`. JSON export kept as secondary link.
diff --git a/app.py b/app.py
index c0f4bb5..2c26e36 100644
--- a/app.py
+++ b/app.py
@@ -103,7 +103,7 @@ else:
application_path = os.path.dirname(__file__)
DATA_FILE = os.path.join(application_path, 'data.json') # legacy JSON; used only for one-shot import / export
-CURRENT_VERSION = "v2.6.5"
+CURRENT_VERSION = "v2.6.3"
RELEASES_REPO_URL = repo_url()
RELEASES_API_LATEST = api_latest_url()
BIN_DIR = os.environ.get('TUNNEL_BIN_DIR', os.path.join(application_path, 'bin'))
@@ -241,12 +241,14 @@ def get_current_user(request: Request, data: Optional[dict] = None):
def tpl(request, template, **kwargs):
data = load_data()
lang = request.cookies.get('lang', 'en')
+ donate_cfg = (data.get('settings') or {}).get('donate') or {}
ctx = {
'request': request,
'current_user': get_current_user(request, data),
'site_settings': data.get('settings', {}).get('appearance', {}),
'captcha_settings': data.get('settings', {}).get('captcha', {}),
'telegram_settings': data.get('settings', {}).get('telegram', {}),
+ 'donate_enabled': bool(donate_cfg.get('enabled', True)),
'bot_running': tg_bot.is_running(),
'lang': lang,
'_': lambda text_id: _t(text_id, lang),
@@ -2359,6 +2361,20 @@ class GuestSettings(BaseModel):
create_xui_panel_id: str = ''
+class DonateMethodSettings(BaseModel):
+ enabled: bool = True
+ title: str = ''
+ details: str = ''
+
+
+class DonateSettings(BaseModel):
+ enabled: bool = True
+ intro: str = ''
+ sbp: DonateMethodSettings = DonateMethodSettings()
+ card: DonateMethodSettings = DonateMethodSettings()
+ crypto: DonateMethodSettings = DonateMethodSettings()
+
+
class UpdateUserRequest(BaseModel):
telegramId: Optional[str] = None
email: Optional[str] = None
@@ -2379,6 +2395,7 @@ class SaveSettingsRequest(BaseModel):
telegram: TelegramSettings
ssl: SSLSettings
guest: GuestSettings = GuestSettings()
+ donate: DonateSettings = DonateSettings()
class ToggleUserRequest(BaseModel):
@@ -6072,6 +6089,18 @@ async def api_my_connection_config(request: Request, connection_id: str):
return JSONResponse({'error': str(e)}, status_code=500)
+@app.get('/support', response_class=HTMLResponse, tags=["System Templates"])
+async def support_page(request: Request):
+ user = get_current_user(request)
+ if not user:
+ return RedirectResponse('/login', status_code=302)
+ data = load_data()
+ donate = dict((data.get('settings') or {}).get('donate') or {})
+ if not donate.get('enabled', True):
+ return RedirectResponse('/my', status_code=302)
+ return tpl(request, 'support.html', donate=donate)
+
+
@app.get('/settings', response_class=HTMLResponse, tags=["System Templates"])
async def settings_page(request: Request):
user = _check_admin(request)
@@ -6381,6 +6410,7 @@ async def save_settings(request: Request, payload: SaveSettingsRequest):
else:
guest['password_hash'] = existing_guest.get('password_hash')
data['settings']['guest'] = guest
+ data['settings']['donate'] = payload.donate.dict()
save_data(data)
logger.info("Settings saved (including captcha and telegram)")
diff --git a/db/store.py b/db/store.py
index e206f08..d3f7f45 100644
--- a/db/store.py
+++ b/db/store.py
@@ -54,6 +54,13 @@ DEFAULT_SETTINGS = {
'create_protocol': 'awg',
'create_server_id': 0,
},
+ 'donate': {
+ 'enabled': True,
+ 'intro': '',
+ 'sbp': {'enabled': True, 'title': '', 'details': ''},
+ 'card': {'enabled': True, 'title': '', 'details': ''},
+ 'crypto': {'enabled': True, 'title': '', 'details': ''},
+ },
}
diff --git a/static/css/style.css b/static/css/style.css
index 65aa2d2..b999b08 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -2207,6 +2207,15 @@ a:hover {
font-weight: 600;
}
+.nav-link-support {
+ opacity: 0.85;
+}
+
+.nav-link-support:hover {
+ opacity: 1;
+ color: #f472b6;
+}
+
.nav-user {
display: flex;
align-items: center;
diff --git a/static/icons.svg b/static/icons.svg
index 3cc7afd..98c620e 100644
--- a/static/icons.svg
+++ b/static/icons.svg
@@ -108,4 +108,7 @@
{{ _('donate_settings_hint') }}
+{{ _('donate_preview_hint') }} /support
++ {% if donate.get('intro') %} + {{ donate.intro }} + {% else %} + {{ _('support_intro') }} + {% endif %} +
++ {{ _('support_footer') }} +
+{% endblock %} diff --git a/translations/en.json b/translations/en.json index c70935d..eb00922 100644 --- a/translations/en.json +++ b/translations/en.json @@ -4,6 +4,7 @@ "nav_invites": "Invite links", "nav_settings": "Settings", "nav_connections": "Connections", + "nav_support": "Support", "nav_logout": "Logout", "theme_dark": "Dark", "theme_light": "Light", @@ -405,6 +406,21 @@ "server_connect_domain_ssh": "SSH", "server_connect_domain_saved": "Connection domain saved", "server_endpoint_label": "Endpoint", + "support_title": "Support the project", + "support_intro": "If this panel helps you, you can support development. Choose a convenient method below.", + "support_footer": "Thank you for using Amnezia Web Panel.", + "donate_sbp": "SBP (Fast Payments)", + "donate_card": "Bank card", + "donate_crypto": "Cryptocurrency", + "donate_details_soon": "Payment details will be added soon.", + "donate_settings_title": "Support / donations", + "donate_settings_hint": "Shown on the /support page for all users. No popups — only when they open the page.", + "donate_enabled": "Show support page in navigation", + "donate_intro": "Page intro text (optional)", + "donate_method_title": "Button title (optional)", + "donate_method_details": "Payment details", + "donate_details_placeholder": "Phone, card number, wallet address…", + "donate_preview_hint": "Preview:", "container_logs": "Container logs", "logs_btn": "📋 Logs", "logs_live": "Live", diff --git a/translations/ru.json b/translations/ru.json index 87399f1..06d8bc3 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -4,6 +4,7 @@ "nav_invites": "Ссылки", "nav_settings": "Настройки", "nav_connections": "Подключения", + "nav_support": "Поддержать", "nav_logout": "Выход", "theme_dark": "Темная", "theme_light": "Светлая", @@ -405,6 +406,21 @@ "server_connect_domain_ssh": "SSH", "server_connect_domain_saved": "Домен подключения сохранён", "server_endpoint_label": "Endpoint", + "support_title": "Поддержать проект", + "support_intro": "Если панель вам помогает, можно поддержать разработку. Выберите удобный способ ниже.", + "support_footer": "Спасибо, что пользуетесь Amnezia Web Panel.", + "donate_sbp": "СБП", + "donate_card": "Банковская карта", + "donate_crypto": "Криптовалюта", + "donate_details_soon": "Реквизиты будут добавлены позже.", + "donate_settings_title": "Поддержка / донат", + "donate_settings_hint": "Страница /support для всех пользователей. Без всплывающих окон — только по ссылке в меню.", + "donate_enabled": "Показывать страницу поддержки в меню", + "donate_intro": "Текст в начале страницы (необязательно)", + "donate_method_title": "Название кнопки (необязательно)", + "donate_method_details": "Реквизиты", + "donate_details_placeholder": "Телефон, номер карты, адрес кошелька…", + "donate_preview_hint": "Предпросмотр:", "container_logs": "Логи контейнера", "logs_btn": "📋 Логи", "logs_live": "В реальном времени",