mirror of
				https://github.com/MHSanaei/3x-ui.git
				synced 2025-10-27 10:30:08 +00:00 
			
		
		
		
	Compare commits
	
		
			1 commit
		
	
	
		
			2218cfba56
			...
			bee4c75a5a
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | bee4c75a5a | 
					 6 changed files with 83 additions and 229 deletions
				
			
		|  | @ -1 +1 @@ | |||
| 2.8.1 | ||||
| 2.8.0 | ||||
							
								
								
									
										2
									
								
								web/assets/css/custom.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								web/assets/css/custom.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -568,7 +568,8 @@ | |||
|                             <tr> | ||||
|                               <td>{{ i18n "pages.inbounds.periodicTrafficResetTitle" }}</td> | ||||
|                               <td> | ||||
|                                 <a-tag color="blue">[[ dbInbound.trafficReset ]]</a-tag> | ||||
|                                 <a-tag color="blue">[[ i18n("pages.inbounds.periodicTrafficReset." + | ||||
|                                   dbInbound.trafficReset) ]]</a-tag> | ||||
|                               </td> | ||||
|                             </tr> | ||||
|                           </table> | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ | |||
|                               </template> | ||||
|                             </a-tooltip> | ||||
|                             <a-tooltip :overlay-class-name="themeSwitcher.currentTheme"> | ||||
|                               <a-button size="small" shape="circle" class="ml-8" @click="openCpuHistory()"> | ||||
|                               <a-button size="small" type="default" class="ml-8" @click="openCpuHistory()"> | ||||
|                                 <a-icon type="history" /> | ||||
|                               </a-button> | ||||
|                             </a-tooltip> | ||||
|  | @ -343,7 +343,7 @@ | |||
|     <a-form layout="inline"> | ||||
|       <a-form-item class="mr-05"> | ||||
|         <a-input-group compact> | ||||
|           <a-select size="small" v-model="logModal.rows" :style="{ width: '70px' }" @change="openLogs()" | ||||
|           <a-select size="small" v-model="logModal.rows" class="w-70" @change="openLogs()" | ||||
|             :dropdown-class-name="themeSwitcher.currentTheme"> | ||||
|             <a-select-option value="10">10</a-select-option> | ||||
|             <a-select-option value="20">20</a-select-option> | ||||
|  | @ -351,7 +351,7 @@ | |||
|             <a-select-option value="100">100</a-select-option> | ||||
|             <a-select-option value="500">500</a-select-option> | ||||
|           </a-select> | ||||
|           <a-select size="small" v-model="logModal.level" :style="{ width: '95px' }" @change="openLogs()" | ||||
|           <a-select size="small" v-model="logModal.level" class="w-95" @change="openLogs()" | ||||
|             :dropdown-class-name="themeSwitcher.currentTheme"> | ||||
|             <a-select-option value="debug">Debug</a-select-option> | ||||
|             <a-select-option value="info">Info</a-select-option> | ||||
|  | @ -365,7 +365,8 @@ | |||
|         <a-checkbox v-model="logModal.syslog" @change="openLogs()">SysLog</a-checkbox> | ||||
|       </a-form-item> | ||||
|       <a-form-item style="float: right;"> | ||||
|         <a-button type="primary" icon="download" @click="FileManager.downloadTextFile(logModal.logs?.join('\n'), 'x-ui.log')"></a-button> | ||||
|         <a-button type="primary" icon="download" | ||||
|           @click="FileManager.downloadTextFile(logModal.logs?.join('\n'), 'x-ui.log')"></a-button> | ||||
|       </a-form-item> | ||||
|     </a-form> | ||||
|     <div class="ant-input log-container" v-html="logModal.formattedLogs"></div> | ||||
|  | @ -381,7 +382,7 @@ | |||
|     <a-form layout="inline"> | ||||
|       <a-form-item class="mr-05"> | ||||
|         <a-input-group compact> | ||||
|           <a-select size="small" v-model="xraylogModal.rows" :style="{ width: '70px' }" @change="openXrayLogs()" | ||||
|           <a-select size="small" v-model="xraylogModal.rows" class="w-70" @change="openXrayLogs()" | ||||
|             :dropdown-class-name="themeSwitcher.currentTheme"> | ||||
|             <a-select-option value="10">10</a-select-option> | ||||
|             <a-select-option value="20">20</a-select-option> | ||||
|  | @ -400,7 +401,8 @@ | |||
|         <a-checkbox v-model="xraylogModal.showProxy" @change="openXrayLogs()">Proxy</a-checkbox> | ||||
|       </a-form-item> | ||||
|       <a-form-item style="float: right;"> | ||||
|         <a-button type="primary" icon="download" @click="downloadXrayLogs"></a-button> | ||||
|         <a-button type="primary" icon="download" | ||||
|           @click="FileManager.downloadTextFile(xraylogModal.logs?.join('\n'), 'x-ui.log')"></a-button> | ||||
|       </a-form-item> | ||||
|     </a-form> | ||||
|     <div class="ant-input log-container" v-html="xraylogModal.formattedLogs"></div> | ||||
|  | @ -429,7 +431,7 @@ | |||
|     @cancel="() => cpuHistoryModal.visible = false" :class="themeSwitcher.currentTheme" width="900px" footer=""> | ||||
|     <template slot="title"> | ||||
|       CPU History | ||||
|       <a-select size="small" v-model="cpuHistoryModal.bucket" class="ml-10" style="width: 80px" | ||||
|       <a-select size="small" v-model="cpuHistoryModal.bucket" class="ml-10" style="width: 140px" | ||||
|         @change="fetchCpuHistoryBucket"> | ||||
|         <a-select-option :value="2">2s</a-select-option> | ||||
|         <a-select-option :value="30">30s</a-select-option> | ||||
|  | @ -439,7 +441,7 @@ | |||
|         <a-select-option :value="300">5m</a-select-option> | ||||
|       </a-select> | ||||
|     </template> | ||||
|     <div style="padding:16px"> | ||||
|     <div style="padding: 8px 0;"> | ||||
|       <sparkline :data="cpuHistoryLong" :labels="cpuHistoryLabels" :vb-width="840" :height="220" | ||||
|         :stroke="status.cpu.color" :stroke-width="2.2" :show-grid="true" :show-axes="true" :tick-count-x="5" | ||||
|         :max-points="cpuHistoryLong.length" :fill-opacity="0.18" :marker-radius="3.2" :show-tooltip="true" /> | ||||
|  | @ -464,7 +466,7 @@ | |||
|       strokeWidth: { type: Number, default: 2 }, | ||||
|       maxPoints: { type: Number, default: 120 }, | ||||
|       showGrid: { type: Boolean, default: true }, | ||||
|       gridColor: { type: String, default: 'rgba(0,0,0,0.1)' }, | ||||
|       gridColor: { type: String, default: 'rgba(255,255,255,0.08)' }, | ||||
|       fillOpacity: { type: Number, default: 0.15 }, | ||||
|       showMarker: { type: Boolean, default: true }, | ||||
|       markerRadius: { type: Number, default: 2.8 }, | ||||
|  | @ -538,7 +540,7 @@ | |||
|         const h = this.drawHeight | ||||
|         const w = this.drawWidth | ||||
|         // draw at 25%, 50%, 75% | ||||
|         return [0, 0.25, 0.5, 0.75, 1] | ||||
|         return [0.25, 0.5, 0.75] | ||||
|           .map(r => Math.round(this.paddingTop + h * r)) | ||||
|           .map(y => ({ x1: this.paddingLeft, y1: y, x2: this.paddingLeft + w, y2: y })) | ||||
|       }, | ||||
|  | @ -604,7 +606,7 @@ | |||
|       }, | ||||
|     }, | ||||
|     template: ` | ||||
|       <svg width="100%" :height="height" :viewBox="viewBoxAttr" preserveAspectRatio="none" class="idx-cpu-history-svg" | ||||
|       <svg width="100%" :height="height" :viewBox="viewBoxAttr" preserveAspectRatio="none" style="display:block" | ||||
|            @mousemove="onMouseMove" @mouseleave="onMouseLeave"> | ||||
|         <defs> | ||||
|           <linearGradient id="spkGrad" x1="0" y1="0" x2="0" y2="1"> | ||||
|  | @ -613,16 +615,16 @@ | |||
|           </linearGradient> | ||||
|         </defs> | ||||
|         <g v-if="showGrid"> | ||||
|           <line v-for="(g,i) in gridLines" :key="i" :x1="g.x1" :y1="g.y1" :x2="g.x2" :y2="g.y2" :stroke="gridColor" stroke-width="1" class="cpu-grid-line" /> | ||||
|           <line v-for="(g,i) in gridLines" :key="i" :x1="g.x1" :y1="g.y1" :x2="g.x2" :y2="g.y2" :stroke="gridColor" stroke-width="1"/> | ||||
|         </g> | ||||
|         <g v-if="showAxes"> | ||||
|           <!-- Y ticks/labels --> | ||||
|           <g v-for="(t,i) in yTicks" :key="'y'+i"> | ||||
|             <text class="cpu-grid-y-text" :x="Math.max(0, paddingLeft - 4)" :y="t.y + 4" text-anchor="end" font-size="10" fill="rgba(0,0,0,0.3)" v-text="t.label"></text> | ||||
|             <text :x="Math.max(0, paddingLeft - 4)" :y="t.y + 4" text-anchor="end" font-size="10" fill="rgba(200,200,200,0.8)" v-text="t.label"></text> | ||||
|           </g> | ||||
|           <!-- X ticks/labels --> | ||||
|           <g v-for="(t,i) in xTicks" :key="'x'+i"> | ||||
|             <text class="cpu-grid-x-text" :x="t.x" :y="paddingTop + drawHeight + 22" text-anchor="middle" font-size="10" fill="rgba(0,0,0,0.3)" v-text="t.label"></text> | ||||
|             <text :x="t.x" :y="paddingTop + drawHeight + 14" text-anchor="middle" font-size="10" fill="rgba(200,200,200,0.8)" v-text="t.label"></text> | ||||
|           </g> | ||||
|         </g> | ||||
|         <path v-if="areaPath" :d="areaPath" fill="url(#spkGrad)" stroke="none" /> | ||||
|  | @ -630,9 +632,9 @@ | |||
|         <circle v-if="showMarker && lastPoint" :cx="lastPoint[0]" :cy="lastPoint[1]" :r="markerRadius" :fill="stroke" /> | ||||
|         <!-- Hover marker/tooltip --> | ||||
|         <g v-if="showTooltip && hoverIdx >= 0"> | ||||
|           <line class="cpu-grid-h-line" :x1="pointsArr[hoverIdx][0]" :x2="pointsArr[hoverIdx][0]" :y1="paddingTop" :y2="paddingTop + drawHeight" stroke="rgba(0,0,0,0.2)" stroke-width="1" /> | ||||
|           <line :x1="pointsArr[hoverIdx][0]" :x2="pointsArr[hoverIdx][0]" :y1="paddingTop" :y2="paddingTop + drawHeight" stroke="rgba(255,255,255,0.25)" stroke-width="1" /> | ||||
|           <circle :cx="pointsArr[hoverIdx][0]" :cy="pointsArr[hoverIdx][1]" r="3.5" :fill="stroke" /> | ||||
|           <text class="cpu-grid-text" :x="pointsArr[hoverIdx][0]" :y="paddingTop + 12" text-anchor="middle" font-size="11" fill="rgba(0,0,0,0.8)" v-text="fmtHoverText()"></text> | ||||
|           <text :x="pointsArr[hoverIdx][0]" :y="paddingTop + 12" text-anchor="middle" font-size="11" fill="#fff" style="paint-order: stroke; stroke: rgba(0,0,0,0.35); stroke-width: 3;" v-text="fmtHoverText()"></text> | ||||
|         </g> | ||||
|       </svg> | ||||
|     `, | ||||
|  | @ -794,74 +796,59 @@ | |||
|   }; | ||||
| 
 | ||||
