@@ -221,6 +221,20 @@ function isJsonObject(value: unknown): value is JsonObject {
221221 return typeof value === 'object' && value !== null && ! Array . isArray ( value )
222222}
223223
224+ function vlessReverseTagFromSettings ( settings : Record < string , unknown > ) : string {
225+ const reverse = settings . reverse
226+ if ( typeof reverse === 'string' ) return reverse . trim ( )
227+ if ( ! reverse || typeof reverse !== 'object' || Array . isArray ( reverse ) ) return ''
228+ const tag = ( reverse as Record < string , unknown > ) . tag
229+ return typeof tag === 'string' ? tag . trim ( ) : ''
230+ }
231+
232+ function hasVlessReverseSettings ( settings : Record < string , unknown > ) : boolean {
233+ const reverse = settings . reverse
234+ if ( typeof reverse === 'string' ) return reverse . trim ( ) . length > 0
235+ return ! ! reverse && typeof reverse === 'object' && ! Array . isArray ( reverse )
236+ }
237+
224238function uniqueOutboundTags ( outbounds : Outbound [ ] | undefined ) : string [ ] {
225239 return [ ...new Set ( ( outbounds ?? [ ] ) . map ( outbound => String ( outbound . tag ?? '' ) . trim ( ) ) . filter ( Boolean ) ) ]
226240}
@@ -336,6 +350,19 @@ function collectOutboundRequiredIssues(ob: Outbound, t: (key: string, opts?: Rec
336350 const p = ob . protocol
337351 if ( PROXY_ENDPOINT_PROTOCOLS . has ( p ) ) {
338352 const s = flattenOutboundSettings ( ob )
353+
354+ if ( p === 'vless' && hasVlessReverseSettings ( s ) ) {
355+ if ( ! vlessReverseTagFromSettings ( s ) ) {
356+ issues . push ( {
357+ field : 'reverse' ,
358+ message : t ( 'coreEditor.outbound.validation.reverseTagRequired' , {
359+ defaultValue : 'Reverse outbound tag is required.' ,
360+ } ) ,
361+ } )
362+ }
363+ return issues
364+ }
365+
339366 const address = String ( s . address ?? '' ) . trim ( )
340367 const portRaw = s . port
341368 const portNum = typeof portRaw === 'number' ? portRaw : Number ( portRaw )
@@ -1238,6 +1265,23 @@ function OutboundProxyEndpointSection({ ob, patchOutbound, t }: OutboundProxyEnd
12381265 if ( ! vlessVisionFlowIncompatibleWithStreamSecurity ( streamSecForFlow , flowStr ) ) return
12391266 patchOutboundWithSettingsMerge ( ob , patchOutbound , s => ( { ...s , flow : '' } ) )
12401267 } , [ p , ob , flowStr , streamSecForFlow , patchOutbound ] )
1268+
1269+ if ( p === 'vless' && hasVlessReverseSettings ( flat ) ) {
1270+ const reverseTag = vlessReverseTagFromSettings ( flat )
1271+ return (
1272+ < div className = "rounded-md border p-4" >
1273+ < p className = "text-sm font-medium" > { t ( 'coreEditor.outbound.reverseSection' , { defaultValue : 'Reverse proxy' } ) } </ p >
1274+ < p className = "mt-2 text-xs text-muted-foreground" >
1275+ { t ( 'coreEditor.outbound.reverseHint' , {
1276+ defaultValue : reverseTag
1277+ ? `Traffic routed to this outbound is forwarded through reverse tag "${ reverseTag } ".`
1278+ : 'Set a reverse tag below. VLESS reverse mode does not use address, port, UUID, or encryption fields.' ,
1279+ } ) }
1280+ </ p >
1281+ </ div >
1282+ )
1283+ }
1284+
12411285 const address = String ( flat . address ?? '' )
12421286 const portStr = flat . port != null && flat . port !== '' ? String ( flat . port ) : ''
12431287 const id = String ( flat . id ?? '' )
0 commit comments