diff --git a/app/core/hosts.py b/app/core/hosts.py index baa43881..9118d26d 100644 --- a/app/core/hosts.py +++ b/app/core/hosts.py @@ -167,7 +167,7 @@ async def _prepare_subscription_inbound_data( # Get VLESS encryption from inbound encryption = inbound_config.get("encryption", "none") - # Get flow from inbound (user can override later in share.py) + # Get flow from inbound for subscription generation. inbound_flow = inbound_config.get("flow", "") if inbound_flow == "none": inbound_flow = "" diff --git a/app/db/crud/bulk.py b/app/db/crud/bulk.py index 6c8922cf..14feb989 100644 --- a/app/db/crud/bulk.py +++ b/app/db/crud/bulk.py @@ -436,13 +436,6 @@ async def update_users_proxy_settings( # Prepare the update statement if dialect == "postgresql": proxy_settings_expr = cast(User.proxy_settings, JSONB) - if bulk_model.flow is not None: - proxy_settings_expr = func.jsonb_set( - proxy_settings_expr, - text("'{vless,flow}'"), - cast(f"{bulk_model.flow.value}", JSONB), - True, - ) if bulk_model.method is not None: proxy_settings_expr = func.jsonb_set( proxy_settings_expr, @@ -452,8 +445,6 @@ async def update_users_proxy_settings( ) else: proxy_settings_expr = User.proxy_settings - if bulk_model.flow is not None: - proxy_settings_expr = func.json_set(proxy_settings_expr, "$.vless.flow", f"{bulk_model.flow.value}") if bulk_model.method is not None: proxy_settings_expr = func.json_set( proxy_settings_expr, "$.shadowsocks.method", f"{bulk_model.method.value}" diff --git a/app/db/crud/user.py b/app/db/crud/user.py index 62546141..377341a8 100644 --- a/app/db/crud/user.py +++ b/app/db/crud/user.py @@ -1070,7 +1070,6 @@ async def bulk_reset_user_data_usage( def _build_revoked_proxy_settings(db_user: User) -> dict: proxy_settings = ProxyTable() - proxy_settings.vless.flow = db_user.proxy_settings.get("vless", {}).get("flow", "") proxy_settings.shadowsocks.method = db_user.proxy_settings.get("shadowsocks", {}).get( "method", "chacha20-ietf-poly1305" ) @@ -1118,11 +1117,6 @@ async def reset_user_by_next(db: AsyncSession, db_user: User, *, clean_chart_dat if db_user.next_plan.user_template.extra_settings: proxy_settings = deepcopy(db_user.proxy_settings) - proxy_settings["vless"]["flow"] = ( - db_user.next_plan.user_template.extra_settings["flow"] - if db_user.next_plan.user_template.extra_settings["flow"] - else "" - ) proxy_settings["shadowsocks"]["method"] = ( db_user.next_plan.user_template.extra_settings["method"] if db_user.next_plan.user_template.extra_settings["method"] diff --git a/app/models/proxy.py b/app/models/proxy.py index 941867ae..79a770b1 100644 --- a/app/models/proxy.py +++ b/app/models/proxy.py @@ -13,15 +13,8 @@ class VMessSettings(BaseModel): id: UUID = Field(default_factory=uuid4) -class XTLSFlows(StrEnum): - NONE = "" - VISION = "xtls-rprx-vision" - VISION_UDP = "xtls-rprx-vision-udp443" - - class VlessSettings(BaseModel): id: UUID = Field(default_factory=uuid4) - flow: XTLSFlows = XTLSFlows.NONE class TrojanSettings(BaseModel): diff --git a/app/models/settings.py b/app/models/settings.py index 55ca8fef..dc7ea380 100644 --- a/app/models/settings.py +++ b/app/models/settings.py @@ -4,7 +4,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator -from app.models.proxy import ShadowsocksMethods, XTLSFlows +from app.models.proxy import ShadowsocksMethods from .notification_enable import NotificationEnable from .validators import DiscordValidator, ProxyValidator, URLValidator @@ -294,7 +294,6 @@ class HWIDSettings(BaseModel): class General(BaseModel): - default_flow: XTLSFlows = Field(default=XTLSFlows.NONE) default_method: ShadowsocksMethods = Field(default=ShadowsocksMethods.CHACHA20_POLY1305) diff --git a/app/models/user.py b/app/models/user.py index 220eb149..29099dd4 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -6,7 +6,7 @@ from app.db.models import DataLimitResetStrategy, UserStatus from app.models.admin import AdminBase, AdminContactInfo -from app.models.proxy import ProxyTable, ShadowsocksMethods, XTLSFlows +from app.models.proxy import ProxyTable, ShadowsocksMethods from app.models.stats import Period from app.utils.helpers import fix_datetime_timezone @@ -431,7 +431,6 @@ class BulkUser(BulkUserFilter): class BulkUsersProxy(BulkUserFilter): - flow: XTLSFlows | None = Field(default=None) method: ShadowsocksMethods | None = Field(default=None) diff --git a/app/models/user_template.py b/app/models/user_template.py index b2c49cb7..44fb47fb 100644 --- a/app/models/user_template.py +++ b/app/models/user_template.py @@ -4,13 +4,12 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator from app.db.models import DataLimitResetStrategy, UserStatusCreate -from app.models.proxy import ShadowsocksMethods, XTLSFlows +from app.models.proxy import ShadowsocksMethods from .validators import ListValidator, UserValidator class ExtraSettings(BaseModel): - flow: XTLSFlows | None = Field(XTLSFlows.NONE) method: ShadowsocksMethods | None = Field(ShadowsocksMethods.CHACHA20_POLY1305) def dict(self, *, no_obj=True, **kwargs): diff --git a/app/node/user.py b/app/node/user.py index 19fd7a10..1afffc59 100644 --- a/app/node/user.py +++ b/app/node/user.py @@ -57,11 +57,7 @@ def _serialize_user_for_node( if ProxyProtocol.vmess in allowed_protocols: proxy_kwargs["vmess_id"] = user_settings.get("vmess", {}).get("id") if ProxyProtocol.vless in allowed_protocols: - vless_settings = dict(user_settings.get("vless", {})) - if vless_settings.get("flow") == "xtls-rprx-vision-udp443": - vless_settings["flow"] = "xtls-rprx-vision" - proxy_kwargs["vless_id"] = vless_settings.get("id") - proxy_kwargs["vless_flow"] = vless_settings.get("flow") + proxy_kwargs["vless_id"] = user_settings.get("vless", {}).get("id") if ProxyProtocol.trojan in allowed_protocols: proxy_kwargs["trojan_password"] = user_settings.get("trojan", {}).get("password") if ProxyProtocol.shadowsocks in allowed_protocols: diff --git a/app/operation/user.py b/app/operation/user.py index 5bbd7910..2328ef6a 100644 --- a/app/operation/user.py +++ b/app/operation/user.py @@ -974,12 +974,8 @@ def load_base_user_args(template: UserTemplate) -> dict: @staticmethod def apply_settings(user_args: UserCreate | UserModify, template: UserTemplate) -> dict: if template.extra_settings: - flow = template.extra_settings.get("flow", None) method = template.extra_settings.get("method", None) - if flow is not None: - user_args.proxy_settings.vless.flow = flow - if method is not None: user_args.proxy_settings.shadowsocks.method = method @@ -1196,6 +1192,8 @@ async def bulk_modify_datalimit(self, db: AsyncSession, bulk_model: BulkUser): return users_count async def bulk_modify_proxy_settings(self, db: AsyncSession, bulk_model: BulkUsersProxy): + if bulk_model.method is None: + await self.raise_error(message="No supported proxy settings were provided", code=400, db=db) if bulk_model.dry_run: n = await count_bulk_proxy_targets(db, bulk_model) return BulkOperationDryRunResponse(affected_users=n) diff --git a/app/subscription/clash.py b/app/subscription/clash.py index 2e77fa94..9e534682 100644 --- a/app/subscription/clash.py +++ b/app/subscription/clash.py @@ -432,8 +432,7 @@ def _build_vless(self, remark: str, address: str, inbound: SubscriptionInboundDa if inbound.encryption != "none": node["encryption"] = inbound.encryption - # Only add flow if inbound supports it - if inbound.flow_enabled and (flow := settings.get("flow", "")): + if inbound.flow_enabled and (flow := inbound.inbound_flow): node["flow"] = flow self._apply_tls(node, inbound.tls_config, "vless") diff --git a/app/subscription/links.py b/app/subscription/links.py index ff9f9b23..e4032c2b 100644 --- a/app/subscription/links.py +++ b/app/subscription/links.py @@ -268,8 +268,7 @@ def _build_vless(self, remark: str, address: str, inbound: SubscriptionInboundDa "headerType": getattr(inbound.transport_config, "header_type", "none"), } - # Only add flow if inbound supports it - if inbound.flow_enabled and (flow := settings.get("flow", "")): + if inbound.flow_enabled and (flow := inbound.inbound_flow): payload["flow"] = flow self._apply_transport_settings(payload, "vless", inbound, path) diff --git a/app/subscription/share.py b/app/subscription/share.py index 75abff6c..d966f2c9 100644 --- a/app/subscription/share.py +++ b/app/subscription/share.py @@ -233,11 +233,6 @@ async def process_host( if user_id is not None: settings["_user_id"] = user_id - # Handle flow: user settings have priority, fall back to inbound flow - if "flow" in settings and settings["flow"] == "": - # User has empty flow, use inbound flow as default - settings["flow"] = inbound.inbound_flow - # Update format variables format_variables.update({"PROTOCOL": inbound.protocol}) format_variables.update({"TRANSPORT": inbound.network}) diff --git a/app/subscription/singbox.py b/app/subscription/singbox.py index 3aa06c02..a966284f 100644 --- a/app/subscription/singbox.py +++ b/app/subscription/singbox.py @@ -274,8 +274,7 @@ def _build_vless(self, remark: str, address: str, inbound: SubscriptionInboundDa id = self.vless_route(id, inbound.vless_route) user_settings = {"uuid": id} - # Only add flow if inbound supports it - if inbound.flow_enabled and (flow := settings.get("flow", "")): + if inbound.flow_enabled and (flow := inbound.inbound_flow): user_settings["flow"] = flow return self._build_outbound( diff --git a/app/subscription/xray.py b/app/subscription/xray.py index 17f49052..200282f0 100644 --- a/app/subscription/xray.py +++ b/app/subscription/xray.py @@ -417,8 +417,7 @@ def _build_vless(self, address: str, inbound: SubscriptionInboundData, settings: user_settings = {"id": id, "encryption": inbound.encryption} - # Only add flow if inbound supports it - if inbound.flow_enabled and (flow := settings.get("flow", "")): + if inbound.flow_enabled and (flow := inbound.inbound_flow): user_settings["flow"] = flow return self._build_outbound( diff --git a/dashboard/public/statics/locales/en.json b/dashboard/public/statics/locales/en.json index 4484c207..fce3a125 100644 --- a/dashboard/public/statics/locales/en.json +++ b/dashboard/public/statics/locales/en.json @@ -140,11 +140,6 @@ "saveSuccess": "General settings saved successfully", "saveFailed": "Failed to save general settings", "cancelSuccess": "Changes cancelled and original settings restored", - "defaultFlow": { - "title": "Default VLESS Flow", - "description": "Autofill the flow field for new VLESS users", - "none": "None" - }, "defaultMethod": { "title": "Default Shadowsocks Method", "description": "Autofill the encryption method for new Shadowsocks users" @@ -1485,8 +1480,6 @@ "proxySettings.id": "ID", "proxySettings.password": "Password", "proxySettings.method": "Method", - "proxySettings.flow": "Flow", - "proxySettings.flowDeprecated": "Deprecated. This will be removed in future versions. Use inbound.settings.flow", "proxySettings.hysteria": "Hysteria", "proxySettings.hysteriaAuth": "Hysteria Auth", "proxySettings.wireguardPrivateKey": "WireGuard Private key", diff --git a/dashboard/public/statics/locales/fa.json b/dashboard/public/statics/locales/fa.json index f6ef5744..d00894eb 100644 --- a/dashboard/public/statics/locales/fa.json +++ b/dashboard/public/statics/locales/fa.json @@ -456,11 +456,6 @@ "saveSuccess": "تنظیمات عمومی با موفقیت ذخیره شد", "saveFailed": "ذخیره تنظیمات عمومی ناموفق بود", "cancelSuccess": "تغییرات لغو شد و تنظیمات اصلی بازیابی شد", - "defaultFlow": { - "title": "جریان VLESS پیش‌فرض", - "description": "پر کردن خودکار فیلد جریان برای کاربران جدید VLESS", - "none": "بدون" - }, "defaultMethod": { "title": "روش پیش‌فرض Shadowsocks", "description": "پر کردن خودکار روش رمزنگاری برای کاربران جدید Shadowsocks" @@ -1322,8 +1317,6 @@ "userDialog.proxySettings.id": "شناسه (ID)", "userDialog.proxySettings.password": "رمز عبور", "userDialog.proxySettings.method": "روش رمزنگاری", - "userDialog.proxySettings.flow": "جریان (Flow)", - "userDialog.proxySettings.flowDeprecated": "این مورد منسوخ شده است و در نسخه‌های بعدی حذف می‌شود. به‌جای آن از inbound.settings.flow استفاده کنید", "userDialog.proxySettings.hysteria": "Hysteria", "userDialog.proxySettings.hysteriaAuth": "احراز هویت Hysteria", "userDialog.proxySettings.wireguardPrivateKey": "کلید خصوصی وایرگارد", diff --git a/dashboard/public/statics/locales/ru.json b/dashboard/public/statics/locales/ru.json index 453b685d..9c5941bf 100644 --- a/dashboard/public/statics/locales/ru.json +++ b/dashboard/public/statics/locales/ru.json @@ -588,11 +588,6 @@ "saveSuccess": "Общие настройки успешно сохранены", "saveFailed": "Не удалось сохранить общие настройки", "cancelSuccess": "Изменения отменены и исходные настройки восстановлены", - "defaultFlow": { - "title": "Поток VLESS по умолчанию", - "description": "Автоматически заполнять поле потока для новых пользователей VLESS", - "none": "Нет" - }, "defaultMethod": { "title": "Метод Shadowsocks по умолчанию", "description": "Автоматически заполнять метод шифрования для новых пользователей Shadowsocks" @@ -2597,8 +2592,6 @@ "proxySettings.id": "ID", "proxySettings.password": "Пароль", "proxySettings.method": "Метод", - "proxySettings.flow": "Поток (Flow)", - "proxySettings.flowDeprecated": "Устарело. Будет удалено в следующих версиях. Используйте вместо этого inbound.settings.flow", "proxySettings.hysteria": "Hysteria", "proxySettings.hysteriaAuth": "Аутентификация Hysteria", "proxySettings.wireguardPrivateKey": "Приватный ключ WireGuard", diff --git a/dashboard/public/statics/locales/zh.json b/dashboard/public/statics/locales/zh.json index 9eaaac2f..6bd9ef39 100644 --- a/dashboard/public/statics/locales/zh.json +++ b/dashboard/public/statics/locales/zh.json @@ -600,11 +600,6 @@ "saveSuccess": "常规设置保存成功", "saveFailed": "常规设置保存失败", "cancelSuccess": "更改已取消,原始设置已恢复", - "defaultFlow": { - "title": "默认 VLESS 流", - "description": "为新 VLESS 用户自动填写流字段", - "none": "无" - }, "defaultMethod": { "title": "默认 Shadowsocks 加密方式", "description": "为新 Shadowsocks 用户自动填写加密方式" @@ -1456,8 +1451,6 @@ "userDialog.proxySettings.id": "ID", "userDialog.proxySettings.password": "密码", "userDialog.proxySettings.method": "加密方式", - "userDialog.proxySettings.flow": "流 (Flow)", - "userDialog.proxySettings.flowDeprecated": "此项已弃用,将在后续版本中移除。请改用 inbound.settings.flow", "userDialog.proxySettings.hysteria": "Hysteria", "userDialog.proxySettings.hysteriaAuth": "Hysteria 认证", "userDialog.proxySettings.wireguardPrivateKey": "WireGuard 私钥", diff --git a/dashboard/src/features/users/dialogs/user-modal.tsx b/dashboard/src/features/users/dialogs/user-modal.tsx index 621a28f5..af8866ac 100644 --- a/dashboard/src/features/users/dialogs/user-modal.tsx +++ b/dashboard/src/features/users/dialogs/user-modal.tsx @@ -1123,7 +1123,6 @@ function UserModal({ isDialogOpen, onOpenChange, form, editingUser, editingUserI }, vless: { id: uuidv4(), - flow: '' as '' | 'xtls-rprx-vision' | 'xtls-rprx-vision-udp443' | undefined, }, trojan: { password: generatePassword(), @@ -1216,9 +1215,6 @@ function UserModal({ isDialogOpen, onOpenChange, form, editingUser, editingUserI {} as Record, ) - if (protocol === 'vless' && !cleanedProtocolSettings.flow) { - delete cleanedProtocolSettings.flow - } if (protocol === 'shadowsocks' && !cleanedProtocolSettings.method) { delete cleanedProtocolSettings.method } @@ -1359,7 +1355,6 @@ function UserModal({ isDialogOpen, onOpenChange, form, editingUser, editingUserI form.setValue('proxy_settings', undefined) form.setValue('data_limit', 0) if (generalSettings) { - form.setValue('proxy_settings.vless.flow', generalSettings.default_flow || '') const validMethods = ['aes-128-gcm', 'aes-256-gcm', 'chacha20-ietf-poly1305', 'xchacha20-poly1305'] as const const method = validMethods.find(m => m === generalSettings.default_method) if (method) { @@ -1826,38 +1821,6 @@ function UserModal({ isDialogOpen, onOpenChange, form, editingUser, editingUserI ) }} /> - ( - - - {t('userDialog.proxySettings.vless')} {t('userDialog.proxySettings.flow')} - - - - -

{t('userDialog.proxySettings.flowDeprecated')}

- -
- )} - /> {/* Trojan */} { }, vless: { id: undefined, - flow: '', }, trojan: { password: undefined, diff --git a/dashboard/src/pages/_dashboard.settings.general.tsx b/dashboard/src/pages/_dashboard.settings.general.tsx index 6488d7c8..2cf6166f 100644 --- a/dashboard/src/pages/_dashboard.settings.general.tsx +++ b/dashboard/src/pages/_dashboard.settings.general.tsx @@ -6,7 +6,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@ import { Separator } from '@/components/ui/separator' import { Skeleton } from '@/components/ui/skeleton' import { DEFAULT_SHADOWSOCKS_METHOD } from '@/constants/Proxies' -import { ShadowsocksMethods, XTLSFlows, useGetGeneralSettings, useReconnectAllNode } from '@/service/api' +import { ShadowsocksMethods, useGetGeneralSettings, useReconnectAllNode } from '@/service/api' import { queryClient } from '@/utils/query-client' import { zodResolver } from '@hookform/resolvers/zod' import { Loader2, RefreshCcw } from 'lucide-react' @@ -17,12 +17,8 @@ import { toast } from 'sonner' import { z } from 'zod' import { useSettingsContext } from './_dashboard.settings' -/** Radix Select forbids `SelectItem value=""`; map API empty flow to this UI value. */ -const DEFAULT_FLOW_SELECT_NONE = '__pg_default_flow_none__' - // general settings validation schema const generalSettingsSchema = z.object({ - default_flow: z.string().default(''), default_method: z.string().default(''), }) @@ -43,14 +39,12 @@ export default function General() { () => generalSettings ? { - default_flow: generalSettings.default_flow || '', default_method: generalSettings.default_method || DEFAULT_SHADOWSOCKS_METHOD, } : { - default_flow: '', default_method: '', }, - [generalSettings?.default_flow, generalSettings?.default_method], + [generalSettings?.default_method], ) const form = useForm({ @@ -64,7 +58,6 @@ export default function General() { const filteredData: any = { general: { ...data, - default_flow: data.default_flow || undefined, default_method: data.default_method || DEFAULT_SHADOWSOCKS_METHOD, }, } @@ -78,7 +71,6 @@ export default function General() { const handleCancel = () => { if (!generalSettings) return form.reset({ - default_flow: generalSettings.default_flow ?? '', default_method: generalSettings.default_method || DEFAULT_SHADOWSOCKS_METHOD, }) toast.success(t('settings.general.cancelSuccess')) @@ -171,39 +163,6 @@ export default function General() {
{/* General Settings */}
- ( - - {t('settings.general.defaultFlow.title')} - - - - {t('settings.general.defaultFlow.description')} - - - )} - />