|   const xraylogModal = { | ||||
|       visible: false, | ||||
|       logs: [], | ||||
|       rows: 20, | ||||
|       showDirect: true, | ||||
|       showBlocked: true, | ||||
|       showProxy: true, | ||||
|       loading: false, | ||||
|       show(logs) { | ||||
|         this.visible = true; | ||||
|         this.logs = logs; | ||||
|         this.formattedLogs = this.logs?.length > 0 ? this.formatLogs(this.logs) : "No Record..."; | ||||
|       }, | ||||
|       formatLogs(logs) { | ||||
|         let formattedLogs = ` | ||||
| <style> | ||||
|   table { | ||||
|     border-collapse: collapse; | ||||
|     width: auto; | ||||
|   } | ||||
|     visible: false, | ||||
|     logs: [], | ||||
|     rows: 20, | ||||
|     showDirect: true, | ||||
|     showBlocked: true, | ||||
|     showProxy: true, | ||||
|     loading: false, | ||||
|     show(logs) { | ||||
|       this.visible = true; | ||||
|       this.logs = logs; | ||||
|       this.formattedLogs = this.logs?.length > 0 ? this.formatLogs(this.logs) : "No Record..."; | ||||
|     }, | ||||
|     formatLogs(logs) { | ||||
|       let formattedLogs = ''; | ||||
| 
 | ||||
|   table td, table th { | ||||
|     padding: 2px 15px; | ||||
|   } | ||||
| </style> | ||||
|       logs.forEach((log, index) => { | ||||
|         if (index > 0) formattedLogs += '<br>'; | ||||
| 
 | ||||
| <table> | ||||
|     <tr> | ||||
|         <th>Date</th> | ||||
|         <th>From</th> | ||||
|         <th>To</th> | ||||
|         <th>Inbound</th> | ||||
|         <th>Outbound</th> | ||||
|         <th>Email</th> | ||||
|     </tr> | ||||
| `; | ||||
|         const parts = log.split(' '); | ||||
| 
 | ||||
|         if (parts.length === 10) { | ||||
|           const dateTime = `<b>${parts[0]} ${parts[1]}</b>`; | ||||
|           const from = `<b>${parts[3]}</b>`; | ||||
|           const to = `<b>${parts[5].replace(/^\/+/, "")}</b>`; | ||||
| 
 | ||||
|         logs.reverse().forEach((log, index) => { | ||||
|           let outboundColor = ''; | ||||
|           if (log.Event === 1) { | ||||
|           if (parts[9] === "b") { | ||||
|             outboundColor = ' style="color: #e04141;"'; //red for blocked | ||||
|           } | ||||
|           else if (log.Event === 2) { | ||||
|           else if (parts[9] === "p") { | ||||
|             outboundColor = ' style="color: #3c89e8;"'; //blue for proxies | ||||
|           } | ||||
| 
 | ||||
|           let text = ``; | ||||
|           if (log.Email !== "") { | ||||
|             text = `<td>${log.Email}</td>`; | ||||
|           } | ||||
|           formattedLogs += `<span${outboundColor}> | ||||
| ${dateTime} | ||||
|  ${parts[2]} | ||||
|  ${from} | ||||
|  ${parts[4]} | ||||
|  ${to} | ||||
|  ${parts.slice(6, 9).join(' ')} | ||||
| </span>`; | ||||
|         } else { | ||||
|           formattedLogs += `<span>${log}</span>`; | ||||
|         } | ||||
|       }); | ||||
| 
 | ||||
|           formattedLogs += ` | ||||
| <tr ${outboundColor}> | ||||
|     <td><b>${new Date(log.DateTime).toLocaleString()}</b></td> | ||||
|     <td>${log.FromAddress}</td> | ||||
|     <td>${log.ToAddress}</td> | ||||
|     <td>${log.Inbound}</td> | ||||
|     <td>${log.Outbound}</td> | ||||
|     ${text} | ||||
| </tr> | ||||
| `; | ||||
|         }); | ||||
|       return formattedLogs; | ||||
|     }, | ||||
|     hide() { | ||||
|       this.visible = false; | ||||
|     }, | ||||
|   }; | ||||
| 
 | ||||
