mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-03-19 17:15:49 +00:00
docs: add comments for all functions
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
// Package service provides business logic services for the 3x-ui web panel,
|
||||
// including inbound/outbound management, user administration, settings, and Xray integration.
|
||||
package service
|
||||
|
||||
import (
|
||||
@@ -17,10 +19,15 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// InboundService provides business logic for managing Xray inbound configurations.
|
||||
// It handles CRUD operations for inbounds, client management, traffic monitoring,
|
||||
// and integration with the Xray API for real-time updates.
|
||||
type InboundService struct {
|
||||
xrayApi xray.XrayAPI
|
||||
}
|
||||
|
||||
// GetInbounds retrieves all inbounds for a specific user.
|
||||
// Returns a slice of inbound models with their associated client statistics.
|
||||
func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
@@ -31,6 +38,8 @@ func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
|
||||
return inbounds, nil
|
||||
}
|
||||
|
||||
// GetAllInbounds retrieves all inbounds from the database.
|
||||
// Returns a slice of all inbound models with their associated client statistics.
|
||||
func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
@@ -163,6 +172,10 @@ func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbound) (stri
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// AddInbound creates a new inbound configuration.
|
||||
// It validates port uniqueness, client email uniqueness, and required fields,
|
||||
// then saves the inbound to the database and optionally adds it to the running Xray instance.
|
||||
// Returns the created inbound, whether Xray needs restart, and any error.
|
||||
func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
|
||||
exist, err := s.checkPortExist(inbound.Listen, inbound.Port, 0)
|
||||
if err != nil {
|
||||
@@ -269,6 +282,9 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, boo
|
||||
return inbound, needRestart, err
|
||||
}
|
||||
|
||||
// DelInbound deletes an inbound configuration by ID.
|
||||
// It removes the inbound from the database and the running Xray instance if active.
|
||||
// Returns whether Xray needs restart and any error.
|
||||
func (s *InboundService) DelInbound(id int) (bool, error) {
|
||||
db := database.GetDB()
|
||||
|
||||
@@ -322,6 +338,9 @@ func (s *InboundService) GetInbound(id int) (*model.Inbound, error) {
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
// UpdateInbound modifies an existing inbound configuration.
|
||||
// It validates changes, updates the database, and syncs with the running Xray instance.
|
||||
// Returns the updated inbound, whether Xray needs restart, and any error.
|
||||
func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
|
||||
exist, err := s.checkPortExist(inbound.Listen, inbound.Port, inbound.Id)
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// OutboundService provides business logic for managing Xray outbound configurations.
|
||||
// It handles outbound traffic monitoring and statistics.
|
||||
type OutboundService struct{}
|
||||
|
||||
func (s *OutboundService) AddTraffic(traffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (error, bool) {
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/mhsanaei/3x-ui/v2/logger"
|
||||
)
|
||||
|
||||
// PanelService provides business logic for panel management operations.
|
||||
// It handles panel restart, updates, and system-level panel controls.
|
||||
type PanelService struct{}
|
||||
|
||||
func (s *PanelService) RestartPanel(delay time.Duration) error {
|
||||
|
||||
@@ -35,14 +35,18 @@ import (
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
)
|
||||
|
||||
// ProcessState represents the current state of a system process.
|
||||
type ProcessState string
|
||||
|
||||
// Process state constants
|
||||
const (
|
||||
Running ProcessState = "running"
|
||||
Stop ProcessState = "stop"
|
||||
Error ProcessState = "error"
|
||||
Running ProcessState = "running" // Process is running normally
|
||||
Stop ProcessState = "stop" // Process is stopped
|
||||
Error ProcessState = "error" // Process is in error state
|
||||
)
|
||||
|
||||
// Status represents comprehensive system and application status information.
|
||||
// It includes CPU, memory, disk, network statistics, and Xray process status.
|
||||
type Status struct {
|
||||
T time.Time `json:"-"`
|
||||
Cpu float64 `json:"cpu"`
|
||||
@@ -89,10 +93,13 @@ type Status struct {
|
||||
} `json:"appStats"`
|
||||
}
|
||||
|
||||
// Release represents information about a software release from GitHub.
|
||||
type Release struct {
|
||||
TagName string `json:"tag_name"`
|
||||
TagName string `json:"tag_name"` // The tag name of the release
|
||||
}
|
||||
|
||||
// ServerService provides business logic for server monitoring and management.
|
||||
// It handles system status collection, IP detection, and application statistics.
|
||||
type ServerService struct {
|
||||
xrayService XrayService
|
||||
inboundService InboundService
|
||||
|
||||
@@ -75,6 +75,8 @@ var defaultValueMap = map[string]string{
|
||||
"externalTrafficInformURI": "",
|
||||
}
|
||||
|
||||
// SettingService provides business logic for application settings management.
|
||||
// It handles configuration storage, retrieval, and validation for all system settings.
|
||||
type SettingService struct{}
|
||||
|
||||
func (s *SettingService) GetDefaultJsonConfig() (any, error) {
|
||||
|
||||
@@ -65,14 +65,18 @@ var (
|
||||
|
||||
var userStates = make(map[int64]string)
|
||||
|
||||
// LoginStatus represents the result of a login attempt.
|
||||
type LoginStatus byte
|
||||
|
||||
// Login status constants
|
||||
const (
|
||||
LoginSuccess LoginStatus = 1
|
||||
LoginFail LoginStatus = 0
|
||||
EmptyTelegramUserID = int64(0)
|
||||
LoginSuccess LoginStatus = 1 // Login was successful
|
||||
LoginFail LoginStatus = 0 // Login failed
|
||||
EmptyTelegramUserID = int64(0) // Default value for empty Telegram user ID
|
||||
)
|
||||
|
||||
// Tgbot provides business logic for Telegram bot integration.
|
||||
// It handles bot commands, user interactions, and status reporting via Telegram.
|
||||
type Tgbot struct {
|
||||
inboundService InboundService
|
||||
settingService SettingService
|
||||
@@ -81,18 +85,22 @@ type Tgbot struct {
|
||||
lastStatus *Status
|
||||
}
|
||||
|
||||
// NewTgbot creates a new Tgbot instance.
|
||||
func (t *Tgbot) NewTgbot() *Tgbot {
|
||||
return new(Tgbot)
|
||||
}
|
||||
|
||||
// I18nBot retrieves a localized message for the bot interface.
|
||||
func (t *Tgbot) I18nBot(name string, params ...string) string {
|
||||
return locale.I18n(locale.Bot, name, params...)
|
||||
}
|
||||
|
||||
// GetHashStorage returns the hash storage instance for callback queries.
|
||||
func (t *Tgbot) GetHashStorage() *global.HashStorage {
|
||||
return hashStorage
|
||||
}
|
||||
|
||||
// Start initializes and starts the Telegram bot with the provided translation files.
|
||||
func (t *Tgbot) Start(i18nFS embed.FS) error {
|
||||
// Initialize localizer
|
||||
err := locale.InitLocalizer(i18nFS, &t.settingService)
|
||||
@@ -173,6 +181,7 @@ func (t *Tgbot) Start(i18nFS embed.FS) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewBot creates a new Telegram bot instance with optional proxy and API server settings.
|
||||
func (t *Tgbot) NewBot(token string, proxyUrl string, apiServerUrl string) (*telego.Bot, error) {
|
||||
if proxyUrl == "" && apiServerUrl == "" {
|
||||
return telego.NewBot(token)
|
||||
@@ -209,10 +218,12 @@ func (t *Tgbot) NewBot(token string, proxyUrl string, apiServerUrl string) (*tel
|
||||
return telego.NewBot(token, telego.WithAPIServer(apiServerUrl))
|
||||
}
|
||||
|
||||
// IsRunning checks if the Telegram bot is currently running.
|
||||
func (t *Tgbot) IsRunning() bool {
|
||||
return isRunning
|
||||
}
|
||||
|
||||
// SetHostname sets the hostname for the bot.
|
||||
func (t *Tgbot) SetHostname() {
|
||||
host, err := os.Hostname()
|
||||
if err != nil {
|
||||
@@ -223,6 +234,7 @@ func (t *Tgbot) SetHostname() {
|
||||
hostname = host
|
||||
}
|
||||
|
||||
// Stop stops the Telegram bot and cleans up resources.
|
||||
func (t *Tgbot) Stop() {
|
||||
if botHandler != nil {
|
||||
botHandler.Stop()
|
||||
@@ -232,6 +244,7 @@ func (t *Tgbot) Stop() {
|
||||
adminIds = nil
|
||||
}
|
||||
|
||||
// encodeQuery encodes the query string if it's longer than 64 characters.
|
||||
func (t *Tgbot) encodeQuery(query string) string {
|
||||
// NOTE: we only need to hash for more than 64 chars
|
||||
if len(query) <= 64 {
|
||||
@@ -241,6 +254,7 @@ func (t *Tgbot) encodeQuery(query string) string {
|
||||
return hashStorage.SaveHash(query)
|
||||
}
|
||||
|
||||
// decodeQuery decodes a hashed query string back to its original form.
|
||||
func (t *Tgbot) decodeQuery(query string) (string, error) {
|
||||
if !hashStorage.IsMD5(query) {
|
||||
return query, nil
|
||||
@@ -254,6 +268,7 @@ func (t *Tgbot) decodeQuery(query string) (string, error) {
|
||||
return decoded, nil
|
||||
}
|
||||
|
||||
// OnReceive starts the message receiving loop for the Telegram bot.
|
||||
func (t *Tgbot) OnReceive() {
|
||||
params := telego.GetUpdatesParams{
|
||||
Timeout: 10,
|
||||
@@ -430,6 +445,7 @@ func (t *Tgbot) OnReceive() {
|
||||
botHandler.Start()
|
||||
}
|
||||
|
||||
// answerCommand processes incoming command messages from Telegram users.
|
||||
func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin bool) {
|
||||
msg, onlyMessage := "", false
|
||||
|
||||
@@ -505,7 +521,7 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to send the message based on onlyMessage flag.
|
||||
// sendResponse sends the response message based on the onlyMessage flag.
|
||||
func (t *Tgbot) sendResponse(chatId int64, msg string, onlyMessage, isAdmin bool) {
|
||||
if onlyMessage {
|
||||
t.SendMsgToTgbot(chatId, msg)
|
||||
@@ -514,6 +530,7 @@ func (t *Tgbot) sendResponse(chatId int64, msg string, onlyMessage, isAdmin bool
|
||||
}
|
||||
}
|
||||
|
||||
// randomLowerAndNum generates a random string of lowercase letters and numbers.
|
||||
func (t *Tgbot) randomLowerAndNum(length int) string {
|
||||
charset := "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
bytes := make([]byte, length)
|
||||
@@ -524,6 +541,7 @@ func (t *Tgbot) randomLowerAndNum(length int) string {
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// randomShadowSocksPassword generates a random password for Shadowsocks.
|
||||
func (t *Tgbot) randomShadowSocksPassword() string {
|
||||
array := make([]byte, 32)
|
||||
_, err := rand.Read(array)
|
||||
@@ -533,6 +551,7 @@ func (t *Tgbot) randomShadowSocksPassword() string {
|
||||
return base64.StdEncoding.EncodeToString(array)
|
||||
}
|
||||
|
||||
// answerCallback processes callback queries from inline keyboards.
|
||||
func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool) {
|
||||
chatId := callbackQuery.Message.GetChat().ID
|
||||
|
||||
@@ -1815,6 +1834,7 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
|
||||
}
|
||||
}
|
||||
|
||||
// BuildInboundClientDataMessage builds a message with client data for the given inbound and protocol.
|
||||
func (t *Tgbot) BuildInboundClientDataMessage(inbound_remark string, protocol model.Protocol) (string, error) {
|
||||
var message string
|
||||
|
||||
@@ -1864,6 +1884,7 @@ func (t *Tgbot) BuildInboundClientDataMessage(inbound_remark string, protocol mo
|
||||
return message, nil
|
||||
}
|
||||
|
||||
// BuildJSONForProtocol builds a JSON string for the given protocol with client data.
|
||||
func (t *Tgbot) BuildJSONForProtocol(protocol model.Protocol) (string, error) {
|
||||
var jsonString string
|
||||
|
||||
@@ -1942,6 +1963,7 @@ func (t *Tgbot) BuildJSONForProtocol(protocol model.Protocol) (string, error) {
|
||||
return jsonString, nil
|
||||
}
|
||||
|
||||
// SubmitAddClient submits the client addition request to the inbound service.
|
||||
func (t *Tgbot) SubmitAddClient() (bool, error) {
|
||||
|
||||
inbound, err := t.inboundService.GetInbound(receiver_inbound_ID)
|
||||
@@ -1964,6 +1986,7 @@ func (t *Tgbot) SubmitAddClient() (bool, error) {
|
||||
return t.inboundService.AddInboundClient(newInbound)
|
||||
}
|
||||
|
||||
// checkAdmin checks if the given Telegram ID is an admin.
|
||||
func checkAdmin(tgId int64) bool {
|
||||
for _, adminId := range adminIds {
|
||||
if adminId == tgId {
|
||||
@@ -1973,6 +1996,7 @@ func checkAdmin(tgId int64) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SendAnswer sends a response message with an inline keyboard to the specified chat.
|
||||
func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
|
||||
numericKeyboard := tu.InlineKeyboard(
|
||||
tu.InlineKeyboardRow(
|
||||
@@ -2028,6 +2052,7 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
|
||||
t.SendMsgToTgbot(chatId, msg, ReplyMarkup)
|
||||
}
|
||||
|
||||
// SendMsgToTgbot sends a message to the Telegram bot with optional reply markup.
|
||||
func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, replyMarkup ...telego.ReplyMarkup) {
|
||||
if !isRunning {
|
||||
return
|
||||
@@ -2143,6 +2168,7 @@ func (t *Tgbot) buildSubscriptionURLs(email string) (string, string, error) {
|
||||
return subURL, subJsonURL, nil
|
||||
}
|
||||
|
||||
// sendClientSubLinks sends the subscription links for the client to the chat.
|
||||
func (t *Tgbot) sendClientSubLinks(chatId int64, email string) {
|
||||
subURL, subJsonURL, err := t.buildSubscriptionURLs(email)
|
||||
if err != nil {
|
||||
@@ -2338,6 +2364,7 @@ func (t *Tgbot) sendClientQRLinks(chatId int64, email string) {
|
||||
}
|
||||
}
|
||||
|
||||
// SendMsgToTgbotAdmins sends a message to all admin Telegram chats.
|
||||
func (t *Tgbot) SendMsgToTgbotAdmins(msg string, replyMarkup ...telego.ReplyMarkup) {
|
||||
if len(replyMarkup) > 0 {
|
||||
for _, adminId := range adminIds {
|
||||
@@ -2350,6 +2377,7 @@ func (t *Tgbot) SendMsgToTgbotAdmins(msg string, replyMarkup ...telego.ReplyMark
|
||||
}
|
||||
}
|
||||
|
||||
// SendReport sends a periodic report to admin chats.
|
||||
func (t *Tgbot) SendReport() {
|
||||
runTime, err := t.settingService.GetTgbotRuntime()
|
||||
if err == nil && len(runTime) > 0 {
|
||||
@@ -2371,6 +2399,7 @@ func (t *Tgbot) SendReport() {
|
||||
}
|
||||
}
|
||||
|
||||
// SendBackupToAdmins sends a database backup to admin chats.
|
||||
func (t *Tgbot) SendBackupToAdmins() {
|
||||
if !t.IsRunning() {
|
||||
return
|
||||
@@ -2380,6 +2409,7 @@ func (t *Tgbot) SendBackupToAdmins() {
|
||||
}
|
||||
}
|
||||
|
||||
// sendExhaustedToAdmins sends notifications about exhausted clients to admins.
|
||||
func (t *Tgbot) sendExhaustedToAdmins() {
|
||||
if !t.IsRunning() {
|
||||
return
|
||||
@@ -2389,6 +2419,7 @@ func (t *Tgbot) sendExhaustedToAdmins() {
|
||||
}
|
||||
}
|
||||
|
||||
// getServerUsage retrieves and formats server usage information.
|
||||
func (t *Tgbot) getServerUsage(chatId int64, messageID ...int) string {
|
||||
info := t.prepareServerUsageInfo()
|
||||
|
||||
@@ -2410,6 +2441,7 @@ func (t *Tgbot) sendServerUsage() string {
|
||||
return info
|
||||
}
|
||||
|
||||
// prepareServerUsageInfo prepares the server usage information string.
|
||||
func (t *Tgbot) prepareServerUsageInfo() string {
|
||||
info, ipv4, ipv6 := "", "", ""
|
||||
|
||||
@@ -2459,6 +2491,7 @@ func (t *Tgbot) prepareServerUsageInfo() string {
|
||||
return info
|
||||
}
|
||||
|
||||
// UserLoginNotify sends a notification about user login attempts to admins.
|
||||
func (t *Tgbot) UserLoginNotify(username string, password string, ip string, time string, status LoginStatus) {
|
||||
if !t.IsRunning() {
|
||||
return
|
||||
@@ -2490,6 +2523,7 @@ func (t *Tgbot) UserLoginNotify(username string, password string, ip string, tim
|
||||
t.SendMsgToTgbotAdmins(msg)
|
||||
}
|
||||
|
||||
// getInboundUsages retrieves and formats inbound usage information.
|
||||
func (t *Tgbot) getInboundUsages() string {
|
||||
info := ""
|
||||
// get traffic
|
||||
@@ -2515,6 +2549,8 @@ func (t *Tgbot) getInboundUsages() string {
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// getInbounds creates an inline keyboard with all inbounds.
|
||||
func (t *Tgbot) getInbounds() (*telego.InlineKeyboardMarkup, error) {
|
||||
inbounds, err := t.inboundService.GetAllInbounds()
|
||||
if err != nil {
|
||||
@@ -2546,8 +2582,7 @@ func (t *Tgbot) getInbounds() (*telego.InlineKeyboardMarkup, error) {
|
||||
return keyboard, nil
|
||||
}
|
||||
|
||||
// getInboundsFor builds an inline keyboard of inbounds where each button leads to a custom next action
|
||||
// nextAction should be one of: get_clients_for_sub|get_clients_for_individual|get_clients_for_qr
|
||||
// getInboundsFor builds an inline keyboard of inbounds for a custom next action.
|
||||
func (t *Tgbot) getInboundsFor(nextAction string) (*telego.InlineKeyboardMarkup, error) {
|
||||
inbounds, err := t.inboundService.GetAllInbounds()
|
||||
if err != nil {
|
||||
@@ -2614,6 +2649,7 @@ func (t *Tgbot) getInboundClientsFor(inboundID int, action string) (*telego.Inli
|
||||
return keyboard, nil
|
||||
}
|
||||
|
||||
// getInboundsAddClient creates an inline keyboard for adding clients to inbounds.
|
||||
func (t *Tgbot) getInboundsAddClient() (*telego.InlineKeyboardMarkup, error) {
|
||||
inbounds, err := t.inboundService.GetAllInbounds()
|
||||
if err != nil {
|
||||
@@ -2656,6 +2692,7 @@ func (t *Tgbot) getInboundsAddClient() (*telego.InlineKeyboardMarkup, error) {
|
||||
return keyboard, nil
|
||||
}
|
||||
|
||||
// getInboundClients creates an inline keyboard with clients of a specific inbound.
|
||||
func (t *Tgbot) getInboundClients(id int) (*telego.InlineKeyboardMarkup, error) {
|
||||
inbound, err := t.inboundService.GetInbound(id)
|
||||
if err != nil {
|
||||
@@ -2690,6 +2727,7 @@ func (t *Tgbot) getInboundClients(id int) (*telego.InlineKeyboardMarkup, error)
|
||||
return keyboard, nil
|
||||
}
|
||||
|
||||
// clientInfoMsg formats client information message based on traffic and flags.
|
||||
func (t *Tgbot) clientInfoMsg(
|
||||
traffic *xray.ClientTraffic,
|
||||
printEnabled bool,
|
||||
@@ -2796,6 +2834,7 @@ func (t *Tgbot) clientInfoMsg(
|
||||
return output
|
||||
}
|
||||
|
||||
// getClientUsage retrieves and sends client usage information to the chat.
|
||||
func (t *Tgbot) getClientUsage(chatId int64, tgUserID int64, email ...string) {
|
||||
traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserID)
|
||||
if err != nil {
|
||||
@@ -2838,6 +2877,7 @@ func (t *Tgbot) getClientUsage(chatId int64, tgUserID int64, email ...string) {
|
||||
t.SendAnswer(chatId, output, false)
|
||||
}
|
||||
|
||||
// searchClientIps searches and sends client IP addresses for the given email.
|
||||
func (t *Tgbot) searchClientIps(chatId int64, email string, messageID ...int) {
|
||||
ips, err := t.inboundService.GetInboundClientIps(email)
|
||||
if err != nil || len(ips) == 0 {
|
||||
@@ -2865,6 +2905,7 @@ func (t *Tgbot) searchClientIps(chatId int64, email string, messageID ...int) {
|
||||
}
|
||||
}
|
||||
|
||||
// clientTelegramUserInfo retrieves and sends Telegram user info for the client.
|
||||
func (t *Tgbot) clientTelegramUserInfo(chatId int64, email string, messageID ...int) {
|
||||
traffic, client, err := t.inboundService.GetClientByEmail(email)
|
||||
if err != nil {
|
||||
@@ -2917,6 +2958,7 @@ func (t *Tgbot) clientTelegramUserInfo(chatId int64, email string, messageID ...
|
||||
}
|
||||
}
|
||||
|
||||
// searchClient searches for a client by email and sends the information.
|
||||
func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) {
|
||||
traffic, err := t.inboundService.GetClientTrafficByEmail(email)
|
||||
if err != nil {
|
||||
@@ -2962,6 +3004,7 @@ func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) {
|
||||
}
|
||||
}
|
||||
|
||||
// addClient handles the process of adding a new client to an inbound.
|
||||
func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
|
||||
inbound, err := t.inboundService.GetInbound(receiver_inbound_ID)
|
||||
if err != nil {
|
||||
@@ -3058,6 +3101,7 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
|
||||
|
||||
}
|
||||
|
||||
// searchInbound searches for inbounds by remark and sends the results.
|
||||
func (t *Tgbot) searchInbound(chatId int64, remark string) {
|
||||
inbounds, err := t.inboundService.SearchInbounds(remark)
|
||||
if err != nil {
|
||||
@@ -3095,6 +3139,7 @@ func (t *Tgbot) searchInbound(chatId int64, remark string) {
|
||||
}
|
||||
}
|
||||
|
||||
// getExhausted retrieves and sends information about exhausted clients.
|
||||
func (t *Tgbot) getExhausted(chatId int64) {
|
||||
trDiff := int64(0)
|
||||
exDiff := int64(0)
|
||||
@@ -3191,6 +3236,7 @@ func (t *Tgbot) getExhausted(chatId int64) {
|
||||
}
|
||||
}
|
||||
|
||||
// notifyExhausted sends notifications for exhausted clients.
|
||||
func (t *Tgbot) notifyExhausted() {
|
||||
trDiff := int64(0)
|
||||
exDiff := int64(0)
|
||||
@@ -3262,6 +3308,7 @@ func (t *Tgbot) notifyExhausted() {
|
||||
}
|
||||
}
|
||||
|
||||
// int64Contains checks if an int64 slice contains a specific item.
|
||||
func int64Contains(slice []int64, item int64) bool {
|
||||
for _, s := range slice {
|
||||
if s == item {
|
||||
@@ -3271,6 +3318,7 @@ func int64Contains(slice []int64, item int64) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// onlineClients retrieves and sends information about online clients.
|
||||
func (t *Tgbot) onlineClients(chatId int64, messageID ...int) {
|
||||
if !p.IsRunning() {
|
||||
return
|
||||
@@ -3305,6 +3353,7 @@ func (t *Tgbot) onlineClients(chatId int64, messageID ...int) {
|
||||
}
|
||||
}
|
||||
|
||||
// sendBackup sends a backup of the database and configuration files.
|
||||
func (t *Tgbot) sendBackup(chatId int64) {
|
||||
output := t.I18nBot("tgbot.messages.backupTime", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
|
||||
t.SendMsgToTgbot(chatId, output)
|
||||
@@ -3344,6 +3393,7 @@ func (t *Tgbot) sendBackup(chatId int64) {
|
||||
}
|
||||
}
|
||||
|
||||
// sendBanLogs sends the ban logs to the specified chat.
|
||||
func (t *Tgbot) sendBanLogs(chatId int64, dt bool) {
|
||||
if dt {
|
||||
output := t.I18nBot("tgbot.messages.datetime", "DateTime=="+time.Now().Format("2006-01-02 15:04:05"))
|
||||
@@ -3393,6 +3443,7 @@ func (t *Tgbot) sendBanLogs(chatId int64, dt bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// sendCallbackAnswerTgBot answers a callback query with a message.
|
||||
func (t *Tgbot) sendCallbackAnswerTgBot(id string, message string) {
|
||||
params := telego.AnswerCallbackQueryParams{
|
||||
CallbackQueryID: id,
|
||||
@@ -3403,6 +3454,7 @@ func (t *Tgbot) sendCallbackAnswerTgBot(id string, message string) {
|
||||
}
|
||||
}
|
||||
|
||||
// editMessageCallbackTgBot edits the reply markup of a message.
|
||||
func (t *Tgbot) editMessageCallbackTgBot(chatId int64, messageID int, inlineKeyboard *telego.InlineKeyboardMarkup) {
|
||||
params := telego.EditMessageReplyMarkupParams{
|
||||
ChatID: tu.ID(chatId),
|
||||
@@ -3414,6 +3466,7 @@ func (t *Tgbot) editMessageCallbackTgBot(chatId int64, messageID int, inlineKeyb
|
||||
}
|
||||
}
|
||||
|
||||
// editMessageTgBot edits the text and reply markup of a message.
|
||||
func (t *Tgbot) editMessageTgBot(chatId int64, messageID int, text string, inlineKeyboard ...*telego.InlineKeyboardMarkup) {
|
||||
params := telego.EditMessageTextParams{
|
||||
ChatID: tu.ID(chatId),
|
||||
@@ -3429,6 +3482,7 @@ func (t *Tgbot) editMessageTgBot(chatId int64, messageID int, text string, inlin
|
||||
}
|
||||
}
|
||||
|
||||
// SendMsgToTgbotDeleteAfter sends a message and deletes it after a specified delay.
|
||||
func (t *Tgbot) SendMsgToTgbotDeleteAfter(chatId int64, msg string, delayInSeconds int, replyMarkup ...telego.ReplyMarkup) {
|
||||
// Determine if replyMarkup was passed; otherwise, set it to nil
|
||||
var replyMarkupParam telego.ReplyMarkup
|
||||
@@ -3455,6 +3509,7 @@ func (t *Tgbot) SendMsgToTgbotDeleteAfter(chatId int64, msg string, delayInSecon
|
||||
}()
|
||||
}
|
||||
|
||||
// deleteMessageTgBot deletes a message from the chat.
|
||||
func (t *Tgbot) deleteMessageTgBot(chatId int64, messageID int) {
|
||||
params := telego.DeleteMessageParams{
|
||||
ChatID: tu.ID(chatId),
|
||||
@@ -3467,6 +3522,7 @@ func (t *Tgbot) deleteMessageTgBot(chatId int64, messageID int) {
|
||||
}
|
||||
}
|
||||
|
||||
// isSingleWord checks if the text contains only a single word.
|
||||
func (t *Tgbot) isSingleWord(text string) bool {
|
||||
text = strings.TrimSpace(text)
|
||||
re := regexp.MustCompile(`\s+`)
|
||||
|
||||
@@ -12,10 +12,14 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserService provides business logic for user management and authentication.
|
||||
// It handles user creation, login, password management, and 2FA operations.
|
||||
type UserService struct {
|
||||
settingService SettingService
|
||||
}
|
||||
|
||||
// GetFirstUser retrieves the first user from the database.
|
||||
// This is typically used for initial setup or when there's only one admin user.
|
||||
func (s *UserService) GetFirstUser() (*model.User, error) {
|
||||
db := database.GetDB()
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"github.com/mhsanaei/3x-ui/v2/util/common"
|
||||
)
|
||||
|
||||
// WarpService provides business logic for Cloudflare WARP integration.
|
||||
// It manages WARP configuration and connectivity settings.
|
||||
type WarpService struct {
|
||||
SettingService
|
||||
}
|
||||
|
||||
@@ -20,16 +20,20 @@ var (
|
||||
result string
|
||||
)
|
||||
|
||||
// XrayService provides business logic for Xray process management.
|
||||
// It handles starting, stopping, restarting Xray, and managing its configuration.
|
||||
type XrayService struct {
|
||||
inboundService InboundService
|
||||
settingService SettingService
|
||||
xrayAPI xray.XrayAPI
|
||||
}
|
||||
|
||||
// IsXrayRunning checks if the Xray process is currently running.
|
||||
func (s *XrayService) IsXrayRunning() bool {
|
||||
return p != nil && p.IsRunning()
|
||||
}
|
||||
|
||||
// GetXrayErr returns the error from the Xray process, if any.
|
||||
func (s *XrayService) GetXrayErr() error {
|
||||
if p == nil {
|
||||
return nil
|
||||
@@ -46,6 +50,7 @@ func (s *XrayService) GetXrayErr() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetXrayResult returns the result string from the Xray process.
|
||||
func (s *XrayService) GetXrayResult() string {
|
||||
if result != "" {
|
||||
return result
|
||||
@@ -68,6 +73,7 @@ func (s *XrayService) GetXrayResult() string {
|
||||
return result
|
||||
}
|
||||
|
||||
// GetXrayVersion returns the version of the running Xray process.
|
||||
func (s *XrayService) GetXrayVersion() string {
|
||||
if p == nil {
|
||||
return "Unknown"
|
||||
@@ -75,10 +81,13 @@ func (s *XrayService) GetXrayVersion() string {
|
||||
return p.GetVersion()
|
||||
}
|
||||
|
||||
// RemoveIndex removes an element at the specified index from a slice.
|
||||
// Returns a new slice with the element removed.
|
||||
func RemoveIndex(s []any, index int) []any {
|
||||
return append(s[:index], s[index+1:]...)
|
||||
}
|
||||
|
||||
// GetXrayConfig retrieves and builds the Xray configuration from settings and inbounds.
|
||||
func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||
templateConfig, err := s.settingService.GetXrayConfigTemplate()
|
||||
if err != nil {
|
||||
@@ -182,6 +191,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||
return xrayConfig, nil
|
||||
}
|
||||
|
||||
// GetXrayTraffic fetches the current traffic statistics from the running Xray process.
|
||||
func (s *XrayService) GetXrayTraffic() ([]*xray.Traffic, []*xray.ClientTraffic, error) {
|
||||
if !s.IsXrayRunning() {
|
||||
err := errors.New("xray is not running")
|
||||
@@ -200,6 +210,7 @@ func (s *XrayService) GetXrayTraffic() ([]*xray.Traffic, []*xray.ClientTraffic,
|
||||
return traffic, clientTraffic, nil
|
||||
}
|
||||
|
||||
// RestartXray restarts the Xray process, optionally forcing a restart even if config unchanged.
|
||||
func (s *XrayService) RestartXray(isForce bool) error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
@@ -229,6 +240,7 @@ func (s *XrayService) RestartXray(isForce bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopXray stops the running Xray process.
|
||||
func (s *XrayService) StopXray() error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
@@ -240,15 +252,17 @@ func (s *XrayService) StopXray() error {
|
||||
return errors.New("xray is not running")
|
||||
}
|
||||
|
||||
// SetToNeedRestart marks that Xray needs to be restarted.
|
||||
func (s *XrayService) SetToNeedRestart() {
|
||||
isNeedXrayRestart.Store(true)
|
||||
}
|
||||
|
||||
// IsNeedRestartAndSetFalse checks if restart is needed and resets the flag to false.
|
||||
func (s *XrayService) IsNeedRestartAndSetFalse() bool {
|
||||
return isNeedXrayRestart.CompareAndSwap(true, false)
|
||||
}
|
||||
|
||||
// Check if Xray is not running and wasn't stopped manually, i.e. crashed
|
||||
// DidXrayCrash checks if Xray crashed by verifying it's not running and wasn't manually stopped.
|
||||
func (s *XrayService) DidXrayCrash() bool {
|
||||
return !s.IsXrayRunning() && !isManuallyStopped.Load()
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/mhsanaei/3x-ui/v2/xray"
|
||||
)
|
||||
|
||||
// XraySettingService provides business logic for Xray configuration management.
|
||||
// It handles validation and storage of Xray template configurations.
|
||||
type XraySettingService struct {
|
||||
SettingService
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user