diff --git a/migrations/001_init.sql b/migrations/001_init.sql index 739a1de..a584315 100644 --- a/migrations/001_init.sql +++ b/migrations/001_init.sql @@ -208,6 +208,10 @@ INSERT INTO translations (language_code, translation_key, translation_value) VAL ('en', 'clients.traffic_limit', 'Traffic Limit'), ('en', 'clients.unlimited', 'Unlimited'), ('en', 'clients.overlimit', 'Over Limit'), +('en', 'clients.custom_seconds', 'Custom (seconds)'), +('en', 'clients.custom_mb', 'Custom (MB)'), +('en', 'clients.enter_seconds', 'Enter seconds'), +('en', 'clients.enter_megabytes', 'Enter megabytes'), ('en', 'backups.title', 'Server Backups'), ('en', 'backups.create', 'Create Backup'), ('en', 'backups.restore', 'Restore'), diff --git a/migrations/0025_translations_es.sql b/migrations/0025_translations_es.sql deleted file mode 100644 index 5528bf6..0000000 --- a/migrations/0025_translations_es.sql +++ /dev/null @@ -1,127 +0,0 @@ --- Spanish translations --- This migration adds Spanish language translations - -INSERT INTO translations (language_code, translation_key, translation_value) VALUES -('es', 'auth.email', 'Correo electrónico'), -('es', 'auth.login', 'Iniciar sesión'), -('es', 'auth.name', 'Nombre'), -('es', 'auth.password', 'Contraseña'), -('es', 'auth.register', 'Registrarse'), -('es', 'backups.create', 'Crear copia de seguridad'), -('es', 'backups.create_confirm', '¿Crear copia de seguridad de todos los clientes en este servidor?'), -('es', 'backups.created_success', 'Copia de seguridad creada exitosamente'), -('es', 'backups.delete_confirm', '¿Eliminar esta copia de seguridad permanentemente?'), -('es', 'backups.deleted_success', 'Copia de seguridad eliminada exitosamente'), -('es', 'backups.login_required', 'Por favor inicie sesión vía API para gestionar copias de seguridad'), -('es', 'backups.no_backups', 'Aún no hay copias de seguridad'), -('es', 'backups.restore', 'Restaurar'), -('es', 'backups.restore_confirm', '¿Restaurar clientes desde esta copia de seguridad? Los clientes existentes no se verán afectados.'), -('es', 'backups.restored_success', 'Restaurado'), -('es', 'backups.title', 'Copias de seguridad del servidor'), -('es', 'clients.actions', 'Acciones'), -('es', 'clients.add', 'Agregar cliente'), -('es', 'clients.create', 'Crear cliente'), -('es', 'clients.delete', 'Eliminar'), -('es', 'clients.download_config', 'Descargar configuración'), -('es', 'clients.expiration', 'Vencimiento'), -('es', 'clients.expired', 'Vencido'), -('es', 'clients.ip', 'Dirección IP'), -('es', 'clients.last_handshake', 'Último contacto'), -('es', 'clients.name', 'Nombre del cliente'), -('es', 'clients.never_expires', 'Nunca vence'), -('es', 'clients.qr_code', 'Código QR'), -('es', 'clients.received', 'Recibido'), -('es', 'clients.restore', 'Restaurar'), -('es', 'clients.revoke', 'Revocar'), -('es', 'clients.sent', 'Enviado'), -('es', 'clients.server', 'Servidor'), -('es', 'clients.status', 'Estado'), -('es', 'clients.sync_stats', 'Sincronizar estadísticas'), -('es', 'clients.title', 'Clientes'), -('es', 'clients.traffic', 'Tráfico'), -('es', 'common.days', 'días'), -('es', 'dashboard.active_clients', 'Clientes activos'), -('es', 'dashboard.add_first_server', 'Agregar primer servidor'), -('es', 'dashboard.get_started', 'Comience agregando su primer servidor VPN'), -('es', 'dashboard.no_servers', 'Aún no hay servidores'), -('es', 'dashboard.quick_actions', 'Acciones rápidas'), -('es', 'dashboard.recent_servers', 'Servidores recientes'), -('es', 'dashboard.title', 'Panel de control'), -('es', 'dashboard.total_clients', 'Total de clientes'), -('es', 'dashboard.total_servers', 'Total de servidores'), -('es', 'dashboard.total_traffic', 'Tráfico total'), -('es', 'dashboard.welcome', 'Bienvenido al Panel de Gestión de Amnezia VPN'), -('es', 'form.cancel', 'Cancelar'), -('es', 'form.close', 'Cerrar'), -('es', 'form.create', 'Crear'), -('es', 'form.loading', 'Cargando...'), -('es', 'form.processing', 'Procesando...'), -('es', 'form.save', 'Guardar'), -('es', 'form.submit', 'Enviar'), -('es', 'form.update', 'Actualizar'), -('es', 'menu.clients', 'Clientes'), -('es', 'menu.dashboard', 'Panel de control'), -('es', 'menu.logout', 'Cerrar sesión'), -('es', 'menu.servers', 'Servidores'), -('es', 'menu.settings', 'Configuración'), -('es', 'menu.users', 'Usuarios'), -('es', 'message.confirm', '¿Está seguro?'), -('es', 'message.deleted', 'Eliminado exitosamente'), -('es', 'message.deployed', 'Implementado exitosamente'), -('es', 'message.error', 'Ha ocurrido un error'), -('es', 'message.saved', 'Guardado exitosamente'), -('es', 'message.success', 'Operación completada exitosamente'), -('es', 'servers.actions', 'Acciones'), -('es', 'servers.add', 'Agregar servidor'), -('es', 'servers.clients', 'Clientes'), -('es', 'servers.delete', 'Eliminar'), -('es', 'servers.deploy', 'Implementar'), -('es', 'servers.edit', 'Editar'), -('es', 'servers.host', 'Host'), -('es', 'servers.name', 'Nombre'), -('es', 'servers.port', 'Puerto'), -('es', 'servers.status', 'Estado'), -('es', 'servers.title', 'Servidores'), -('es', 'servers.view', 'Ver'), -('es', 'settings.actions', 'Acciones'), -('es', 'settings.api_key_configured', 'Clave API configurada'), -('es', 'settings.api_keys', 'Claves API'), -('es', 'settings.api_keys_desc', 'Configurar claves API para servicios externos'), -('es', 'settings.auto_translate', 'Auto-traducir'), -('es', 'settings.change_password', 'Cambiar contraseña'), -('es', 'settings.confirm_password', 'Confirmar contraseña'), -('es', 'settings.confirm_translate', '¿Iniciar traducción automática? Esto puede tomar unos minutos.'), -('es', 'settings.current_password', 'Contraseña actual'), -('es', 'settings.description', 'Gestionar configuración del panel e integraciones API'), -('es', 'settings.error_empty_key', 'La clave API no puede estar vacía'), -('es', 'settings.error_invalid_key', 'Formato de clave API inválido'), -('es', 'settings.error_key_test', 'Prueba de clave API fallida'), -('es', 'settings.for_translation', 'para auto-traducción'), -('es', 'settings.get_key_at', 'Obtenga su clave API en'), -('es', 'settings.key_saved', 'Clave API guardada exitosamente'), -('es', 'settings.keys', 'claves'), -('es', 'settings.language', 'Idioma'), -('es', 'settings.min_6_chars', 'Mínimo 6 caracteres'), -('es', 'settings.new_password', 'Nueva contraseña'), -('es', 'settings.no_api_key', 'No hay clave API configurada. La auto-traducción no funcionará.'), -('es', 'settings.profile', 'Perfil'), -('es', 'settings.progress', 'Progreso'), -('es', 'settings.skip_validation', 'Omitir validación (guardar sin probar)'), -('es', 'settings.translation_complete', 'Traducción completada'), -('es', 'settings.translation_status', 'Estado de traducción'), -('es', 'settings.translations', 'Traducciones'), -('es', 'settings.users', 'Usuarios'), -('es', 'status.active', 'Activo'), -('es', 'status.deploying', 'Implementando'), -('es', 'status.disabled', 'Deshabilitado'), -('es', 'status.error', 'Error'), -('es', 'status.inactive', 'Inactivo'), -('es', 'users.add_user', 'Agregar usuario'), -('es', 'users.administrator', 'Administrador'), -('es', 'users.all_users', 'Todos los usuarios'), -('es', 'users.created', 'Creado'), -('es', 'users.delete_confirm', '¿Eliminar {0}?'), -('es', 'users.role', 'Rol'), -('es', 'users.role_admin', 'Administrador'), -('es', 'users.role_user', 'Usuario') -ON DUPLICATE KEY UPDATE translation_value=VALUES(translation_value); diff --git a/migrations/002_translations_ru.sql b/migrations/002_translations_ru.sql index 2f10516..8f57c3e 100644 --- a/migrations/002_translations_ru.sql +++ b/migrations/002_translations_ru.sql @@ -46,6 +46,10 @@ INSERT INTO translations (language_code, translation_key, translation_value) VAL ('ru', 'clients.traffic', 'Трафик'), ('ru', 'clients.traffic_limit', 'Лимит трафика'), ('ru', 'clients.unlimited', 'Безлимитно'), +('ru', 'clients.custom_seconds', 'Своё значение (секунды)'), +('ru', 'clients.custom_mb', 'Своё значение (МБ)'), +('ru', 'clients.enter_seconds', 'Введите секунды'), +('ru', 'clients.enter_megabytes', 'Введите мегабайты'), ('ru', 'common.days', 'дней'), ('ru', 'dashboard.active_clients', 'Активные клиенты'), ('ru', 'dashboard.add_first_server', 'Добавить первый сервер'), diff --git a/migrations/003_translations_es.sql b/migrations/003_translations_es.sql index be98810..43ae907 100644 --- a/migrations/003_translations_es.sql +++ b/migrations/003_translations_es.sql @@ -46,6 +46,10 @@ INSERT INTO translations (language_code, translation_key, translation_value) VAL ('es', 'clients.traffic', 'Tráfico'), ('es', 'clients.traffic_limit', 'Límite de tráfico'), ('es', 'clients.unlimited', 'Ilimitado'), +('es', 'clients.custom_seconds', 'Personalizado (segundos)'), +('es', 'clients.custom_mb', 'Personalizado (MB)'), +('es', 'clients.enter_seconds', 'Ingrese segundos'), +('es', 'clients.enter_megabytes', 'Ingrese megabytes'), ('es', 'common.days', 'días'), ('es', 'dashboard.active_clients', 'Clientes activos'), ('es', 'dashboard.add_first_server', 'Agregar primer servidor'), diff --git a/migrations/0044_translations_de.sql b/migrations/0044_translations_de.sql deleted file mode 100644 index 2b54a85..0000000 --- a/migrations/0044_translations_de.sql +++ /dev/null @@ -1,127 +0,0 @@ --- German translations --- This migration adds German language translations - -INSERT INTO translations (language_code, translation_key, translation_value) VALUES -('de', 'auth.email', 'E-Mail'), -('de', 'auth.login', 'Anmelden'), -('de', 'auth.name', 'Name'), -('de', 'auth.password', 'Passwort'), -('de', 'auth.register', 'Registrieren'), -('de', 'backups.create', 'Backup erstellen'), -('de', 'backups.create_confirm', 'Backup aller Clients auf diesem Server erstellen?'), -('de', 'backups.created_success', 'Backup erfolgreich erstellt'), -('de', 'backups.delete_confirm', 'Dieses Backup endgültig löschen?'), -('de', 'backups.deleted_success', 'Backup erfolgreich gelöscht'), -('de', 'backups.login_required', 'Bitte melden Sie sich über die API an, um Backups zu verwalten'), -('de', 'backups.no_backups', 'Noch keine Backups'), -('de', 'backups.restore', 'Wiederherstellen'), -('de', 'backups.restore_confirm', 'Clients aus diesem Backup wiederherstellen? Bestehende Clients bleiben unberührt.'), -('de', 'backups.restored_success', 'Wiederhergestellt'), -('de', 'backups.title', 'Server-Backups'), -('de', 'clients.actions', 'Aktionen'), -('de', 'clients.add', 'Client hinzufügen'), -('de', 'clients.create', 'Client erstellen'), -('de', 'clients.delete', 'Löschen'), -('de', 'clients.download_config', 'Konfiguration herunterladen'), -('de', 'clients.expiration', 'Ablaufdatum'), -('de', 'clients.expired', 'Abgelaufen'), -('de', 'clients.ip', 'IP-Adresse'), -('de', 'clients.last_handshake', 'Letzter Handshake'), -('de', 'clients.name', 'Client-Name'), -('de', 'clients.never_expires', 'Läuft nie ab'), -('de', 'clients.qr_code', 'QR-Code'), -('de', 'clients.received', 'Empfangen'), -('de', 'clients.restore', 'Wiederherstellen'), -('de', 'clients.revoke', 'Widerrufen'), -('de', 'clients.sent', 'Gesendet'), -('de', 'clients.server', 'Server'), -('de', 'clients.status', 'Status'), -('de', 'clients.sync_stats', 'Statistiken synchronisieren'), -('de', 'clients.title', 'Clients'), -('de', 'clients.traffic', 'Datenverkehr'), -('de', 'common.days', 'Tage'), -('de', 'dashboard.active_clients', 'Aktive Clients'), -('de', 'dashboard.add_first_server', 'Ersten Server hinzufügen'), -('de', 'dashboard.get_started', 'Beginnen Sie mit dem Hinzufügen Ihres ersten VPN-Servers'), -('de', 'dashboard.no_servers', 'Noch keine Server'), -('de', 'dashboard.quick_actions', 'Schnellaktionen'), -('de', 'dashboard.recent_servers', 'Aktuelle Server'), -('de', 'dashboard.title', 'Dashboard'), -('de', 'dashboard.total_clients', 'Gesamtzahl Clients'), -('de', 'dashboard.total_servers', 'Gesamtzahl Server'), -('de', 'dashboard.total_traffic', 'Gesamter Datenverkehr'), -('de', 'dashboard.welcome', 'Willkommen im Amnezia VPN Verwaltungspanel'), -('de', 'form.cancel', 'Abbrechen'), -('de', 'form.close', 'Schließen'), -('de', 'form.create', 'Erstellen'), -('de', 'form.loading', 'Lädt...'), -('de', 'form.processing', 'Verarbeitung...'), -('de', 'form.save', 'Speichern'), -('de', 'form.submit', 'Absenden'), -('de', 'form.update', 'Aktualisieren'), -('de', 'menu.clients', 'Clients'), -('de', 'menu.dashboard', 'Dashboard'), -('de', 'menu.logout', 'Abmelden'), -('de', 'menu.servers', 'Server'), -('de', 'menu.settings', 'Einstellungen'), -('de', 'menu.users', 'Benutzer'), -('de', 'message.confirm', 'Sind Sie sicher?'), -('de', 'message.deleted', 'Erfolgreich gelöscht'), -('de', 'message.deployed', 'Erfolgreich bereitgestellt'), -('de', 'message.error', 'Ein Fehler ist aufgetreten'), -('de', 'message.saved', 'Erfolgreich gespeichert'), -('de', 'message.success', 'Vorgang erfolgreich abgeschlossen'), -('de', 'servers.actions', 'Aktionen'), -('de', 'servers.add', 'Server hinzufügen'), -('de', 'servers.clients', 'Clients'), -('de', 'servers.delete', 'Löschen'), -('de', 'servers.deploy', 'Bereitstellen'), -('de', 'servers.edit', 'Bearbeiten'), -('de', 'servers.host', 'Host'), -('de', 'servers.name', 'Name'), -('de', 'servers.port', 'Port'), -('de', 'servers.status', 'Status'), -('de', 'servers.title', 'Server'), -('de', 'servers.view', 'Ansehen'), -('de', 'settings.actions', 'Aktionen'), -('de', 'settings.api_key_configured', 'API-Schlüssel konfiguriert'), -('de', 'settings.api_keys', 'API-Schlüssel'), -('de', 'settings.api_keys_desc', 'API-Schlüssel für externe Dienste konfigurieren'), -('de', 'settings.auto_translate', 'Automatische Übersetzung'), -('de', 'settings.change_password', 'Passwort ändern'), -('de', 'settings.confirm_password', 'Passwort bestätigen'), -('de', 'settings.confirm_translate', 'Automatische Übersetzung starten? Dies kann einige Minuten dauern.'), -('de', 'settings.current_password', 'Aktuelles Passwort'), -('de', 'settings.description', 'Panel-Konfiguration und API-Integrationen verwalten'), -('de', 'settings.error_empty_key', 'API-Schlüssel darf nicht leer sein'), -('de', 'settings.error_invalid_key', 'Ungültiges API-Schlüssel-Format'), -('de', 'settings.error_key_test', 'API-Schlüssel-Test fehlgeschlagen'), -('de', 'settings.for_translation', 'für automatische Übersetzung'), -('de', 'settings.get_key_at', 'Holen Sie sich Ihren API-Schlüssel bei'), -('de', 'settings.key_saved', 'API-Schlüssel erfolgreich gespeichert'), -('de', 'settings.keys', 'Schlüssel'), -('de', 'settings.language', 'Sprache'), -('de', 'settings.min_6_chars', 'Mindestens 6 Zeichen'), -('de', 'settings.new_password', 'Neues Passwort'), -('de', 'settings.no_api_key', 'Kein API-Schlüssel konfiguriert. Automatische Übersetzung wird nicht funktionieren.'), -('de', 'settings.profile', 'Profil'), -('de', 'settings.progress', 'Fortschritt'), -('de', 'settings.skip_validation', 'Validierung überspringen (ohne Test speichern)'), -('de', 'settings.translation_complete', 'Übersetzung abgeschlossen'), -('de', 'settings.translation_status', 'Übersetzungsstatus'), -('de', 'settings.translations', 'Übersetzungen'), -('de', 'settings.users', 'Benutzer'), -('de', 'status.active', 'Aktiv'), -('de', 'status.deploying', 'Wird bereitgestellt'), -('de', 'status.disabled', 'Deaktiviert'), -('de', 'status.error', 'Fehler'), -('de', 'status.inactive', 'Inaktiv'), -('de', 'users.add_user', 'Benutzer hinzufügen'), -('de', 'users.administrator', 'Administrator'), -('de', 'users.all_users', 'Alle Benutzer'), -('de', 'users.created', 'Erstellt'), -('de', 'users.delete_confirm', '{0} löschen?'), -('de', 'users.role', 'Rolle'), -('de', 'users.role_admin', 'Admin'), -('de', 'users.role_user', 'Benutzer') -ON DUPLICATE KEY UPDATE translation_value=VALUES(translation_value); diff --git a/migrations/004_translations_de.sql b/migrations/004_translations_de.sql index 1ebcfa7..6d9a0b7 100644 --- a/migrations/004_translations_de.sql +++ b/migrations/004_translations_de.sql @@ -46,6 +46,10 @@ INSERT INTO translations (language_code, translation_key, translation_value) VAL ('de', 'clients.traffic', 'Datenverkehr'), ('de', 'clients.traffic_limit', 'Traffic-Limit'), ('de', 'clients.unlimited', 'Unbegrenzt'), +('de', 'clients.custom_seconds', 'Benutzerdefiniert (Sekunden)'), +('de', 'clients.custom_mb', 'Benutzerdefiniert (MB)'), +('de', 'clients.enter_seconds', 'Sekunden eingeben'), +('de', 'clients.enter_megabytes', 'Megabytes eingeben'), ('de', 'common.days', 'Tage'), ('de', 'dashboard.active_clients', 'Aktive Clients'), ('de', 'dashboard.add_first_server', 'Ersten Server hinzufügen'), diff --git a/migrations/0053_translations_ru.sql b/migrations/0053_translations_ru.sql deleted file mode 100644 index e61b5ba..0000000 --- a/migrations/0053_translations_ru.sql +++ /dev/null @@ -1,127 +0,0 @@ --- Russian translations --- This migration adds Russian language translations - -INSERT INTO translations (language_code, translation_key, translation_value) VALUES -('ru', 'auth.email', 'Email'), -('ru', 'auth.login', 'Вход'), -('ru', 'auth.name', 'Имя'), -('ru', 'auth.password', 'Пароль'), -('ru', 'auth.register', 'Регистрация'), -('ru', 'backups.create', 'Создать резервную копию'), -('ru', 'backups.create_confirm', 'Создать резервную копию всех клиентов на этом сервере?'), -('ru', 'backups.created_success', 'Резервная копия успешно создана'), -('ru', 'backups.delete_confirm', 'Удалить эту резервную копию навсегда?'), -('ru', 'backups.deleted_success', 'Резервная копия успешно удалена'), -('ru', 'backups.login_required', 'Пожалуйста, войдите через API для управления резервными копиями'), -('ru', 'backups.no_backups', 'Пока нет резервных копий'), -('ru', 'backups.restore', 'Восстановить'), -('ru', 'backups.restore_confirm', 'Восстановить клиентов из этой резервной копии? Существующие клиенты не будут затронуты.'), -('ru', 'backups.restored_success', 'Восстановлено'), -('ru', 'backups.title', 'Резервные копии сервера'), -('ru', 'clients.actions', 'Действия'), -('ru', 'clients.add', 'Добавить клиента'), -('ru', 'clients.create', 'Создать клиента'), -('ru', 'clients.delete', 'Удалить'), -('ru', 'clients.download_config', 'Скачать конфигурацию'), -('ru', 'clients.expiration', 'Срок действия'), -('ru', 'clients.expired', 'Истек'), -('ru', 'clients.ip', 'IP-адрес'), -('ru', 'clients.last_handshake', 'Последнее соединение'), -('ru', 'clients.name', 'Имя клиента'), -('ru', 'clients.never_expires', 'Бессрочно'), -('ru', 'clients.qr_code', 'QR-код'), -('ru', 'clients.received', 'Получено'), -('ru', 'clients.restore', 'Восстановить'), -('ru', 'clients.revoke', 'Отозвать'), -('ru', 'clients.sent', 'Отправлено'), -('ru', 'clients.server', 'Сервер'), -('ru', 'clients.status', 'Статус'), -('ru', 'clients.sync_stats', 'Синхронизировать статистику'), -('ru', 'clients.title', 'Клиенты'), -('ru', 'clients.traffic', 'Трафик'), -('ru', 'common.days', 'дней'), -('ru', 'dashboard.active_clients', 'Активные клиенты'), -('ru', 'dashboard.add_first_server', 'Добавить первый сервер'), -('ru', 'dashboard.get_started', 'Начните с добавления вашего первого VPN-сервера'), -('ru', 'dashboard.no_servers', 'Пока нет серверов'), -('ru', 'dashboard.quick_actions', 'Быстрые действия'), -('ru', 'dashboard.recent_servers', 'Недавние серверы'), -('ru', 'dashboard.title', 'Панель управления'), -('ru', 'dashboard.total_clients', 'Всего клиентов'), -('ru', 'dashboard.total_servers', 'Всего серверов'), -('ru', 'dashboard.total_traffic', 'Общий трафик'), -('ru', 'dashboard.welcome', 'Добро пожаловать в панель управления Amnezia VPN'), -('ru', 'form.cancel', 'Отмена'), -('ru', 'form.close', 'Закрыть'), -('ru', 'form.create', 'Создать'), -('ru', 'form.loading', 'Загрузка...'), -('ru', 'form.processing', 'Обработка...'), -('ru', 'form.save', 'Сохранить'), -('ru', 'form.submit', 'Отправить'), -('ru', 'form.update', 'Обновить'), -('ru', 'menu.clients', 'Клиенты'), -('ru', 'menu.dashboard', 'Панель управления'), -('ru', 'menu.logout', 'Выход'), -('ru', 'menu.servers', 'Серверы'), -('ru', 'menu.settings', 'Настройки'), -('ru', 'menu.users', 'Пользователи'), -('ru', 'message.confirm', 'Вы уверены?'), -('ru', 'message.deleted', 'Успешно удалено'), -('ru', 'message.deployed', 'Успешно развернуто'), -('ru', 'message.error', 'Произошла ошибка'), -('ru', 'message.saved', 'Успешно сохранено'), -('ru', 'message.success', 'Операция успешно завершена'), -('ru', 'servers.actions', 'Действия'), -('ru', 'servers.add', 'Добавить сервер'), -('ru', 'servers.clients', 'Клиенты'), -('ru', 'servers.delete', 'Удалить'), -('ru', 'servers.deploy', 'Развернуть'), -('ru', 'servers.edit', 'Редактировать'), -('ru', 'servers.host', 'Хост'), -('ru', 'servers.name', 'Имя'), -('ru', 'servers.port', 'Порт'), -('ru', 'servers.status', 'Статус'), -('ru', 'servers.title', 'Серверы'), -('ru', 'servers.view', 'Просмотр'), -('ru', 'settings.actions', 'Действия'), -('ru', 'settings.api_key_configured', 'API-ключ настроен'), -('ru', 'settings.api_keys', 'API-ключи'), -('ru', 'settings.api_keys_desc', 'Настройка API-ключей для внешних сервисов'), -('ru', 'settings.auto_translate', 'Автоперевод'), -('ru', 'settings.change_password', 'Изменить пароль'), -('ru', 'settings.confirm_password', 'Подтвердите пароль'), -('ru', 'settings.confirm_translate', 'Начать автоматический перевод? Это может занять несколько минут.'), -('ru', 'settings.current_password', 'Текущий пароль'), -('ru', 'settings.description', 'Управление конфигурацией панели и интеграциями API'), -('ru', 'settings.error_empty_key', 'API-ключ не может быть пустым'), -('ru', 'settings.error_invalid_key', 'Неверный формат API-ключа'), -('ru', 'settings.error_key_test', 'Тест API-ключа не удался'), -('ru', 'settings.for_translation', 'для автоперевода'), -('ru', 'settings.get_key_at', 'Получите ваш API-ключ на'), -('ru', 'settings.key_saved', 'API-ключ успешно сохранен'), -('ru', 'settings.keys', 'ключи'), -('ru', 'settings.language', 'Язык'), -('ru', 'settings.min_6_chars', 'Минимум 6 символов'), -('ru', 'settings.new_password', 'Новый пароль'), -('ru', 'settings.no_api_key', 'API-ключ не настроен. Автоперевод не будет работать.'), -('ru', 'settings.profile', 'Профиль'), -('ru', 'settings.progress', 'Прогресс'), -('ru', 'settings.skip_validation', 'Пропустить проверку (сохранить без тестирования)'), -('ru', 'settings.translation_complete', 'Перевод завершен'), -('ru', 'settings.translation_status', 'Статус перевода'), -('ru', 'settings.translations', 'Переводы'), -('ru', 'settings.users', 'Пользователи'), -('ru', 'status.active', 'Активен'), -('ru', 'status.deploying', 'Развертывание'), -('ru', 'status.disabled', 'Отключен'), -('ru', 'status.error', 'Ошибка'), -('ru', 'status.inactive', 'Неактивен'), -('ru', 'users.add_user', 'Добавить пользователя'), -('ru', 'users.administrator', 'Администратор'), -('ru', 'users.all_users', 'Все пользователи'), -('ru', 'users.created', 'Создан'), -('ru', 'users.delete_confirm', 'Удалить {0}?'), -('ru', 'users.role', 'Роль'), -('ru', 'users.role_admin', 'Администратор'), -('ru', 'users.role_user', 'Пользователь') -ON DUPLICATE KEY UPDATE translation_value=VALUES(translation_value); diff --git a/migrations/0057_translations_fr.sql b/migrations/0057_translations_fr.sql deleted file mode 100644 index 655185d..0000000 --- a/migrations/0057_translations_fr.sql +++ /dev/null @@ -1,127 +0,0 @@ --- French translations --- This migration adds French language translations - -INSERT INTO translations (language_code, translation_key, translation_value) VALUES -('fr', 'auth.email', 'Email'), -('fr', 'auth.login', 'Connexion'), -('fr', 'auth.name', 'Nom'), -('fr', 'auth.password', 'Mot de passe'), -('fr', 'auth.register', 'S''inscrire'), -('fr', 'backups.create', 'Créer une sauvegarde'), -('fr', 'backups.create_confirm', 'Créer une sauvegarde de tous les clients sur ce serveur ?'), -('fr', 'backups.created_success', 'Sauvegarde créée avec succès'), -('fr', 'backups.delete_confirm', 'Supprimer définitivement cette sauvegarde ?'), -('fr', 'backups.deleted_success', 'Sauvegarde supprimée avec succès'), -('fr', 'backups.login_required', 'Veuillez vous connecter via l''API pour gérer les sauvegardes'), -('fr', 'backups.no_backups', 'Aucune sauvegarde pour le moment'), -('fr', 'backups.restore', 'Restaurer'), -('fr', 'backups.restore_confirm', 'Restaurer les clients depuis cette sauvegarde ? Les clients existants ne seront pas affectés.'), -('fr', 'backups.restored_success', 'Restauré'), -('fr', 'backups.title', 'Sauvegardes du serveur'), -('fr', 'clients.actions', 'Actions'), -('fr', 'clients.add', 'Ajouter un client'), -('fr', 'clients.create', 'Créer un client'), -('fr', 'clients.delete', 'Supprimer'), -('fr', 'clients.download_config', 'Télécharger la configuration'), -('fr', 'clients.expiration', 'Expiration'), -('fr', 'clients.expired', 'Expiré'), -('fr', 'clients.ip', 'Adresse IP'), -('fr', 'clients.last_handshake', 'Dernière connexion'), -('fr', 'clients.name', 'Nom du client'), -('fr', 'clients.never_expires', 'N''expire jamais'), -('fr', 'clients.qr_code', 'Code QR'), -('fr', 'clients.received', 'Reçu'), -('fr', 'clients.restore', 'Restaurer'), -('fr', 'clients.revoke', 'Révoquer'), -('fr', 'clients.sent', 'Envoyé'), -('fr', 'clients.server', 'Serveur'), -('fr', 'clients.status', 'Statut'), -('fr', 'clients.sync_stats', 'Synchroniser les statistiques'), -('fr', 'clients.title', 'Clients'), -('fr', 'clients.traffic', 'Trafic'), -('fr', 'common.days', 'jours'), -('fr', 'dashboard.active_clients', 'Clients actifs'), -('fr', 'dashboard.add_first_server', 'Ajouter le premier serveur'), -('fr', 'dashboard.get_started', 'Commencez par ajouter votre premier serveur VPN'), -('fr', 'dashboard.no_servers', 'Aucun serveur pour le moment'), -('fr', 'dashboard.quick_actions', 'Actions rapides'), -('fr', 'dashboard.recent_servers', 'Serveurs récents'), -('fr', 'dashboard.title', 'Tableau de bord'), -('fr', 'dashboard.total_clients', 'Total des clients'), -('fr', 'dashboard.total_servers', 'Total des serveurs'), -('fr', 'dashboard.total_traffic', 'Trafic total'), -('fr', 'dashboard.welcome', 'Bienvenue sur le panneau de gestion Amnezia VPN'), -('fr', 'form.cancel', 'Annuler'), -('fr', 'form.close', 'Fermer'), -('fr', 'form.create', 'Créer'), -('fr', 'form.loading', 'Chargement...'), -('fr', 'form.processing', 'Traitement...'), -('fr', 'form.save', 'Enregistrer'), -('fr', 'form.submit', 'Soumettre'), -('fr', 'form.update', 'Mettre à jour'), -('fr', 'menu.clients', 'Clients'), -('fr', 'menu.dashboard', 'Tableau de bord'), -('fr', 'menu.logout', 'Déconnexion'), -('fr', 'menu.servers', 'Serveurs'), -('fr', 'menu.settings', 'Paramètres'), -('fr', 'menu.users', 'Utilisateurs'), -('fr', 'message.confirm', 'Êtes-vous sûr ?'), -('fr', 'message.deleted', 'Supprimé avec succès'), -('fr', 'message.deployed', 'Déployé avec succès'), -('fr', 'message.error', 'Une erreur est survenue'), -('fr', 'message.saved', 'Enregistré avec succès'), -('fr', 'message.success', 'Opération terminée avec succès'), -('fr', 'servers.actions', 'Actions'), -('fr', 'servers.add', 'Ajouter un serveur'), -('fr', 'servers.clients', 'Clients'), -('fr', 'servers.delete', 'Supprimer'), -('fr', 'servers.deploy', 'Déployer'), -('fr', 'servers.edit', 'Modifier'), -('fr', 'servers.host', 'Hôte'), -('fr', 'servers.name', 'Nom'), -('fr', 'servers.port', 'Port'), -('fr', 'servers.status', 'Statut'), -('fr', 'servers.title', 'Serveurs'), -('fr', 'servers.view', 'Voir'), -('fr', 'settings.actions', 'Actions'), -('fr', 'settings.api_key_configured', 'Clé API configurée'), -('fr', 'settings.api_keys', 'Clés API'), -('fr', 'settings.api_keys_desc', 'Configurer les clés API pour les services externes'), -('fr', 'settings.auto_translate', 'Traduction automatique'), -('fr', 'settings.change_password', 'Changer le mot de passe'), -('fr', 'settings.confirm_password', 'Confirmer le mot de passe'), -('fr', 'settings.confirm_translate', 'Démarrer la traduction automatique ? Cela peut prendre quelques minutes.'), -('fr', 'settings.current_password', 'Mot de passe actuel'), -('fr', 'settings.description', 'Gérer la configuration du panneau et les intégrations API'), -('fr', 'settings.error_empty_key', 'La clé API ne peut pas être vide'), -('fr', 'settings.error_invalid_key', 'Format de clé API invalide'), -('fr', 'settings.error_key_test', 'Test de la clé API échoué'), -('fr', 'settings.for_translation', 'pour la traduction automatique'), -('fr', 'settings.get_key_at', 'Obtenez votre clé API sur'), -('fr', 'settings.key_saved', 'Clé API enregistrée avec succès'), -('fr', 'settings.keys', 'clés'), -('fr', 'settings.language', 'Langue'), -('fr', 'settings.min_6_chars', 'Minimum 6 caractères'), -('fr', 'settings.new_password', 'Nouveau mot de passe'), -('fr', 'settings.no_api_key', 'Aucune clé API configurée. La traduction automatique ne fonctionnera pas.'), -('fr', 'settings.profile', 'Profil'), -('fr', 'settings.progress', 'Progression'), -('fr', 'settings.skip_validation', 'Ignorer la validation (enregistrer sans tester)'), -('fr', 'settings.translation_complete', 'Traduction terminée'), -('fr', 'settings.translation_status', 'État de la traduction'), -('fr', 'settings.translations', 'Traductions'), -('fr', 'settings.users', 'Utilisateurs'), -('fr', 'status.active', 'Actif'), -('fr', 'status.deploying', 'Déploiement'), -('fr', 'status.disabled', 'Désactivé'), -('fr', 'status.error', 'Erreur'), -('fr', 'status.inactive', 'Inactif'), -('fr', 'users.add_user', 'Ajouter un utilisateur'), -('fr', 'users.administrator', 'Administrateur'), -('fr', 'users.all_users', 'Tous les utilisateurs'), -('fr', 'users.created', 'Créé'), -('fr', 'users.delete_confirm', 'Supprimer {0} ?'), -('fr', 'users.role', 'Rôle'), -('fr', 'users.role_admin', 'Administrateur'), -('fr', 'users.role_user', 'Utilisateur') -ON DUPLICATE KEY UPDATE translation_value=VALUES(translation_value); diff --git a/migrations/005_translations_fr.sql b/migrations/005_translations_fr.sql index b416972..c0968e6 100644 --- a/migrations/005_translations_fr.sql +++ b/migrations/005_translations_fr.sql @@ -46,6 +46,10 @@ INSERT INTO translations (language_code, translation_key, translation_value) VAL ('fr', 'clients.traffic', 'Trafic'), ('fr', 'clients.traffic_limit', 'Limite de trafic'), ('fr', 'clients.unlimited', 'Illimité'), +('fr', 'clients.custom_seconds', 'Personnalisé (secondes)'), +('fr', 'clients.custom_mb', 'Personnalisé (MB)'), +('fr', 'clients.enter_seconds', 'Saisissez les secondes'), +('fr', 'clients.enter_megabytes', 'Saisissez les mégaoctets'), ('fr', 'common.days', 'jours'), ('fr', 'dashboard.active_clients', 'Clients actifs'), ('fr', 'dashboard.add_first_server', 'Ajouter le premier serveur'), diff --git a/migrations/006_translations_zh.sql b/migrations/006_translations_zh.sql index 68cfcac..c675f7f 100644 --- a/migrations/006_translations_zh.sql +++ b/migrations/006_translations_zh.sql @@ -46,6 +46,10 @@ INSERT INTO translations (language_code, translation_key, translation_value) VAL ('zh', 'clients.traffic', '流量'), ('zh', 'clients.traffic_limit', '流量限制'), ('zh', 'clients.unlimited', '无限制'), +('zh', 'clients.custom_seconds', '自定义(秒)'), +('zh', 'clients.custom_mb', '自定义(MB)'), +('zh', 'clients.enter_seconds', '输入秒数'), +('zh', 'clients.enter_megabytes', '输入兆字节'), ('zh', 'common.days', '天'), ('zh', 'dashboard.active_clients', '活跃客户端'), ('zh', 'dashboard.add_first_server', '添加第一个服务器'), diff --git a/migrations/007_add_traffic_limit.sql b/migrations/007_add_traffic_limit.sql index bdbaf06..7d20ee5 100644 --- a/migrations/007_add_traffic_limit.sql +++ b/migrations/007_add_traffic_limit.sql @@ -2,5 +2,5 @@ -- This migration adds traffic limit functionality to clients ALTER TABLE vpn_clients -ADD COLUMN traffic_limit BIGINT UNSIGNED NULL COMMENT 'Traffic limit in bytes (NULL = unlimited)' AFTER traffic_received, +ADD COLUMN traffic_limit BIGINT UNSIGNED NULL COMMENT 'Traffic limit in bytes (NULL = unlimited)' AFTER expires_at, ADD INDEX idx_traffic_limit (traffic_limit); diff --git a/migrations/init.sql b/migrations/init.sql deleted file mode 100644 index 874d192..0000000 --- a/migrations/init.sql +++ /dev/null @@ -1,300 +0,0 @@ --- Amnezia VPN Panel - Complete Database Schema --- Single migration file containing all tables and initial data - --- Users table -CREATE TABLE IF NOT EXISTS users ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - email VARCHAR(255) NOT NULL UNIQUE, - password_hash VARCHAR(255) NOT NULL, - name VARCHAR(255) NOT NULL, - role ENUM('admin', 'user') DEFAULT 'user', - preferred_language VARCHAR(10) DEFAULT 'en', - status ENUM('active', 'disabled') DEFAULT 'active', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - last_login_at TIMESTAMP NULL, - INDEX idx_email (email), - INDEX idx_role (role), - INDEX idx_language (preferred_language) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- VPN Servers table -CREATE TABLE IF NOT EXISTS vpn_servers ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - user_id INT UNSIGNED NOT NULL, - name VARCHAR(255) NOT NULL, - host VARCHAR(255) NOT NULL, - port INT UNSIGNED NOT NULL, - username VARCHAR(255) NOT NULL, - password VARCHAR(255) NOT NULL, - container_name VARCHAR(255) DEFAULT 'amnezia-awg', - vpn_port INT UNSIGNED NULL, - vpn_subnet VARCHAR(50) DEFAULT '10.8.1.0/24', - server_public_key TEXT NULL, - preshared_key TEXT NULL, - awg_params JSON NULL COMMENT 'Jc, Jmin, Jmax, S1, S2, H1-H4', - status ENUM('deploying', 'active', 'stopped', 'error') DEFAULT 'deploying', - deployed_at TIMESTAMP NULL, - last_check_at TIMESTAMP NULL, - error_message TEXT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_user_id (user_id), - INDEX idx_status (status), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- VPN Clients table -CREATE TABLE IF NOT EXISTS vpn_clients ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - server_id INT UNSIGNED NOT NULL, - user_id INT UNSIGNED NOT NULL, - name VARCHAR(255) NOT NULL, - client_ip VARCHAR(50) NOT NULL, - public_key TEXT NOT NULL, - private_key TEXT NOT NULL, - preshared_key TEXT NULL, - config TEXT NULL COMMENT 'Full WireGuard config file', - qr_code LONGTEXT NULL COMMENT 'Base64 encoded QR code image', - bytes_sent BIGINT UNSIGNED DEFAULT 0 COMMENT 'Total bytes sent by client', - bytes_received BIGINT UNSIGNED DEFAULT 0 COMMENT 'Total bytes received by client', - last_handshake TIMESTAMP NULL COMMENT 'Last successful WireGuard handshake', - last_sync_at TIMESTAMP NULL COMMENT 'Last time stats were synced from server', - status ENUM('active', 'disabled') DEFAULT 'active', - expires_at TIMESTAMP NULL COMMENT 'Client expiration date (NULL = never expires)', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_server_id (server_id), - INDEX idx_user_id (user_id), - INDEX idx_status (status), - INDEX idx_expires_at (expires_at), - INDEX idx_last_handshake (last_handshake), - UNIQUE KEY unique_server_client_ip (server_id, client_ip), - FOREIGN KEY (server_id) REFERENCES vpn_servers(id) ON DELETE CASCADE, - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- API Tokens table -CREATE TABLE IF NOT EXISTS api_tokens ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - user_id INT UNSIGNED NOT NULL, - token VARCHAR(255) NOT NULL UNIQUE, - name VARCHAR(255) NOT NULL, - last_used_at TIMESTAMP NULL, - expires_at TIMESTAMP NULL, - revoked_at TIMESTAMP NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - INDEX idx_token (token), - INDEX idx_user_id (user_id), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- Settings table -CREATE TABLE IF NOT EXISTS settings ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - user_id INT UNSIGNED NULL COMMENT 'NULL for global settings', - namespace VARCHAR(100) NOT NULL, - `key` VARCHAR(100) NOT NULL, - value JSON NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - UNIQUE KEY unique_setting (user_id, namespace, `key`), - INDEX idx_namespace (namespace) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- Languages table -CREATE TABLE IF NOT EXISTS languages ( - id INT AUTO_INCREMENT PRIMARY KEY, - code VARCHAR(10) NOT NULL UNIQUE COMMENT 'Language code (en, ru, es, de, fr, zh)', - name VARCHAR(50) NOT NULL COMMENT 'Language name in English', - native_name VARCHAR(50) NOT NULL COMMENT 'Language name in native language', - is_active TINYINT(1) DEFAULT 1, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - INDEX idx_code (code), - INDEX idx_active (is_active) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- Translations table -CREATE TABLE IF NOT EXISTS translations ( - id INT AUTO_INCREMENT PRIMARY KEY, - language_code VARCHAR(10) NOT NULL, - translation_key VARCHAR(255) NOT NULL COMMENT 'Translation key (e.g., menu.dashboard)', - translation_value TEXT NOT NULL COMMENT 'Translated text', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - UNIQUE KEY unique_translation (language_code, translation_key), - FOREIGN KEY (language_code) REFERENCES languages(code) ON DELETE CASCADE, - INDEX idx_key (translation_key), - INDEX idx_language (language_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- API Keys table -CREATE TABLE IF NOT EXISTS api_keys ( - id INT AUTO_INCREMENT PRIMARY KEY, - service_name VARCHAR(50) NOT NULL UNIQUE COMMENT 'Service name (e.g., openrouter)', - api_key TEXT NOT NULL COMMENT 'API key value', - is_active TINYINT(1) DEFAULT 1, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_service (service_name), - INDEX idx_active (is_active) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- Server Backups table -CREATE TABLE IF NOT EXISTS server_backups ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - server_id INT UNSIGNED NOT NULL, - backup_name VARCHAR(255) NOT NULL COMMENT 'Backup file name', - backup_path VARCHAR(500) NOT NULL COMMENT 'Path to backup file', - backup_size BIGINT UNSIGNED DEFAULT 0 COMMENT 'Backup file size in bytes', - clients_count INT UNSIGNED DEFAULT 0 COMMENT 'Number of clients in backup', - backup_type ENUM('manual', 'automatic') DEFAULT 'manual', - status ENUM('creating', 'completed', 'failed') DEFAULT 'creating', - error_message TEXT NULL COMMENT 'Error message if backup failed', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - created_by INT UNSIGNED NULL COMMENT 'User who created the backup', - INDEX idx_server_id (server_id), - INDEX idx_status (status), - INDEX idx_created_at (created_at), - FOREIGN KEY (server_id) REFERENCES vpn_servers(id) ON DELETE CASCADE, - FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- Insert default admin user -INSERT IGNORE INTO users (email, password_hash, name, role, status) -VALUES ('admin@amnez.ia', '$2y$10$SKEI6ogiWr2gsSG/nELLp.JcfpGhxsDLAAI7gdtTOI3ELz4zJzzPG', 'Administrator', 'admin', 'active'); - --- Insert supported languages -INSERT INTO languages (code, name, native_name) VALUES -('en', 'English', 'English'), -('ru', 'Russian', 'Русский'), -('es', 'Spanish', 'Español'), -('de', 'German', 'Deutsch'), -('fr', 'French', 'Français'), -('zh', 'Chinese', '中文') -ON DUPLICATE KEY UPDATE name=VALUES(name); - --- Insert English translations -INSERT INTO translations (language_code, translation_key, translation_value) VALUES -('en', 'auth.email', 'Email'), -('en', 'auth.login', 'Login'), -('en', 'auth.name', 'Name'), -('en', 'auth.password', 'Password'), -('en', 'auth.register', 'Register'), -('en', 'clients.actions', 'Actions'), -('en', 'clients.add', 'Add Client'), -('en', 'clients.create', 'Create Client'), -('en', 'clients.delete', 'Delete'), -('en', 'clients.download_config', 'Download Config'), -('en', 'clients.expiration', 'Expiration'), -('en', 'clients.expired', 'Expired'), -('en', 'clients.never_expires', 'Never expires'), -('en', 'clients.ip', 'IP Address'), -('en', 'clients.last_handshake', 'Last Handshake'), -('en', 'clients.name', 'Client Name'), -('en', 'clients.qr_code', 'QR Code'), -('en', 'clients.received', 'Received'), -('en', 'clients.restore', 'Restore'), -('en', 'clients.revoke', 'Revoke'), -('en', 'clients.sent', 'Sent'), -('en', 'clients.server', 'Server'), -('en', 'clients.status', 'Status'), -('en', 'clients.sync_stats', 'Sync Stats'), -('en', 'clients.title', 'Clients'), -('en', 'clients.traffic', 'Traffic'), -('en', 'backups.title', 'Server Backups'), -('en', 'backups.create', 'Create Backup'), -('en', 'backups.restore', 'Restore'), -('en', 'backups.no_backups', 'No backups yet'), -('en', 'backups.create_confirm', 'Create backup of all clients on this server?'), -('en', 'backups.restore_confirm', 'Restore clients from this backup? Existing clients will not be affected.'), -('en', 'backups.delete_confirm', 'Delete this backup permanently?'), -('en', 'backups.created_success', 'Backup created successfully'), -('en', 'backups.restored_success', 'Restored'), -('en', 'backups.deleted_success', 'Backup deleted successfully'), -('en', 'backups.login_required', 'Please login via API to manage backups'), -('en', 'common.days', 'days'), -('en', 'dashboard.active_clients', 'Active Clients'), -('en', 'dashboard.add_first_server', 'Add First Server'), -('en', 'dashboard.get_started', 'Get started by adding your first VPN server'), -('en', 'dashboard.no_servers', 'No servers yet'), -('en', 'dashboard.quick_actions', 'Quick Actions'), -('en', 'dashboard.recent_servers', 'Recent Servers'), -('en', 'dashboard.title', 'Dashboard'), -('en', 'dashboard.total_clients', 'Total Clients'), -('en', 'dashboard.total_servers', 'Total Servers'), -('en', 'dashboard.total_traffic', 'Total Traffic'), -('en', 'dashboard.welcome', 'Welcome to Amnezia VPN Management Panel'), -('en', 'form.cancel', 'Cancel'), -('en', 'form.close', 'Close'), -('en', 'form.create', 'Create'), -('en', 'form.loading', 'Loading...'), -('en', 'form.processing', 'Processing...'), -('en', 'form.save', 'Save'), -('en', 'form.submit', 'Submit'), -('en', 'form.update', 'Update'), -('en', 'menu.clients', 'Clients'), -('en', 'menu.dashboard', 'Dashboard'), -('en', 'menu.logout', 'Logout'), -('en', 'menu.servers', 'Servers'), -('en', 'menu.settings', 'Settings'), -('en', 'menu.users', 'Users'), -('en', 'message.confirm', 'Are you sure?'), -('en', 'message.deleted', 'Deleted successfully'), -('en', 'message.deployed', 'Deployed successfully'), -('en', 'message.error', 'An error occurred'), -('en', 'message.saved', 'Saved successfully'), -('en', 'message.success', 'Operation completed successfully'), -('en', 'servers.actions', 'Actions'), -('en', 'servers.add', 'Add Server'), -('en', 'servers.clients', 'Clients'), -('en', 'servers.delete', 'Delete'), -('en', 'servers.deploy', 'Deploy'), -('en', 'servers.edit', 'Edit'), -('en', 'servers.host', 'Host'), -('en', 'servers.name', 'Name'), -('en', 'servers.port', 'Port'), -('en', 'servers.status', 'Status'), -('en', 'servers.title', 'Servers'), -('en', 'servers.view', 'View'), -('en', 'settings.actions', 'Actions'), -('en', 'settings.api_keys', 'API Keys'), -('en', 'settings.api_keys_desc', 'Configure API keys for external services'), -('en', 'settings.auto_translate', 'Auto-translate'), -('en', 'settings.change_password', 'Change Password'), -('en', 'settings.confirm_password', 'Confirm Password'), -('en', 'settings.confirm_translate', 'Start automatic translation? This may take a few minutes.'), -('en', 'settings.current_password', 'Current Password'), -('en', 'settings.description', 'Manage panel configuration and API integrations'), -('en', 'settings.error_empty_key', 'API key cannot be empty'), -('en', 'settings.error_invalid_key', 'Invalid API key format'), -('en', 'settings.error_key_test', 'API key test failed'), -('en', 'settings.for_translation', 'for auto-translation'), -('en', 'settings.get_key_at', 'Get your API key at'), -('en', 'settings.key_saved', 'API key saved successfully'), -('en', 'settings.keys', 'keys'), -('en', 'settings.language', 'Language'), -('en', 'settings.min_6_chars', 'Minimum 6 characters'), -('en', 'settings.new_password', 'New Password'), -('en', 'settings.profile', 'Profile'), -('en', 'settings.progress', 'Progress'), -('en', 'settings.translations', 'Translations'), -('en', 'settings.translation_complete', 'Translation completed'), -('en', 'settings.translation_status', 'Translation Status'), -('en', 'settings.users', 'Users'), -('en', 'status.active', 'Active'), -('en', 'status.deploying', 'Deploying'), -('en', 'status.disabled', 'Disabled'), -('en', 'status.error', 'Error'), -('en', 'status.inactive', 'Inactive'), -('en', 'users.add_user', 'Add User'), -('en', 'users.all_users', 'All Users'), -('en', 'users.administrator', 'Administrator'), -('en', 'users.created', 'Created'), -('en', 'users.delete_confirm', 'Delete {0}?'), -('en', 'users.role', 'Role'), -('en', 'users.role_admin', 'Admin'), -('en', 'users.role_user', 'User'), -('en', 'settings.api_key_configured', 'API Key Configured'), -('en', 'settings.no_api_key', 'No API key configured. Auto-translation will not work.'), -('en', 'settings.skip_validation', 'Skip validation (save without testing)') -ON DUPLICATE KEY UPDATE translation_value=VALUES(translation_value); diff --git a/public/index.php b/public/index.php index 59e9d5c..c78dbca 100644 --- a/public/index.php +++ b/public/index.php @@ -395,8 +395,25 @@ Router::post('/servers/{id}/clients/create', function ($params) { requireAuth(); $serverId = (int)$params['id']; $clientName = trim($_POST['name'] ?? ''); - $expiresInDays = !empty($_POST['expires_in_days']) ? (int)$_POST['expires_in_days'] : null; - $trafficLimitGb = !empty($_POST['traffic_limit_gb']) ? (float)$_POST['traffic_limit_gb'] : null; + + // Handle expiration: either from dropdown (days) or custom input (seconds) + $expiresInDays = null; + if (!empty($_POST['expires_in_seconds'])) { + // Convert seconds to days (round up) + $expiresInDays = (int)ceil((int)$_POST['expires_in_seconds'] / 86400); + } elseif (!empty($_POST['expires_in_days']) && $_POST['expires_in_days'] !== 'custom') { + $expiresInDays = (int)$_POST['expires_in_days']; + } + + // Handle traffic limit: either from dropdown (GB) or custom input (MB) + $trafficLimitBytes = null; + if (!empty($_POST['traffic_limit_mb'])) { + // Convert MB to bytes + $trafficLimitBytes = (int)((float)$_POST['traffic_limit_mb'] * 1048576); + } elseif (!empty($_POST['traffic_limit_gb']) && $_POST['traffic_limit_gb'] !== 'custom') { + // Convert GB to bytes + $trafficLimitBytes = (int)((float)$_POST['traffic_limit_gb'] * 1073741824); + } if (empty($clientName)) { redirect('/servers/' . $serverId . '?error=Client+name+is+required'); @@ -418,10 +435,9 @@ Router::post('/servers/{id}/clients/create', function ($params) { $clientId = VpnClient::create($serverId, $user['id'], $clientName, $expiresInDays); // Set traffic limit if specified - if ($trafficLimitGb !== null) { + if ($trafficLimitBytes !== null && $trafficLimitBytes > 0) { $client = new VpnClient($clientId); - $limitBytes = (int)($trafficLimitGb * 1073741824); // Convert GB to bytes - $client->setTrafficLimit($limitBytes); + $client->setTrafficLimit($trafficLimitBytes); } redirect('/clients/' . $clientId); diff --git a/templates/clients/view.twig b/templates/clients/view.twig index 5ceb28a..937a300 100644 --- a/templates/clients/view.twig +++ b/templates/clients/view.twig @@ -64,6 +64,85 @@ +
+
+

{{ t('clients.expiration') }}

+
+
+ + + +
+
+
CURRENT VALUE:
+
+ {% if client.expires_at %} + {% set expires_ts = client.expires_at|date('U') %} + {% set now_ts = "now"|date('U') %} + {% set diff_days = ((expires_ts - now_ts) / 86400)|round %} + {{ client.expires_at|date('Y-m-d H:i:s') }} ({{ diff_days }} {{ t('common.days') }}) + {% else %} + {{ t('clients.never_expires') }} + {% endif %} +
+
+ +
+
+ +
+

{{ t('clients.traffic_limit') }}

+
+
+ + + +
+
+
CURRENT VALUE:
+
+ {% if client.traffic_limit %} + {% set limit_gb = (client.traffic_limit / 1073741824)|number_format(2) %} + {% set total_traffic = (client.bytes_sent|default(0) + client.bytes_received|default(0)) %} + {% set used_gb = (total_traffic / 1073741824)|number_format(2) %} + {{ limit_gb }} GB (used: {{ used_gb }} GB) + {% else %} + {{ t('clients.unlimited') }} + {% endif %} +
+
+ +
+
+
+ {% if client.qr_code %}

QR Code

@@ -74,6 +153,152 @@