@@ -4,7 +4,7 @@ import { useEvents } from '@/composables/events'
44import  { onUnmounted , computed , useTemplateRef , onMounted , reactive , watch , ref , type  ComponentPublicInstance   } from  ' vue' 
55import  { usePrettyDates  } from  ' @/composables/usePrettyDate' 
66import  { usePrettyHttp  } from  ' @/composables/http' 
7- import  { Modal  } from  ' bootstrap' 
7+ import  { Modal ,  Tooltip  } from  ' bootstrap' 
88import  { useService  } from  ' @/composables/services' 
99import  RegexInput  from  ' @/components/RegexInput.vue' 
1010
@@ -48,10 +48,12 @@ interface Filter {
4848    response:  { 
4949        headers:  FilterItem  
5050    } 
51+     clientIP:  FilterItem  
5152} 
5253interface  FilterItem  {
5354    checkbox:  boolean  
5455    value:  any  
56+     title? :  string  
5557} 
5658interface  MultiFilterItem  {
5759    checkbox:  boolean  
@@ -67,7 +69,11 @@ const filter = reactive<Filter>({
6769    }, 
6870    response: { 
6971         headers: { checkbox: false , value: [{ name: ' ' ' '  
70-     } 
72+     }, 
73+     clientIP: { checkbox: false , value: ' ' ` <b>IP Filter Options:</b><br>  
74+           • Multiple entries (comma-separated): 192.168.0.1,10.0.0.1<br> 
75+           • Negation: -127.0.0.1<br> 
76+           • CIDR notation: 192.168.0.0/24 `  }
7177}) 
7278
7379onMounted (() =>  {
@@ -82,6 +88,10 @@ onMounted(() => {
8288            } 
8389        } 
8490    } 
91+     const =  document .querySelectorAll (' [data-bs-toggle="tooltip"]'  
92+     tooltipTriggerList .forEach (el  =>  new  Tooltip (el , { 
93+         trigger: ' hover'  
94+     })) 
8595}) 
8696
8797function  goToRequest(event :  ServiceEvent ){
@@ -169,6 +179,38 @@ const events = computed<ServiceEvent[]>(() => {
169179            return  true  
170180        }) 
171181    } 
182+     if  (filter .clientIP .checkbox  &&  filter .clientIP .value .length  >  0 ) { 
183+         const =  (filter .clientIP .value  as  string ).split (' ,' map (x  =>  x .trim ()).filter (x  =>  x .length  >  0 ) 
184+         result  =  result .filter (x  =>  { 
185+             const =  x .data  as  HttpEventData  
186+ 
187+             let  matched =  false ; 
188+ 
189+             for  (let  value of  values ) { 
190+                 let  not =  false  
191+                 if  (value [0 ] ===  ' -'  
192+                     not  =  true  
193+                     value  =  value .substring (1 ) 
194+                 } 
195+                 let  isMatch =  false  
196+                 if  (value .includes (' /'  
197+                     isMatch  =  cidrMatch (data .clientIP , value ) 
198+                 } else  { 
199+                     isMatch  =  data .clientIP  ===  value  
200+                 } 
201+ 
202+                 if  (not  &&  isMatch ) { 
203+                     //  Negated value matches → reject immediately 
204+                     return  false  
205+                 } 
206+                 if  (! not  &&  isMatch ) { 
207+                     //  Normal value matches → mark as matched 
208+                     matched  =  true ; 
209+                 } 
210+             } 
211+             return  matched  ||  values .every (v  =>  v [0 ] ===  ' -'  
212+         }) 
213+     } 
172214
173215    return  result  
174216}) 
@@ -276,6 +318,9 @@ const activeFiltersCount = computed(() => {
276318    if  (filter .response .headers .checkbox  &&  filter .response .headers .value .length  >  1 ) { 
277319        counter ++ ; 
278320    } 
321+     if  (filter .clientIP .checkbox  &&  filter .clientIP .value .length  >  0 ) { 
322+         counter ++  
323+     } 
279324    return  counter ; 
280325}) 
281326function  getFilterCacheKey() {
@@ -417,6 +462,30 @@ const responseHeaderFilter = computed(() => {
417462    } 
418463    return  result  
419464}) 
465+ function  ipToInt(ip :  string ):  number  {
466+     return  ip  
467+         .split (' .'  
468+         .map ((octet ) =>  parseInt (octet , 10 )) 
469+         .reduce ((acc , octet ) =>  (acc  <<  8 ) +  octet , 0 ); 
470+ } 
471+ 
472+ function  cidrMatch(ip :  string , cidr :  string ):  boolean  {
473+     const :  string [] =  cidr .split (' /'  
474+     if  (parts .length  !==  2 ) { 
475+         return  false  
476+     } 
477+ 
478+     const =  parts ; 
479+     const :  number  =  parseInt (bitsStr ! , 10 ); 
480+ 
481+     if  (isNaN (bits ) ||  bits  <  0  ||  bits  >  32 ) { 
482+         return  false  
483+     } 
484+ 
485+     const :  number  =  ~ (2  **  (32  -  bits ) -  1 ); 
486+ 
487+     return  (ipToInt (ip ) &  mask ) ===  (ipToInt (range ! ) &  mask ); 
488+ } 
420489script >
421490
422491<template >
@@ -619,6 +688,23 @@ const responseHeaderFilter = computed(() => {
619688                        </div >
620689                    </div >
621690
691+                     <!--  Client IP --> 
692+                     <div  class =" row" 
693+                         <div  class =" col-4" 
694+                             <div  class =" form-check" data-bs-toggle =" tooltip" data-bs-delay =' {"show": 200, "hide": 100}' :title =" filter.clientIP.title" data-bs-offset =" [-150, 0]" data-bs-html =" true" 
695+                                 <input  class =" form-check-input" type =" checkbox" v-model =" filter.clientIP.checkbox" id =" clientIP" 
696+                                 <label  class =" form-check-label" for =" clientIP" label >
697+                             </div >
698+                         </div >
699+                         <div  class =" col" v-show =" filter.clientIP.checkbox" 
700+                              <div  class =" col ps-0 pe-1" data-bs-toggle =" tooltip" data-bs-delay =' {"show": 200, "hide": 100}' :title =" filter.clientIP.title" data-bs-html =" true" 
701+                                 <div  class =" row me-0" 
702+                                     <input  type =" text" class =" form-control form-control-sm" id =" clientIP" v-model =" filter.clientIP.value" 
703+                                 </div >
704+                             </div >
705+                         </div >
706+                     </div >
707+ 
622708                </div >
623709            </div >
624710        </div >
0 commit comments