|         return formattedLogs += "</table>"; | ||||
|       }, | ||||
|       hide() { | ||||
|         this.visible = false; | ||||
|       }, | ||||
|     }; | ||||
|   const backupModal = { | ||||
|     visible: false, | ||||
|     show() { | ||||
|  | @ -1036,25 +1023,6 @@ | |||
|         await PromiseUtil.sleep(500); | ||||
|         xraylogModal.loading = false; | ||||
|       }, | ||||
|       downloadXrayLogs() { | ||||
|         if (!Array.isArray(this.xraylogModal.logs) || this.xraylogModal.logs.length === 0) { | ||||
|           FileManager.downloadTextFile('', 'x-ui.log'); | ||||
|           return; | ||||
|         } | ||||
|         const lines = this.xraylogModal.logs.map(l => { | ||||
|           try { | ||||
|             const dt = l.DateTime ? new Date(l.DateTime) : null; | ||||
|             const dateStr = dt && !isNaN(dt.getTime()) ? dt.toISOString() : ''; | ||||
|             const eventMap = { 0: 'DIRECT', 1: 'BLOCKED', 2: 'PROXY' }; | ||||
|             const eventText = eventMap[l.Event] || String(l.Event ?? ''); | ||||
|             const emailPart = l.Email ? ` Email=${l.Email}` : ''; | ||||
|             return `${dateStr} FROM=${l.FromAddress || ''} TO=${l.ToAddress || ''} INBOUND=${l.Inbound || ''} OUTBOUND=${l.Outbound || ''}${emailPart} EVENT=${eventText}`.trim(); | ||||
|           } catch (e) { | ||||
|             return JSON.stringify(l); | ||||
|           } | ||||
|         }).join('\n'); | ||||
|         FileManager.downloadTextFile(lines, 'x-ui.log'); | ||||
|       }, | ||||
|       async openConfig() { | ||||
|         this.loading(true); | ||||
|         const msg = await HttpUtil.get('/panel/api/server/getConfigJson'); | ||||
|  | @ -1103,6 +1071,7 @@ | |||
|         fileInput.click(); | ||||
|       }, | ||||
|     }, | ||||
|     computed: {}, | ||||
|     async mounted() { | ||||
|       if (window.location.protocol !== "https:") { | ||||
|         this.showAlert = true; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| {{ template "page/body_start" .}} | ||||
| <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme + ' login-app'"> | ||||
|   <transition name="list" appear> | ||||
|   <a-layout-content class="under min-h-0"> | ||||
|   <a-layout-content class="under min-h-100vh"> | ||||
|       <div class="waves-header"> | ||||
|         <div class="waves-inner-header"></div> | ||||
|         <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" | ||||
|  | @ -20,7 +20,7 @@ | |||
|           </g> | ||||
|         </svg> | ||||
|       </div> | ||||
|   <a-row type="flex" justify="center" align="middle" class="h-100 overflow-y-auto overflow-x-hidden"> | ||||
|   <a-row type="flex" justify="center" align="middle" class="h-100 overflow-hidden-auto"> | ||||
|         <a-col :xs="22" :sm="12" :md="10" :lg="8" :xl="6" :xxl="5" id="login" class="my-3rem"> | ||||
|           <template v-if="!loadingStates.fetched"> | ||||
|             <div class="text-center"> | ||||
|  | @ -184,80 +184,5 @@ | |||
|       newWord.classList.add('is-visible'); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   const pm_input_selector = 'input.ant-input, textarea.ant-input'; | ||||
|   const pm_strip_props = [ | ||||
|     'background', | ||||
|     'background-color', | ||||
|     'background-image', | ||||
|     'color' | ||||
|   ]; | ||||
| 
 | ||||
|   const pm_observed_forms = new WeakSet(); | ||||
| 
 | ||||
|   function pm_strip_inline(el) { | ||||
|     if (!el || el.nodeType !== 1 || !el.matches?.(pm_input_selector)) return; | ||||
| 
 | ||||
|     let did_change = false; | ||||
|     for (const prop of pm_strip_props) { | ||||
|       if (el.style.getPropertyValue(prop)) { | ||||
|         el.style.removeProperty(prop); | ||||
|         did_change = true; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if (did_change && el.style.length === 0) { | ||||
|       el.removeAttribute('style'); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function pm_attach_observer(form) { | ||||
|     if (pm_observed_forms.has(form)) return; | ||||
|     pm_observed_forms.add(form); | ||||
| 
 | ||||
|     form.querySelectorAll(pm_input_selector).forEach(pm_strip_inline); | ||||
| 
 | ||||
|     const pm_mo = new MutationObserver(mutations => { | ||||
|       for (const m of mutations) { | ||||
|         if (m.type === 'attributes') { | ||||
|           pm_strip_inline(m.target); | ||||
|         } else if (m.type === 'childList') { | ||||
|           for (const n of m.addedNodes) { | ||||
|             if (n.nodeType !== 1) continue; | ||||
|             if (n.matches?.(pm_input_selector)) pm_strip_inline(n); | ||||
|             n.querySelectorAll?.(pm_input_selector).forEach(pm_strip_inline); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     pm_mo.observe(form, { | ||||
|       attributes: true, | ||||
|       attributeFilter: ['style'], | ||||
|       childList: true, | ||||
|       subtree: true | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   function pm_init() { | ||||
|     document.querySelectorAll('form.ant-form').forEach(pm_attach_observer); | ||||
|     const pm_host = document.getElementById('login') || document.body; | ||||
|     const pm_wait_for_forms = new MutationObserver(mutations => { | ||||
|       for (const m of mutations) { | ||||
|         for (const n of m.addedNodes) { | ||||
|           if (n.nodeType !== 1) continue; | ||||
|           if (n.matches?.('form.ant-form')) pm_attach_observer(n); | ||||
|           n.querySelectorAll?.('form.ant-form').forEach(pm_attach_observer); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|     pm_wait_for_forms.observe(pm_host, { childList: true, subtree: true }); | ||||
|   } | ||||
| 
 | ||||
|   if (document.readyState === 'loading') { | ||||
|     document.addEventListener('DOMContentLoaded', pm_init, { once: true }); | ||||
|   } else { | ||||
|     pm_init(); | ||||
|   } | ||||
| </script> | ||||
| {{ template "page/body_end" .}} | ||||
|  | @ -174,16 +174,6 @@ type CPUSample struct { | |||
| 	Cpu float64 `json:"cpu"` // percent 0..100
 | ||||
| } | ||||
| 
 | ||||
| type LogEntry struct { | ||||
| 	DateTime    time.Time | ||||
| 	FromAddress string | ||||
| 	ToAddress   string | ||||
| 	Inbound     string | ||||
| 	Outbound    string | ||||
| 	Email       string | ||||
| 	Event       int | ||||
| } | ||||
| 
 | ||||
| func getPublicIP(url string) string { | ||||
| 	client := &http.Client{ | ||||
| 		Timeout: 3 * time.Second, | ||||
|  | @ -714,25 +704,19 @@ func (s *ServerService) GetXrayLogs( | |||
| 	showBlocked string, | ||||
| 	showProxy string, | ||||
| 	freedoms []string, | ||||
| 	blackholes []string) []LogEntry { | ||||
| 
 | ||||
| 	const ( | ||||
| 		Direct = iota | ||||
| 		Blocked | ||||
| 		Proxied | ||||
| 	) | ||||
| 	blackholes []string) []string { | ||||
| 
 | ||||
| 	countInt, _ := strconv.Atoi(count) | ||||
| 	var entries []LogEntry | ||||
| 	var lines []string | ||||
| 
 | ||||
| 	pathToAccessLog, err := xray.GetAccessLogPath() | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 		return lines | ||||
| 	} | ||||
| 
 | ||||
| 	file, err := os.Open(pathToAccessLog) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 		return lines | ||||
| 	} | ||||
| 	defer file.Close() | ||||
| 
 | ||||
|  | @ -751,62 +735,37 @@ func (s *ServerService) GetXrayLogs( | |||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		var entry LogEntry | ||||
| 		parts := strings.Fields(line) | ||||
| 
 | ||||
| 		for i, part := range parts { | ||||
| 
 | ||||
| 			if i == 0 { | ||||
| 				dateTime, err := time.Parse("2006/01/02 15:04:05.999999", parts[0]+" "+parts[1]) | ||||
| 				if err != nil { | ||||
| 					continue | ||||
| 				} | ||||
| 				entry.DateTime = dateTime | ||||
| 			} | ||||
| 
 | ||||
| 			if part == "from" { | ||||
| 				entry.FromAddress = parts[i+1] | ||||
| 			} else if part == "accepted" { | ||||
| 				entry.ToAddress = parts[i+1] | ||||
| 			} else if strings.HasPrefix(part, "[") { | ||||
| 				entry.Inbound = part[1:] | ||||
| 			} else if strings.HasSuffix(part, "]") { | ||||
| 				entry.Outbound = part[:len(part)-1] | ||||
| 			} else if part == "email:" { | ||||
| 				entry.Email = parts[i+1] | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if logEntryContains(line, freedoms) { | ||||
| 		//adding suffixes to further distinguish entries by outbound
 | ||||
| 		if hasSuffix(line, freedoms) { | ||||
| 			if showDirect == "false" { | ||||
| 				continue | ||||
| 			} | ||||
| 			entry.Event = Direct | ||||
| 		} else if logEntryContains(line, blackholes) { | ||||
| 			line = line + " f" | ||||
| 		} else if hasSuffix(line, blackholes) { | ||||
| 			if showBlocked == "false" { | ||||
| 				continue | ||||
| 			} | ||||
| 			entry.Event = Blocked | ||||
| 			line = line + " b" | ||||
| 		} else { | ||||
| 			if showProxy == "false" { | ||||
| 				continue | ||||
| 			} | ||||
| 			entry.Event = Proxied | ||||
| 			line = line + " p" | ||||
| 		} | ||||
| 
 | ||||
| 		entries = append(entries, entry) | ||||
| 		lines = append(lines, line) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(entries) > countInt { | ||||
| 		entries = entries[len(entries)-countInt:] | ||||
| 	if len(lines) > countInt { | ||||
| 		lines = lines[len(lines)-countInt:] | ||||
| 	} | ||||
| 
 | ||||
| 	return entries | ||||
| 	return lines | ||||
| } | ||||
| 
 | ||||
| func logEntryContains(line string, suffixes []string) bool { | ||||
| func hasSuffix(line string, suffixes []string) bool { | ||||
| 	for _, sfx := range suffixes { | ||||
| 		if strings.Contains(line, sfx+"]") { | ||||
| 		if strings.HasSuffix(line, sfx+"]") { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue