mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-11-29 02:42:51 +00:00
205 lines
6.4 KiB
Go
205 lines
6.4 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/mhsanaei/3x-ui/v2/database"
|
|
"github.com/mhsanaei/3x-ui/v2/database/model"
|
|
"github.com/mhsanaei/3x-ui/v2/logger"
|
|
)
|
|
|
|
type MultiServerService struct{}
|
|
|
|
func (s *MultiServerService) GetServers() ([]*model.Server, error) {
|
|
db := database.GetDB()
|
|
var servers []*model.Server
|
|
err := db.Find(&servers).Error
|
|
return servers, err
|
|
}
|
|
|
|
func (s *MultiServerService) GetServer(id int) (*model.Server, error) {
|
|
db := database.GetDB()
|
|
var server model.Server
|
|
err := db.First(&server, id).Error
|
|
return &server, err
|
|
}
|
|
|
|
// GetOnlineClients
|
|
func (s *MultiServerService) GetOnlineClients() (map[int][]string, error) {
|
|
db := database.GetDB()
|
|
var servers []*model.Server
|
|
err := db.Find(&servers).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
clients := make(map[int][]string)
|
|
for _, server := range servers {
|
|
var onlineResp struct {
|
|
Success bool `json:"success"`
|
|
Msg string `json:"msg"`
|
|
Obj []string `json:"obj"`
|
|
}
|
|
url := fmt.Sprintf("http://%s:%d%spanel/api/inbounds/onlines", server.Address, server.Port, server.SecretWebPath)
|
|
resp, err := http.Post(url, "application/json", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
if err := json.NewDecoder(resp.Body).Decode(&onlineResp); err != nil {
|
|
return nil, fmt.Errorf("decode online: %w", err)
|
|
}
|
|
if !onlineResp.Success {
|
|
return nil, fmt.Errorf("failed to get online list at %s", server.Address)
|
|
}
|
|
clients[server.Id] = onlineResp.Obj
|
|
}
|
|
return clients, nil
|
|
}
|
|
|
|
func (s *MultiServerService) AddServer(server *model.Server) error {
|
|
db := database.GetDB()
|
|
return db.Create(server).Error
|
|
}
|
|
|
|
func (s *MultiServerService) UpdateServer(server *model.Server) error {
|
|
db := database.GetDB()
|
|
return db.Save(server).Error
|
|
}
|
|
|
|
func (s *MultiServerService) DeleteServer(id int) error {
|
|
db := database.GetDB()
|
|
return db.Delete(&model.Server{}, id).Error
|
|
}
|
|
|
|
// SyncServer synchronizes the inbounds list between the given server and the local inbounds list.
|
|
// It gets the inbounds list from the server, and then syncs it with the local inbounds list.
|
|
// If an inbound exists on the server but not locally, it adds the inbound.
|
|
// If an inbound exists locally but not on the server, it removes the inbound.
|
|
// If an inbound exists on both the server and locally, it updates the inbound if they are different.
|
|
func (s *MultiServerService) SyncServer(id int) error {
|
|
inboundService := &InboundService{}
|
|
inboundsSource, err := inboundService.GetAllInbounds()
|
|
if err != nil {
|
|
logger.Error("failed to get all inbounds", "err", err)
|
|
return err
|
|
}
|
|
|
|
db := database.GetDB()
|
|
var server model.Server
|
|
if err = db.First(&server, id).Error; err != nil {
|
|
logger.Error("failed to get server", "err", err)
|
|
return err
|
|
}
|
|
|
|
//get inbounds from server throw api
|
|
listURL := fmt.Sprintf("http://%s:%d%spanel/api/inbounds/list", server.Address, server.Port, server.SecretWebPath)
|
|
req, _ := http.NewRequest("GET", listURL, nil)
|
|
req.Header.Set("X-API-KEY", server.APIKey)
|
|
httpResp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
logger.Error("failed to get inbounds from server", "err", err)
|
|
return err
|
|
}
|
|
defer httpResp.Body.Close()
|
|
|
|
var resp struct {
|
|
Success bool `json:"success"`
|
|
Msg string `json:"msg"`
|
|
Obj []model.Inbound `json:"obj"`
|
|
}
|
|
if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil {
|
|
logger.Error("failed to decode inbounds response", "err", err)
|
|
return err
|
|
}
|
|
|
|
type InboundPayload struct {
|
|
Up int64 `json:"up"`
|
|
Down int64 `json:"down"`
|
|
Total int64 `json:"total"`
|
|
Remark string `json:"remark"`
|
|
Enable bool `json:"enable"`
|
|
ExpiryTime int64 `json:"expiryTime"`
|
|
Listen string `json:"listen"`
|
|
Port int `json:"port"`
|
|
Protocol model.Protocol `json:"protocol"`
|
|
Settings string `json:"settings"`
|
|
StreamSettings string `json:"streamSettings"`
|
|
Sniffing string `json:"sniffing"`
|
|
}
|
|
|
|
//sync inbounds
|
|
for _, src := range inboundsSource {
|
|
logger.Debugf("syncing inbound %d", src.Id)
|
|
found := false
|
|
for _, remote := range resp.Obj {
|
|
if remote.Tag == src.Tag {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
payload := InboundPayload{
|
|
Up: src.Up, Down: src.Down, Total: src.Total,
|
|
Remark: src.Remark, Enable: src.Enable,
|
|
ExpiryTime: src.ExpiryTime, Listen: src.Listen,
|
|
Port: src.Port, Protocol: src.Protocol,
|
|
Settings: src.Settings, StreamSettings: src.StreamSettings, Sniffing: src.Sniffing,
|
|
}
|
|
|
|
data, _ := json.Marshal(payload)
|
|
|
|
if found {
|
|
//update inbound trow api
|
|
updateURL := fmt.Sprintf("http://%s:%d%spanel/api/inbounds/update/%d", server.Address, server.Port, server.SecretWebPath, src.Id)
|
|
req, _ := http.NewRequest("POST", updateURL, bytes.NewBuffer(data))
|
|
req.Header.Set("X-API-KEY", server.APIKey)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
logger.Error("failed to update inbounds at server", "err", err)
|
|
return err
|
|
}
|
|
var updateResp struct {
|
|
Success bool `json:"success"`
|
|
Msg string `json:"msg"`
|
|
}
|
|
if err := json.NewDecoder(httpResp.Body).Decode(&updateResp); err != nil {
|
|
logger.Error("failed to decode update inbounds response", "err", err)
|
|
return fmt.Errorf("decode update inbounds: %w", err)
|
|
}
|
|
resp.Body.Close()
|
|
if !updateResp.Success {
|
|
return fmt.Errorf("failed to update inbounds at %s %s", server.Name, server.Address)
|
|
}
|
|
} else {
|
|
// add inbound trow api
|
|
addURL := fmt.Sprintf("http://%s:%d%spanel/api/inbounds/add", server.Address, server.Port, server.SecretWebPath)
|
|
req, _ := http.NewRequest("POST", addURL, bytes.NewBuffer(data))
|
|
req.Header.Set("X-API-KEY", server.APIKey)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
logger.Error("failed to add inbounds at server", "err", err)
|
|
return err
|
|
}
|
|
|
|
var addResp struct {
|
|
Success bool `json:"success"`
|
|
Msg string `json:"msg"`
|
|
}
|
|
if err := json.NewDecoder(httpResp.Body).Decode(&addResp); err != nil {
|
|
logger.Error("failed to decode add inbounds response", "err", err)
|
|
return fmt.Errorf("decode add inbounds: %w", err)
|
|
}
|
|
resp.Body.Close()
|
|
if !addResp.Success {
|
|
return fmt.Errorf("failed to add inbounds at %s %s", server.Name, server.Address)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|