diff --git a/app/db/crud.py b/app/db/crud.py index 0a391e6..79f71cd 100644 --- a/app/db/crud.py +++ b/app/db/crud.py @@ -678,6 +678,9 @@ def update_node(db: Session, dbnode: Node, modify: NodeModify): if modify.usage_coefficient: dbnode.usage_coefficient = modify.usage_coefficient + if modify.connection_backend: + dbnode.connection_backend = modify.connection_backend + db.commit() db.refresh(dbnode) return dbnode diff --git a/dashboard/public/locales/en.json b/dashboard/public/locales/en.json index 0dd14c7..274a5ef 100644 --- a/dashboard/public/locales/en.json +++ b/dashboard/public/locales/en.json @@ -89,7 +89,8 @@ } }, "usage_coefficient": "Usage Coefficient", - "add_as_new_host": "Add as new hosts", + "connection_backend": "Backend Connection", + "connection_backend_desc": "Used for remote tls connections", "certificate-alert": { "title": "Nodes Certificate Configuration", "desc": "If you aim to configure your nodes certificate, checkout settings page.", diff --git a/dashboard/public/locales/fa.json b/dashboard/public/locales/fa.json index 5b8ea7c..e0be303 100644 --- a/dashboard/public/locales/fa.json +++ b/dashboard/public/locales/fa.json @@ -1,164 +1,165 @@ { - "home": "خانه", - "name": "نام", - "status": "حالت", + "home": "خاٿٿ", + "name": "ٿام", + "status": "حاٿت", "address": "آدرس", - "users": "کاربران", - "nodes": "نود", - "services": "سرویسها", - "hosts": "هاست", - "username": "یوزرنیم", - "password": "پسوورد", - "inbounds": "اینباند ها", - "submit": "ارسال", - "login": "ورود", - "edit": "ویرایش", - "actions": "فعالیتها", - "settings": "تنظیمات", - "logs": "لاگ", - "note": "یادداشت", - "config": "کانفیگ", + "users": "کاربراٿ", + "nodes": "ٿٿد", + "services": "سرٿۿسٿا", + "hosts": "ٿاست", + "username": "ۿٿزرٿۿم", + "password": "پسٿٿرد", + "inbounds": "اۿٿباٿد ٿا", + "submit": "ارساٿ", + "login": "ٿرٿد", + "edit": "ٿۿراۿش", + "actions": "ٿعاٿۿتٿا", + "settings": "تٿظۿمات", + "logs": "ٿاگ", + "note": "ۿادداشت", + "config": "کاٿٿۿگ", "tag": "تگ", - "protocol": "پروتکل", - "online": "آنلاین", - "offline": "آفلاین", - "not_connected": "وصل نشده", - "apply": "اعمال", - "open": "باز کردن", - "delete": "حذف", - "port": "پورت", + "protocol": "پرٿتکٿ", + "online": "آٿٿاۿٿ", + "offline": "آٿٿاۿٿ", + "not_connected": "ٿصٿ ٿشدٿ", + "apply": "اعماٿ", + "open": "باز کردٿ", + "delete": "حذٿ", + "port": "پٿرت", "create": "ساخت", - "path": "مسیر", - "host": "هاست", + "path": "مسۿر", + "host": "ٿاست", "alpn": "ALPN", - "security": "امنیت", - "user_info": "اطلاعات کاربر", + "security": "امٿۿت", + "user_info": "اطٿاعات کاربر", "subscription": "اشتراک", - "subscription_link": "لینک اشتراک", - "fingerprint": "فینگرپرینت", + "subscription_link": "ٿۿٿک اشتراک", + "fingerprint": "ٿۿٿگرپرۿٿت", "page": { "settings": { "certificate": { - "copy": "کپی سرتیفیکت", - "error": "سرتیفیکت در دسترس نیست", - "copied": "سرتیفیکت کپی شد" + "copy": "کپۿ سرتۿٿۿکت", + "error": "سرتۿٿۿکت در دسترس ٿۿست", + "copied": "سرتۿٿۿکت کپۿ شد" } }, "login": { "username": { "error": { - "isRequired": "نام کاربری را وارد کنید" + "isRequired": "ٿام کاربرۿ را ٿارد کٿۿد" } }, "password": { "error": { - "is-required": "رمز عبور را وارد کنید" + "is-required": "رمز عبٿر را ٿارد کٿۿد" } } }, "nodes": { "settings": { - "title": "تنظیمات نود", - "detail": "جزئیات", + "title": "تٿظۿمات ٿٿد", + "detail": "جزئۿات", "log-container": { - "title": "لاگ اکسری" + "title": "ٿاگ اکسرۿ" } }, "dialogs": { "creation": { - "title": "افزودن نود" + "title": "اٿزٿدٿ ٿٿد" }, "deletion": { - "title": "حذف نود" + "title": "حذٿ ٿٿد" }, "edition": { - "title": "ویرایش نود" + "title": "ٿۿراۿش ٿٿد" } }, - "usage_coefficient": "ضریب مصرف", - "add_as_new_host": "افزودن هاست های وابسته", + "usage_coefficient": "ضرۿب مصرٿ", + "connection_backend": "Backend Connection", + "connection_backend_desc": "Used for remote tls connections", "certificate-alert": { - "title": "کانفیگ سرتیفیکت نود", - "desc": "اگر قصد برداشت سرتیفیکت نود را دارید، صفحه تنظیمات را بررسی کنید", - "click": "اینجا کلیک کنید" + "title": "کاٿٿۿگ سرتۿٿۿکت ٿٿد", + "desc": "اگر ٿصد برداشت سرتۿٿۿکت ٿٿد را دارۿدؿ صٿحٿ تٿظۿمات را بررسۿ کٿۿد", + "click": "اۿٿجا کٿۿک کٿۿد" } }, "services": { "settings": { - "title": "تنظیمات سرویس", - "detail": "جزئیات" + "title": "تٿظۿمات سرٿۿس", + "detail": "جزئۿات" }, "dialogs": { "creation": { - "title": "ساخت سرویس" + "title": "ساخت سرٿۿس" }, "deletion": { - "title": "حذف سرویس" + "title": "حذٿ سرٿۿس" }, "edition": { - "title": "ویرایش سرویس" + "title": "ٿۿراۿش سرٿۿس" } } }, "users": { - "note-placeholder": "یادداشتی در مورد کاربر", - "data_limit": "محدودیت حجم", - "used_traffic": "ترافیک مصرفی", - "data_limit_reset_strategy": "بازه ریست حجم مصرفی", - "online_at": "آخرین استفاده", - "expire_date": "تاریخ انقضا", - "unlimited": "نامحدود", + "note-placeholder": "ۿادداشتۿ در مٿرد کاربر", + "data_limit": "محدٿدۿت حجم", + "used_traffic": "تراٿۿک مصرٿۿ", + "data_limit_reset_strategy": "بازٿ رۿست حجم مصرٿۿ", + "online_at": "آخرۿٿ استٿادٿ", + "expire_date": "تارۿخ اٿٿضا", + "unlimited": "ٿامحدٿد", "determined_expire": "مشخص", - "expire_method": "انقضا", - "onhold_expire": "شروع از اولین اتصال", - "reset_usage": "ریست حجم مصرفی", - "revoke_subscription": "ابطال اشتراک", - "on_hold_expire_duration": "مدت زمان", - "on_hold_timeout": "انقضا", + "expire_method": "اٿٿضا", + "onhold_expire": "شرٿع از اٿٿۿٿ اتصاٿ", + "reset_usage": "رۿست حجم مصرٿۿ", + "revoke_subscription": "ابطاٿ اشتراک", + "on_hold_expire_duration": "مدت زماٿ", + "on_hold_timeout": "اٿٿضا", "settings": { "subscription_link": { - "copied": "لینک اشتراک کپی شد", - "copy": "کپی لینک اشتراک", - "error": "لینک اشتراک کپی نشد" + "copied": "ٿۿٿک اشتراک کپۿ شد", + "copy": "کپۿ ٿۿٿک اشتراک", + "error": "ٿۿٿک اشتراک کپۿ ٿشد" } }, "services-alert": { - "title": "سرویسی یافت نشد", - "desc": "برای ساخت یوزر ابتدا سرویسی ایجاد کنید", - "click": "اینجا را کلیک کنید" + "title": "سرٿۿسۿ ۿاٿت ٿشد", + "desc": "براۿ ساخت ۿٿزر ابتدا سرٿۿسۿ اۿجاد کٿۿد", + "click": "اۿٿجا را کٿۿک کٿۿد" }, "dialogs": { "edition": { - "title": "ویرایش کاربر" + "title": "ٿۿراۿش کاربر" }, "creation": { - "title": "افزودن کاربر" + "title": "اٿزٿدٿ کاربر" }, "deletion": { - "title": "حذف کاربر" + "title": "حذٿ کاربر" }, "mutation": { "note": { - "desc": "یادداشت" + "desc": "ۿادداشت" } } } }, "hosts": { - "allowinsecure": "پذیرش کانکشن ناامن", + "allowinsecure": "پذۿرش کاٿکشٿ ٿاامٿ", "dialogs": { "edition": { - "title": "ویرایش هاست" + "title": "ٿۿراۿش ٿاست" }, "creation": { - "title": "افزودن هاست" + "title": "اٿزٿدٿ ٿاست" }, "deletion": { - "title": "حذف هاست" + "title": "حذٿ ٿاست" }, "mutation": { "allowinsecure": { - "alert": "فعال کردن این گزینه پیشنهاد نمÛشود." + "alert": "ٿعاٿ کردٿ اۿٿ گزۿٿٿ پۿشٿٿاد ٿمÛشٿد." } } } @@ -166,30 +167,30 @@ }, "events": { "delete": { - "error": "خطا در حذف {{name}}", + "error": "خطا در حذٿ {{name}}", "success": { - "title": "با موفقیت حذف شد {{name}}", + "title": "با مٿٿٿۿت حذٿ شد {{name}}", "desc": "" } }, "create": { - "error": "خطا در حذف {{name}}", + "error": "خطا در حذٿ {{name}}", "success": { - "title": "{{name}} با موفقیت حذف شد", + "title": "{{name}} با مٿٿٿۿت حذٿ شد", "desc": "" } }, "update": { - "error": "خطا در آپدیت {{name}}", + "error": "خطا در آپدۿت {{name}}", "success": { - "title": "{{name}} با موفقیت آپدیت شد", + "title": "{{name}} با مٿٿٿۿت آپدۿت شد", "desc": "" } }, "user_revoke": { - "error": "خطا در ابطال اشتراک {{name}}", + "error": "خطا در ابطاٿ اشتراک {{name}}", "success": { - "title": "با موفقیت ایجاد شد.{{name}}", + "title": "با مٿٿٿۿت اۿجاد شد.{{name}}", "desc": "" } }, @@ -202,8 +203,8 @@ } }, "delete-confirmation": { - "title": "آیا از انجام این کار مطمئنید؟", - "desc": "حذف اطلاعات بازگشت ناپذیر است." + "title": "آۿا از اٿجام اۿٿ کار مطمئٿۿدؿ", + "desc": "حذٿ اطٿاعات بازگشت ٿاپذۿر است." }, "table": { "row-per-page": "Rows per page", @@ -213,15 +214,15 @@ "filter-placeholder": "Filter {{name}}" }, "statuses": { - "active": "فعال", - "connected": "متصل شده", - "healthy": "سالم", - "disabled": "غیرفعال", - "expired": "منقضی", - "on_hold": "شروع از اولین اتصال", - "connecting": "در حال اتصال", - "unhealthy": "ناسالم", - "limited": "محدود شده", + "active": "ٿعاٿ", + "connected": "متصٿ شدٿ", + "healthy": "ساٿم", + "disabled": "غۿرٿعاٿ", + "expired": "مٿٿضۿ", + "on_hold": "شرٿع از اٿٿۿٿ اتصاٿ", + "connecting": "در حاٿ اتصاٿ", + "unhealthy": "ٿاساٿم", + "limited": "محدٿد شدٿ", "error": "خطا" } } diff --git a/dashboard/public/locales/kur.json b/dashboard/public/locales/kur.json index 2486032..6726912 100644 --- a/dashboard/public/locales/kur.json +++ b/dashboard/public/locales/kur.json @@ -1,290 +1,291 @@ { - "name": "Naw", - "status": "Dox", - "address": "Edrês", - "users": "Bekarhêneran", - "nodes": "Girêkan", - "services": "Rajekan", - "hosts": "Hostkan", - "username": "Nawî Bekarhêneran", - "password": "Têperrewişe", - "inbounds": "înbawendkan", - "submit": "Nardin", - "login": "Çûnejûrewe", - "edit": "Desitkarîkirdn", - "actions": "Fermankan", - "settings": "Sazkarîyekan", - "logs": "Tomarekan", - "config": "Kanfîg", - "open": "Kirnewe", - "delete": "Sirrînewe", - "port": "Port", - "create": "Dirust bike", - "path": "Path", - "host": "Host", - "alpn": "ALPN", - "security": "Asayiş", - "sni": "SNI", - "user_info": "Zanyarî bekarhêneran", - "subscription": "Beşdarî", - "subscription_link": "Link Beşdarî", - "fingerprint": "Fingerprint", - "enable": "Çalakî ke", - "disable": "Naçalakî ke", - "close": "Dayixe", - "save": "Seyv Bike", - "days": "Roj", - "home": "Malê", - "light": "Runak", - "dark": "Tarîk", - "system": "Sîstim", - "advanced-options": "Bijardikanî pêşkewtû", - "page": { - "settings": { - "certificate": { - "copy": "Kopî Birrwaname", - "error": "Birrwaname berdest nîye", - "copied": "Birrwaname Kopî kira" - } - }, - "login": { - "username": { - "error": { - "isRequired": "Nawî bekarhêner pêwîste" - } - }, - "password": { - "error": { - "is-required": "Têperwişe pêwîste" - } - } - }, - "nodes": { - "settings": { - "title": "Sazkarîyekanî Node", - "detail": "Wurdekaryekan", - "log-container": { - "title": "Xray logs" - } - }, - "dialogs": { - "creation": { - "title": "Nodes Creation" - }, - "deletion": { - "title": "Nodes Deletion" - }, - "edition": { - "title": "Nodes Edition" - } - }, - "usage_coefficient": "Usage Coefficient", - "add_as_new_host": "Add as new hosts", - "certificate-alert": { - "title": "Nodes Certificate Configuration", - "desc": "If you aim to configure your nodes certificate, checkout settings page.", - "click": "Click Here" - } - }, - "services": { - "settings": { - "title": "Servicecs Settings", - "detail": "Details" - }, - "dialogs": { - "creation": { - "title": "Services Creation" - }, - "deletion": { - "title": "Services Deletion" - }, - "edition": { - "title": "Services Edition" - } - } - }, - "users": { - "note-placeholder": "Têbînî leser bekarhêner", - "data_limit": "Sinûrî data", - "used_traffic": "Traffic bekarhêndiraw", - "data_limit_reset_strategy": "Şêwazî sirrinewey sinûrî data", - "online_at": "Online Le", - "expire_date": "Katî beserçûn", - "unlimited": "Bêsinûr", - "limited": "Sinûrdar", - "data_limit_method": "Çonêtî Sinûrî Data", - "determined_expire": "Diyarî kiraw", - "expire_method": "Sêwazî beserçûn", - "onhold_expire": "Ragîraw", - "reset_usage": "Rêsitkirdinî bekarhênraw", - "revoke_subscription": "Hellweşandinewey Beşdarîkirdn", - "on_hold_expire_duration": "Ragîraw", - "on_hold_timeout": "Kotayî hatnî ragirtn", - "settings": { - "subscription_link": { - "copied": "Subscription Link Copied", - "copy": "Copy Subscription Link", - "error": "Unable to copy Subscription Link" - } - }, - "services-alert": { - "title": "No Service were found", - "desc": "Make sure you create services to enable your users with inbounds to connect with.", - "click": "Click Here" - }, - "dialogs": { - "edition": { - "title": "Users Edition" - }, - "creation": { - "title": "Users Creation" - }, - "deletion": { - "title": "Users Deletion" - }, - "mutation": { - "note": { - "desc": "Leave a note about your user" - } - } - } - }, - "hosts": { - "fragment": { - "interval": "Interval", - "length": "Length", - "packets": "Packets", - "length-error": "Length can not exceed 32 char and must be number-number", - "interval-error": "Interval can not exceed 32 char and must be number-number", - "packets-error": "Packets can not exceed 32 char and must be number-number or be tlshello" - }, - "popover-guide": { - "title": "Using this setting, you are able to assign specific address for each inbound.", - "desc": "Use these variables to make it dynamic", - "username": "The username of the user", - "dataUsage": "The current usage of the user", - "remainingData": "Remaining data of the user", - "dataLimit": "The usage limit of the user", - "remainingDays": "Remaining days of the user", - "expireDate": "Expiry date of the user", - "jalaliExpireDate": "Expiry date of the user in solar calendar", - "remainingTime": "Remaining time of the user", - "statusEmoji": "User status as an emoji (✅,⌛,🪫,❌,🔌)", - "proxyProtocol": "Proxy protocol (e.g. VMess)", - "proxyMethod": "Proxy transport method (e.g. ws)", - "currentServer": "Current server ip address", - "security": "Security Layer", - "host": "Request Host", - "port": "Port", - "path": "Path", - "sni": "SNI", - "advancedOptions": "Advanced options", - "addHost": "Add host", - "savedSuccess": "Hosts saved successfully", - "loading": "loading...", - "apply": "Apply", - "port.info": "By default, a host uses the default port of the inbound. You can set a custom port in case this host is a server that forwards traffic from a port that differs from your server's port. For example, the server may forward traffic from port 8443 to the default port of your inbound server.", - "path.info": "Set a path for host users, useful behind a reverse proxy.", - "sni.info": "By default, a host uses the default SNI of the inbound. You can set a custom SNI in case this host is a server that has a different SNI. For example, the server may receive traffic with a different SSL certificate, perform SSL termination, and forward it to your inbound server.", - "host.info": "By default, if a request host is set in the Xray config, this host is used. However, you can set a custom request host here if needed.", - "security.info": "If the middleware server of this host uses a different security layer than the inbound's default, you can set a custom security layer here.", - "alpn": "ALPN", - "fingerprint": "Fingerprint", - "host.multiHost": "To set multiple addresses, separate them with , Each time an address is chosen randomly.", - "host.wildcard": "Use * to generate a random string (works for wildcard domain names)" - }, - "mux": { - "title": "Connection multiplexing", - "desc": "Currently supported by v2rayng while using custom config" - }, - "no-inbound-selection-alert": { - "title": "Hîç înbawendêk hell nebijardirawe", - "desc": "Debêt înbawendêk hell bibjêrî ta we ku hostêk dirust bikey." - }, - "allowinsecure": "Allow insecure", - "dialogs": { - "edition": { - "title": "Gorînî Host" - }, - "creation": { - "title": "Dirust kirdin Host" - }, - "deletion": { - "title": "Sirînewey Host" - }, - "mutation": { - "allowinsecure": { - "alert": "Due to security concerns, it is not recommended to allow Insecure" - } - } - } - } - }, - "events": { - "delete": { - "error": "Failed to delete {{name}}", - "success": { - "title": "{{name}} is successfuly deleted", - "desc": "" - } - }, - "create": { - "error": "Failed to create {{name}}", - "success": { - "title": "{{name}} is successfuly created", - "desc": "" - } - }, - "update": { - "error": "Failed to update {{name}}", - "success": { - "title": "{{name}} is successfuly updated", - "desc": "" - } - }, - "user_revoke": { - "error": "Failed to revoke {{name}} subscription", - "success": { - "title": "{{name}} is successfuly created", - "desc": "" - } - }, - "user_reset": { - "error": "Failed to reset {{name}} data usage", - "success": { - "title": "{{name}} data usage is successfuly reseted", - "desc": "" - } - }, - "user_status": { - "error": "Şikistî hêna le gorrînî doxî çalak kirdinî {{name}}", - "success": { - "title": "Doxî çalakî {{name}} nwêkirawe", - "desc": "Doxî çalak kirdin be serkewtûyî nwêkirawe" - } - } - }, - "delete-confirmation": { - "title": "Are you sure of this?", - "desc": "This action cannot be undone. This will permanently delete your account and remove your data from our servers." - }, - "table": { - "row-per-page": "Rows per page", - "no-result": "No results", - "selected": "Selected", - "toggle-column": "Toggle columns", - "filter-placeholder": "Filter {{name}}" - }, - "statuses": { - "active": "Active", - "connected": "Connected", - "healthy": "Healthy", - "disabled": "Disabled", - "expired": "Expired", - "on_hold": "On Hold", - "connecting": "Connecting", - "unhealthy": "Unhealthy", - "limited": "Limited", - "error": "Error" - } + "name": "Naw", + "status": "Dox", + "address": "Edrês", + "users": "Bekarhêneran", + "nodes": "Girêkan", + "services": "Rajekan", + "hosts": "Hostkan", + "username": "Nawî Bekarhêneran", + "password": "Têperrewişe", + "inbounds": "înbawendkan", + "submit": "Nardin", + "login": "Çûnejûrewe", + "edit": "Desitkarîkirdn", + "actions": "Fermankan", + "settings": "Sazkarîyekan", + "logs": "Tomarekan", + "config": "Kanfîg", + "open": "Kirnewe", + "delete": "Sirrînewe", + "port": "Port", + "create": "Dirust bike", + "path": "Path", + "host": "Host", + "alpn": "ALPN", + "security": "Asayiş", + "sni": "SNI", + "user_info": "Zanyarî bekarhêneran", + "subscription": "Beşdarî", + "subscription_link": "Link Beşdarî", + "fingerprint": "Fingerprint", + "enable": "Çalakî ke", + "disable": "Naçalakî ke", + "close": "Dayixe", + "save": "Seyv Bike", + "days": "Roj", + "home": "Malê", + "light": "Runak", + "dark": "Tarîk", + "system": "Sîstim", + "advanced-options": "Bijardikanî pêşkewtû", + "page": { + "settings": { + "certificate": { + "copy": "Kopî Birrwaname", + "error": "Birrwaname berdest nîye", + "copied": "Birrwaname Kopî kira" + } + }, + "login": { + "username": { + "error": { + "isRequired": "Nawî bekarhêner pêwîste" + } + }, + "password": { + "error": { + "is-required": "Têperwişe pêwîste" + } + } + }, + "nodes": { + "settings": { + "title": "Sazkarîyekanî Node", + "detail": "Wurdekaryekan", + "log-container": { + "title": "Xray logs" + } + }, + "dialogs": { + "creation": { + "title": "Nodes Creation" + }, + "deletion": { + "title": "Nodes Deletion" + }, + "edition": { + "title": "Nodes Edition" + } + }, + "usage_coefficient": "Usage Coefficient", + "connection_backend": "Backend Connection", + "connection_backend_desc": "Used for remote tls connections", + "certificate-alert": { + "title": "Nodes Certificate Configuration", + "desc": "If you aim to configure your nodes certificate, checkout settings page.", + "click": "Click Here" + } + }, + "services": { + "settings": { + "title": "Servicecs Settings", + "detail": "Details" + }, + "dialogs": { + "creation": { + "title": "Services Creation" + }, + "deletion": { + "title": "Services Deletion" + }, + "edition": { + "title": "Services Edition" + } + } + }, + "users": { + "note-placeholder": "Têbînî leser bekarhêner", + "data_limit": "Sinûrî data", + "used_traffic": "Traffic bekarhêndiraw", + "data_limit_reset_strategy": "Şêwazî sirrinewey sinûrî data", + "online_at": "Online Le", + "expire_date": "Katî beserçûn", + "unlimited": "Bêsinûr", + "limited": "Sinûrdar", + "data_limit_method": "Çonêtî Sinûrî Data", + "determined_expire": "Diyarî kiraw", + "expire_method": "Sêwazî beserçûn", + "onhold_expire": "Ragîraw", + "reset_usage": "Rêsitkirdinî bekarhênraw", + "revoke_subscription": "Hellweşandinewey Beşdarîkirdn", + "on_hold_expire_duration": "Ragîraw", + "on_hold_timeout": "Kotayî hatnî ragirtn", + "settings": { + "subscription_link": { + "copied": "Subscription Link Copied", + "copy": "Copy Subscription Link", + "error": "Unable to copy Subscription Link" + } + }, + "services-alert": { + "title": "No Service were found", + "desc": "Make sure you create services to enable your users with inbounds to connect with.", + "click": "Click Here" + }, + "dialogs": { + "edition": { + "title": "Users Edition" + }, + "creation": { + "title": "Users Creation" + }, + "deletion": { + "title": "Users Deletion" + }, + "mutation": { + "note": { + "desc": "Leave a note about your user" + } + } + } + }, + "hosts": { + "fragment": { + "interval": "Interval", + "length": "Length", + "packets": "Packets", + "length-error": "Length can not exceed 32 char and must be number-number", + "interval-error": "Interval can not exceed 32 char and must be number-number", + "packets-error": "Packets can not exceed 32 char and must be number-number or be tlshello" + }, + "popover-guide": { + "title": "Using this setting, you are able to assign specific address for each inbound.", + "desc": "Use these variables to make it dynamic", + "username": "The username of the user", + "dataUsage": "The current usage of the user", + "remainingData": "Remaining data of the user", + "dataLimit": "The usage limit of the user", + "remainingDays": "Remaining days of the user", + "expireDate": "Expiry date of the user", + "jalaliExpireDate": "Expiry date of the user in solar calendar", + "remainingTime": "Remaining time of the user", + "statusEmoji": "User status as an emoji (✅,⌛,🪫,❌,🔌)", + "proxyProtocol": "Proxy protocol (e.g. VMess)", + "proxyMethod": "Proxy transport method (e.g. ws)", + "currentServer": "Current server ip address", + "security": "Security Layer", + "host": "Request Host", + "port": "Port", + "path": "Path", + "sni": "SNI", + "advancedOptions": "Advanced options", + "addHost": "Add host", + "savedSuccess": "Hosts saved successfully", + "loading": "loading...", + "apply": "Apply", + "port.info": "By default, a host uses the default port of the inbound. You can set a custom port in case this host is a server that forwards traffic from a port that differs from your server's port. For example, the server may forward traffic from port 8443 to the default port of your inbound server.", + "path.info": "Set a path for host users, useful behind a reverse proxy.", + "sni.info": "By default, a host uses the default SNI of the inbound. You can set a custom SNI in case this host is a server that has a different SNI. For example, the server may receive traffic with a different SSL certificate, perform SSL termination, and forward it to your inbound server.", + "host.info": "By default, if a request host is set in the Xray config, this host is used. However, you can set a custom request host here if needed.", + "security.info": "If the middleware server of this host uses a different security layer than the inbound's default, you can set a custom security layer here.", + "alpn": "ALPN", + "fingerprint": "Fingerprint", + "host.multiHost": "To set multiple addresses, separate them with , Each time an address is chosen randomly.", + "host.wildcard": "Use * to generate a random string (works for wildcard domain names)" + }, + "mux": { + "title": "Connection multiplexing", + "desc": "Currently supported by v2rayng while using custom config" + }, + "no-inbound-selection-alert": { + "title": "Hîç înbawendêk hell nebijardirawe", + "desc": "Debêt înbawendêk hell bibjêrî ta we ku hostêk dirust bikey." + }, + "allowinsecure": "Allow insecure", + "dialogs": { + "edition": { + "title": "Gorînî Host" + }, + "creation": { + "title": "Dirust kirdin Host" + }, + "deletion": { + "title": "Sirînewey Host" + }, + "mutation": { + "allowinsecure": { + "alert": "Due to security concerns, it is not recommended to allow Insecure" + } + } + } + } + }, + "events": { + "delete": { + "error": "Failed to delete {{name}}", + "success": { + "title": "{{name}} is successfuly deleted", + "desc": "" + } + }, + "create": { + "error": "Failed to create {{name}}", + "success": { + "title": "{{name}} is successfuly created", + "desc": "" + } + }, + "update": { + "error": "Failed to update {{name}}", + "success": { + "title": "{{name}} is successfuly updated", + "desc": "" + } + }, + "user_revoke": { + "error": "Failed to revoke {{name}} subscription", + "success": { + "title": "{{name}} is successfuly created", + "desc": "" + } + }, + "user_reset": { + "error": "Failed to reset {{name}} data usage", + "success": { + "title": "{{name}} data usage is successfuly reseted", + "desc": "" + } + }, + "user_status": { + "error": "Şikistî hêna le gorrînî doxî çalak kirdinî {{name}}", + "success": { + "title": "Doxî çalakî {{name}} nwêkirawe", + "desc": "Doxî çalak kirdin be serkewtûyî nwêkirawe" + } + } + }, + "delete-confirmation": { + "title": "Are you sure of this?", + "desc": "This action cannot be undone. This will permanently delete your account and remove your data from our servers." + }, + "table": { + "row-per-page": "Rows per page", + "no-result": "No results", + "selected": "Selected", + "toggle-column": "Toggle columns", + "filter-placeholder": "Filter {{name}}" + }, + "statuses": { + "active": "Active", + "connected": "Connected", + "healthy": "Healthy", + "disabled": "Disabled", + "expired": "Expired", + "on_hold": "On Hold", + "connecting": "Connecting", + "unhealthy": "Unhealthy", + "limited": "Limited", + "error": "Error" + } } diff --git a/dashboard/src/features/nodes/dialogs/mutation.dialog.tsx b/dashboard/src/features/nodes/dialogs/mutation.dialog.tsx index 4466d43..0cc245f 100644 --- a/dashboard/src/features/nodes/dialogs/mutation.dialog.tsx +++ b/dashboard/src/features/nodes/dialogs/mutation.dialog.tsx @@ -1,150 +1,170 @@ -import { FC } from "react"; +import type { FC } from "react"; import { - DialogTitle, - DialogContent, - Dialog, - DialogHeader, - Form, - FormItem, - FormControl, - FormMessage, - FormLabel, - Input, - FormField, - Checkbox, - Button -} from '@marzneshin/components'; -import { useTranslation } from 'react-i18next'; -import { NodeType, NodeSchema, useNodesCreationMutation, useNodesUpdateMutation } from ".."; + DialogTitle, + DialogContent, + Dialog, + DialogHeader, + Form, + FormItem, + FormControl, + FormMessage, + FormLabel, + Input, + FormField, + Button, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + FormDescription, + HStack, +} from "@marzneshin/components"; +import { useTranslation } from "react-i18next"; +import { + NodeSchema, + useNodesCreationMutation, + useNodesUpdateMutation, +} from ".."; +import type { NodeType } from ".."; import { useMutationDialog } from "@marzneshin/hooks"; interface MutationDialogProps { - entity: NodeType | null; - open: boolean; - onOpenChange: (state: boolean) => void; + entity: NodeType | null; + open: boolean; + onOpenChange: (state: boolean) => void; } const getDefaultValue = (): NodeType => ({ - name: '', - address: '', - status: 'none', - port: 62050, - usage_coefficient: 1, - add_as_new_host: true, + name: "", + address: "", + status: "none", + port: 62050, + usage_coefficient: 1, + connection_backend: "grpclib", }); export const MutationDialog: FC = ({ - entity, - open, - onOpenChange, + entity, + open, + onOpenChange, }) => { - const updateMutation = useNodesUpdateMutation(); - const createMutation = useNodesCreationMutation(); - const { t } = useTranslation(); - const { form, handleSubmit } = useMutationDialog({ - onOpenChange, - entity, - schema: NodeSchema, - createMutation, - updateMutation, - getDefaultValue - }) - return ( - - - - - {entity - ? t('page.nodes.dialogs.edition.title') - : t('page.nodes.dialogs.creation.title')} - - -
- - ( - - {t('name')} - - - - - - )} - /> -
- ( - - {t('address')} - - - - - - )} - /> - ( - - {t('port')} - - - - - - )} - /> - ( - - {t('page.nodes.usage_coefficient')} - - - - - - )} - /> -
- ( - - -
- - - {t('page.nodes.add_as_new_host')} - -
-
- -
- )} - /> - - - -
-
- ); + const updateMutation = useNodesUpdateMutation(); + const createMutation = useNodesCreationMutation(); + const { t } = useTranslation(); + const { form, handleSubmit } = useMutationDialog({ + onOpenChange, + entity, + schema: NodeSchema, + createMutation, + updateMutation, + getDefaultValue, + }); + return ( + + + + + {entity + ? t("page.nodes.dialogs.edition.title") + : t("page.nodes.dialogs.creation.title")} + + +
+ + ( + + {t("name")} + + + + + + )} + /> +
+ ( + + {t("address")} + + + + + + )} + /> + ( + + {t("port")} + + + + + + )} + /> +
+ + ( + + {t("page.nodes.connection_backend")} + + + {t("page.nodes.connection_backend_desc")} + + + + )} + /> + ( + + {t("page.nodes.usage_coefficient")} + + + + + + )} + /> + + + + +
+
+ ); }; diff --git a/dashboard/src/features/nodes/types/index.ts b/dashboard/src/features/nodes/types/index.ts index 1d4030a..f1c4256 100644 --- a/dashboard/src/features/nodes/types/index.ts +++ b/dashboard/src/features/nodes/types/index.ts @@ -1,40 +1,45 @@ -import { StatusType } from "@marzneshin/types/status"; +import type { StatusType } from "@marzneshin/types/status"; import { PowerOff, Zap, ZapOff } from "lucide-react"; import { z } from "zod"; - export const NodesStatus = { - healthy: { - label: 'healthy', - icon: Zap, - }, - unhealthy: { - label: 'unhealthy', - icon: ZapOff, - }, - disabled: { - label: 'disabled', - icon: PowerOff, - }, - none: { - label: 'none', - icon: null, - } + healthy: { + label: "healthy", + icon: Zap, + }, + unhealthy: { + label: "unhealthy", + icon: ZapOff, + }, + disabled: { + label: "disabled", + icon: PowerOff, + }, + none: { + label: "none", + icon: null, + }, } as Record; export const NodeSchema = z.object({ - name: z.string().min(1), - address: z.string().min(1), - port: z - .number() - .min(1) - .or(z.string().transform((v) => parseFloat(v))), - id: z.number().nullable().optional(), - status: z - .enum([NodesStatus.healthy.label, NodesStatus.unhealthy.label, 'none', NodesStatus.disabled.label]), - usage_coefficient: z.number().default(1.0) - .or(z.string().transform((v) => parseFloat(v))), - add_as_new_host: z.boolean().optional(), + name: z.string().min(1), + address: z.string().min(1), + port: z + .number() + .min(1) + .or(z.string().transform((v) => Number.parseFloat(v))), + id: z.number().nullable().optional(), + status: z.enum([ + NodesStatus.healthy.label, + NodesStatus.unhealthy.label, + "none", + NodesStatus.disabled.label, + ]), + usage_coefficient: z + .number() + .default(1.0) + .or(z.string().transform((v) => Number.parseFloat(v))), + connection_backend: z.enum(["grpclib", "grpcio"]).default("grpclib"), }); export type NodeType = z.infer;