mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-04-19 21:42:24 +00:00
Merge pull request #2732 from shishkevichd/refactor/refactor-1
Code refactoring
This commit is contained in:
commit
42fa64770b
18 changed files with 307 additions and 325 deletions
1
web/assets/base64/base64.min.js
vendored
1
web/assets/base64/base64.min.js
vendored
|
@ -1 +0,0 @@
|
||||||
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory(global):typeof define==="function"&&define.amd?define(factory):factory(global)})(typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:this,function(global){"use strict";var _Base64=global.Base64;var version="2.5.0";var buffer;if(typeof module!=="undefined"&&module.exports){try{buffer=eval("require('buffer').Buffer")}catch(err){buffer=undefined}}var b64chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var b64tab=function(bin){var t={};for(var i=0,l=bin.length;i<l;i++)t[bin.charAt(i)]=i;return t}(b64chars);var fromCharCode=String.fromCharCode;var cb_utob=function(c){if(c.length<2){var cc=c.charCodeAt(0);return cc<128?c:cc<2048?fromCharCode(192|cc>>>6)+fromCharCode(128|cc&63):fromCharCode(224|cc>>>12&15)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}else{var cc=65536+(c.charCodeAt(0)-55296)*1024+(c.charCodeAt(1)-56320);return fromCharCode(240|cc>>>18&7)+fromCharCode(128|cc>>>12&63)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}};var re_utob=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;var utob=function(u){return u.replace(re_utob,cb_utob)};var cb_encode=function(ccc){var padlen=[0,2,1][ccc.length%3],ord=ccc.charCodeAt(0)<<16|(ccc.length>1?ccc.charCodeAt(1):0)<<8|(ccc.length>2?ccc.charCodeAt(2):0),chars=[b64chars.charAt(ord>>>18),b64chars.charAt(ord>>>12&63),padlen>=2?"=":b64chars.charAt(ord>>>6&63),padlen>=1?"=":b64chars.charAt(ord&63)];return chars.join("")};var btoa=global.btoa?function(b){return global.btoa(b)}:function(b){return b.replace(/[\s\S]{1,3}/g,cb_encode)};var _encode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(u){return(u.constructor===buffer.constructor?u:buffer.from(u)).toString("base64")}:function(u){return(u.constructor===buffer.constructor?u:new buffer(u)).toString("base64")}:function(u){return btoa(utob(u))};var encode=function(u,urisafe){return!urisafe?_encode(String(u)):_encode(String(u)).replace(/[+\/]/g,function(m0){return m0=="+"?"-":"_"}).replace(/=/g,"")};var encodeURI=function(u){return encode(u,true)};var re_btou=new RegExp(["[À-ß][-¿]","[à-ï][-¿]{2}","[ð-÷][-¿]{3}"].join("|"),"g");var cb_btou=function(cccc){switch(cccc.length){case 4:var cp=(7&cccc.charCodeAt(0))<<18|(63&cccc.charCodeAt(1))<<12|(63&cccc.charCodeAt(2))<<6|63&cccc.charCodeAt(3),offset=cp-65536;return fromCharCode((offset>>>10)+55296)+fromCharCode((offset&1023)+56320);case 3:return fromCharCode((15&cccc.charCodeAt(0))<<12|(63&cccc.charCodeAt(1))<<6|63&cccc.charCodeAt(2));default:return fromCharCode((31&cccc.charCodeAt(0))<<6|63&cccc.charCodeAt(1))}};var btou=function(b){return b.replace(re_btou,cb_btou)};var cb_decode=function(cccc){var len=cccc.length,padlen=len%4,n=(len>0?b64tab[cccc.charAt(0)]<<18:0)|(len>1?b64tab[cccc.charAt(1)]<<12:0)|(len>2?b64tab[cccc.charAt(2)]<<6:0)|(len>3?b64tab[cccc.charAt(3)]:0),chars=[fromCharCode(n>>>16),fromCharCode(n>>>8&255),fromCharCode(n&255)];chars.length-=[0,0,2,1][padlen];return chars.join("")};var _atob=global.atob?function(a){return global.atob(a)}:function(a){return a.replace(/\S{1,4}/g,cb_decode)};var atob=function(a){return _atob(String(a).replace(/[^A-Za-z0-9\+\/]/g,""))};var _decode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(a){return(a.constructor===buffer.constructor?a:buffer.from(a,"base64")).toString()}:function(a){return(a.constructor===buffer.constructor?a:new buffer(a,"base64")).toString()}:function(a){return btou(_atob(a))};var decode=function(a){return _decode(String(a).replace(/[-_]/g,function(m0){return m0=="-"?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))};var noConflict=function(){var Base64=global.Base64;global.Base64=_Base64;return Base64};global.Base64={VERSION:version,atob:atob,btoa:btoa,fromBase64:decode,toBase64:encode,utob:utob,encode:encode,encodeURI:encodeURI,btou:btou,decode:decode,noConflict:noConflict,__buffer__:buffer};if(typeof Object.defineProperty==="function"){var noEnum=function(v){return{value:v,enumerable:false,writable:true,configurable:true}};global.Base64.extendString=function(){Object.defineProperty(String.prototype,"fromBase64",noEnum(function(){return decode(this)}));Object.defineProperty(String.prototype,"toBase64",noEnum(function(urisafe){return encode(this,urisafe)}));Object.defineProperty(String.prototype,"toBase64URI",noEnum(function(){return encode(this,true)}))}}if(global["Meteor"]){Base64=global.Base64}if(typeof module!=="undefined"&&module.exports){module.exports.Base64=global.Base64}else if(typeof define==="function"&&define.amd){define([],function(){return global.Base64})}return{Base64:global.Base64}});
|
|
|
@ -62,20 +62,20 @@ const supportLangs = [
|
||||||
];
|
];
|
||||||
|
|
||||||
function getLang() {
|
function getLang() {
|
||||||
let lang = getCookie("lang");
|
let lang = CookieManager.getCookie("lang");
|
||||||
|
|
||||||
if (!lang) {
|
if (!lang) {
|
||||||
if (window.navigator) {
|
if (window.navigator) {
|
||||||
lang = window.navigator.language || window.navigator.userLanguage;
|
lang = window.navigator.language || window.navigator.userLanguage;
|
||||||
|
|
||||||
if (isSupportLang(lang)) {
|
if (isSupportLang(lang)) {
|
||||||
setCookie("lang", lang, 150);
|
CookieManager.setCookie("lang", lang, 150);
|
||||||
} else {
|
} else {
|
||||||
setCookie("lang", "en-US", 150);
|
CookieManager.setCookie("lang", "en-US", 150);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setCookie("lang", "en-US", 150);
|
CookieManager.setCookie("lang", "en-US", 150);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ function setLang(lang) {
|
||||||
lang = "en-US";
|
lang = "en-US";
|
||||||
}
|
}
|
||||||
|
|
||||||
setCookie("lang", lang, 150);
|
CookieManager.setCookie("lang", lang, 150);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,11 @@ class DBInbound {
|
||||||
}
|
}
|
||||||
|
|
||||||
get totalGB() {
|
get totalGB() {
|
||||||
return toFixed(this.total / ONE_GB, 2);
|
return NumberFormatter.toFixed(this.total / SizeFormatter.ONE_GB, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
set totalGB(gb) {
|
set totalGB(gb) {
|
||||||
this.total = toFixed(gb * ONE_GB, 0);
|
this.total = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isVMess() {
|
get isVMess() {
|
||||||
|
|
|
@ -1302,7 +1302,7 @@ class Inbound extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'vmess://' + base64(JSON.stringify(obj, null, 2));
|
return 'vmess://' + Base64.encode(JSON.stringify(obj, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
genVLESSLink(address = '', port = this.port, forceTls, remark = '', clientId, flow) {
|
genVLESSLink(address = '', port = this.port, forceTls, remark = '', clientId, flow) {
|
||||||
|
@ -1474,7 +1474,7 @@ class Inbound extends XrayCommonClass {
|
||||||
if (this.isSS2022) password.push(settings.password);
|
if (this.isSS2022) password.push(settings.password);
|
||||||
if (this.isSSMultiUser) password.push(clientPassword);
|
if (this.isSSMultiUser) password.push(clientPassword);
|
||||||
|
|
||||||
let link = `ss://${safeBase64(settings.method + ':' + password.join(':'))}@${address}:${port}`;
|
let link = `ss://${Base64.encode(`${settings.method}:${password.join(':')}`, true)}@${address}:${port}`;
|
||||||
const url = new URL(link);
|
const url = new URL(link);
|
||||||
for (const [key, value] of params) {
|
for (const [key, value] of params) {
|
||||||
url.searchParams.set(key, value)
|
url.searchParams.set(key, value)
|
||||||
|
@ -1837,11 +1837,11 @@ Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get _totalGB() {
|
get _totalGB() {
|
||||||
return toFixed(this.totalGB / ONE_GB, 2);
|
return NumberFormatter.toFixed(this.totalGB / SizeFormatter.ONE_GB, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
set _totalGB(gb) {
|
set _totalGB(gb) {
|
||||||
this.totalGB = toFixed(gb * ONE_GB, 0);
|
this.totalGB = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1947,11 +1947,11 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get _totalGB() {
|
get _totalGB() {
|
||||||
return toFixed(this.totalGB / ONE_GB, 2);
|
return NumberFormatter.toFixed(this.totalGB / SizeFormatter.ONE_GB, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
set _totalGB(gb) {
|
set _totalGB(gb) {
|
||||||
this.totalGB = toFixed(gb * ONE_GB, 0);
|
this.totalGB = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Inbound.VLESSSettings.Fallback = class extends XrayCommonClass {
|
Inbound.VLESSSettings.Fallback = class extends XrayCommonClass {
|
||||||
|
@ -2099,11 +2099,11 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get _totalGB() {
|
get _totalGB() {
|
||||||
return toFixed(this.totalGB / ONE_GB, 2);
|
return NumberFormatter.toFixed(this.totalGB / SizeFormatter.ONE_GB, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
set _totalGB(gb) {
|
set _totalGB(gb) {
|
||||||
this.totalGB = toFixed(gb * ONE_GB, 0);
|
this.totalGB = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -2263,11 +2263,11 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get _totalGB() {
|
get _totalGB() {
|
||||||
return toFixed(this.totalGB / ONE_GB, 2);
|
return NumberFormatter.toFixed(this.totalGB / SizeFormatter.ONE_GB, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
set _totalGB(gb) {
|
set _totalGB(gb) {
|
||||||
this.totalGB = toFixed(gb * ONE_GB, 0);
|
this.totalGB = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,212 +0,0 @@
|
||||||
const ONE_KB = 1024;
|
|
||||||
const ONE_MB = ONE_KB * 1024;
|
|
||||||
const ONE_GB = ONE_MB * 1024;
|
|
||||||
const ONE_TB = ONE_GB * 1024;
|
|
||||||
const ONE_PB = ONE_TB * 1024;
|
|
||||||
|
|
||||||
function sizeFormat(size) {
|
|
||||||
if (size <= 0) return "0 B";
|
|
||||||
|
|
||||||
if (size < ONE_KB) {
|
|
||||||
return size.toFixed(0) + " B";
|
|
||||||
} else if (size < ONE_MB) {
|
|
||||||
return (size / ONE_KB).toFixed(2) + " KB";
|
|
||||||
} else if (size < ONE_GB) {
|
|
||||||
return (size / ONE_MB).toFixed(2) + " MB";
|
|
||||||
} else if (size < ONE_TB) {
|
|
||||||
return (size / ONE_GB).toFixed(2) + " GB";
|
|
||||||
} else if (size < ONE_PB) {
|
|
||||||
return (size / ONE_TB).toFixed(2) + " TB";
|
|
||||||
} else {
|
|
||||||
return (size / ONE_PB).toFixed(2) + " PB";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cpuSpeedFormat(speed) {
|
|
||||||
if (speed > 1000) {
|
|
||||||
const GHz = speed / 1000;
|
|
||||||
return GHz.toFixed(2) + " GHz";
|
|
||||||
} else {
|
|
||||||
return speed.toFixed(2) + " MHz";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cpuCoreFormat(cores) {
|
|
||||||
if (cores === 1) {
|
|
||||||
return "1 Core";
|
|
||||||
} else {
|
|
||||||
return cores + " Cores";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function base64(str) {
|
|
||||||
return Base64.encode(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeBase64(str) {
|
|
||||||
return base64(str)
|
|
||||||
.replace(/\+/g, '-')
|
|
||||||
.replace(/=/g, '')
|
|
||||||
.replace(/\//g, '_');
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatSecond(second) {
|
|
||||||
if (second < 60) {
|
|
||||||
return second.toFixed(0) + 's';
|
|
||||||
} else if (second < 3600) {
|
|
||||||
return (second / 60).toFixed(0) + 'm';
|
|
||||||
} else if (second < 3600 * 24) {
|
|
||||||
return (second / 3600).toFixed(0) + 'h';
|
|
||||||
} else {
|
|
||||||
day = Math.floor(second / 3600 / 24);
|
|
||||||
remain = ((second / 3600) - (day * 24)).toFixed(0);
|
|
||||||
return day + 'd' + (remain > 0 ? ' ' + remain + 'h' : '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyToClipboard(text) {
|
|
||||||
// !! here old way of copying is used because not everyone can afford https connection
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const textarea = document.createElement("textarea");
|
|
||||||
|
|
||||||
textarea.value = text;
|
|
||||||
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
|
|
||||||
textarea.select();
|
|
||||||
document.execCommand("copy");
|
|
||||||
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
|
|
||||||
resolve(text)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function addZero(num) {
|
|
||||||
if (num < 10) {
|
|
||||||
return "0" + num;
|
|
||||||
} else {
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toFixed(num, n) {
|
|
||||||
n = Math.pow(10, n);
|
|
||||||
return Math.floor(num * n) / n;
|
|
||||||
}
|
|
||||||
|
|
||||||
function debounce(fn, delay) {
|
|
||||||
var timeoutID = null;
|
|
||||||
return function () {
|
|
||||||
clearTimeout(timeoutID);
|
|
||||||
var args = arguments;
|
|
||||||
var that = this;
|
|
||||||
timeoutID = setTimeout(function () {
|
|
||||||
fn.apply(that, args);
|
|
||||||
}, delay);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCookie(cname) {
|
|
||||||
let name = cname + '=';
|
|
||||||
let ca = document.cookie.split(';');
|
|
||||||
for (let i = 0; i < ca.length; i++) {
|
|
||||||
let c = ca[i];
|
|
||||||
while (c.charAt(0) == ' ') {
|
|
||||||
c = c.substring(1);
|
|
||||||
}
|
|
||||||
if (c.indexOf(name) == 0) {
|
|
||||||
// decode cookie value only
|
|
||||||
return decodeURIComponent(c.substring(name.length, c.length));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function setCookie(cname, cvalue, exdays) {
|
|
||||||
const d = new Date();
|
|
||||||
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
|
|
||||||
let expires = 'expires=' + d.toUTCString();
|
|
||||||
// encode cookie value
|
|
||||||
document.cookie = cname + '=' + encodeURIComponent(cvalue) + ';' + expires + ';path=/';
|
|
||||||
}
|
|
||||||
|
|
||||||
function usageColor(data, threshold, total) {
|
|
||||||
switch (true) {
|
|
||||||
case data === null:
|
|
||||||
return "purple";
|
|
||||||
case total < 0:
|
|
||||||
return "green";
|
|
||||||
case total == 0:
|
|
||||||
return "purple";
|
|
||||||
case data < total - threshold:
|
|
||||||
return "green";
|
|
||||||
case data < total:
|
|
||||||
return "orange";
|
|
||||||
default:
|
|
||||||
return "red";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clientUsageColor(clientStats, trafficDiff) {
|
|
||||||
switch (true) {
|
|
||||||
case !clientStats || clientStats.total == 0:
|
|
||||||
return "#7a316f"; // purple
|
|
||||||
case clientStats.up + clientStats.down < clientStats.total - trafficDiff:
|
|
||||||
return "#008771"; // Green
|
|
||||||
case clientStats.up + clientStats.down < clientStats.total:
|
|
||||||
return "#f37b24"; // Orange
|
|
||||||
default:
|
|
||||||
return "#cf3c3c"; // Red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function userExpiryColor(threshold, client, isDark = false) {
|
|
||||||
if (!client.enable) {
|
|
||||||
return isDark ? '#2c3950' : '#bcbcbc';
|
|
||||||
}
|
|
||||||
now = new Date().getTime(),
|
|
||||||
expiry = client.expiryTime;
|
|
||||||
switch (true) {
|
|
||||||
case expiry === null:
|
|
||||||
return "#7a316f"; // purple
|
|
||||||
case expiry < 0:
|
|
||||||
return "#008771"; // Green
|
|
||||||
case expiry == 0:
|
|
||||||
return "#7a316f"; // purple
|
|
||||||
case now < expiry - threshold:
|
|
||||||
return "#008771"; // Green
|
|
||||||
case now < expiry:
|
|
||||||
return "#f37b24"; // Orange
|
|
||||||
default:
|
|
||||||
return "#cf3c3c"; // Red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function doAllItemsExist(array1, array2) {
|
|
||||||
for (let i = 0; i < array1.length; i++) {
|
|
||||||
if (!array2.includes(array1[i])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildURL({ host, port, isTLS, base, path }) {
|
|
||||||
if (!host || host.length === 0) host = window.location.hostname;
|
|
||||||
if (!port || port.length === 0) port = window.location.port;
|
|
||||||
|
|
||||||
if (isTLS === undefined) isTLS = window.location.protocol === "https:";
|
|
||||||
|
|
||||||
const protocol = isTLS ? "https:" : "http:";
|
|
||||||
|
|
||||||
port = String(port);
|
|
||||||
if (port === "" || (isTLS && port === "443") || (!isTLS && port === "80")) {
|
|
||||||
port = "";
|
|
||||||
} else {
|
|
||||||
port = `:${port}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${protocol}//${host}${port}${base}${path}`;
|
|
||||||
}
|
|
|
@ -108,14 +108,14 @@ Date.prototype.setMaxTime = function () {
|
||||||
* Formatting date
|
* Formatting date
|
||||||
*/
|
*/
|
||||||
Date.prototype.formatDate = function () {
|
Date.prototype.formatDate = function () {
|
||||||
return this.getFullYear() + "-" + addZero(this.getMonth() + 1) + "-" + addZero(this.getDate());
|
return this.getFullYear() + "-" + NumberFormatter.addZero(this.getMonth() + 1) + "-" + NumberFormatter.addZero(this.getDate());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format time
|
* Format time
|
||||||
*/
|
*/
|
||||||
Date.prototype.formatTime = function () {
|
Date.prototype.formatTime = function () {
|
||||||
return addZero(this.getHours()) + ":" + addZero(this.getMinutes()) + ":" + addZero(this.getSeconds());
|
return NumberFormatter.addZero(this.getHours()) + ":" + NumberFormatter.addZero(this.getMinutes()) + ":" + NumberFormatter.addZero(this.getSeconds());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -479,3 +479,194 @@ class Wireguard {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ClipboardManager {
|
||||||
|
static copyText(content = "") {
|
||||||
|
// !! here old way of copying is used because not everyone can afford https connection
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
try {
|
||||||
|
const textarea = window.document.createElement('textarea');
|
||||||
|
|
||||||
|
textarea.style.fontSize = '12pt';
|
||||||
|
textarea.style.border = '0';
|
||||||
|
textarea.style.padding = '0';
|
||||||
|
textarea.style.margin = '0';
|
||||||
|
textarea.style.position = 'absolute';
|
||||||
|
textarea.style.left = '-9999px';
|
||||||
|
textarea.style.top = `${window.pageYOffset || document.documentElement.scrollTop}px`;
|
||||||
|
textarea.setAttribute('readonly', '');
|
||||||
|
textarea.value = content;
|
||||||
|
|
||||||
|
window.document.body.appendChild(textarea);
|
||||||
|
|
||||||
|
textarea.select();
|
||||||
|
window.document.execCommand("copy");
|
||||||
|
|
||||||
|
window.document.body.removeChild(textarea);
|
||||||
|
|
||||||
|
resolve(true)
|
||||||
|
} catch {
|
||||||
|
resolve(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Base64 {
|
||||||
|
static encode(content = "", safe = false) {
|
||||||
|
if (safe) {
|
||||||
|
return window.btoa(content)
|
||||||
|
.replace(/\+/g, '-')
|
||||||
|
.replace(/=/g, '')
|
||||||
|
.replace(/\//g, '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.btoa(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
static decode(content = "") {
|
||||||
|
return window.atob(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SizeFormatter {
|
||||||
|
static ONE_KB = 1024;
|
||||||
|
static ONE_MB = this.ONE_KB * 1024;
|
||||||
|
static ONE_GB = this.ONE_MB * 1024;
|
||||||
|
static ONE_TB = this.ONE_GB * 1024;
|
||||||
|
static ONE_PB = this.ONE_TB * 1024;
|
||||||
|
|
||||||
|
static sizeFormat(size) {
|
||||||
|
if (size <= 0) return "0 B";
|
||||||
|
if (size < this.ONE_KB) return size.toFixed(0) + " B";
|
||||||
|
if (size < this.ONE_MB) return (size / this.ONE_KB).toFixed(2) + " KB";
|
||||||
|
if (size < this.ONE_GB) return (size / this.ONE_MB).toFixed(2) + " MB";
|
||||||
|
if (size < this.ONE_TB) return (size / this.ONE_GB).toFixed(2) + " GB";
|
||||||
|
if (size < this.ONE_PB) return (size / this.ONE_TB).toFixed(2) + " TB";
|
||||||
|
return (size / this.ONE_PB).toFixed(2) + " PB";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CPUFormatter {
|
||||||
|
static cpuSpeedFormat(speed) {
|
||||||
|
return speed > 1000 ? (speed / 1000).toFixed(2) + " GHz" : speed.toFixed(2) + " MHz";
|
||||||
|
}
|
||||||
|
|
||||||
|
static cpuCoreFormat(cores) {
|
||||||
|
return cores === 1 ? "1 Core" : cores + " Cores";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TimeFormatter {
|
||||||
|
static formatSecond(second) {
|
||||||
|
if (second < 60) return second.toFixed(0) + 's';
|
||||||
|
if (second < 3600) return (second / 60).toFixed(0) + 'm';
|
||||||
|
if (second < 3600 * 24) return (second / 3600).toFixed(0) + 'h';
|
||||||
|
let day = Math.floor(second / 3600 / 24);
|
||||||
|
let remain = ((second / 3600) - (day * 24)).toFixed(0);
|
||||||
|
return day + 'd' + (remain > 0 ? ' ' + remain + 'h' : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NumberFormatter {
|
||||||
|
static addZero(num) {
|
||||||
|
return num < 10 ? "0" + num : num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static toFixed(num, n) {
|
||||||
|
n = Math.pow(10, n);
|
||||||
|
return Math.floor(num * n) / n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Utils {
|
||||||
|
static debounce(fn, delay) {
|
||||||
|
let timeoutID = null;
|
||||||
|
return function () {
|
||||||
|
clearTimeout(timeoutID);
|
||||||
|
let args = arguments;
|
||||||
|
let that = this;
|
||||||
|
timeoutID = setTimeout(() => fn.apply(that, args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CookieManager {
|
||||||
|
static getCookie(cname) {
|
||||||
|
let name = cname + '=';
|
||||||
|
let ca = document.cookie.split(';');
|
||||||
|
for (let c of ca) {
|
||||||
|
c = c.trim();
|
||||||
|
if (c.indexOf(name) === 0) {
|
||||||
|
return decodeURIComponent(c.substring(name.length, c.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
static setCookie(cname, cvalue, exdays) {
|
||||||
|
const d = new Date();
|
||||||
|
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
|
||||||
|
let expires = 'expires=' + d.toUTCString();
|
||||||
|
document.cookie = cname + '=' + encodeURIComponent(cvalue) + ';' + expires + ';path=/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ColorUtils {
|
||||||
|
static usageColor(data, threshold, total) {
|
||||||
|
switch (true) {
|
||||||
|
case data === null: return "purple";
|
||||||
|
case total < 0: return "green";
|
||||||
|
case total == 0: return "purple";
|
||||||
|
case data < total - threshold: return "green";
|
||||||
|
case data < total: return "orange";
|
||||||
|
default: return "red";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static clientUsageColor(clientStats, trafficDiff) {
|
||||||
|
switch (true) {
|
||||||
|
case !clientStats || clientStats.total == 0: return "#7a316f";
|
||||||
|
case clientStats.up + clientStats.down < clientStats.total - trafficDiff: return "#008771";
|
||||||
|
case clientStats.up + clientStats.down < clientStats.total: return "#f37b24";
|
||||||
|
default: return "#cf3c3c";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static userExpiryColor(threshold, client, isDark = false) {
|
||||||
|
if (!client.enable) return isDark ? '#2c3950' : '#bcbcbc';
|
||||||
|
let now = new Date().getTime(), expiry = client.expiryTime;
|
||||||
|
switch (true) {
|
||||||
|
case expiry === null: return "#7a316f";
|
||||||
|
case expiry < 0: return "#008771";
|
||||||
|
case expiry == 0: return "#7a316f";
|
||||||
|
case now < expiry - threshold: return "#008771";
|
||||||
|
case now < expiry: return "#f37b24";
|
||||||
|
default: return "#cf3c3c";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArrayUtils {
|
||||||
|
static doAllItemsExist(array1, array2) {
|
||||||
|
return array1.every(item => array2.includes(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class URLBuilder {
|
||||||
|
static buildURL({ host, port, isTLS, base, path }) {
|
||||||
|
if (!host || host.length === 0) host = window.location.hostname;
|
||||||
|
if (!port || port.length === 0) port = window.location.port;
|
||||||
|
if (isTLS === undefined) isTLS = window.location.protocol === "https:";
|
||||||
|
|
||||||
|
const protocol = isTLS ? "https:" : "http:";
|
||||||
|
port = String(port);
|
||||||
|
if (port === "" || (isTLS && port === "443") || (!isTLS && port === "80")) {
|
||||||
|
port = "";
|
||||||
|
} else {
|
||||||
|
port = `:${port}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${protocol}//${host}${port}${base}${path}`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,9 +5,8 @@
|
||||||
<script src="{{ .base_path }}assets/axios/axios.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/axios/axios.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/qs/qs.min.js"></script>
|
<script src="{{ .base_path }}assets/qs/qs.min.js"></script>
|
||||||
<script src="{{ .base_path }}assets/js/axios-init.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/axios-init.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/js/util/common.js?{{ .cur_ver }}"></script>
|
|
||||||
<script src="{{ .base_path }}assets/js/util/date-util.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/util/date-util.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/js/util/utils.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/util/index.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/js/langs.js"></script>
|
<script src="{{ .base_path }}assets/js/langs.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const basePath = '{{ .base_path }}';
|
const basePath = '{{ .base_path }}';
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<a-tag color="purple" class="qr-tag"><span>{{ i18n "pages.settings.subSettings"}}</span></a-tag>
|
<a-tag color="purple" class="qr-tag"><span>{{ i18n "pages.settings.subSettings"}}</span></a-tag>
|
||||||
<tr-qr-bg class="qr-bg-sub">
|
<tr-qr-bg class="qr-bg-sub">
|
||||||
<tr-qr-bg-inner class="qr-bg-sub-inner">
|
<tr-qr-bg-inner class="qr-bg-sub-inner">
|
||||||
<canvas @click="copyToClipboard(genSubLink(qrModal.client.subId))" id="qrCode-sub" class="qr-cv"></canvas>
|
<canvas @click="qrModal.copy(genSubLink(qrModal.client.subId))" id="qrCode-sub" class="qr-cv"></canvas>
|
||||||
</tr-qr-bg-inner>
|
</tr-qr-bg-inner>
|
||||||
</tr-qr-bg>
|
</tr-qr-bg>
|
||||||
</tr-qr-box>
|
</tr-qr-box>
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
<a-tag color="purple" class="qr-tag"><span>{{ i18n "pages.settings.subSettings"}} Json</span></a-tag>
|
<a-tag color="purple" class="qr-tag"><span>{{ i18n "pages.settings.subSettings"}} Json</span></a-tag>
|
||||||
<tr-qr-bg class="qr-bg-sub">
|
<tr-qr-bg class="qr-bg-sub">
|
||||||
<tr-qr-bg-inner class="qr-bg-sub-inner">
|
<tr-qr-bg-inner class="qr-bg-sub-inner">
|
||||||
<canvas @click="copyToClipboard(genSubJsonLink(qrModal.client.subId))" id="qrCode-subJson" class="qr-cv"></canvas>
|
<canvas @click="qrModal.copy(genSubJsonLink(qrModal.client.subId))" id="qrCode-subJson" class="qr-cv"></canvas>
|
||||||
</tr-qr-bg-inner>
|
</tr-qr-bg-inner>
|
||||||
</tr-qr-bg>
|
</tr-qr-bg>
|
||||||
</tr-qr-box>
|
</tr-qr-box>
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
<tr-qr-box class="qr-box">
|
<tr-qr-box class="qr-box">
|
||||||
<a-tag color="green" class="qr-tag"><span>[[ row.remark ]]</span></a-tag>
|
<a-tag color="green" class="qr-tag"><span>[[ row.remark ]]</span></a-tag>
|
||||||
<tr-qr-bg class="qr-bg">
|
<tr-qr-bg class="qr-bg">
|
||||||
<canvas @click="copyToClipboard(row.link)" :id="'qrCode-'+index" class="qr-cv"></canvas>
|
<canvas @click="qrModal.copy(row.link)" :id="'qrCode-'+index" class="qr-cv"></canvas>
|
||||||
</tr-qr-bg>
|
</tr-qr-bg>
|
||||||
</tr-qr-box>
|
</tr-qr-box>
|
||||||
</template>
|
</template>
|
||||||
|
@ -78,8 +78,10 @@
|
||||||
qrModal: qrModal,
|
qrModal: qrModal,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
copyToClipboard(content) {
|
copy(content) {
|
||||||
return copyToClipboard(content).then(() => {
|
ClipboardManager
|
||||||
|
.copyText(content)
|
||||||
|
.then(() => {
|
||||||
app.$message.success('{{ i18n "copied" }}')
|
app.$message.success('{{ i18n "copied" }}')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
},
|
},
|
||||||
copy: function (content = '') {
|
copy: function (content = '') {
|
||||||
copyToClipboard(content).then(() => {
|
ClipboardManager
|
||||||
|
.copyText(content)
|
||||||
|
.then(() => {
|
||||||
app.$message.success('{{ i18n "copied" }}')
|
app.$message.success('{{ i18n "copied" }}')
|
||||||
this.close();
|
this.close();
|
||||||
})
|
})
|
||||||
|
|
|
@ -126,10 +126,10 @@
|
||||||
<a-input-number v-model.number="client._totalGB" :min="0"></a-input-number>
|
<a-input-number v-model.number="client._totalGB" :min="0"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="isEdit && clientStats" label='{{ i18n "usage" }}'>
|
<a-form-item v-if="isEdit && clientStats" label='{{ i18n "usage" }}'>
|
||||||
<a-tag :color="clientUsageColor(clientStats, app.trafficDiff)">
|
<a-tag :color="ColorUtils.clientUsageColor(clientStats, app.trafficDiff)">
|
||||||
[[ sizeFormat(clientStats.up) ]] /
|
[[ SizeFormatter.sizeFormat(clientStats.up) ]] /
|
||||||
[[ sizeFormat(clientStats.down) ]]
|
[[ SizeFormatter.sizeFormat(clientStats.down) ]]
|
||||||
([[ sizeFormat(clientStats.up + clientStats.down) ]])
|
([[ SizeFormatter.sizeFormat(clientStats.up + clientStats.down) ]])
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||||
|
|
|
@ -55,18 +55,18 @@
|
||||||
<template slot="content" v-if="client.email">
|
<template slot="content" v-if="client.email">
|
||||||
<table cellpadding="2" width="100%">
|
<table cellpadding="2" width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td>↑[[ sizeFormat(getUpStats(record, client.email)) ]]</td>
|
<td>↑[[ SizeFormatter.sizeFormat(getUpStats(record, client.email)) ]]</td>
|
||||||
<td>↓[[ sizeFormat(getDownStats(record, client.email)) ]]</td>
|
<td>↓[[ SizeFormatter.sizeFormat(getDownStats(record, client.email)) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="client.totalGB > 0">
|
<tr v-if="client.totalGB > 0">
|
||||||
<td>{{ i18n "remained" }}</td>
|
<td>{{ i18n "remained" }}</td>
|
||||||
<td>[[ sizeFormat(getRemStats(record, client.email)) ]]</td>
|
<td>[[ SizeFormatter.sizeFormat(getRemStats(record, client.email)) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
<table>
|
<table>
|
||||||
<tr class="tr-table-box">
|
<tr class="tr-table-box">
|
||||||
<td class="tr-table-rt"> [[ sizeFormat(getSumStats(record, client.email)) ]] </td>
|
<td class="tr-table-rt"> [[ SizeFormatter.sizeFormat(getSumStats(record, client.email)) ]] </td>
|
||||||
<td class="tr-table-bar" v-if="!client.enable">
|
<td class="tr-table-bar" v-if="!client.enable">
|
||||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
||||||
</td>
|
</td>
|
||||||
|
@ -124,9 +124,9 @@
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="min-width: 50px; border: none;" :color="userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
<a-tag style="min-width: 50px; border: none;" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-tag v-else :color="userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)" style="border: none;" class="infinite-tag">
|
<a-tag v-else :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)" style="border: none;" class="infinite-tag">
|
||||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||||
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -172,7 +172,7 @@
|
||||||
<td colspan="3" style="text-align: center;">{{ i18n "pages.inbounds.traffic" }}</td>
|
<td colspan="3" style="text-align: center;">{{ i18n "pages.inbounds.traffic" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="80px" style="margin:0; text-align: right;font-size: 1em;"> [[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] </td>
|
<td width="80px" style="margin:0; text-align: right;font-size: 1em;"> [[ SizeFormatter.sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] </td>
|
||||||
<td width="120px" v-if="!client.enable">
|
<td width="120px" v-if="!client.enable">
|
||||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
||||||
</td>
|
</td>
|
||||||
|
@ -181,12 +181,12 @@
|
||||||
<template slot="content" v-if="client.email">
|
<template slot="content" v-if="client.email">
|
||||||
<table cellpadding="2" width="100%">
|
<table cellpadding="2" width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td>↑[[ sizeFormat(getUpStats(record, client.email)) ]]</td>
|
<td>↑[[ SizeFormatter.sizeFormat(getUpStats(record, client.email)) ]]</td>
|
||||||
<td>↓[[ sizeFormat(getDownStats(record, client.email)) ]]</td>
|
<td>↓[[ SizeFormatter.sizeFormat(getDownStats(record, client.email)) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "remained" }}</td>
|
<td>{{ i18n "remained" }}</td>
|
||||||
<td>[[ sizeFormat(getRemStats(record, client.email)) ]]</td>
|
<td>[[ SizeFormatter.sizeFormat(getRemStats(record, client.email)) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
|
@ -244,7 +244,7 @@
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="min-width: 50px; border: none;" :color="userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
<a-tag style="min-width: 50px; border: none;" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-tag v-else :color="client.enable ? 'purple' : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'" class="infinite-tag">
|
<a-tag v-else :color="client.enable ? 'purple' : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'" class="infinite-tag">
|
||||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||||
|
|
|
@ -181,8 +181,8 @@
|
||||||
<tr v-if="infoModal.clientStats">
|
<tr v-if="infoModal.clientStats">
|
||||||
<td>{{ i18n "usage" }}</td>
|
<td>{{ i18n "usage" }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a-tag color="green">[[ sizeFormat(infoModal.clientStats.up + infoModal.clientStats.down) ]]</a-tag>
|
<a-tag color="green">[[ SizeFormatter.sizeFormat(infoModal.clientStats.up + infoModal.clientStats.down) ]]</a-tag>
|
||||||
<a-tag>↑ [[ sizeFormat(infoModal.clientStats.up) ]] / [[ sizeFormat(infoModal.clientStats.down) ]] ↓</a-tag>
|
<a-tag>↑ [[ SizeFormatter.sizeFormat(infoModal.clientStats.up) ]] / [[ SizeFormatter.sizeFormat(infoModal.clientStats.down) ]] ↓</a-tag>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="infoModal.clientSettings.comment">
|
<tr v-if="infoModal.clientSettings.comment">
|
||||||
|
@ -224,7 +224,7 @@
|
||||||
<a-tag v-if="infoModal.clientStats && infoModal.clientSettings.totalGB > 0" :color="statsColor(infoModal.clientStats)"> [[ getRemStats() ]] </a-tag>
|
<a-tag v-if="infoModal.clientStats && infoModal.clientSettings.totalGB > 0" :color="statsColor(infoModal.clientStats)"> [[ getRemStats() ]] </a-tag>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a-tag v-if="infoModal.clientSettings.totalGB > 0" :color="statsColor(infoModal.clientStats)"> [[ sizeFormat(infoModal.clientSettings.totalGB) ]] </a-tag>
|
<a-tag v-if="infoModal.clientSettings.totalGB > 0" :color="statsColor(infoModal.clientStats)"> [[ SizeFormatter.sizeFormat(infoModal.clientSettings.totalGB) ]] </a-tag>
|
||||||
<a-tag v-else color="purple" class="infinite-tag">
|
<a-tag v-else color="purple" class="infinite-tag">
|
||||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||||
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
||||||
|
@ -233,7 +233,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<template v-if="infoModal.clientSettings.expiryTime > 0">
|
<template v-if="infoModal.clientSettings.expiryTime > 0">
|
||||||
<a-tag :color="usageColor(new Date().getTime(), app.expireDiff, infoModal.clientSettings.expiryTime)">
|
<a-tag :color="ColorUtils.usageColor(new Date().getTime(), app.expireDiff, infoModal.clientSettings.expiryTime)">
|
||||||
<template v-if="app.datepicker === 'gregorian'">
|
<template v-if="app.datepicker === 'gregorian'">
|
||||||
[[ DateUtil.formatMillis(infoModal.clientSettings.expiryTime) ]]
|
[[ DateUtil.formatMillis(infoModal.clientSettings.expiryTime) ]]
|
||||||
</template>
|
</template>
|
||||||
|
@ -258,7 +258,7 @@
|
||||||
<tr-info-title class="tr-info-title">
|
<tr-info-title class="tr-info-title">
|
||||||
<a-tag color="purple">Subscription Link</a-tag>
|
<a-tag color="purple">Subscription Link</a-tag>
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button size="small" icon="snippets" @click="copyToClipboard(infoModal.subLink)"></a-button>
|
<a-button size="small" icon="snippets" @click="copy(infoModal.subLink)"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
<a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a>
|
<a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a>
|
||||||
|
@ -267,7 +267,7 @@
|
||||||
<tr-info-title class="tr-info-title">
|
<tr-info-title class="tr-info-title">
|
||||||
<a-tag color="purple">Json Link</a-tag>
|
<a-tag color="purple">Json Link</a-tag>
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button size="small" icon="snippets" @click="copyToClipboard(infoModal.subJsonLink)"></a-button>
|
<a-button size="small" icon="snippets" @click="copy(infoModal.subJsonLink)"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
<a :href="[[ infoModal.subJsonLink ]]" target="_blank">[[ infoModal.subJsonLink ]]</a>
|
<a :href="[[ infoModal.subJsonLink ]]" target="_blank">[[ infoModal.subJsonLink ]]</a>
|
||||||
|
@ -279,7 +279,7 @@
|
||||||
<tr-info-title class="tr-info-title">
|
<tr-info-title class="tr-info-title">
|
||||||
<a-tag color="blue">[[ infoModal.clientSettings.tgId ]]</a-tag>
|
<a-tag color="blue">[[ infoModal.clientSettings.tgId ]]</a-tag>
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button size="small" icon="snippets" @click="copyToClipboard(infoModal.clientSettings.tgId)"></a-button>
|
<a-button size="small" icon="snippets" @click="copy(infoModal.clientSettings.tgId)"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
</tr-info-row>
|
</tr-info-row>
|
||||||
|
@ -290,7 +290,7 @@
|
||||||
<tr-info-title class="tr-info-title">
|
<tr-info-title class="tr-info-title">
|
||||||
<a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
|
<a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button style="min-width: 24px;" size="small" icon="snippets" @click="copyToClipboard(link.link)"></a-button>
|
<a-button style="min-width: 24px;" size="small" icon="snippets" @click="copy(link.link)"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
<code>[[ link.link ]]</code>
|
<code>[[ link.link ]]</code>
|
||||||
|
@ -304,7 +304,7 @@
|
||||||
<tr-info-title class="tr-info-title">
|
<tr-info-title class="tr-info-title">
|
||||||
<a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
|
<a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button style="min-width: 24px;" size="small" icon="snippets" @click="copyToClipboard(link.link)"></a-button>
|
<a-button style="min-width: 24px;" size="small" icon="snippets" @click="copy(link.link)"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
<code>[[ link.link ]]</code>
|
<code>[[ link.link ]]</code>
|
||||||
|
@ -431,7 +431,7 @@
|
||||||
<tr-info-title class="tr-info-title">
|
<tr-info-title class="tr-info-title">
|
||||||
<a-tag color="blue">Config</a-tag>
|
<a-tag color="blue">Config</a-tag>
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button style="min-width: 24px;" size="small" icon="snippets" @click="copyToClipboard(infoModal.links[index])"></a-button>
|
<a-button style="min-width: 24px;" size="small" icon="snippets" @click="copy(infoModal.links[index])"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
<div v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)" style="border-radius: 1rem; padding: 0.5rem;" class="client-table-odd-row">
|
<div v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)" style="border-radius: 1rem; padding: 0.5rem;" class="client-table-odd-row">
|
||||||
|
@ -532,17 +532,19 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
copyToClipboard(content) {
|
copy(content) {
|
||||||
return copyToClipboard(content).then(() => {
|
ClipboardManager
|
||||||
|
.copyText(content)
|
||||||
|
.then(() => {
|
||||||
app.$message.success('{{ i18n "copied" }}')
|
app.$message.success('{{ i18n "copied" }}')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
statsColor(stats) {
|
statsColor(stats) {
|
||||||
return usageColor(stats.up + stats.down, app.trafficDiff, stats.total);
|
return ColorUtils.usageColor(stats.up + stats.down, app.trafficDiff, stats.total);
|
||||||
},
|
},
|
||||||
getRemStats() {
|
getRemStats() {
|
||||||
remained = this.infoModal.clientStats.total - this.infoModal.clientStats.up - this.infoModal.clientStats.down;
|
remained = this.infoModal.clientStats.total - this.infoModal.clientStats.up - this.infoModal.clientStats.down;
|
||||||
return remained > 0 ? sizeFormat(remained) : '-';
|
return remained > 0 ? SizeFormatter.sizeFormat(remained) : '-';
|
||||||
},
|
},
|
||||||
refreshIPs() {
|
refreshIPs() {
|
||||||
this.refreshing = true;
|
this.refreshing = true;
|
||||||
|
|
|
@ -141,11 +141,11 @@
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :xs="24" :sm="24" :lg="12">
|
<a-col :xs="24" :sm="24" :lg="12">
|
||||||
{{ i18n "pages.inbounds.totalDownUp" }}:
|
{{ i18n "pages.inbounds.totalDownUp" }}:
|
||||||
<a-tag color="green">[[ sizeFormat(total.up) ]] / [[ sizeFormat(total.down) ]]</a-tag>
|
<a-tag color="green">[[ SizeFormatter.sizeFormat(total.up) ]] / [[ SizeFormatter.sizeFormat(total.down) ]]</a-tag>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :sm="24" :lg="12">
|
<a-col :xs="24" :sm="24" :lg="12">
|
||||||
{{ i18n "pages.inbounds.totalUsage" }}:
|
{{ i18n "pages.inbounds.totalUsage" }}:
|
||||||
<a-tag color="green">[[ sizeFormat(total.up + total.down) ]]</a-tag>
|
<a-tag color="green">[[ SizeFormatter.sizeFormat(total.up + total.down) ]]</a-tag>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :sm="24" :lg="12">
|
<a-col :xs="24" :sm="24" :lg="12">
|
||||||
{{ i18n "pages.inbounds.inboundCount" }}:
|
{{ i18n "pages.inbounds.inboundCount" }}:
|
||||||
|
@ -375,19 +375,19 @@
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<table cellpadding="2" width="100%">
|
<table cellpadding="2" width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td>↑[[ sizeFormat(dbInbound.up) ]]</td>
|
<td>↑[[ SizeFormatter.sizeFormat(dbInbound.up) ]]</td>
|
||||||
<td>↓[[ sizeFormat(dbInbound.down) ]]</td>
|
<td>↓[[ SizeFormatter.sizeFormat(dbInbound.down) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total">
|
<tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total">
|
||||||
<td>{{ i18n "remained" }}</td>
|
<td>{{ i18n "remained" }}</td>
|
||||||
<td>[[ sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down) ]]</td>
|
<td>[[ SizeFormatter.sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
<a-tag :color="usageColor(dbInbound.up + dbInbound.down, app.trafficDiff, dbInbound.total)">
|
<a-tag :color="ColorUtils.usageColor(dbInbound.up + dbInbound.down, app.trafficDiff, dbInbound.total)">
|
||||||
[[ sizeFormat(dbInbound.up + dbInbound.down) ]] /
|
[[ SizeFormatter.sizeFormat(dbInbound.up + dbInbound.down) ]] /
|
||||||
<template v-if="dbInbound.total > 0">
|
<template v-if="dbInbound.total > 0">
|
||||||
[[ sizeFormat(dbInbound.total) ]]
|
[[ SizeFormatter.sizeFormat(dbInbound.total) ]]
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||||
|
@ -408,7 +408,7 @@
|
||||||
<template v-else slot="content">
|
<template v-else slot="content">
|
||||||
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
|
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="min-width: 50px;" :color="usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
|
<a-tag style="min-width: 50px;" :color="ColorUtils.usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
|
||||||
[[ remainedDays(dbInbound._expiryTime) ]]
|
[[ remainedDays(dbInbound._expiryTime) ]]
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
|
@ -474,19 +474,19 @@
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<table cellpadding="2" width="100%">
|
<table cellpadding="2" width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td>↑[[ sizeFormat(dbInbound.up) ]]</td>
|
<td>↑[[ SizeFormatter.sizeFormat(dbInbound.up) ]]</td>
|
||||||
<td>↓[[ sizeFormat(dbInbound.down) ]]</td>
|
<td>↓[[ SizeFormatter.sizeFormat(dbInbound.down) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total">
|
<tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total">
|
||||||
<td>{{ i18n "remained" }}</td>
|
<td>{{ i18n "remained" }}</td>
|
||||||
<td>[[ sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down) ]]</td>
|
<td>[[ SizeFormatter.sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
<a-tag :color="usageColor(dbInbound.up + dbInbound.down, app.trafficDiff, dbInbound.total)">
|
<a-tag :color="ColorUtils.usageColor(dbInbound.up + dbInbound.down, app.trafficDiff, dbInbound.total)">
|
||||||
[[ sizeFormat(dbInbound.up + dbInbound.down) ]] /
|
[[ SizeFormatter.sizeFormat(dbInbound.up + dbInbound.down) ]] /
|
||||||
<template v-if="dbInbound.total > 0">
|
<template v-if="dbInbound.total > 0">
|
||||||
[[ sizeFormat(dbInbound.total) ]]
|
[[ SizeFormatter.sizeFormat(dbInbound.total) ]]
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||||
|
@ -544,7 +544,6 @@
|
||||||
</a-layout>
|
</a-layout>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "js" .}}
|
||||||
<script src="{{ .base_path }}assets/base64/base64.min.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
|
||||||
|
@ -884,7 +883,7 @@
|
||||||
this.exportSubs(dbInbound.id);
|
this.exportSubs(dbInbound.id);
|
||||||
break;
|
break;
|
||||||
case "clipboard":
|
case "clipboard":
|
||||||
this.copyToClipboard(dbInbound.id);
|
this.copy(dbInbound.id);
|
||||||
break;
|
break;
|
||||||
case "resetTraffic":
|
case "resetTraffic":
|
||||||
this.resetTraffic(dbInbound.id);
|
this.resetTraffic(dbInbound.id);
|
||||||
|
@ -1275,9 +1274,9 @@
|
||||||
return remained>0 ? remained : 0;
|
return remained>0 ? remained : 0;
|
||||||
},
|
},
|
||||||
clientStatsColor(dbInbound, email) {
|
clientStatsColor(dbInbound, email) {
|
||||||
if (email.length == 0) return clientUsageColor();
|
if (email.length == 0) return ColorUtils.clientUsageColor();
|
||||||
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
|
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
|
||||||
return clientUsageColor(clientStats, app.trafficDiff)
|
return ColorUtils.clientUsageColor(clientStats, app.trafficDiff)
|
||||||
},
|
},
|
||||||
statsProgress(dbInbound, email) {
|
statsProgress(dbInbound, email) {
|
||||||
if (email.length == 0) return 100;
|
if (email.length == 0) return 100;
|
||||||
|
@ -1295,17 +1294,17 @@
|
||||||
},
|
},
|
||||||
remainedDays(expTime){
|
remainedDays(expTime){
|
||||||
if (expTime == 0) return null;
|
if (expTime == 0) return null;
|
||||||
if (expTime < 0) return formatSecond(expTime/-1000);
|
if (expTime < 0) return TimeFormatter.formatSecond(expTime/-1000);
|
||||||
now = new Date().getTime();
|
now = new Date().getTime();
|
||||||
if (expTime < now) return '{{ i18n "depleted" }}';
|
if (expTime < now) return '{{ i18n "depleted" }}';
|
||||||
return formatSecond((expTime-now)/1000);
|
return TimeFormatter.formatSecond((expTime-now)/1000);
|
||||||
},
|
},
|
||||||
statsExpColor(dbInbound, email){
|
statsExpColor(dbInbound, email){
|
||||||
if (email.length == 0) return '#7a316f';
|
if (email.length == 0) return '#7a316f';
|
||||||
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
|
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
|
||||||
if (!clientStats) return '#7a316f';
|
if (!clientStats) return '#7a316f';
|
||||||
statsColor = usageColor(clientStats.down + clientStats.up, this.trafficDiff, clientStats.total);
|
statsColor = ColorUtils.usageColor(clientStats.down + clientStats.up, this.trafficDiff, clientStats.total);
|
||||||
expColor = usageColor(new Date().getTime(), this.expireDiff, clientStats.expiryTime);
|
expColor = ColorUtils.usageColor(new Date().getTime(), this.expireDiff, clientStats.expiryTime);
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case statsColor == "red" || expColor == "red":
|
case statsColor == "red" || expColor == "red":
|
||||||
return "#cf3c3c"; // Red
|
return "#cf3c3c"; // Red
|
||||||
|
@ -1383,7 +1382,7 @@
|
||||||
}
|
}
|
||||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText.join('\r\n'), 'All-Inbounds');
|
txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText.join('\r\n'), 'All-Inbounds');
|
||||||
},
|
},
|
||||||
copyToClipboard(dbInboundId) {
|
copy(dbInboundId) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
txtModal.show('{{ i18n "pages.inbounds.inboundData" }}', JSON.stringify(dbInbound, null, 2));
|
txtModal.show('{{ i18n "pages.inbounds.inboundData" }}', JSON.stringify(dbInbound, null, 2));
|
||||||
},
|
},
|
||||||
|
@ -1439,7 +1438,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
searchKey: debounce(function (newVal) {
|
searchKey: Utils.debounce(function (newVal) {
|
||||||
this.searchInbounds(newVal);
|
this.searchInbounds(newVal);
|
||||||
}, 500)
|
}, 500)
|
||||||
},
|
},
|
||||||
|
|
|
@ -45,11 +45,11 @@
|
||||||
<a-progress type="dashboard" status="normal"
|
<a-progress type="dashboard" status="normal"
|
||||||
:stroke-color="status.cpu.color"
|
:stroke-color="status.cpu.color"
|
||||||
:percent="status.cpu.percent"></a-progress>
|
:percent="status.cpu.percent"></a-progress>
|
||||||
<div><b>CPU:</b> [[ cpuCoreFormat(status.cpuCores) ]] <a-tooltip>
|
<div><b>CPU:</b> [[ CPUFormatter.cpuCoreFormat(status.cpuCores) ]] <a-tooltip>
|
||||||
<a-icon type="area-chart"></a-icon>
|
<a-icon type="area-chart"></a-icon>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<div><b>Logical Processors:</b> [[ (status.logicalPro) ]]</div>
|
<div><b>Logical Processors:</b> [[ (status.logicalPro) ]]</div>
|
||||||
<div><b>Speed:</b> [[ cpuSpeedFormat(status.cpuSpeedMhz) ]]</div>
|
<div><b>Speed:</b> [[ CPUFormatter.cpuSpeedFormat(status.cpuSpeedMhz) ]]</div>
|
||||||
</template>
|
</template>
|
||||||
</a-tooltip></div>
|
</a-tooltip></div>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
:stroke-color="status.mem.color"
|
:stroke-color="status.mem.color"
|
||||||
:percent="status.mem.percent"></a-progress>
|
:percent="status.mem.percent"></a-progress>
|
||||||
<div>
|
<div>
|
||||||
<b>{{ i18n "pages.index.memory"}}:</b> [[ sizeFormat(status.mem.current) ]] / [[ sizeFormat(status.mem.total) ]]
|
<b>{{ i18n "pages.index.memory"}}:</b> [[ SizeFormatter.sizeFormat(status.mem.current) ]] / [[ SizeFormatter.sizeFormat(status.mem.total) ]]
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
:stroke-color="status.swap.color"
|
:stroke-color="status.swap.color"
|
||||||
:percent="status.swap.percent"></a-progress>
|
:percent="status.swap.percent"></a-progress>
|
||||||
<div>
|
<div>
|
||||||
<b>Swap:</b> [[ sizeFormat(status.swap.current) ]] / [[ sizeFormat(status.swap.total) ]]
|
<b>Swap:</b> [[ SizeFormatter.sizeFormat(status.swap.current) ]] / [[ SizeFormatter.sizeFormat(status.swap.total) ]]
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12" style="text-align: center">
|
<a-col :span="12" style="text-align: center">
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
:stroke-color="status.disk.color"
|
:stroke-color="status.disk.color"
|
||||||
:percent="status.disk.percent"></a-progress>
|
:percent="status.disk.percent"></a-progress>
|
||||||
<div>
|
<div>
|
||||||
<b>{{ i18n "pages.index.hard"}}:</b> [[ sizeFormat(status.disk.current) ]] / [[ sizeFormat(status.disk.total) ]]
|
<b>{{ i18n "pages.index.hard"}}:</b> [[ SizeFormatter.sizeFormat(status.disk.current) ]] / [[ SizeFormatter.sizeFormat(status.disk.total) ]]
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
@ -99,8 +99,8 @@
|
||||||
<a-col :sm="24" :lg="12">
|
<a-col :sm="24" :lg="12">
|
||||||
<a-card hoverable>
|
<a-card hoverable>
|
||||||
<b>{{ i18n "pages.index.operationHours" }}:</b>
|
<b>{{ i18n "pages.index.operationHours" }}:</b>
|
||||||
<a-tag :color="status.xray.color">Xray: [[ formatSecond(status.appStats.uptime) ]]</a-tag>
|
<a-tag :color="status.xray.color">Xray: [[ TimeFormatter.formatSecond(status.appStats.uptime) ]]</a-tag>
|
||||||
<a-tag color="green">OS: [[ formatSecond(status.uptime) ]]</a-tag>
|
<a-tag color="green">OS: [[ TimeFormatter.formatSecond(status.uptime) ]]</a-tag>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :lg="12">
|
<a-col :sm="24" :lg="12">
|
||||||
|
@ -145,7 +145,7 @@
|
||||||
<a-col :sm="24" :lg="12">
|
<a-col :sm="24" :lg="12">
|
||||||
<a-card hoverable>
|
<a-card hoverable>
|
||||||
<b>{{ i18n "usage"}}:</b>
|
<b>{{ i18n "usage"}}:</b>
|
||||||
<a-tag color="green"> RAM: [[ sizeFormat(status.appStats.mem) ]] </a-tag>
|
<a-tag color="green"> RAM: [[ SizeFormatter.sizeFormat(status.appStats.mem) ]] </a-tag>
|
||||||
<a-tag color="green"> Threads: [[ status.appStats.threads ]] </a-tag>
|
<a-tag color="green"> Threads: [[ status.appStats.threads ]] </a-tag>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
@ -207,7 +207,7 @@
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-tag>
|
<a-tag>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<a-icon type="arrow-up"></a-icon> Up: [[ sizeFormat(status.netIO.up) ]]/s
|
<a-icon type="arrow-up"></a-icon> Up: [[ SizeFormatter.sizeFormat(status.netIO.up) ]]/s
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.upSpeed" }}
|
{{ i18n "pages.index.upSpeed" }}
|
||||||
</template>
|
</template>
|
||||||
|
@ -217,7 +217,7 @@
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-tag>
|
<a-tag>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<a-icon type="arrow-down"></a-icon> Down: [[ sizeFormat(status.netIO.down) ]]/s
|
<a-icon type="arrow-down"></a-icon> Down: [[ SizeFormatter.sizeFormat(status.netIO.down) ]]/s
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.downSpeed" }}
|
{{ i18n "pages.index.downSpeed" }}
|
||||||
</template>
|
</template>
|
||||||
|
@ -236,7 +236,7 @@
|
||||||
<a-icon type="cloud-upload"></a-icon>
|
<a-icon type="cloud-upload"></a-icon>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.totalSent" }}
|
{{ i18n "pages.index.totalSent" }}
|
||||||
</template> Out: [[ sizeFormat(status.netTraffic.sent) ]]
|
</template> Out: [[ SizeFormatter.sizeFormat(status.netTraffic.sent) ]]
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
@ -246,7 +246,7 @@
|
||||||
<a-icon type="cloud-download"></a-icon>
|
<a-icon type="cloud-download"></a-icon>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.totalReceive" }}
|
{{ i18n "pages.index.totalReceive" }}
|
||||||
</template> In: [[ sizeFormat(status.netTraffic.recv) ]]
|
</template> In: [[ SizeFormatter.sizeFormat(status.netTraffic.recv) ]]
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
@ -353,7 +353,7 @@
|
||||||
if (this.total === 0) {
|
if (this.total === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return toFixed(this.current / this.total * 100, 2);
|
return NumberFormatter.toFixed(this.current / this.total * 100, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
get color() {
|
get color() {
|
||||||
|
@ -396,7 +396,7 @@
|
||||||
this.logicalPro = data.logicalPro;
|
this.logicalPro = data.logicalPro;
|
||||||
this.cpuSpeedMhz = data.cpuSpeedMhz;
|
this.cpuSpeedMhz = data.cpuSpeedMhz;
|
||||||
this.disk = new CurTotal(data.disk.current, data.disk.total);
|
this.disk = new CurTotal(data.disk.current, data.disk.total);
|
||||||
this.loads = data.loads.map(load => toFixed(load, 2));
|
this.loads = data.loads.map(load => NumberFormatter.toFixed(load, 2));
|
||||||
this.mem = new CurTotal(data.mem.current, data.mem.total);
|
this.mem = new CurTotal(data.mem.current, data.mem.total);
|
||||||
this.netIO = data.netIO;
|
this.netIO = data.netIO;
|
||||||
this.netTraffic = data.netTraffic;
|
this.netTraffic = data.netTraffic;
|
||||||
|
|
|
@ -832,7 +832,7 @@
|
||||||
if (host == this.oldAllSetting.webDomain) host = null;
|
if (host == this.oldAllSetting.webDomain) host = null;
|
||||||
if (port == this.oldAllSetting.webPort) port = null;
|
if (port == this.oldAllSetting.webPort) port = null;
|
||||||
const isTLS = webCertFile !== "" || webKeyFile !== "";
|
const isTLS = webCertFile !== "" || webKeyFile !== "";
|
||||||
const url = buildURL({ host, port, isTLS, base, path: "panel/settings" });
|
const url = URLBuilder.buildURL({ host, port, isTLS, base, path: "panel/settings" });
|
||||||
window.location.replace(url);
|
window.location.replace(url);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -65,15 +65,15 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>WARP+ Data</td>
|
<td>WARP+ Data</td>
|
||||||
<td>[[ sizeFormat(warpModal.warpConfig.account.premium_data) ]]</td>
|
<td>[[ SizeFormatter.sizeFormat(warpModal.warpConfig.account.premium_data) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="client-table-odd-row">
|
<tr class="client-table-odd-row">
|
||||||
<td>Quota</td>
|
<td>Quota</td>
|
||||||
<td>[[ sizeFormat(warpModal.warpConfig.account.quota) ]]</td>
|
<td>[[ SizeFormatter.sizeFormat(warpModal.warpConfig.account.quota) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account.usage)">
|
<tr v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account.usage)">
|
||||||
<td>Usage</td>
|
<td>Usage</td>
|
||||||
<td>[[ sizeFormat(warpModal.warpConfig.account.usage) ]]</td>
|
<td>[[ SizeFormatter.sizeFormat(warpModal.warpConfig.account.usage) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -1177,10 +1177,10 @@
|
||||||
findOutboundTraffic(o) {
|
findOutboundTraffic(o) {
|
||||||
for (const otraffic of this.outboundsTraffic) {
|
for (const otraffic of this.outboundsTraffic) {
|
||||||
if (otraffic.tag == o.tag) {
|
if (otraffic.tag == o.tag) {
|
||||||
return sizeFormat(otraffic.up) + ' / ' + sizeFormat(otraffic.down);
|
return SizeFormatter.sizeFormat(otraffic.up) + ' / ' + SizeFormatter.sizeFormat(otraffic.down);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sizeFormat(0) + ' / ' + sizeFormat(0);
|
return SizeFormatter.sizeFormat(0) + ' / ' + SizeFormatter.sizeFormat(0);
|
||||||
},
|
},
|
||||||
findOutboundAddress(o) {
|
findOutboundAddress(o) {
|
||||||
serverObj = null;
|
serverObj = null;
|
||||||
|
@ -1929,7 +1929,7 @@
|
||||||
},
|
},
|
||||||
torrentSettings: {
|
torrentSettings: {
|
||||||
get: function () {
|
get: function () {
|
||||||
return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols);
|
return ArrayUtils.doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols);
|
||||||
},
|
},
|
||||||
set: function (newValue) {
|
set: function (newValue) {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
|
@ -1942,7 +1942,7 @@
|
||||||
familyProtectSettings: {
|
familyProtectSettings: {
|
||||||
get: function () {
|
get: function () {
|
||||||
if (!this.templateSettings || !this.templateSettings.dns || !this.templateSettings.dns.servers) return false;
|
if (!this.templateSettings || !this.templateSettings.dns || !this.templateSettings.dns.servers) return false;
|
||||||
return doAllItemsExist(this.settingsData.familyProtectDNS.servers, this.templateSettings.dns.servers);
|
return ArrayUtils.doAllItemsExist(this.settingsData.familyProtectDNS.servers, this.templateSettings.dns.servers);
|
||||||
},
|
},
|
||||||
set: function (newValue) {
|
set: function (newValue) {
|
||||||
newTemplateSettings = this.templateSettings;
|
newTemplateSettings = this.templateSettings;
|
||||||
|
|
Loading…
Reference in a new issue