diff --git a/app/dashboard/public/locales/en.json b/app/dashboard/public/locales/en.json
index 3569d7d19..6d068e152 100644
--- a/app/dashboard/public/locales/en.json
+++ b/app/dashboard/public/locales/en.json
@@ -75,6 +75,8 @@
"hostsDialog.remainingData": "Remaining data of the user",
"hostsDialog.dataLimit": "The usage limit of the user",
"hostsDialog.remainingDays": "Remaining days of the user",
+ "hostsDialog.expireDate": "Expiry date of the user",
+ "hostsDialog.jalaliExpireDate": "Expiry date of the user in solar calendar",
"hostsDialog.remainingTime": "Remaining time of the user",
"hostsDialog.statusEmoji": "User status as an emoji (✅,⌛️,🪫,❌)",
"hostsDialog.proxyProtocol": "Proxy protocol (e.g. VMess)",
diff --git a/app/dashboard/public/locales/fa.json b/app/dashboard/public/locales/fa.json
index 6075aec0e..7390f55b3 100644
--- a/app/dashboard/public/locales/fa.json
+++ b/app/dashboard/public/locales/fa.json
@@ -78,6 +78,8 @@
"hostsDialog.remainingData": "حجم باقیمانده کاربر",
"hostsDialog.dataLimit": "حد مصرف کاربر",
"hostsDialog.remainingDays": "روزهای باقی مانده کاربر",
+ "hostsDialog.expireDate": "تاریخ انقضای کاربر به میلادی",
+ "hostsDialog.jalaliExpireDate": "تاریخ انقضای کاربر به شمسی",
"hostsDialog.remainingTime": "زمان باقی مانده کاربر",
"hostsDialog.statusEmoji": "وضعیت کاربر در قالب اموجی (✅,⌛️,🪫,❌)",
"hostsDialog.proxyProtocol": "پروتکل پروکسی (مانند VMess)",
diff --git a/app/dashboard/public/locales/ru.json b/app/dashboard/public/locales/ru.json
index 8396228d5..e636e42d8 100644
--- a/app/dashboard/public/locales/ru.json
+++ b/app/dashboard/public/locales/ru.json
@@ -1,156 +1,158 @@
{
- "password": "Пароль",
- "login": "Вход",
- "cancel": "Отмена",
- "apply": "Применить",
- "delete": "Удалить",
- "reset": "Сбросить",
- "createUser": "Создать",
- "username": "Имя пользователя",
- "expires": "Истекает через {{time}}",
- "expired": "Истекло {{time}} назад",
- "dateFormat": "MMMM d, yyy",
- "inbound": "inbound",
- "login.loginYourAccount": "Войдите в свой аккаунт",
- "login.welcomeBack": "Пожалуйста, введите свои данные",
- "login.fieldRequired": "Это поле обязательно для заполнения",
- "header.hostSettings": "Настройки хоста",
- "header.nodeSettings": "Настройки узлов",
- "header.nodesUsage": "Использование узлов",
- "header.donation": "Пожертвование",
- "header.logout": "Выйти",
- "deleteUser.title": "Удалить пользователя",
- "deleteUser.prompt": "Вы уверены, что хотите удалить пользователя {{username}}?",
- "deleteUser.deleteSuccess": "{{username}} успешно удален.",
- "usersTable.status": "Статус",
- "usersTable.dataUsage": "Расход трафика",
- "usersTable.noUserMatched": "Похоже, нет пользователя, соответствующего вашему запросу",
- "usersTable.noUser": "В системе нет добавленных пользователей",
- "usersTable.copyLink": "Скопировать ссылку на подписку",
- "usersTable.copied": "Скопировано",
- "usersTable.copyConfigs": "Скопировать конфигурации",
- "usersTable.total": "Всего",
- "userDialog.dataLimit": "Лимит трафика",
- "userDialog.periodicUsageReset": "Период сброса трафика",
- "userDialog.warningNoProtocol": "Пожалуйста, выберите хотя бы один протокол",
- "userDialog.expiryDate": "Дата истечения срока",
- "userDialog.note": "Примечание",
- "userDialog.resetUsage": "Сбросить трафик",
- "userDialog.usage": "Потребление",
- "userDialog.protocols": "Протоколы",
- "userDialog.editUserTitle": "Редактировать пользователя",
- "userDialog.editUser": "Редактировать",
- "userDialog.userEdited": "Пользователь {{username}} отредактирован.",
- "userDialog.userCreated": "Пользователь {{username}} создан.",
- "userDialog.userAlreadyExists": "Пользователь уже существует",
- "userDialog.vmessDesc": "Быстрый и безопасный",
- "userDialog.vlessDesc": "Легковесный, быстрый и безопасный",
- "userDialog.trojanDesc": "Легковесный, безопасный и быстрый",
- "userDialog.shadowsocksDesc": "Быстрый и безопасный, но не такой эффективный, как другие",
- "userDialog.resetStrategyNo": "Нет",
- "userDialog.resetStrategyDaily": "Ежедневно",
- "userDialog.resetStrategyWeekly": "Еженедельно",
- "userDialog.resetStrategyMonthly": "Ежемесячно",
- "userDialog.resetStrategyAnnually": "Ежегодно",
- "userDialog.selectOneProtocol": "Пожалуйста, выберите хотя бы один протокол",
- "userDialog.optional": "необязательно",
- "userDialog.method": "Метод",
- "userDialog.generatedByDefault": "по умолчанию",
- "userDialog.hours": "Часы",
- "userDialog.days": "Дни",
- "userDialog.weeks": "Недели",
- "userDialog.months": "Месяцы",
- "userDialog.relative": "Относительно",
- "userDialog.absolute": "Абсолютно",
- "userDialog.custom": "Пользовательский",
- "userDialog.startDate": "Дата начала",
- "userDialog.endDate": "Дата окончания",
- "userDialog.revokeSubscription": "Отозвать подписку",
- "revoke": "Отозвать",
- "userDialog.total": "Всего: ",
- "hostsDialog.title": "Используя эту настройку, вы можете настроить свои inbound.",
- "hostsDialog.desc": "Используйте эти переменные, чтобы сделать его динамическим",
- "hostsDialog.username": "Имя пользователя",
- "hostsDialog.dataUsage": "Текущий расход трафика пользователя",
- "hostsDialog.remainingData": "Оставшийся трафик пользователя",
- "hostsDialog.dataLimit": "Лимит трафика пользователя",
- "hostsDialog.remainingDays": "Оставшиеся дни пользователя",
- "hostsDialog.remainingTime": "Оставшееся время пользователя",
- "hostsDialog.statusEmoji": "Статус пользователя в виде смайлика (✅,⌛️,🪫,❌)",
- "hostsDialog.proxyProtocol": "Протокол прокси (например, VMess)",
- "hostsDialog.proxyMethod": "Метод транспорта прокси (например, ws)",
- "hostsDialog.currentServer": "Текущий IP-адрес сервера",
- "hostsDialog.security": "Уровень безопасности",
- "hostsDialog.host": "Host",
- "hostsDialog.port": "Port",
- "hostsDialog.sni": "SNI",
- "hostsDialog.advancedOptions": "Дополнительные опции",
- "hostsDialog.addHost": "Добавить хост",
- "hostsDialog.savedSuccess": "Хосты успешно сохранены",
- "hostsDialog.loading": "загрузка...",
- "hostsDialog.apply": "Применить",
- "hostsDialog.port.info": "По умолчанию хост использует порт входящего соединения. Вы можете установить пользовательский порт, если этот хост является сервером, который перенаправляет трафик с порта, отличного от порта вашего сервера. Например, сервер может перенаправлять трафик с порта 8443 на порт входящего сервера по умолчанию.",
- "hostsDialog.sni.info": "По умолчанию хост использует SNI входящего соединения. Вы можете установить пользовательский SNI, если этот хост является сервером, у которого есть другой SNI. Например, сервер может принимать трафик с другим SSL-сертификатом, выполнять окончание SSL и перенаправлять его на ваш входящий сервер.",
- "hostsDialog.host.info": "По умолчанию, если в конфигурации Xray задан запрашиваемый хост, будет использоваться этот хост. Однако, если необходимо, вы можете установить здесь пользовательский запрашиваемый хост.",
- "hostsDialog.security.info": "Если промежуточный сервер этого хоста использует другой уровень безопасности, отличный от уровня безопасности входящего соединения по умолчанию, вы можете установить здесь пользовательский уровень безопасности.",
- "hostsDialog.alpn": "ALPN",
- "hostsDialog.fingerprint": "Fingerprint",
- "hostsDialog.host.multiHost": "Чтобы установить несколько адресов, разделяйте их с помощью ,. Каждый раз выбирается случайный адрес.",
- "hostsDialog.host.wildcard": "Используйте *, чтобы сгенерировать случайную строку (работает для доменных имен с подстановочными знаками)",
- "nodes.title": "Используя Marzban-Node, вы можете повысить качество соединения, добавляя узлы на разных серверах.",
- "nodes.addNewMarzbanNode": "Добавить новый узел Marzban",
- "nodes.certificate": "Сертификат",
- "nodes.addHostForEveryInbound": "Добавить этот узел как новый хост для каждого inbound",
- "nodes.addNode": "Добавить узел",
- "nodes.addNodeSuccess": "Узел {{name}} успешно добавлен",
- "nodes.apply": "editNode",
- "nodes.nodeName": "Имя",
- "nodes.nodeAddress": "Адрес",
- "nodes.nodePort": "Порт",
- "nodes.nodeAPIPort": "API порт",
- "nodes.editNode": "Редактировать узел",
- "nodes.reconnect": "Переподключиться",
- "nodes.connection-hint": "Для настройки узла Marzban, необходимо установить на нем данный сертификат, для инициализации безопасного соединения между главным сервером и узлом",
- "nodes.download-certificate": "Скачать сертификат",
- "nodes.show-certificate": "Показать сертификат",
- "nodes.hide-certificate": "Скрыть сертификат",
- "nodes.reconnecting": "Переподключение...",
- "deleteNode.title": "Удалить узел",
- "deleteNode.prompt": "Вы уверены, что хотите удалить узел {{name}}?",
- "deleteNode.deleteSuccess": "Узел {{name}} успешно удален",
- "users": "Пользователи",
- "activeUsers": "Пользователи",
- "dataUsage": "Трафик",
- "memoryUsage": "Память",
- "itemsPerPage": "Элементов на страницу",
- "previous": "Назад",
- "next": "Вперед",
- "createNewUser": "Создать нового пользователя",
- "search": "Поиск",
- "resetAllUsage": "Сбросить расход трафика",
- "qrcodeDialog.sublink": "Ссылка на подписку",
- "resetUserUsage.prompt": "Вы уверены, что хотите сбросить расход трафика для пользователя {{username}}?",
- "resetUserUsage.title": "Сбросить расход трафика пользователя",
- "resetUserUsage.success": "расход трафика пользователя {{username}} успешно сброшен.",
- "resetUserUsage.error": "Сброс расхода не удался, пожалуйста, попробуйте еще раз.",
- "revokeUserSub.prompt": "Вы уверены, что хотите отозвать подписку для пользователя {{username}}?",
- "revokeUserSub.title": "Отозвать подписку пользователя",
- "revokeUserSub.success": "Подписка пользователя {{username}} успешно отозвана.",
- "revokeUserSub.error": "Отзыв подписки не удался, пожалуйста, попробуйте еще раз.",
- "resetAllUsage.title": "Сбросить расход трафика для всех пользователей",
- "resetAllUsage.prompt": "Это действие полностью очищает весь расход трафика пользователей. Вы уверены? ЭТО ДЕЙСТВИЕ НЕОБРАТИМО!",
- "resetAllUsage.success": "расход трафика успешно сброшен.",
- "resetAllUsage.error": "Сброс расхода трафика не удался, пожалуйста, попробуйте еще раз.",
- "core.title": "Основные настройки",
- "core.socket.connecting": "Соединение...",
- "core.socket.connected": "Подключено",
- "core.socket.not_connected": "Не подключено",
- "core.socket.closed": "Закрыто",
- "core.restarting": "Перезагрузка...",
- "core.restartCore": "Перезагрузить ядро",
- "core.save": "Сохранить",
- "core.logs": "Логи",
- "core.configuration": "Конфигурация",
- "core.generalErrorMessage": "Что-то пошло не так, пожалуйста, проверьте конфигурацию",
- "core.successMessage": "Основные настройки успешно обновлены"
-}
\ No newline at end of file
+ "password": "Пароль",
+ "login": "Вход",
+ "cancel": "Отмена",
+ "apply": "Применить",
+ "delete": "Удалить",
+ "reset": "Сбросить",
+ "createUser": "Создать",
+ "username": "Имя пользователя",
+ "expires": "Истекает через {{time}}",
+ "expired": "Истекло {{time}} назад",
+ "dateFormat": "MMMM d, yyy",
+ "inbound": "inbound",
+ "login.loginYourAccount": "Войдите в свой аккаунт",
+ "login.welcomeBack": "Пожалуйста, введите свои данные",
+ "login.fieldRequired": "Это поле обязательно для заполнения",
+ "header.hostSettings": "Настройки хоста",
+ "header.nodeSettings": "Настройки узлов",
+ "header.nodesUsage": "Использование узлов",
+ "header.donation": "Пожертвование",
+ "header.logout": "Выйти",
+ "deleteUser.title": "Удалить пользователя",
+ "deleteUser.prompt": "Вы уверены, что хотите удалить пользователя {{username}}?",
+ "deleteUser.deleteSuccess": "{{username}} успешно удален.",
+ "usersTable.status": "Статус",
+ "usersTable.dataUsage": "Расход трафика",
+ "usersTable.noUserMatched": "Похоже, нет пользователя, соответствующего вашему запросу",
+ "usersTable.noUser": "В системе нет добавленных пользователей",
+ "usersTable.copyLink": "Скопировать ссылку на подписку",
+ "usersTable.copied": "Скопировано",
+ "usersTable.copyConfigs": "Скопировать конфигурации",
+ "usersTable.total": "Всего",
+ "userDialog.dataLimit": "Лимит трафика",
+ "userDialog.periodicUsageReset": "Период сброса трафика",
+ "userDialog.warningNoProtocol": "Пожалуйста, выберите хотя бы один протокол",
+ "userDialog.expiryDate": "Дата истечения срока",
+ "userDialog.note": "Примечание",
+ "userDialog.resetUsage": "Сбросить трафик",
+ "userDialog.usage": "Потребление",
+ "userDialog.protocols": "Протоколы",
+ "userDialog.editUserTitle": "Редактировать пользователя",
+ "userDialog.editUser": "Редактировать",
+ "userDialog.userEdited": "Пользователь {{username}} отредактирован.",
+ "userDialog.userCreated": "Пользователь {{username}} создан.",
+ "userDialog.userAlreadyExists": "Пользователь уже существует",
+ "userDialog.vmessDesc": "Быстрый и безопасный",
+ "userDialog.vlessDesc": "Легковесный, быстрый и безопасный",
+ "userDialog.trojanDesc": "Легковесный, безопасный и быстрый",
+ "userDialog.shadowsocksDesc": "Быстрый и безопасный, но не такой эффективный, как другие",
+ "userDialog.resetStrategyNo": "Нет",
+ "userDialog.resetStrategyDaily": "Ежедневно",
+ "userDialog.resetStrategyWeekly": "Еженедельно",
+ "userDialog.resetStrategyMonthly": "Ежемесячно",
+ "userDialog.resetStrategyAnnually": "Ежегодно",
+ "userDialog.selectOneProtocol": "Пожалуйста, выберите хотя бы один протокол",
+ "userDialog.optional": "необязательно",
+ "userDialog.method": "Метод",
+ "userDialog.generatedByDefault": "по умолчанию",
+ "userDialog.hours": "Часы",
+ "userDialog.days": "Дни",
+ "userDialog.weeks": "Недели",
+ "userDialog.months": "Месяцы",
+ "userDialog.relative": "Относительно",
+ "userDialog.absolute": "Абсолютно",
+ "userDialog.custom": "Пользовательский",
+ "userDialog.startDate": "Дата начала",
+ "userDialog.endDate": "Дата окончания",
+ "userDialog.revokeSubscription": "Отозвать подписку",
+ "revoke": "Отозвать",
+ "userDialog.total": "Всего: ",
+ "hostsDialog.title": "Используя эту настройку, вы можете настроить свои inbound.",
+ "hostsDialog.desc": "Используйте эти переменные, чтобы сделать его динамическим",
+ "hostsDialog.username": "Имя пользователя",
+ "hostsDialog.dataUsage": "Текущий расход трафика пользователя",
+ "hostsDialog.remainingData": "Оставшийся трафик пользователя",
+ "hostsDialog.dataLimit": "Лимит трафика пользователя",
+ "hostsDialog.remainingDays": "Оставшиеся дни пользователя",
+ "hostsDialog.expireDate": "Срок действия пользователя",
+ "hostsDialog.jalaliExpireDate": "Срок действия пользователя в солнечном календаре",
+ "hostsDialog.remainingTime": "Оставшееся время пользователя",
+ "hostsDialog.statusEmoji": "Статус пользователя в виде смайлика (✅,⌛️,🪫,❌)",
+ "hostsDialog.proxyProtocol": "Протокол прокси (например, VMess)",
+ "hostsDialog.proxyMethod": "Метод транспорта прокси (например, ws)",
+ "hostsDialog.currentServer": "Текущий IP-адрес сервера",
+ "hostsDialog.security": "Уровень безопасности",
+ "hostsDialog.host": "Host",
+ "hostsDialog.port": "Port",
+ "hostsDialog.sni": "SNI",
+ "hostsDialog.advancedOptions": "Дополнительные опции",
+ "hostsDialog.addHost": "Добавить хост",
+ "hostsDialog.savedSuccess": "Хосты успешно сохранены",
+ "hostsDialog.loading": "загрузка...",
+ "hostsDialog.apply": "Применить",
+ "hostsDialog.port.info": "По умолчанию хост использует порт входящего соединения. Вы можете установить пользовательский порт, если этот хост является сервером, который перенаправляет трафик с порта, отличного от порта вашего сервера. Например, сервер может перенаправлять трафик с порта 8443 на порт входящего сервера по умолчанию.",
+ "hostsDialog.sni.info": "По умолчанию хост использует SNI входящего соединения. Вы можете установить пользовательский SNI, если этот хост является сервером, у которого есть другой SNI. Например, сервер может принимать трафик с другим SSL-сертификатом, выполнять окончание SSL и перенаправлять его на ваш входящий сервер.",
+ "hostsDialog.host.info": "По умолчанию, если в конфигурации Xray задан запрашиваемый хост, будет использоваться этот хост. Однако, если необходимо, вы можете установить здесь пользовательский запрашиваемый хост.",
+ "hostsDialog.security.info": "Если промежуточный сервер этого хоста использует другой уровень безопасности, отличный от уровня безопасности входящего соединения по умолчанию, вы можете установить здесь пользовательский уровень безопасности.",
+ "hostsDialog.alpn": "ALPN",
+ "hostsDialog.fingerprint": "Fingerprint",
+ "hostsDialog.host.multiHost": "Чтобы установить несколько адресов, разделяйте их с помощью ,. Каждый раз выбирается случайный адрес.",
+ "hostsDialog.host.wildcard": "Используйте *, чтобы сгенерировать случайную строку (работает для доменных имен с подстановочными знаками)",
+ "nodes.title": "Используя Marzban-Node, вы можете повысить качество соединения, добавляя узлы на разных серверах.",
+ "nodes.addNewMarzbanNode": "Добавить новый узел Marzban",
+ "nodes.certificate": "Сертификат",
+ "nodes.addHostForEveryInbound": "Добавить этот узел как новый хост для каждого inbound",
+ "nodes.addNode": "Добавить узел",
+ "nodes.addNodeSuccess": "Узел {{name}} успешно добавлен",
+ "nodes.apply": "editNode",
+ "nodes.nodeName": "Имя",
+ "nodes.nodeAddress": "Адрес",
+ "nodes.nodePort": "Порт",
+ "nodes.nodeAPIPort": "API порт",
+ "nodes.editNode": "Редактировать узел",
+ "nodes.reconnect": "Переподключиться",
+ "nodes.connection-hint": "Для настройки узла Marzban, необходимо установить на нем данный сертификат, для инициализации безопасного соединения между главным сервером и узлом",
+ "nodes.download-certificate": "Скачать сертификат",
+ "nodes.show-certificate": "Показать сертификат",
+ "nodes.hide-certificate": "Скрыть сертификат",
+ "nodes.reconnecting": "Переподключение...",
+ "deleteNode.title": "Удалить узел",
+ "deleteNode.prompt": "Вы уверены, что хотите удалить узел {{name}}?",
+ "deleteNode.deleteSuccess": "Узел {{name}} успешно удален",
+ "users": "Пользователи",
+ "activeUsers": "Пользователи",
+ "dataUsage": "Трафик",
+ "memoryUsage": "Память",
+ "itemsPerPage": "Элементов на страницу",
+ "previous": "Назад",
+ "next": "Вперед",
+ "createNewUser": "Создать нового пользователя",
+ "search": "Поиск",
+ "resetAllUsage": "Сбросить расход трафика",
+ "qrcodeDialog.sublink": "Ссылка на подписку",
+ "resetUserUsage.prompt": "Вы уверены, что хотите сбросить расход трафика для пользователя {{username}}?",
+ "resetUserUsage.title": "Сбросить расход трафика пользователя",
+ "resetUserUsage.success": "расход трафика пользователя {{username}} успешно сброшен.",
+ "resetUserUsage.error": "Сброс расхода не удался, пожалуйста, попробуйте еще раз.",
+ "revokeUserSub.prompt": "Вы уверены, что хотите отозвать подписку для пользователя {{username}}?",
+ "revokeUserSub.title": "Отозвать подписку пользователя",
+ "revokeUserSub.success": "Подписка пользователя {{username}} успешно отозвана.",
+ "revokeUserSub.error": "Отзыв подписки не удался, пожалуйста, попробуйте еще раз.",
+ "resetAllUsage.title": "Сбросить расход трафика для всех пользователей",
+ "resetAllUsage.prompt": "Это действие полностью очищает весь расход трафика пользователей. Вы уверены? ЭТО ДЕЙСТВИЕ НЕОБРАТИМО!",
+ "resetAllUsage.success": "расход трафика успешно сброшен.",
+ "resetAllUsage.error": "Сброс расхода трафика не удался, пожалуйста, попробуйте еще раз.",
+ "core.title": "Основные настройки",
+ "core.socket.connecting": "Соединение...",
+ "core.socket.connected": "Подключено",
+ "core.socket.not_connected": "Не подключено",
+ "core.socket.closed": "Закрыто",
+ "core.restarting": "Перезагрузка...",
+ "core.restartCore": "Перезагрузить ядро",
+ "core.save": "Сохранить",
+ "core.logs": "Логи",
+ "core.configuration": "Конфигурация",
+ "core.generalErrorMessage": "Что-то пошло не так, пожалуйста, проверьте конфигурацию",
+ "core.successMessage": "Основные настройки успешно обновлены"
+}
diff --git a/app/dashboard/public/locales/zh.json b/app/dashboard/public/locales/zh.json
index 78f58b549..cf8b41429 100644
--- a/app/dashboard/public/locales/zh.json
+++ b/app/dashboard/public/locales/zh.json
@@ -71,6 +71,8 @@
"hostsDialog.remainingData": "用户剩余流量情况",
"hostsDialog.dataLimit": "用户的流量限制",
"hostsDialog.remainingDays": "用户的剩余天数",
+ "hostsDialog.expireDate": "用户的有效期",
+ "hostsDialog.jalaliExpireDate": "用户阳历有效日期",
"hostsDialog.remainingTime": "用户剩余时间",
"hostsDialog.statusEmoji": "用户状态作为表情符号 (✅,⌛️,🪫,❌)",
"hostsDialog.proxyProtocol": "代理协议(例如 VMess)",
@@ -130,4 +132,4 @@
"resetAllUsage.prompt": "此操作将清除所有用户统计,您确定要执行此操作吗? 这不能被撤消!",
"resetAllUsage.success": "所有统计重置完成。",
"resetAllUsage.error": "重置失败,请稍候再试!"
-}
\ No newline at end of file
+}
diff --git a/app/dashboard/src/components/HostsDialog.tsx b/app/dashboard/src/components/HostsDialog.tsx
index 65a9dcae8..1dd45a3d7 100644
--- a/app/dashboard/src/components/HostsDialog.tsx
+++ b/app/dashboard/src/components/HostsDialog.tsx
@@ -7,7 +7,7 @@ import {
Badge,
Box,
Button,
- chakra,
+ Select as ChakraSelect,
FormControl,
FormErrorMessage,
FormLabel,
@@ -28,11 +28,11 @@ import {
PopoverContent,
PopoverTrigger,
Portal,
- Select as ChakraSelect,
Text,
Tooltip,
- useToast,
VStack,
+ chakra,
+ useToast,
} from "@chakra-ui/react";
import { InformationCircleIcon, LinkIcon } from "@heroicons/react/24/outline";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -271,6 +271,18 @@ const AccordionInbound: FC = ({
{" "}
{t("hostsDialog.remainingDays")}
+
+
+ {"{"}EXPIRE_DATE{"}"}
+ {" "}
+ {t("hostsDialog.expireDate")}
+
+
+
+ {"{"}JALALI_EXPIRE_DATE{"}"}
+ {" "}
+ {t("hostsDialog.jalaliExpireDate")}
+
{"{"}TIME_LEFT{"}"}
@@ -369,6 +381,18 @@ const AccordionInbound: FC = ({
{" "}
{t("hostsDialog.remainingDays")}
+
+
+ {"{"}EXPIRE_DATE{"}"}
+ {" "}
+ {t("hostsDialog.expireDate")}
+
+
+
+ {"{"}JALALI_EXPIRE_DATE{"}"}
+ {" "}
+ {t("hostsDialog.jalaliExpireDate")}
+
{"{"}TIME_LEFT{"}"}
diff --git a/app/utils/share.py b/app/utils/share.py
index 3c3342031..ae9802714 100644
--- a/app/utils/share.py
+++ b/app/utils/share.py
@@ -8,6 +8,7 @@
from uuid import UUID
import yaml
+from jdatetime import date as jd
from app import xray
from app.models.proxy import FormatVariables
@@ -811,12 +812,17 @@ def setup_format_variables(extra_data: dict) -> dict:
if user_status != UserStatus.on_hold:
if expire_timestamp is not None and expire_timestamp >= 0:
seconds_left = expire_timestamp - int(dt.utcnow().timestamp())
- days_left = (dt.fromtimestamp(
- expire_timestamp) - dt.utcnow()).days + 1
+ expire_datetime = dt.fromtimestamp(expire_timestamp)
+ expire_date = expire_datetime.date()
+ jalali_expire_date = jd.fromgregorian(
+ year=expire_date.year, month=expire_date.month, day=expire_date.day).strftime("%Y-%m-%d")
+ days_left = (expire_datetime - dt.utcnow()).days + 1
time_left = format_time_left(seconds_left)
else:
days_left = '∞'
time_left = '∞'
+ expire_date = '∞'
+ jalali_expire_date = '∞'
else:
if on_hold_expire_duration is not None and on_hold_expire_duration >= 0:
days_left = timedelta(seconds=on_hold_expire_duration).days
@@ -824,6 +830,8 @@ def setup_format_variables(extra_data: dict) -> dict:
else:
days_left = '∞'
time_left = '∞'
+ expire_date = '∞'
+ jalali_expire_date = '∞'
if extra_data.get('data_limit'):
data_limit = readable_size(extra_data['data_limit'])
@@ -844,6 +852,8 @@ def setup_format_variables(extra_data: dict) -> dict:
"DATA_LIMIT": data_limit,
"DATA_LEFT": data_left,
"DAYS_LEFT": days_left,
+ "EXPIRE_DATE": expire_date,
+ "JALALI_EXPIRE_DATE": jalali_expire_date,
"TIME_LEFT": time_left,
"STATUS_EMOJI": status_emoji
}
diff --git a/requirements.txt b/requirements.txt
index 49c93043c..d444e32cf 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -21,6 +21,7 @@ httptools==0.5.0
idna==3.4
importlib-metadata==5.1.0
importlib-resources==5.10.0
+jdatetime==4.1.1
Jinja2==3.1.2
Mako==1.2.4
markdown-it-py==2.2.0