版本更新

This commit is contained in:
hahwu 2025-02-11 15:35:38 +08:00
parent eadb5d5471
commit 1ea4bd7559
25 changed files with 4990 additions and 3274 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
*.exe
release/*.tar
release/*.tar
.vscode/

44
common/feishu.go Normal file
View File

@ -0,0 +1,44 @@
package common
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func SendFeishuMsg(msg string) error {
// 创建请求体
payload := map[string]interface{}{
"msg_type": "text",
"content": map[string]string{
"text": msg,
},
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
// 创建HTTP请求
req, err := http.NewRequest("POST", GetFeishuUrl(), bytes.NewBuffer(payloadBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to send message, status code: %d", resp.StatusCode)
}
return nil
}

View File

@ -1,4 +1,4 @@
package model
package common
import (
"fmt"
@ -25,9 +25,15 @@ type MysqlConfig struct {
Database string `yaml:"database"`
}
type SystemConfig struct {
NMap bool `yaml:"nmap"` // 是否开启端口扫描
FeishUrl string `yaml:"feishu_url"` // 飞书机器人url
}
type Config struct {
Servers []SshConfig `yaml:"servers"`
Mysqls []MysqlConfig `yaml:"mysqls"`
System SystemConfig `yaml:"system"`
}
var config *Config
@ -73,3 +79,11 @@ func GetMysqlConfig(name string) (*MysqlConfig, error) {
func GetWsConfig(name string) (*SshConfig, error) {
return GetServerConfig("ws")
}
func GetFeishuUrl() string {
return config.System.FeishUrl
}
func GetNMap() bool {
return config.System.NMap
}

View File

@ -1,10 +1,13 @@
system:
nmap: false
feishu_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/70e24a79-b019-434a-b4d1-4592bbf7c311'
mysqls:
- host: '127.0.0.1'
name: 'merge_pet_test'
port: 3306
user: 'root'
password: 'Z4rf7eZZe500dxa'
database: 'pet_home_1'
database: 'pet_home_%d'
idleTimeout: Infinity
- host: '127.0.0.1'
name: 'merge_pet_sdk'
@ -13,12 +16,19 @@ mysqls:
password: 'Z4rf7eZZe500dxa'
database: 'Merge_Pet_sdk'
idleTimeout: Infinity
- host: '127.0.0.1'
name: 'merge_pet_london'
port: 3306
user: 'root'
password: 'Z4rf7eZZe500dxa'
database: 'merge_pet_london_%d'
idleTimeout: Infinity
- host: '127.0.0.1'
name: 'merge_pet_online'
port: 3306
user: 'root'
password: 'Xijing1!'
database: 'merge_pet_1'
database: 'merge_pet_%d'
idleTimeout: Infinity
- host: 'rm-f8zd2030feam53n43.mysql.rds.aliyuncs.com'
name: 'log'
@ -39,6 +49,11 @@ servers:
port: 22
username: 'root'
password: 'ByWayStudios01!'
- name: 'london'
host: '8.208.47.208'
port: 22
username: 'root'
password: '3/Qr-g]ljJ&9@bM&'
- name: 'log'
host: '8.155.14.94'
port: 22

29
controller/mail.go Normal file
View File

@ -0,0 +1,29 @@
package controller
import (
"backend/model"
"github.com/gin-gonic/gin"
)
func SendMail(c *gin.Context) {
Mail := model.Mail{}
c.BindJSON(&Mail)
err := Mail.SendMail()
if err != nil {
failed(c, err.Error())
return
}
success(c, nil)
}
func MailList(c *gin.Context) {
Mail := model.Mail{}
c.BindJSON(&Mail)
MailList, err := Mail.MailList()
if err != nil {
failed(c, err.Error())
return
}
success(c, MailList)
}

115
controller/server.go Normal file
View File

@ -0,0 +1,115 @@
package controller
import (
"backend/common"
"backend/model"
"fmt"
"time"
"github.com/gin-gonic/gin"
)
func AppList(c *gin.Context) {
Server := model.Server{}
AppList, err := Server.AppList()
if err != nil {
failed(c, err.Error())
return
}
success(c, AppList)
}
func ServerList(c *gin.Context) {
Server := model.Server{}
c.BindJSON(&Server)
ServerList, err := Server.ServerList()
if err != nil {
failed(c, err.Error())
return
}
success(c, ServerList)
}
func AddServer(c *gin.Context) {
Server := model.Server{}
c.BindJSON(&Server)
err := Server.AddServer()
if err != nil {
failed(c, err.Error())
return
}
success(c, nil)
}
func UpdateApp(c *gin.Context) {
Server := model.Server{}
c.BindJSON(&Server)
msg, err := Server.UpdateApp()
if err != nil {
failed(c, err.Error())
return
}
success(c, map[string]string{"msg": msg})
}
func UpdateAppFeishu(c *gin.Context) {
Server := model.Server{}
c.BindJSON(&Server)
go Server.UpdateAppFeishu()
success(c, map[string]string{"msg": "have send feishu"})
}
func RestartServer(c *gin.Context) {
Server := model.Server{}
c.BindJSON(&Server)
msg, err := Server.RestartServer()
if err != nil {
failed(c, err.Error())
return
}
success(c, map[string]string{"msg": msg})
}
func ReloadServer(c *gin.Context) {
Server := model.Server{}
c.BindJSON(&Server)
msg, err := Server.ReloadServer()
if err != nil {
failed(c, err.Error())
return
}
success(c, map[string]string{"msg": msg})
}
func AppPortNmap() {
NMapEnable := common.GetNMap()
if !NMapEnable {
return
}
for {
Server := model.Server{}
AppList, err := Server.AppList()
if err != nil {
return
}
for _, app := range AppList {
Server.AppId = app.AppId
ServerList, err := Server.ServerList()
if err != nil {
continue
}
App, err := model.GetAppConfig(app.AppId)
if err != nil {
continue
}
for _, server := range ServerList {
err := model.PortMap(App.WsHost, fmt.Sprintf("%d", server.ServerId+App.WsPort))
if err != nil {
common.SendFeishuMsg(fmt.Sprintf("App: %s, Server: %s, 端口扫描异常 failed: %v", App.AppName, server.ServerName, err))
}
}
}
time.Sleep(5 * time.Minute)
}
}

22
controller/statistics.go Normal file
View File

@ -0,0 +1,22 @@
package controller
import (
"backend/model"
"github.com/gin-gonic/gin"
)
func StatisticsLevel(c *gin.Context) {
statistics := model.Statistics{}
err := c.BindJSON(&statistics)
if err != nil {
failed(c, err.Error())
return
}
res, err := statistics.StatisticsLevel()
if err != nil {
failed(c, err.Error())
return
}
success(c, res)
}

View File

@ -23,6 +23,7 @@ func UserInfo(c *gin.Context) {
func UserList(c *gin.Context) {
var request struct {
Id int `json:"Id"`
ServerId int `json:"ServerId"`
PageSize int `json:"pageSize"`
CurrentPage int `json:"currentPage"`
}
@ -32,7 +33,7 @@ func UserList(c *gin.Context) {
failed(c, err.Error())
return
}
user, total, err := model.GetUserList(request.Id, request.PageSize, request.CurrentPage)
user, total, err := model.GetUserList(request.Id, request.ServerId, request.PageSize, request.CurrentPage)
if err != nil {
fmt.Print(err)
failed(c, err.Error())

1
go.mod
View File

@ -14,6 +14,7 @@ require (
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Ullaakut/nmap v2.0.2+incompatible // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect

2
go.sum
View File

@ -1,5 +1,7 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Ullaakut/nmap v2.0.2+incompatible h1:edw45QpSQBQ2B/Hqfg86Bt5rrK79tp/fAcqIHyNSdQs=
github.com/Ullaakut/nmap v2.0.2+incompatible/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=

26
main.go
View File

@ -13,15 +13,33 @@ func main() {
api := r.Group("/api")
{
api.GET("/user/info", controller.UserInfo)
api.POST("/auth/login", controller.Login)
api.POST("/user/list", controller.UserList)
api.GET("/auth/codes", controller.Codes)
api.POST("/log/user", controller.UserDetail)
api.POST("/log/asset", controller.Asset)
api.POST("/log/event", controller.Event)
api.POST("/log/order", controller.Order)
api.GET("/auth/codes", controller.Codes)
}
api.POST("/user/list", controller.UserList)
api.GET("/user/info", controller.UserInfo)
api.POST("/server/list", controller.AppList)
api.POST("/server/serverList", controller.ServerList)
api.POST("/server/addServer", controller.AddServer)
api.POST("/server/updateApp", controller.UpdateApp)
api.POST("/server/updateAppFeishu", controller.UpdateAppFeishu)
api.POST("/server/restart", controller.RestartServer)
api.POST("/server/reload", controller.ReloadServer)
api.POST("/statistics/level", controller.StatisticsLevel)
// 邮件
api.POST("/mail/send", controller.SendMail)
api.POST("/mail/list", controller.MailList)
}
go controller.AppPortNmap()
r.Run(":5320") // 在 0.0.0.0:5320 上监听并服务
}

View File

@ -1,9 +1,11 @@
package model
import (
"backend/common"
"context"
"fmt"
"net"
"strings"
"github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
@ -17,12 +19,12 @@ func init() {
MysqlList = make(map[string]*sqlx.DB)
}
func connectToMySQLViaSSH(ServerName, MysqlName string) (*sqlx.DB, error) {
SshConfig, err := GetServerConfig(ServerName)
func connectToMySQLViaSSH(ServerName, MysqlName string, ServerId int) (*sqlx.DB, error) {
SshConfig, err := common.GetServerConfig(ServerName)
if err != nil {
return nil, fmt.Errorf("failed to get SSH config: %v", err)
}
MysqlConfig, err := GetMysqlConfig(MysqlName)
MysqlConfig, err := common.GetMysqlConfig(MysqlName)
if err != nil {
return nil, fmt.Errorf("failed to get MySQL config: %v", err)
}
@ -52,9 +54,14 @@ func connectToMySQLViaSSH(ServerName, MysqlName string) (*sqlx.DB, error) {
mysql.RegisterDialContext("mysql+tcp", func(ctx context.Context, addr string) (net.Conn, error) {
return mysqlConn, nil
})
var Database string
if strings.Contains(MysqlConfig.Database, "%") {
Database = fmt.Sprintf(MysqlConfig.Database, ServerId)
} else {
Database = MysqlConfig.Database
}
// 连接到 MySQL 数据库
dsn := fmt.Sprintf("%s:%s@mysql+tcp(%s:%d)/%s", MysqlConfig.Username, MysqlConfig.Password, MysqlConfig.Host, MysqlConfig.Port, MysqlConfig.Database)
dsn := fmt.Sprintf("%s:%s@mysql+tcp(%s:%d)/%s", MysqlConfig.Username, MysqlConfig.Password, MysqlConfig.Host, MysqlConfig.Port, Database)
db, err := sqlx.Open("mysql", dsn)
if err != nil {
return nil, fmt.Errorf("failed to open MySQL: %v", err)
@ -64,11 +71,11 @@ func connectToMySQLViaSSH(ServerName, MysqlName string) (*sqlx.DB, error) {
}
func connectToTopicMySQLViaSSH(ServerName, MysqlName, Topic string) (*sqlx.DB, error) {
SshConfig, err := GetServerConfig(ServerName)
SshConfig, err := common.GetServerConfig(ServerName)
if err != nil {
return nil, fmt.Errorf("failed to get SSH config: %v", err)
}
MysqlConfig, err := GetMysqlConfig(MysqlName)
MysqlConfig, err := common.GetMysqlConfig(MysqlName)
if err != nil {
return nil, fmt.Errorf("failed to get MySQL config: %v", err)
}
@ -109,12 +116,30 @@ func connectToTopicMySQLViaSSH(ServerName, MysqlName, Topic string) (*sqlx.DB, e
return db, nil
}
func GetMysqlDB(Server, Mysql string) *sqlx.DB {
key := fmt.Sprintf("%s_%s", Server, Mysql)
func GetMysqlDB(Server, Mysql string, ServerId int) *sqlx.DB {
key := fmt.Sprintf("%s_%s_%d", Server, Mysql, ServerId)
if db, ok := MysqlList[key]; ok {
return db
if err := db.Ping(); err == nil {
return db
}
}
Db, err := connectToMySQLViaSSH(Server, Mysql)
Db, err := connectToMySQLViaSSH(Server, Mysql, ServerId)
if err != nil {
return nil
}
MysqlList[key] = Db
return Db
}
func GetGameDB() *sqlx.DB {
Server, Mysql := "log", "log"
key := fmt.Sprintf("%s_%s_game", Server, Mysql)
if db, ok := MysqlList[key]; ok {
if err := db.Ping(); err == nil {
return db
}
}
Db, err := connectToTopicMySQLViaSSH(Server, Mysql, "game")
if err != nil {
return nil
}
@ -124,9 +149,11 @@ func GetMysqlDB(Server, Mysql string) *sqlx.DB {
func GetTopicDB(Topic string) *sqlx.DB {
Server, Mysql := "log", "log"
key := fmt.Sprintf("%s_%s_topic", Server, Mysql)
key := fmt.Sprintf("%s_%s_%s", Server, Mysql, Topic)
if db, ok := MysqlList[key]; ok {
return db
if err := db.Ping(); err == nil {
return db
}
}
Db, err := connectToTopicMySQLViaSSH(Server, Mysql, Topic)
if err != nil {
@ -137,7 +164,7 @@ func GetTopicDB(Topic string) *sqlx.DB {
}
func GetAppConfig(AppId int) (*AppStruct, error) {
Db := GetMysqlDB("log", "log")
Db := GetMysqlDB("log", "log", 0)
var app AppStruct
err := Db.Get(&app, "SELECT * FROM app WHERE `AppId` = ?", AppId)
if err != nil {

53
model/Statistics.go Normal file
View File

@ -0,0 +1,53 @@
package model
import (
"fmt"
"sort"
)
type Statistics struct {
AppId int `json:"AppId"`
ServerList []int `json:"ServerList"`
}
func (s *Statistics) StatisticsLevel() (interface{}, error) {
AppConfig, err := GetAppConfig(s.AppId)
if err != nil {
return nil, err
}
res := make([]map[string]interface{}, 0)
res1 := make(map[int]int)
type dbres struct {
Sum int `db:"sum"`
Level int `db:"level"`
}
for _, v := range s.ServerList {
Db := GetMysqlDB(AppConfig.ServerName, AppConfig.MysqlName, v)
if Db == nil {
return nil, fmt.Errorf("failed to get mysql database")
}
var result []dbres
err = Db.Select(&result, "select count(*) as sum , `level` from t_player_baseinfo group by `level`")
if err != nil {
return nil, fmt.Errorf("failed to get level count: %v", err)
}
for r := range result {
res1[result[r].Level] += result[r].Sum
}
}
keys := make([]int, 0, len(res1))
for k := range res1 {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
res = append(res, map[string]interface{}{
"Level": k,
"Sum": res1[k],
})
}
return map[string]interface{}{
"data": res,
"total": len(res),
}, nil
}

View File

@ -10,6 +10,7 @@ type AppStruct struct {
Topic string `db:"Topic"`
Update int `db:"Update"`
AppId int `db:"AppId"`
Path string `db:"Path"`
}
type User struct {

View File

@ -3,20 +3,15 @@ package model
import (
"backend/msg"
util "backend/uitl"
"encoding/json"
"fmt"
"net"
"time"
"golang.org/x/net/websocket"
)
func GetUserList(AppId, PageSize, CurrentPage int) ([]*User, int, error) {
func GetUserList(AppId, ServerId, PageSize, CurrentPage int) ([]*User, int, error) {
App, err := GetAppConfig(AppId)
if err != nil {
return nil, 0, err
}
db := GetMysqlDB(App.ServerName, App.MysqlName)
db := GetMysqlDB(App.ServerName, App.MysqlName, ServerId)
if db == nil {
return nil, 0, fmt.Errorf("failed to get MySQL database")
}
@ -31,7 +26,7 @@ func GetUserList(AppId, PageSize, CurrentPage int) ([]*User, int, error) {
return nil, 0, fmt.Errorf("failed to get user count: %v", err)
}
for _, user := range users {
if user.LogoutTime == 0 {
if user.LoginTime > user.LogoutTime {
user.Online = "在线"
} else {
user.Online = "离线"
@ -46,45 +41,15 @@ func UserDetail(Uid int) (map[string]interface{}, error) {
}
AppId := Uid / 100000000
ServerId := Uid % 100000000 / 100000
App, err := GetAppConfig(AppId)
ws, err := getWebsocket(AppId, ServerId)
if err != nil {
return nil, err
}
Server, err := GetServerConfig(App.ServerName)
if err != nil {
return nil, err
}
origin := "http://localhost/"
url := fmt.Sprintf("ws://%s:%d/", Server.Host, App.WsPort+ServerId)
config, err := websocket.NewConfig(url, origin)
if err != nil {
return nil, fmt.Errorf("failed to create websocket config: %v", err)
}
config.Dialer = &net.Dialer{
Timeout: 5 * time.Second,
}
ws, err := websocket.DialConfig(config)
if err != nil {
return nil, fmt.Errorf("failed to connect to websocket: %v", err)
return nil, fmt.Errorf("failed to get websocket: %v", err)
}
defer ws.Close()
reqBuf := util.PackMsg(req)
_, err = ws.Write(reqBuf)
r, err := util.SendAdminMsg(ws, req)
if err != nil {
return nil, fmt.Errorf("failed to write to websocket: %v", err)
return nil, fmt.Errorf("failed to send admin message: %v", err)
}
readbuf := make([]byte, 512)
n, err := ws.Read(readbuf)
if err != nil {
return nil, fmt.Errorf("failed to read from websocket: %v", err)
}
resBuf, _ := util.UnpackMsg(readbuf, n)
fmt.Println(string(resBuf))
r := make(map[string]interface{})
json.Unmarshal(resBuf, &r)
return r, nil
}

View File

@ -7,9 +7,11 @@ import (
)
type Log struct {
Uid int `json:"Id"`
PageSize int `json:"PageSize"`
CurrentPage int `json:"CurrentPage"`
Uid int `json:"Id"`
PageSize int `json:"PageSize"`
CurrentPage int `json:"CurrentPage"`
AppId int `json:"AppId"`
EventParam string `json:"Event"`
}
type ResAsset struct {
@ -30,6 +32,8 @@ type ResOrder struct {
type Order struct {
Id int `db:"id"`
Uid int `db:"Uid"`
AppId int `db:"AppId"`
ServerId int `db:"ServerId"`
OrderId string `db:"OrderId"`
Price float64 `db:"Price"`
PayChannelOrderId string `db:"PayChannelOrderId"`
@ -53,6 +57,8 @@ type ResAssetDetail struct {
type Event struct {
Id int `db:"id"`
Uid int `db:"Uid"`
AppId int `db:"AppId"`
ServerId int `db:"ServerId"`
Event string `db:"Event"`
Label string
Param string `db:"Param"`
@ -112,14 +118,18 @@ func (m *Log) Event() (*ResEvent, error) {
return nil, fmt.Errorf("failed to get mysql database")
}
assets := []*Event{}
err = Db.Select(&assets, "SELECT * FROM log_event WHERE Uid = ? and `Event` != 'asset_change' ORDER BY Timestamp DESC LIMIT ?, ?", m.Uid, (m.CurrentPage-1)*m.PageSize, m.PageSize)
if m.EventParam != "" {
err = Db.Select(&assets, "SELECT * FROM log_event WHERE Uid = ? and `Event` = ? ORDER BY Timestamp DESC LIMIT ?, ?", m.Uid, m.EventParam, (m.CurrentPage-1)*m.PageSize, m.PageSize)
} else {
err = Db.Select(&assets, "SELECT * FROM log_event WHERE Uid = ? and `Event` != 'asset_change' ORDER BY Timestamp DESC LIMIT ?, ?", m.Uid, (m.CurrentPage-1)*m.PageSize, m.PageSize)
}
if err != nil {
return nil, fmt.Errorf("failed to get asset list: %v", err)
return nil, fmt.Errorf("failed to get event list: %v", err)
}
var total int
err = Db.QueryRow("SELECT COUNT(*) FROM log_event WHERE Uid = ? and `Event` != 'asset_change'", m.Uid).Scan(&total)
if err != nil {
return nil, fmt.Errorf("failed to get asset count: %v", err)
return nil, fmt.Errorf("failed to get event count: %v", err)
}
for _, asset := range assets {
asset.Label = asset.Event
@ -131,8 +141,7 @@ func (m *Log) Event() (*ResEvent, error) {
}
func (m *Log) Order() (*ResOrder, error) {
AppId, _ := util.ParseUid(m.Uid)
AppConfig, err := GetAppConfig(AppId)
AppConfig, err := GetAppConfig(m.AppId)
if err != nil {
return nil, err
}
@ -141,7 +150,12 @@ func (m *Log) Order() (*ResOrder, error) {
return nil, fmt.Errorf("failed to get mysql database")
}
assets := []*Order{}
err = Db.Select(&assets, "SELECT * FROM log_order WHERE Uid = ? ORDER BY Timestamp DESC LIMIT ?, ?", m.Uid, (m.CurrentPage-1)*m.PageSize, m.PageSize)
if m.Uid == 0 {
err = Db.Select(&assets, "SELECT * FROM log_order ORDER BY Timestamp DESC LIMIT ?, ?", (m.CurrentPage-1)*m.PageSize, m.PageSize)
} else {
err = Db.Select(&assets, "SELECT * FROM log_order WHERE Uid = ? ORDER BY Timestamp DESC LIMIT ?, ?", m.Uid, (m.CurrentPage-1)*m.PageSize, m.PageSize)
}
if err != nil {
return nil, fmt.Errorf("failed to get asset list: %v", err)
}

76
model/mail.go Normal file
View File

@ -0,0 +1,76 @@
package model
import (
"backend/msg"
util "backend/uitl"
"fmt"
"log"
)
type Mail struct {
AppId int `json:"AppId"`
ServerId int `json:"ServerId"`
PageSize int `json:"PageSize"`
CurrentPage int `json:"CurrentPage"`
MailId int `json:"mail_id" db:"mail_id"`
Title string `json:"title" db:"title"`
Content string `json:"content" db:"content"`
StartTime int64 `json:"start_time" db:"start_time"`
EndTime int64 `json:"end_time" db:"end_time"`
Items string `json:"items" db:"items"`
RegisterTime int64 `json:"register_time" db:"register_time"`
MailType int `json:"mail_type" db:"mail_type"`
ToUids string `json:"to_uids" db:"to_uids"`
CreateTime int64 `json:"create_time" db:"create_time"`
}
type Result struct {
Data interface{} `json:"data"`
Result int `json:"result"`
}
func (m *Mail) MailList() (*Result, error) {
// do something
AppCfg, err := GetAppConfig(m.AppId)
if err != nil {
return nil, err
}
Db := GetMysqlDB(AppCfg.ServerName, AppCfg.MysqlName, m.ServerId)
var mail []*Mail
err = Db.Select(&mail, "SELECT `mail_id`, `title`, `content`, `start_time`, `end_time`, `items`, `register_time`, `mail_type`, `to_uids`, `create_time` FROM system_mail_info")
if err != nil {
return nil, fmt.Errorf("failed to scan rows: %v", err)
}
return &Result{
Data: mail,
Result: 0,
}, nil
}
func (m *Mail) SendMail() error {
AppCfg, err := GetAppConfig(m.AppId)
if err != nil {
return err
}
Db := GetMysqlDB(AppCfg.ServerName, AppCfg.MysqlName, m.ServerId)
_, err = Db.Exec("INSERT INTO system_mail_info (`title`, `content`, `start_time`, `end_time`, `items`, `register_time`, `mail_type`, `to_uids`, `create_time`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", m.Title, m.Content, m.StartTime, m.EndTime, m.Items, m.RegisterTime, m.MailType, m.ToUids, m.CreateTime)
if err != nil {
return fmt.Errorf("failed to insert mail: %v", err)
}
go func() {
ws, err := getWebsocket(m.AppId, m.ServerId)
if err != nil {
log.Printf("failed to get websocket: %v", err)
}
defer ws.Close()
req := &msg.ReqReloadServerMail{}
_, err = util.SendAdminMsg(ws, req)
if err != nil {
log.Printf("failed to send admin message: %v", err)
}
}()
return nil
}

238
model/server.go Normal file
View File

@ -0,0 +1,238 @@
package model
import (
"backend/common"
"backend/msg"
util "backend/uitl"
"encoding/json"
"fmt"
"log"
"github.com/Ullaakut/nmap"
)
const (
DEVOPS_SERVER = "log"
)
type Server struct {
AppId int `json:"AppId"`
ServerId int `json:"ServerId"`
ServerName string `json:"ServerName"`
Status int `json:"Status"`
OpenServerTime int `json:"OpenServerTime"`
Type int `json:"Type"`
}
type App struct {
AppId int `json:"AppId" db:"AppId"`
AppName string `json:"AppName" db:"AppName"`
Update int `json:"Update" db:"Update"`
}
type ServerInfo struct {
AppId int `json:"AppId" db:"AppId"`
ServerId int `json:"ServerId" db:"ServerId"`
ServerName string `json:"ServerName" db:"ServerName"`
Status int `json:"Status" db:"Status"`
CreateTime int `json:"CreateTime" db:"CreateTime"`
OpenServerTime int `json:"OpenServerTime" db:"OpenServerTime"`
StartTime int64 `json:"StartTime"`
PlayerNum int `json:"PlayerNum"`
}
func (s *Server) AppList() ([]*App, error) {
Db := GetGameDB()
var app []*App
err := Db.Select(&app, "SELECT `AppId`, `AppName`, `Update` FROM app")
if err != nil {
return nil, fmt.Errorf("failed to scan rows: %v", err)
}
return app, nil
}
func (s *Server) ServerList() ([]*ServerInfo, error) {
Db := GetGameDB()
var server []*ServerInfo
err := Db.Select(&server, "SELECT `AppId`, `ServerId`, `ServerName`, `Status`, `CreateTime`, `OpenServerTime` FROM server where AppId = ?", s.AppId)
if err != nil {
return nil, fmt.Errorf("failed to scan rows: %v", err)
}
if s.Type == 1 {
return server, nil
}
for _, v := range server {
ws, err := getWebsocket(v.AppId, v.ServerId)
if err != nil {
v.Status = 0
continue
}
req := &msg.ReqServerInfo{}
reqBuf := util.PackMsg(req)
_, err = ws.Write(reqBuf)
if err != nil {
continue
}
readbuf := make([]byte, 4096)
n, err := ws.Read(readbuf)
if err != nil {
continue
}
resBuf, _ := util.UnpackMsg(readbuf, n)
// fmt.Println(string(resBuf))
r := make(map[string]interface{})
json.Unmarshal(resBuf, &r)
if r["StartTime"] != nil {
v.StartTime = int64(r["StartTime"].(float64))
}
if r["PlayerNum"] != nil {
v.PlayerNum = int(r["PlayerNum"].(float64))
}
v.Status = 1
ws.Close()
}
return server, nil
}
func (s *Server) AddServer() error {
Db := GetGameDB()
_, err := Db.Exec("INSERT INTO server (`AppId`, `ServerId`, `ServerName`, `Status`, `CreateTime`, `OpenServerTime`) VALUES (?, ?, ?, ?, ?, ?)",
s.AppId, s.ServerId, s.ServerName, s.Status, util.Now(), s.OpenServerTime)
if err != nil {
return fmt.Errorf("failed to insert: %v", err)
}
return nil
}
func (s *Server) UpdateApp() (string, error) {
AppConfig, err := GetAppConfig(s.AppId)
if err != nil {
return "", err
}
ServerConfig, err := common.GetServerConfig(DEVOPS_SERVER)
if err != nil {
return "", err
}
SshClient, err := NewSshClient(ServerConfig)
if err != nil {
return "", err
}
_, err = SshClient.RunCommand("cd /data/devops && git pull")
if err != nil {
return "", fmt.Errorf("failed to git pull: %v", err)
}
cmd := fmt.Sprintf("ansible-playbook /data/devops/playbook/%s.yml -i /data/devops/playbook/hosts", AppConfig.AppName)
output, err := SshClient.RunCommand(cmd)
if err != nil {
return "", err
}
DB := GetGameDB()
DB.Exec("UPDATE app SET `Update` = ? WHERE `AppId` = ?", util.Now(), s.AppId)
common.SendFeishuMsg(fmt.Sprintf("AppName: %s, 执行文件更新完成", AppConfig.AppName))
return output, nil
}
func (s *Server) UpdateAppFeishu() (string, error) {
AppConfig, err := GetAppConfig(s.AppId)
if err != nil {
return "", err
}
ServerConfig, err := common.GetServerConfig(DEVOPS_SERVER)
if err != nil {
return "", err
}
SshClient, err := NewSshClient(ServerConfig)
if err != nil {
return "", err
}
_, err = SshClient.RunCommand("cd /data/devops && git pull")
if err != nil {
return "", fmt.Errorf("failed to git pull: %v", err)
}
cmd := fmt.Sprintf("ansible-playbook /data/devops/playbook/%s.yml -i /data/devops/playbook/hosts", AppConfig.AppName)
output, err := SshClient.RunCommand(cmd)
if err != nil {
return "", err
}
DB := GetGameDB()
DB.Exec("UPDATE app SET `Update` = ? WHERE `AppId` = ?", util.Now(), s.AppId)
common.SendFeishuMsg(fmt.Sprintf("AppName: %s, 执行文件更新完成", AppConfig.AppName))
return output, nil
}
func (s *Server) RestartServer() (string, error) {
AppConfig, err := GetAppConfig(s.AppId)
if err != nil {
return "", err
}
ServerConfig, err := common.GetServerConfig(AppConfig.ServerName)
if err != nil {
return "", err
}
SshClient, err := NewSshClient(ServerConfig)
if err != nil {
return "", err
}
cmd := fmt.Sprintf("%s/tool/tool restart node %d", AppConfig.Path, s.ServerId)
output, err := SshClient.RunCommand(cmd)
if err != nil {
return "", err
}
common.SendFeishuMsg(fmt.Sprintf("AppName: %s, ServerName: %s, 重启完成", AppConfig.AppName, s.ServerName))
return output, nil
}
func (s *Server) ReloadServer() (string, error) {
AppConfig, err := GetAppConfig(s.AppId)
if err != nil {
return "", err
}
ws, err := getWebsocket(s.AppId, s.ServerId)
if err != nil {
return "", fmt.Errorf("failed to get websocket: %v", err)
}
defer ws.Close()
req := &msg.ReqReload{}
_, err = util.SendAdminMsg(ws, req)
if err != nil {
log.Printf("failed to send admin message: %v", err)
}
common.SendFeishuMsg(fmt.Sprintf("AppName: %s, ServerName: %s, 配置重载完成", AppConfig.AppName, s.ServerName))
return "success", nil
}
// 端口扫描
func PortMap(Ip string, Port string) error {
// 创建一个新的Nmap扫描器
scanner, err := nmap.NewScanner(
nmap.WithTargets(Ip), // 替换为你要扫描的目标IP地址
nmap.WithPorts(Port), // 替换为你要扫描的端口
)
if err != nil {
return err
}
// 执行扫描
result, warnings, err := scanner.Run()
if err != nil {
return err
}
// 打印扫描结果
for _, warning := range warnings {
log.Printf("警告: %v", warning)
}
for _, host := range result.Hosts {
if len(host.Ports) == 0 || host.Status.State != "up" {
return fmt.Errorf("主机不可达")
}
}
return nil
}

45
model/ssh.go Normal file
View File

@ -0,0 +1,45 @@
package model
import (
"backend/common"
"fmt"
"golang.org/x/crypto/ssh"
)
type SshClient struct {
client *ssh.Client
}
func NewSshClient(config *common.SshConfig) (*SshClient, error) {
sshconfig := &ssh.ClientConfig{
User: config.Username,
Auth: []ssh.AuthMethod{
ssh.Password(config.Password),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
addr := fmt.Sprintf("%s:%d", config.Host, config.Port)
client, err := ssh.Dial("tcp", addr, sshconfig)
if err != nil {
return nil, err
}
return &SshClient{client: client}, nil
}
func (s *SshClient) RunCommand(cmd string) (string, error) {
session, err := s.client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
output, err := session.CombinedOutput(cmd)
if err != nil {
return "", err
}
return string(output), nil
}

35
model/websocket.go Normal file
View File

@ -0,0 +1,35 @@
package model
import (
"backend/common"
"fmt"
"golang.org/x/net/websocket"
)
func getWebsocket(AppId, ServerId int) (*websocket.Conn, error) {
App, err := GetAppConfig(AppId)
if err != nil {
return nil, err
}
Server, err := common.GetServerConfig(App.ServerName)
if err != nil {
return nil, err
}
origin := "http://localhost/"
url := fmt.Sprintf("ws://%s:%d/", Server.Host, App.WsPort+ServerId)
config, err := websocket.NewConfig(url, origin)
if err != nil {
return nil, fmt.Errorf("failed to create websocket config: %v", err)
}
// config.Dialer = &net.Dialer{
// Timeout: 5 * time.Second,
// }
ws, err := websocket.DialConfig(config)
if err != nil {
return nil, fmt.Errorf("failed to connect to websocket: %v", err)
}
return ws, nil
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,3 +1,6 @@
system:
nmap: true
feishu_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/70e24a79-b019-434a-b4d1-4592bbf7c311'
mysqls:
- host: '127.0.0.1'
name: 'merge_pet_test'

View File

@ -1,2 +1,2 @@
cd /data/backend
GOOS=linux GOARCH=amd64 go build -o /data/backend/release/backend main.go
cd /data/admin_backend
GOOS=linux GOARCH=amd64 go build -o /data/admin_backend/release/backend main.go

View File

@ -2,9 +2,13 @@ package util
import (
"backend/msg"
"encoding/json"
"fmt"
"reflect"
"strconv"
"time"
"golang.org/x/net/websocket"
"google.golang.org/protobuf/proto"
)
@ -68,3 +72,25 @@ func Int(a interface{}) int {
}
return 0
}
func Now() int64 {
return time.Now().Unix()
}
func SendAdminMsg(ws *websocket.Conn, req proto.Message) (map[string]interface{}, error) {
reqBuf := PackMsg(req)
_, err := ws.Write(reqBuf)
if err != nil {
return nil, fmt.Errorf("failed to write to websocket: %v", err)
}
readbuf := make([]byte, 4096)
n, err := ws.Read(readbuf)
if err != nil {
return nil, fmt.Errorf("failed to read from websocket: %v", err)
}
resBuf, _ := UnpackMsg(readbuf, n)
r := make(map[string]interface{})
json.Unmarshal(resBuf, &r)
return r, nil
}