mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-03-20 17:45:49 +00:00
alireza update pack
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
This commit is contained in:
@@ -394,11 +394,16 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
||||
if len(traffics) == 0 {
|
||||
return nil
|
||||
}
|
||||
db := database.GetDB()
|
||||
dbInbound := db.Model(model.Inbound{})
|
||||
|
||||
traffics, err = s.adjustTraffics(traffics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db := database.GetDB()
|
||||
db = db.Model(xray.ClientTraffic{})
|
||||
tx := db.Begin()
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
@@ -406,7 +411,20 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
||||
tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
err = tx.Save(traffics).Error
|
||||
if err != nil {
|
||||
logger.Warning("AddClientTraffic update data ", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InboundService) adjustTraffics(traffics []*xray.ClientTraffic) (full_traffics []*xray.ClientTraffic, err error) {
|
||||
db := database.GetDB()
|
||||
dbInbound := db.Model(model.Inbound{})
|
||||
txInbound := dbInbound.Begin()
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
txInbound.Rollback()
|
||||
@@ -415,22 +433,23 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
||||
}
|
||||
}()
|
||||
|
||||
for _, traffic := range traffics {
|
||||
for traffic_index, traffic := range traffics {
|
||||
inbound := &model.Inbound{}
|
||||
client := &xray.ClientTraffic{}
|
||||
err := tx.Where("email = ?", traffic.Email).First(client).Error
|
||||
client_traffic := &xray.ClientTraffic{}
|
||||
err := db.Model(xray.ClientTraffic{}).Where("email = ?", traffic.Email).First(client_traffic).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
logger.Warning(err, traffic.Email)
|
||||
}
|
||||
continue
|
||||
}
|
||||
client_traffic.Up += traffic.Up
|
||||
client_traffic.Down += traffic.Down
|
||||
|
||||
err = txInbound.Where("id=?", client.InboundId).First(inbound).Error
|
||||
err = txInbound.Where("id=?", client_traffic.InboundId).First(inbound).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
logger.Warning(err, traffic.Email)
|
||||
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -438,29 +457,35 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
||||
settings := map[string][]model.Client{}
|
||||
json.Unmarshal([]byte(inbound.Settings), &settings)
|
||||
clients := settings["clients"]
|
||||
for _, client := range clients {
|
||||
needUpdate := false
|
||||
for client_index, client := range clients {
|
||||
if traffic.Email == client.Email {
|
||||
traffic.ExpiryTime = client.ExpiryTime
|
||||
traffic.Total = client.TotalGB
|
||||
if client.ExpiryTime < 0 {
|
||||
clients[client_index].ExpiryTime = (time.Now().Unix() * 1000) - client.ExpiryTime
|
||||
needUpdate = true
|
||||
}
|
||||
client_traffic.ExpiryTime = client.ExpiryTime
|
||||
client_traffic.Total = client.TotalGB
|
||||
break
|
||||
}
|
||||
}
|
||||
if tx.Where("inbound_id = ? and email = ?", inbound.Id, traffic.Email).
|
||||
UpdateColumns(map[string]interface{}{
|
||||
"enable": true,
|
||||
"expiry_time": traffic.ExpiryTime,
|
||||
"total": traffic.Total,
|
||||
"up": gorm.Expr("up + ?", traffic.Up),
|
||||
"down": gorm.Expr("down + ?", traffic.Down)}).RowsAffected == 0 {
|
||||
err = tx.Create(traffic).Error
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Warning("AddClientTraffic update data ", err)
|
||||
continue
|
||||
|
||||
if needUpdate {
|
||||
settings["clients"] = clients
|
||||
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = txInbound.Where("id=?", inbound.Id).Update("settings", string(modifiedSettings)).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
traffics[traffic_index] = client_traffic
|
||||
}
|
||||
return
|
||||
return traffics, nil
|
||||
}
|
||||
|
||||
func (s *InboundService) DisableInvalidInbounds() (int64, error) {
|
||||
@@ -545,11 +570,58 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *InboundService) GetClientTrafficTgBot(tguname string) (traffic []*xray.ClientTraffic, err error) {
|
||||
db := database.GetDB()
|
||||
var traffics []*xray.ClientTraffic
|
||||
|
||||
err = db.Model(xray.ClientTraffic{}).Where("email like ?", "%@"+tguname).Find(&traffics).Error
|
||||
func (s *InboundService) ResetAllClientTraffics(id int) error {
|
||||
db := database.GetDB()
|
||||
|
||||
result := db.Model(xray.ClientTraffic{}).
|
||||
Where("inbound_id = ?", id).
|
||||
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
|
||||
|
||||
err := result.Error
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InboundService) ResetAllTraffics() error {
|
||||
db := database.GetDB()
|
||||
|
||||
result := db.Model(model.Inbound{}).
|
||||
Where("user_id > ?", 0).
|
||||
Updates(map[string]interface{}{"up": 0, "down": 0})
|
||||
|
||||
err := result.Error
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTraffic, error) {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": "%s"%%`, tguname)).Find(&inbounds).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
var emails []string
|
||||
for _, inbound := range inbounds {
|
||||
clients, err := s.getClients(inbound)
|
||||
if err != nil {
|
||||
logger.Error("Unable to get clients from inbound")
|
||||
}
|
||||
for _, client := range clients {
|
||||
if client.TgID == tguname {
|
||||
emails = append(emails, client.Email)
|
||||
}
|
||||
}
|
||||
}
|
||||
var traffics []*xray.ClientTraffic
|
||||
err = db.Model(xray.ClientTraffic{}).Where("email IN ?", emails).Find(&traffics).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
logger.Warning(err)
|
||||
@@ -643,4 +715,4 @@ func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error)
|
||||
return nil, err
|
||||
}
|
||||
return inbounds, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,11 +327,11 @@ func (s *ServerService) UpdateXray(version string) error {
|
||||
|
||||
}
|
||||
|
||||
func (s *ServerService) GetLogs() ([]string, error) {
|
||||
func (s *ServerService) GetLogs(count string) ([]string, error) {
|
||||
// Define the journalctl command and its arguments
|
||||
var cmdArgs []string
|
||||
if runtime.GOOS == "linux" {
|
||||
cmdArgs = []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", "100"}
|
||||
cmdArgs = []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", count}
|
||||
} else {
|
||||
return []string{"Unsupported operating system"}, nil
|
||||
}
|
||||
|
||||
@@ -28,14 +28,14 @@ var defaultValueMap = map[string]string{
|
||||
"webKeyFile": "",
|
||||
"secret": random.Seq(32),
|
||||
"webBasePath": "/",
|
||||
"expireDiff": "0",
|
||||
"trafficDiff": "0",
|
||||
"timeLocation": "Asia/Tehran",
|
||||
"tgBotEnable": "false",
|
||||
"tgBotToken": "",
|
||||
"tgBotChatId": "",
|
||||
"tgRunTime": "@daily",
|
||||
"tgBotBackup": "false",
|
||||
"tgExpireDiff": "0",
|
||||
"tgTrafficDiff": "0",
|
||||
"tgCpu": "0",
|
||||
}
|
||||
|
||||
@@ -238,22 +238,6 @@ func (s *SettingService) SetTgBotBackup(value bool) error {
|
||||
return s.setBool("tgBotBackup", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetTgExpireDiff() (int, error) {
|
||||
return s.getInt("tgExpireDiff")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetTgExpireDiff(value int) error {
|
||||
return s.setInt("tgExpireDiff", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetTgTrafficDiff() (int, error) {
|
||||
return s.getInt("tgTrafficDiff")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetTgTrafficDiff(value int) error {
|
||||
return s.setInt("tgTrafficDiff", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetTgCpu() (int, error) {
|
||||
return s.getInt("tgCpu")
|
||||
}
|
||||
@@ -278,6 +262,22 @@ func (s *SettingService) GetKeyFile() (string, error) {
|
||||
return s.getString("webKeyFile")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetExpireDiff() (int, error) {
|
||||
return s.getInt("expireDiff")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetExpireDiff(value int) error {
|
||||
return s.setInt("expireDiff", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetTrafficDiff() (int, error) {
|
||||
return s.getInt("trafficDiff")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetgetTrafficDiff(value int) error {
|
||||
return s.setInt("trafficDiff", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSecret() ([]byte, error) {
|
||||
secret, err := s.getString("secret")
|
||||
if secret == defaultValueMap["secret"] {
|
||||
|
||||
505
web/service/sub.go
Normal file
505
web/service/sub.go
Normal file
@@ -0,0 +1,505 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"x-ui/database"
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SubService struct {
|
||||
address string
|
||||
inboundService InboundService
|
||||
}
|
||||
|
||||
func (s *SubService) GetSubs(subId string, host string) ([]string, error) {
|
||||
s.address = host
|
||||
var result []string
|
||||
inbounds, err := s.getInboundsBySubId(subId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, inbound := range inbounds {
|
||||
clients, err := s.inboundService.getClients(inbound)
|
||||
if err != nil {
|
||||
logger.Error("SubService - GetSub: Unable to get clients from inbound")
|
||||
}
|
||||
if clients == nil {
|
||||
continue
|
||||
}
|
||||
for _, client := range clients {
|
||||
if client.SubID == subId {
|
||||
link := s.getLink(inbound, client.Email)
|
||||
result = append(result, link)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId)).Find(&inbounds).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
return inbounds, nil
|
||||
}
|
||||
|
||||
func (s *SubService) getLink(inbound *model.Inbound, email string) string {
|
||||
switch inbound.Protocol {
|
||||
case "vmess":
|
||||
return s.genVmessLink(inbound, email)
|
||||
case "vless":
|
||||
return s.genVlessLink(inbound, email)
|
||||
case "trojan":
|
||||
return s.genTrojanLink(inbound, email)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
||||
address := s.address
|
||||
if inbound.Protocol != model.VMess {
|
||||
return ""
|
||||
}
|
||||
var stream map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||
network, _ := stream["network"].(string)
|
||||
typeStr := "none"
|
||||
host := ""
|
||||
path := ""
|
||||
sni := ""
|
||||
fp := ""
|
||||
var alpn []string
|
||||
allowInsecure := false
|
||||
switch network {
|
||||
case "tcp":
|
||||
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
||||
header, _ := tcp["header"].(map[string]interface{})
|
||||
typeStr, _ = header["type"].(string)
|
||||
if typeStr == "http" {
|
||||
request := header["request"].(map[string]interface{})
|
||||
requestPath, _ := request["path"].([]interface{})
|
||||
path = requestPath[0].(string)
|
||||
headers, _ := request["headers"].(map[string]interface{})
|
||||
host = searchHost(headers)
|
||||
}
|
||||
case "kcp":
|
||||
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
||||
header, _ := kcp["header"].(map[string]interface{})
|
||||
typeStr, _ = header["type"].(string)
|
||||
path, _ = kcp["seed"].(string)
|
||||
case "ws":
|
||||
ws, _ := stream["wsSettings"].(map[string]interface{})
|
||||
path = ws["path"].(string)
|
||||
headers, _ := ws["headers"].(map[string]interface{})
|
||||
host = searchHost(headers)
|
||||
case "http":
|
||||
network = "h2"
|
||||
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||
path, _ = http["path"].(string)
|
||||
host = searchHost(http)
|
||||
case "quic":
|
||||
quic, _ := stream["quicSettings"].(map[string]interface{})
|
||||
header := quic["header"].(map[string]interface{})
|
||||
typeStr, _ = header["type"].(string)
|
||||
host, _ = quic["security"].(string)
|
||||
path, _ = quic["key"].(string)
|
||||
case "grpc":
|
||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||
path = grpc["serviceName"].(string)
|
||||
}
|
||||
|
||||
security, _ := stream["security"].(string)
|
||||
if security == "tls" {
|
||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
||||
alpns, _ := tlsSetting["alpn"].([]interface{})
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
||||
if tlsSetting != nil {
|
||||
if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
|
||||
sni, _ = sniValue.(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
||||
fp, _ = fpValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
||||
allowInsecure, _ = insecure.(bool)
|
||||
}
|
||||
}
|
||||
serverName, _ := tlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
clients, _ := s.inboundService.getClients(inbound)
|
||||
clientIndex := -1
|
||||
for i, client := range clients {
|
||||
if client.Email == email {
|
||||
clientIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
obj := map[string]interface{}{
|
||||
"v": "2",
|
||||
"ps": email,
|
||||
"add": address,
|
||||
"port": inbound.Port,
|
||||
"id": clients[clientIndex].ID,
|
||||
"aid": clients[clientIndex].AlterIds,
|
||||
"net": network,
|
||||
"type": typeStr,
|
||||
"host": host,
|
||||
"path": path,
|
||||
"tls": security,
|
||||
"sni": sni,
|
||||
"fp": fp,
|
||||
"alpn": strings.Join(alpn, ","),
|
||||
"allowInsecure": allowInsecure,
|
||||
}
|
||||
jsonStr, _ := json.MarshalIndent(obj, "", " ")
|
||||
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
|
||||
}
|
||||
|
||||
func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
address := s.address
|
||||
if inbound.Protocol != model.VLESS {
|
||||
return ""
|
||||
}
|
||||
var stream map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||
clients, _ := s.inboundService.getClients(inbound)
|
||||
clientIndex := -1
|
||||
for i, client := range clients {
|
||||
if client.Email == email {
|
||||
clientIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
uuid := clients[clientIndex].ID
|
||||
port := inbound.Port
|
||||
streamNetwork := stream["network"].(string)
|
||||
params := make(map[string]string)
|
||||
params["type"] = streamNetwork
|
||||
|
||||
switch streamNetwork {
|
||||
case "tcp":
|
||||
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
||||
header, _ := tcp["header"].(map[string]interface{})
|
||||
typeStr, _ := header["type"].(string)
|
||||
if typeStr == "http" {
|
||||
request := header["request"].(map[string]interface{})
|
||||
requestPath, _ := request["path"].([]interface{})
|
||||
params["path"] = requestPath[0].(string)
|
||||
headers, _ := request["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
params["headerType"] = "http"
|
||||
}
|
||||
case "kcp":
|
||||
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
||||
header, _ := kcp["header"].(map[string]interface{})
|
||||
params["headerType"] = header["type"].(string)
|
||||
params["seed"] = kcp["seed"].(string)
|
||||
case "ws":
|
||||
ws, _ := stream["wsSettings"].(map[string]interface{})
|
||||
params["path"] = ws["path"].(string)
|
||||
headers, _ := ws["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
case "http":
|
||||
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||
params["path"] = http["path"].(string)
|
||||
params["host"] = searchHost(http)
|
||||
case "quic":
|
||||
quic, _ := stream["quicSettings"].(map[string]interface{})
|
||||
params["quicSecurity"] = quic["security"].(string)
|
||||
params["key"] = quic["key"].(string)
|
||||
header := quic["header"].(map[string]interface{})
|
||||
params["headerType"] = header["type"].(string)
|
||||
case "grpc":
|
||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||
params["serviceName"] = grpc["serviceName"].(string)
|
||||
}
|
||||
|
||||
security, _ := stream["security"].(string)
|
||||
if security == "tls" {
|
||||
params["security"] = "tls"
|
||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
||||
alpns, _ := tlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
||||
if tlsSetting != nil {
|
||||
if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
|
||||
serverName, _ := tlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
if security == "xtls" {
|
||||
params["security"] = "xtls"
|
||||
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
|
||||
alpns, _ := xtlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
|
||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||
if xtlsSetting != nil {
|
||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
|
||||
serverName, _ := xtlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("vless://%s@%s:%d", uuid, address, port)
|
||||
url, _ := url.Parse(link)
|
||||
q := url.Query()
|
||||
|
||||
for k, v := range params {
|
||||
q.Add(k, v)
|
||||
}
|
||||
|
||||
// Set the new query values on the URL
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
url.Fragment = email
|
||||
return url.String()
|
||||
}
|
||||
|
||||
func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string {
|
||||
address := s.address
|
||||
if inbound.Protocol != model.Trojan {
|
||||
return ""
|
||||
}
|
||||
var stream map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||
clients, _ := s.inboundService.getClients(inbound)
|
||||
clientIndex := -1
|
||||
for i, client := range clients {
|
||||
if client.Email == email {
|
||||
clientIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
password := clients[clientIndex].Password
|
||||
port := inbound.Port
|
||||
streamNetwork := stream["network"].(string)
|
||||
params := make(map[string]string)
|
||||
params["type"] = streamNetwork
|
||||
|
||||
switch streamNetwork {
|
||||
case "tcp":
|
||||
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
||||
header, _ := tcp["header"].(map[string]interface{})
|
||||
typeStr, _ := header["type"].(string)
|
||||
if typeStr == "http" {
|
||||
request := header["request"].(map[string]interface{})
|
||||
requestPath, _ := request["path"].([]interface{})
|
||||
params["path"] = requestPath[0].(string)
|
||||
headers, _ := request["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
params["headerType"] = "http"
|
||||
}
|
||||
case "kcp":
|
||||
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
||||
header, _ := kcp["header"].(map[string]interface{})
|
||||
params["headerType"] = header["type"].(string)
|
||||
params["seed"] = kcp["seed"].(string)
|
||||
case "ws":
|
||||
ws, _ := stream["wsSettings"].(map[string]interface{})
|
||||
params["path"] = ws["path"].(string)
|
||||
headers, _ := ws["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
case "http":
|
||||
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||
params["path"] = http["path"].(string)
|
||||
params["host"] = searchHost(http)
|
||||
case "quic":
|
||||
quic, _ := stream["quicSettings"].(map[string]interface{})
|
||||
params["quicSecurity"] = quic["security"].(string)
|
||||
params["key"] = quic["key"].(string)
|
||||
header := quic["header"].(map[string]interface{})
|
||||
params["headerType"] = header["type"].(string)
|
||||
case "grpc":
|
||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||
params["serviceName"] = grpc["serviceName"].(string)
|
||||
}
|
||||
|
||||
security, _ := stream["security"].(string)
|
||||
if security == "tls" {
|
||||
params["security"] = "tls"
|
||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
||||
alpns, _ := tlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
||||
if tlsSetting != nil {
|
||||
if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serverName, _ := tlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
if security == "xtls" {
|
||||
params["security"] = "xtls"
|
||||
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
|
||||
alpns, _ := xtlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
|
||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||
if xtlsSetting != nil {
|
||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
|
||||
serverName, _ := xtlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("trojan://%s@%s:%d", password, address, port)
|
||||
|
||||
url, _ := url.Parse(link)
|
||||
q := url.Query()
|
||||
|
||||
for k, v := range params {
|
||||
q.Add(k, v)
|
||||
}
|
||||
|
||||
// Set the new query values on the URL
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
url.Fragment = email
|
||||
return url.String()
|
||||
}
|
||||
|
||||
func searchKey(data interface{}, key string) (interface{}, bool) {
|
||||
switch val := data.(type) {
|
||||
case map[string]interface{}:
|
||||
for k, v := range val {
|
||||
if k == key {
|
||||
return v, true
|
||||
}
|
||||
if result, ok := searchKey(v, key); ok {
|
||||
return result, true
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
for _, v := range val {
|
||||
if result, ok := searchKey(v, key); ok {
|
||||
return result, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func searchHost(headers interface{}) string {
|
||||
data, _ := headers.(map[string]interface{})
|
||||
for k, v := range data {
|
||||
if strings.EqualFold(k, "host") {
|
||||
switch v.(type) {
|
||||
case []interface{}:
|
||||
hosts, _ := v.([]interface{})
|
||||
return hosts[0].(string)
|
||||
case interface{}:
|
||||
return v.(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -160,14 +160,14 @@ func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bo
|
||||
t.SendMsgToTgbot(callbackQuery.From.ID, t.getServerUsage())
|
||||
case "inbounds":
|
||||
t.SendMsgToTgbot(callbackQuery.From.ID, t.getInboundUsages())
|
||||
case "exhausted_soon":
|
||||
case "deplete_soon":
|
||||
t.SendMsgToTgbot(callbackQuery.From.ID, t.getExhausted())
|
||||
case "get_backup":
|
||||
t.sendBackup(callbackQuery.From.ID)
|
||||
case "client_traffic":
|
||||
t.getClientUsage(callbackQuery.From.ID, callbackQuery.From.UserName)
|
||||
case "client_commands":
|
||||
t.SendMsgToTgbot(callbackQuery.From.ID, "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UID|Passowrd]</code>\r\n \r\nUse UID for vmess and vless and Password for Trojan.")
|
||||
t.SendMsgToTgbot(callbackQuery.From.ID, "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UID|Passowrd]</code>\r\n \r\nUse UID for vmess/vless and Password for Trojan.")
|
||||
case "commands":
|
||||
t.SendMsgToTgbot(callbackQuery.From.ID, "Search for a client email:\r\n<code>/usage email</code>\r\n \r\nSearch for inbounds (with client stats):\r\n<code>/inbound [remark]</code>")
|
||||
}
|
||||
@@ -190,7 +190,7 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("Get Inbounds", "inbounds"),
|
||||
tgbotapi.NewInlineKeyboardButtonData("Exhausted soon", "exhausted_soon"),
|
||||
tgbotapi.NewInlineKeyboardButtonData("Deplete soon", "deplete_soon"),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("Commands", "commands"),
|
||||
@@ -363,6 +363,11 @@ func (t *Tgbot) getInboundUsages() string {
|
||||
}
|
||||
|
||||
func (t *Tgbot) getClientUsage(chatId int64, tgUserName string) {
|
||||
if len(tgUserName) == 0 {
|
||||
msg := "Your configuration is not found!\nYou should configure your telegram username and ask Admin to add it to your configuration."
|
||||
t.SendMsgToTgbot(chatId, msg)
|
||||
return
|
||||
}
|
||||
traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserName)
|
||||
if err != nil {
|
||||
logger.Warning(err)
|
||||
@@ -373,11 +378,14 @@ func (t *Tgbot) getClientUsage(chatId int64, tgUserName string) {
|
||||
if len(traffics) == 0 {
|
||||
msg := "Your configuration is not found!\nPlease ask your Admin to use your telegram username in your configuration(s).\n\nYour username: <b>@" + tgUserName + "</b>"
|
||||
t.SendMsgToTgbot(chatId, msg)
|
||||
return
|
||||
}
|
||||
for _, traffic := range traffics {
|
||||
expiryTime := ""
|
||||
if traffic.ExpiryTime == 0 {
|
||||
expiryTime = "♾Unlimited"
|
||||
} else if traffic.ExpiryTime < 0 {
|
||||
expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-86400000)
|
||||
} else {
|
||||
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
@@ -412,6 +420,8 @@ func (t *Tgbot) searchClient(chatId int64, email string) {
|
||||
expiryTime := ""
|
||||
if traffic.ExpiryTime == 0 {
|
||||
expiryTime = "♾Unlimited"
|
||||
} else if traffic.ExpiryTime < 0 {
|
||||
expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-86400000)
|
||||
} else {
|
||||
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
@@ -450,6 +460,8 @@ func (t *Tgbot) searchInbound(chatId int64, remark string) {
|
||||
expiryTime := ""
|
||||
if traffic.ExpiryTime == 0 {
|
||||
expiryTime = "♾Unlimited"
|
||||
} else if traffic.ExpiryTime < 0 {
|
||||
expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-86400000)
|
||||
} else {
|
||||
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
@@ -483,6 +495,8 @@ func (t *Tgbot) searchForClient(chatId int64, query string) {
|
||||
expiryTime := ""
|
||||
if traffic.ExpiryTime == 0 {
|
||||
expiryTime = "♾Unlimited"
|
||||
} else if traffic.ExpiryTime < 0 {
|
||||
expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-86400000)
|
||||
} else {
|
||||
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
@@ -507,13 +521,13 @@ func (t *Tgbot) getExhausted() string {
|
||||
var disabledInbounds []model.Inbound
|
||||
var disabledClients []xray.ClientTraffic
|
||||
output := ""
|
||||
TrafficThreshold, err := t.settingService.GetTgTrafficDiff()
|
||||
TrafficThreshold, err := t.settingService.GetTrafficDiff()
|
||||
if err == nil && TrafficThreshold > 0 {
|
||||
trDiff = int64(TrafficThreshold) * 1073741824
|
||||
}
|
||||
ExpireThreshold, err := t.settingService.GetTgExpireDiff()
|
||||
ExpireThreshold, err := t.settingService.GetExpireDiff()
|
||||
if err == nil && ExpireThreshold > 0 {
|
||||
exDiff = int64(ExpireThreshold) * 84600000
|
||||
exDiff = int64(ExpireThreshold) * 86400000
|
||||
}
|
||||
inbounds, err := t.inboundService.GetAllInbounds()
|
||||
if err != nil {
|
||||
@@ -541,7 +555,7 @@ func (t *Tgbot) getExhausted() string {
|
||||
disabledInbounds = append(disabledInbounds, *inbound)
|
||||
}
|
||||
}
|
||||
output += fmt.Sprintf("Exhausted Inbounds count:\r\n🛑 Disabled: %d\r\n🔜 Exhaust soon: %d\r\n \r\n", len(disabledInbounds), len(exhaustedInbounds))
|
||||
output += fmt.Sprintf("Exhausted Inbounds count:\r\n🛑 Disabled: %d\r\n🔜 Deplete soon: %d\r\n \r\n", len(disabledInbounds), len(exhaustedInbounds))
|
||||
if len(exhaustedInbounds) > 0 {
|
||||
output += "Exhausted Inbounds:\r\n"
|
||||
for _, inbound := range exhaustedInbounds {
|
||||
@@ -553,13 +567,15 @@ func (t *Tgbot) getExhausted() string {
|
||||
}
|
||||
}
|
||||
}
|
||||
output += fmt.Sprintf("Exhausted Clients count:\r\n🛑 Disabled: %d\r\n🔜 Exhaust soon: %d\r\n \r\n", len(disabledClients), len(exhaustedClients))
|
||||
output += fmt.Sprintf("Exhausted Clients count:\r\n🛑 Exhausted: %d\r\n🔜 Deplete soon: %d\r\n \r\n", len(disabledClients), len(exhaustedClients))
|
||||
if len(exhaustedClients) > 0 {
|
||||
output += "Exhausted Clients:\r\n"
|
||||
for _, traffic := range exhaustedClients {
|
||||
expiryTime := ""
|
||||
if traffic.ExpiryTime == 0 {
|
||||
expiryTime = "♾Unlimited"
|
||||
} else if traffic.ExpiryTime < 0 {
|
||||
expiryTime += fmt.Sprintf("%d days", traffic.ExpiryTime/-86400000)
|
||||
} else {
|
||||
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
@@ -84,15 +84,16 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||
clients, ok := settings["clients"].([]interface{})
|
||||
if ok {
|
||||
// check users active or not
|
||||
|
||||
clientStats := inbound.ClientStats
|
||||
for _, clientTraffic := range clientStats {
|
||||
|
||||
indexDecrease := 0
|
||||
for index, client := range clients {
|
||||
c := client.(map[string]interface{})
|
||||
if c["email"] == clientTraffic.Email {
|
||||
if !clientTraffic.Enable {
|
||||
clients = RemoveIndex(clients, index)
|
||||
clients = RemoveIndex(clients, index-indexDecrease)
|
||||
indexDecrease++
|
||||
logger.Info("Remove Inbound User", c["email"], "due the expire or traffic limit")
|
||||
|
||||
}
|
||||
@@ -101,7 +102,27 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||
}
|
||||
|
||||
}
|
||||
settings["clients"] = clients
|
||||
|
||||
// clear client config for additional parameters
|
||||
var final_clients []interface{}
|
||||
for _, client := range clients {
|
||||
|
||||
c := client.(map[string]interface{})
|
||||
|
||||
if c["enable"] != nil {
|
||||
if enable, ok := c["enable"].(bool); ok && !enable {
|
||||
continue
|
||||
}
|
||||
}
|
||||
for key := range c {
|
||||
if key != "email" && key != "id" && key != "password" && key != "flow" && key != "alterId" {
|
||||
delete(c, key)
|
||||
}
|
||||
}
|
||||
final_clients = append(final_clients, interface{}(c))
|
||||
}
|
||||
|
||||
settings["clients"] = final_clients
|
||||
modifiedSettings, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user