mirror of
				https://github.com/MHSanaei/3x-ui.git
				synced 2025-10-27 10:30:08 +00:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "99c79d4056a1efb636010739f56bb1c623f47634" and "db7e7dcd2956722a7c39306d59be55f4bc0c1a42" have entirely different histories.
		
	
	
		
			99c79d4056
			...
			db7e7dcd29
		
	
		
					 5 changed files with 15 additions and 35 deletions
				
			
		|  | @ -50,11 +50,7 @@ | |||
|   } | ||||
| 
 | ||||
|   function drawQR(value) { | ||||
|     try { | ||||
|       new QRious({ element: document.getElementById('qrcode'), value, size: 220 }); | ||||
|     } catch (e) { | ||||
|       console.warn(e); | ||||
|     } | ||||
|     try { new QRious({ element: document.getElementById('qrcode'), value, size: 220 }); } catch (e) { console.warn(e); } | ||||
|   } | ||||
| 
 | ||||
|   // Try to extract a human label (email/ps) from different link types
 | ||||
|  | @ -65,18 +61,22 @@ | |||
|         if (json.ps) return json.ps; | ||||
|         if (json.add && json.id) return json.add; // fallback host
 | ||||
|       } else if (link.startsWith('vless://') || link.startsWith('trojan://')) { | ||||
|         // vless://<id>@host:port?...#name
 | ||||
|         const hashIdx = link.indexOf('#'); | ||||
|         if (hashIdx !== -1) return decodeURIComponent(link.substring(hashIdx + 1)); | ||||
|         // email sometimes in query params like sni or remark
 | ||||
|         const qIdx = link.indexOf('?'); | ||||
|         if (qIdx !== -1) { | ||||
|           const qs = new URL('http://x/?' + link.substring(qIdx + 1, hashIdx !== -1 ? hashIdx : undefined)).searchParams; | ||||
|           if (qs.get('remark')) return qs.get('remark'); | ||||
|           if (qs.get('email')) return qs.get('email'); | ||||
|         } | ||||
|         // else take user@host
 | ||||
|         const at = link.indexOf('@'); | ||||
|         const protSep = link.indexOf('://'); | ||||
|         if (at !== -1 && protSep !== -1) return link.substring(protSep + 3, at); | ||||
|       } else if (link.startsWith('ss://')) { | ||||
|         // shadowsocks: label often after #
 | ||||
|         const hashIdx = link.indexOf('#'); | ||||
|         if (hashIdx !== -1) return decodeURIComponent(link.substring(hashIdx + 1)); | ||||
|       } | ||||
|  | @ -96,13 +96,14 @@ | |||
|     }, | ||||
|     async mounted() { | ||||
|       this.lang = LanguageManager.getLanguage(); | ||||
|       // Discover subJsonUrl if provided via template bootstrap
 | ||||
|       const tpl = document.getElementById('subscription-data'); | ||||
|       const sj = tpl ? tpl.getAttribute('data-subjson-url') : ''; | ||||
|       if (sj) this.app.subJsonUrl = sj; | ||||
|       drawQR(this.app.subUrl); | ||||
|       try { | ||||
|         new QRious({ element: document.getElementById('qrcode-subjson'), value: this.app.subJsonUrl || '', size: 220 }); | ||||
|       } catch (e) { /* ignore */ } | ||||
|       // Draw second QR if available
 | ||||
|       try { new QRious({ element: document.getElementById('qrcode-subjson'), value: this.app.subJsonUrl || '', size: 220 }); } catch (e) { /* ignore */ } | ||||
|       // Track viewport width for responsive behavior
 | ||||
|       this._onResize = () => { this.viewportWidth = window.innerWidth; }; | ||||
|       window.addEventListener('resize', this._onResize); | ||||
|     }, | ||||
|  | @ -110,33 +111,15 @@ | |||
|       if (this._onResize) window.removeEventListener('resize', this._onResize); | ||||
|     }, | ||||
|     computed: { | ||||
|       isMobile() { | ||||
|         return this.viewportWidth < 576; | ||||
|       }, | ||||
|       isUnlimited() { | ||||
|         return !this.app.totalByte; | ||||
|       }, | ||||
|       isMobile() { return this.viewportWidth < 576; }, | ||||
|       isUnlimited() { return !this.app.totalByte; }, | ||||
|       isActive() { | ||||
|         const now = Date.now(); | ||||
|         const expiryOk = !this.app.expireMs || this.app.expireMs >= now; | ||||
|         const trafficOk = !this.app.totalByte || (this.app.uploadByte + this.app.downloadByte) <= this.app.totalByte; | ||||
|         return expiryOk && trafficOk; | ||||
|       }, | ||||
|       shadowrocketUrl() { | ||||
|         const rawUrl = this.app.subUrl + '?flag=shadowrocket'; | ||||
|         const base64Url = btoa(rawUrl); | ||||
|         const remark = encodeURIComponent(this.app.sId || 'Subscription'); | ||||
|         return `shadowrocket://add/sub/${base64Url}?remark=${remark}`; | ||||
|       } | ||||
|     }, | ||||
|     methods: { | ||||
|       renderLink, | ||||
|       copy, | ||||
|       open, | ||||
|       linkName, | ||||
|       i18nLabel(key) { | ||||
|         return '{{ i18n "' + key + '" }}'; | ||||
|       }, | ||||
|     }, | ||||
|     methods: { renderLink, copy, open, linkName, i18nLabel(key) { return '{{ i18n "' + key + '" }}'; } }, | ||||
|   }); | ||||
| })(); | ||||
|  |  | |||
|  | @ -37,7 +37,7 @@ | |||
|     <template slot="content" > | ||||
|       {{ i18n "lastOnline" }}: [[ formatLastOnline(client.email) ]] | ||||
|     </template> | ||||
|     <template v-if="client.enable && isClientOnline(client.email)"> | ||||
|     <template v-if="client.enable && isClientOnline(client.email) && !isClientDepleted"> | ||||
|       <a-tag color="green">{{ i18n "online" }}</a-tag> | ||||
|     </template> | ||||
|     <template v-else> | ||||
|  | @ -51,7 +51,7 @@ | |||
|       <template slot="title"> | ||||
|         <template v-if="isClientDepleted">{{ i18n "depleted" }}</template> | ||||
|         <template v-if="!isClientDepleted && !client.enable">{{ i18n "disabled" }}</template> | ||||
|         <template v-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template> | ||||
|         <template v-if="!isClientDepleted && client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template> | ||||
|       </template> | ||||
|       <a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge> | ||||
|     </a-tooltip> | ||||
|  |  | |||
|  | @ -233,7 +233,7 @@ | |||
|                                         <a-menu slot="overlay" | ||||
|                                             :class="themeSwitcher.currentTheme"> | ||||
|                                             <a-menu-item key="ios-shadowrocket" | ||||
|                                                 @click="open(shadowrocketUrl)">Shadowrocket</a-menu-item> | ||||
|                                                 @click="open('shadowrocket://add/subscription?url=' + encodeURIComponent(app.subUrl) + '&remark=' + encodeURIComponent(app.sId))">Shadowrocket</a-menu-item> | ||||
|                                             <a-menu-item key="ios-v2box" | ||||
|                                                 @click="open('v2box://install-sub?url=' + encodeURIComponent(app.subUrl) + '&name=' + encodeURIComponent(app.sId))">V2Box</a-menu-item> | ||||
|                                             <a-menu-item key="ios-streisand" | ||||
|  |  | |||
|  | @ -1953,7 +1953,6 @@ func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.Cl | |||
| 	if t != nil && client != nil { | ||||
| 		t.Enable = client.Enable | ||||
| 		t.SubId = client.SubID | ||||
| 		t.UUID = client.ID | ||||
| 		return t, nil | ||||
| 	} | ||||
| 	return nil, nil | ||||
|  | @ -1995,7 +1994,6 @@ func (s *InboundService) GetClientTrafficByID(id string) ([]xray.ClientTraffic, | |||
| 		if ct, client, e := s.GetClientByEmail(traffics[i].Email); e == nil && ct != nil && client != nil { | ||||
| 			traffics[i].Enable = client.Enable | ||||
| 			traffics[i].SubId = client.SubID | ||||
| 			traffics[i].UUID = client.ID | ||||
| 		} | ||||
| 	} | ||||
| 	return traffics, err | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ type ClientTraffic struct { | |||
| 	InboundId  int    `json:"inboundId" form:"inboundId"` | ||||
| 	Enable     bool   `json:"enable" form:"enable"` | ||||
| 	Email      string `json:"email" form:"email" gorm:"unique"` | ||||
| 	UUID       string `json:"uuid" form:"uuid" gorm:"unique;type:char(36)"` | ||||
| 	SubId      string `json:"subId" form:"subId" gorm:"-"` | ||||
| 	Up         int64  `json:"up" form:"up"` | ||||
| 	Down       int64  `json:"down" form:"down"` | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue