2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-07 10:37:14 +08:00

feat: 数据库同步及fail2ban重构

This commit is contained in:
耗子
2024-11-22 22:46:26 +08:00
parent e1f074f116
commit 214b804062
38 changed files with 287 additions and 230 deletions

2
go.mod
View File

@@ -35,6 +35,7 @@ require (
github.com/mholt/acmez/v2 v2.0.3
github.com/orandin/slog-gorm v1.4.0
github.com/robfig/cron/v3 v3.0.1
github.com/samber/do/v2 v2.0.0-beta.7
github.com/samber/lo v1.47.0
github.com/sethvargo/go-limiter v1.0.0
github.com/shirou/gopsutil v2.21.11+incompatible
@@ -76,6 +77,7 @@ require (
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/samber/go-type-to-string v1.4.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1033 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1033 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect

4
go.sum
View File

@@ -130,6 +130,10 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/samber/do/v2 v2.0.0-beta.7 h1:tmdLOVSCbTA6uGWLU5poi/nZvMRh5QxXFJ9vHytU+Jk=
github.com/samber/do/v2 v2.0.0-beta.7/go.mod h1:+LpV3vu4L81Q1JMZNSkMvSkW9lt4e5eJoXoZHkeBS4c=
github.com/samber/go-type-to-string v1.4.0 h1:KXphToZgiFdnJQxryU25brhlh/CqY/cwJVeX2rfmow0=
github.com/samber/go-type-to-string v1.4.0/go.mod h1:jpU77vIDoIxkahknKDoEx9C8bQ1ADnh2sotZ8I4QqBU=
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/sethvargo/go-limiter v1.0.0 h1:JqW13eWEMn0VFv86OKn8wiYJY/m250WoXdrjRV0kLe4=

View File

@@ -15,7 +15,6 @@ import (
"github.com/TheTNB/panel/internal/data"
"github.com/TheTNB/panel/internal/service"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/os"
"github.com/TheTNB/panel/pkg/shell"
)
@@ -55,7 +54,6 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
continue
}
jailEnabled := strings.Contains(jailRaw, "enabled = true")
jailLogPath := regexp.MustCompile(`logpath = (.*)`).FindStringSubmatch(jailRaw)
jailMaxRetry := regexp.MustCompile(`maxretry = (.*)`).FindStringSubmatch(jailRaw)
jailFindTime := regexp.MustCompile(`findtime = (.*)`).FindStringSubmatch(jailRaw)
jailBanTime := regexp.MustCompile(`bantime = (.*)`).FindStringSubmatch(jailRaw)
@@ -63,7 +61,6 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
jails = append(jails, Jail{
Name: jailName,
Enabled: jailEnabled,
LogPath: jailLogPath[1],
MaxRetry: cast.ToInt(jailMaxRetry[1]),
FindTime: cast.ToInt(jailFindTime[1]),
BanTime: cast.ToInt(jailBanTime[1]),
@@ -128,7 +125,6 @@ port = ` + ports + `
maxretry = ` + jailMaxRetry + `
findtime = ` + jailFindTime + `
bantime = ` + jailBanTime + `
action = %(action_mwl)s
logpath = ` + app.Root + `/wwwlogs/` + website.Name + `.log
# ` + jailWebsiteName + `-` + jailWebsiteMode + `-END
`
@@ -158,25 +154,17 @@ ignoreregex =
}
case "service":
var logPath string
var filter string
var port string
var err error
switch jailName {
case "ssh":
if os.IsDebian() || os.IsUbuntu() {
logPath = "/var/log/auth.log"
} else {
logPath = "/var/log/secure"
}
filter = "sshd"
port, err = shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
case "mysql":
logPath = app.Root + "/server/mysql/mysql-error.log"
filter = "mysqld-auth"
port, err = shell.Execf("cat %s/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'", app.Root)
case "pure-ftpd":
logPath = "/var/log/messages"
filter = "pure-ftpd"
port, err = shell.Execf(`cat %s/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`, app.Root)
default:
@@ -197,8 +185,6 @@ port = ` + port + `
maxretry = ` + jailMaxRetry + `
findtime = ` + jailFindTime + `
bantime = ` + jailBanTime + `
action = %(action_mwl)s
logpath = ` + logPath + `
# ` + jailName + `-END
`
raw += rule
@@ -208,7 +194,7 @@ logpath = ` + logPath + `
}
}
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
if _, err = shell.Execf("fail2ban-client reload"); err != nil {
service.Error(w, http.StatusInternalServerError, "重载配置失败")
return
}

View File

@@ -3,7 +3,6 @@ package fail2ban
type Jail struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
LogPath string `json:"log_path"`
MaxRetry int `json:"max_retry"`
FindTime int `json:"find_time"`
BanTime int `json:"ban_time"`

View File

@@ -15,7 +15,6 @@ func init() {
r.Get("/load", service.Load)
r.Get("/config", service.GetConfig)
r.Post("/config", service.UpdateConfig)
r.Get("/errorLog", service.ErrorLog)
r.Post("/clearErrorLog", service.ClearErrorLog)
r.Get("/slowLog", service.SlowLog)
r.Post("/clearSlowLog", service.ClearSlowLog)

View File

@@ -142,14 +142,9 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
service.Success(w, load)
}
// ErrorLog 获取错误日志
func (s *Service) ErrorLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/mysql/mysql-error.log", app.Root))
}
// ClearErrorLog 清空错误日志
func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("echo '' > %s/server/mysql/mysql-error.log", app.Root); err != nil {
if err := systemctl.LogsClear("mysqld"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}

View File

@@ -66,4 +66,5 @@ type DatabaseRepo interface {
Create(req *request.DatabaseCreate) error
Update(req *request.DatabaseUpdate) error
Delete(id uint) error
Add(serverID uint, name string) error
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/expr-lang/expr"
"github.com/go-rat/utils/collect"
"github.com/hashicorp/go-version"
"github.com/samber/do/v2"
"github.com/spf13/cast"
"github.com/TheTNB/panel/internal/app"
@@ -19,21 +20,15 @@ import (
)
type appRepo struct {
cacheRepo biz.CacheRepo
taskRepo biz.TaskRepo
api *api.API
api *api.API
}
func NewAppRepo() biz.AppRepo {
return &appRepo{
cacheRepo: NewCacheRepo(),
taskRepo: NewTaskRepo(),
api: api.NewAPI(app.Version),
}
return do.MustInvoke[biz.AppRepo](injector)
}
func (r *appRepo) All() api.Apps {
cached, err := r.cacheRepo.Get(biz.CacheKeyApps)
cached, err := NewCacheRepo().Get(biz.CacheKeyApps)
if err != nil {
return nil
}
@@ -190,7 +185,7 @@ func (r *appRepo) Install(channel, slug string) error {
task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
task.Log = "/tmp/" + item.Slug + ".log"
return r.taskRepo.Push(task)
return NewTaskRepo().Push(task)
}
func (r *appRepo) UnInstall(slug string) error {
@@ -245,7 +240,7 @@ func (r *appRepo) UnInstall(slug string) error {
task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
task.Log = "/tmp/" + item.Slug + ".log"
return r.taskRepo.Push(task)
return NewTaskRepo().Push(task)
}
func (r *appRepo) Update(slug string) error {
@@ -300,7 +295,7 @@ func (r *appRepo) Update(slug string) error {
task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
task.Log = "/tmp/" + item.Slug + ".log"
return r.taskRepo.Push(task)
return NewTaskRepo().Push(task)
}
func (r *appRepo) UpdateShow(slug string, show bool) error {
@@ -331,7 +326,7 @@ func (r *appRepo) UpdateCache() error {
return err
}
return r.cacheRepo.Set(biz.CacheKeyApps, string(encoded))
return NewCacheRepo().Set(biz.CacheKeyApps, string(encoded))
}
func (r *appRepo) preCheck(app *api.App) error {

View File

@@ -9,6 +9,7 @@ import (
"strings"
"time"
"github.com/samber/do/v2"
"github.com/shirou/gopsutil/disk"
"github.com/TheTNB/panel/internal/app"
@@ -20,16 +21,10 @@ import (
"github.com/TheTNB/panel/pkg/types"
)
type backupRepo struct {
setting biz.SettingRepo
website biz.WebsiteRepo
}
type backupRepo struct{}
func NewBackupRepo() biz.BackupRepo {
return &backupRepo{
setting: NewSettingRepo(),
website: NewWebsiteRepo(),
}
return do.MustInvoke[biz.BackupRepo](injector)
}
// List 备份列表
@@ -197,7 +192,7 @@ func (r *backupRepo) ClearExpired(path, prefix string, save int) error {
// GetPath 获取备份路径
func (r *backupRepo) GetPath(typ biz.BackupType) (string, error) {
backupPath, err := r.setting.Get(biz.SettingKeyBackupPath)
backupPath, err := NewSettingRepo().Get(biz.SettingKeyBackupPath)
if err != nil {
return "", err
}
@@ -217,7 +212,7 @@ func (r *backupRepo) GetPath(typ biz.BackupType) (string, error) {
// createWebsite 创建网站备份
func (r *backupRepo) createWebsite(to string, name string) error {
website, err := r.website.GetByName(name)
website, err := NewWebsiteRepo().GetByName(name)
if err != nil {
return err
}
@@ -241,7 +236,7 @@ func (r *backupRepo) createWebsite(to string, name string) error {
// createMySQL 创建 MySQL 备份
func (r *backupRepo) createMySQL(to string, name string) error {
rootPassword, err := r.setting.Get(biz.SettingKeyMySQLRootPassword)
rootPassword, err := NewSettingRepo().Get(biz.SettingKeyMySQLRootPassword)
if err != nil {
return err
}
@@ -370,7 +365,7 @@ func (r *backupRepo) restoreWebsite(backup, target string) error {
return errors.New("备份文件不存在")
}
website, err := r.website.GetByName(target)
website, err := NewWebsiteRepo().GetByName(target)
if err != nil {
return err
}
@@ -397,7 +392,7 @@ func (r *backupRepo) restoreMySQL(backup, target string) error {
return errors.New("备份文件不存在")
}
rootPassword, err := r.setting.Get(biz.SettingKeyMySQLRootPassword)
rootPassword, err := NewSettingRepo().Get(biz.SettingKeyMySQLRootPassword)
if err != nil {
return err
}

View File

@@ -3,6 +3,7 @@ package data
import (
"errors"
"github.com/samber/do/v2"
"gorm.io/gorm"
"github.com/TheTNB/panel/internal/app"
@@ -12,7 +13,7 @@ import (
type cacheRepo struct{}
func NewCacheRepo() biz.CacheRepo {
return &cacheRepo{}
return do.MustInvoke[biz.CacheRepo](injector)
}
func (r *cacheRepo) Get(key biz.CacheKey, defaultValue ...string) (string, error) {

View File

@@ -8,6 +8,8 @@ import (
"strings"
"time"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
@@ -24,7 +26,7 @@ type certRepo struct {
}
func NewCertRepo() biz.CertRepo {
return &certRepo{}
return do.MustInvoke[biz.CertRepo](injector)
}
func (r *certRepo) List(page, limit uint) ([]*types.CertList, int64, error) {

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/go-resty/resty/v2"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
@@ -18,7 +19,7 @@ import (
type certAccountRepo struct{}
func NewCertAccountRepo() biz.CertAccountRepo {
return &certAccountRepo{}
return do.MustInvoke[biz.CertAccountRepo](injector)
}
func (r certAccountRepo) List(page, limit uint) ([]*biz.CertAccount, int64, error) {

View File

@@ -1,6 +1,8 @@
package data
import (
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
@@ -9,7 +11,7 @@ import (
type certDNSRepo struct{}
func NewCertDNSRepo() biz.CertDNSRepo {
return &certDNSRepo{}
return do.MustInvoke[biz.CertDNSRepo](injector)
}
func (r certDNSRepo) List(page, limit uint) ([]*biz.CertDNS, int64, error) {

View File

@@ -1,15 +1,13 @@
package data
import (
"context"
"fmt"
"net"
"net/http"
"slices"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
@@ -22,23 +20,8 @@ type containerRepo struct {
client *resty.Client
}
func NewContainerRepo(sock ...string) biz.ContainerRepo {
if len(sock) == 0 {
sock = append(sock, "/var/run/docker.sock")
}
client := resty.New()
client.SetTimeout(1 * time.Minute)
client.SetRetryCount(2)
client.SetTransport(&http.Transport{
DialContext: func(ctx context.Context, _ string, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", sock[0])
},
})
client.SetBaseURL("http://d/v1.40")
return &containerRepo{
client: client,
}
func NewContainerRepo() biz.ContainerRepo {
return do.MustInvoke[biz.ContainerRepo](injector)
}
// ListAll 列出所有容器

View File

@@ -1,15 +1,13 @@
package data
import (
"context"
"fmt"
"net"
"net/http"
"slices"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
@@ -23,23 +21,8 @@ type containerImageRepo struct {
client *resty.Client
}
func NewContainerImageRepo(sock ...string) biz.ContainerImageRepo {
if len(sock) == 0 {
sock = append(sock, "/var/run/docker.sock")
}
client := resty.New()
client.SetTimeout(1 * time.Minute)
client.SetRetryCount(2)
client.SetTransport(&http.Transport{
DialContext: func(ctx context.Context, _ string, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", sock[0])
},
})
client.SetBaseURL("http://d/v1.40")
return &containerImageRepo{
client: client,
}
func NewContainerImageRepo() biz.ContainerImageRepo {
return do.MustInvoke[biz.ContainerImageRepo](injector)
}
// List 列出镜像

View File

@@ -1,15 +1,13 @@
package data
import (
"context"
"fmt"
"net"
"net/http"
"slices"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
@@ -22,23 +20,8 @@ type containerNetworkRepo struct {
client *resty.Client
}
func NewContainerNetworkRepo(sock ...string) biz.ContainerNetworkRepo {
if len(sock) == 0 {
sock = append(sock, "/var/run/docker.sock")
}
client := resty.New()
client.SetTimeout(1 * time.Minute)
client.SetRetryCount(2)
client.SetTransport(&http.Transport{
DialContext: func(ctx context.Context, _ string, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", sock[0])
},
})
client.SetBaseURL("http://d/v1.40")
return &containerNetworkRepo{
client: client,
}
func NewContainerNetworkRepo() biz.ContainerNetworkRepo {
return do.MustInvoke[biz.ContainerNetworkRepo](injector)
}
// List 列出网络

View File

@@ -1,15 +1,13 @@
package data
import (
"context"
"fmt"
"net"
"net/http"
"slices"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
@@ -23,23 +21,8 @@ type containerVolumeRepo struct {
client *resty.Client
}
func NewContainerVolumeRepo(sock ...string) biz.ContainerVolumeRepo {
if len(sock) == 0 {
sock = append(sock, "/var/run/docker.sock")
}
client := resty.New()
client.SetTimeout(1 * time.Minute)
client.SetRetryCount(2)
client.SetTransport(&http.Transport{
DialContext: func(ctx context.Context, _ string, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", sock[0])
},
})
client.SetBaseURL("http://d/v1.40")
return &containerVolumeRepo{
client: client,
}
func NewContainerVolumeRepo() biz.ContainerVolumeRepo {
return do.MustInvoke[biz.ContainerVolumeRepo](injector)
}
// List 列出存储卷

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/go-rat/utils/str"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
@@ -18,14 +19,10 @@ import (
"github.com/TheTNB/panel/pkg/systemctl"
)
type cronRepo struct {
settingRepo biz.SettingRepo
}
type cronRepo struct{}
func NewCronRepo() biz.CronRepo {
return &cronRepo{
settingRepo: NewSettingRepo(),
}
return do.MustInvoke[biz.CronRepo](injector)
}
func (r *cronRepo) Count() (int64, error) {

View File

@@ -3,20 +3,18 @@ package data
import (
"fmt"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/db"
)
type databaseRepo struct {
databaseServer biz.DatabaseServerRepo
}
type databaseRepo struct{}
func NewDatabaseRepo() biz.DatabaseRepo {
return &databaseRepo{
databaseServer: NewDatabaseServerRepo(),
}
return do.MustInvoke[biz.DatabaseRepo](injector)
}
func (r databaseRepo) Count() (int64, error) {
@@ -45,7 +43,7 @@ func (r databaseRepo) Get(id uint) (*biz.Database, error) {
}
func (r databaseRepo) Create(req *request.DatabaseCreate) error {
server, err := r.databaseServer.Get(req.ServerID)
server, err := NewDatabaseServerRepo().Get(req.ServerID)
if err != nil {
return err
}
@@ -107,3 +105,15 @@ func (r databaseRepo) Update(req *request.DatabaseUpdate) error {
func (r databaseRepo) Delete(id uint) error {
return app.Orm.Delete(&biz.Database{}, id).Error
}
func (r databaseRepo) Add(serverID uint, name string) error {
database := &biz.Database{
Name: name,
Username: name,
ServerID: serverID,
Status: biz.DatabaseStatusNormal,
Remark: "sync from server",
}
return app.Orm.Create(database).Error
}

View File

@@ -6,16 +6,20 @@ import (
"slices"
"strings"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/db"
)
type databaseServerRepo struct{}
type databaseServerRepo struct {
database biz.DatabaseRepo
}
func NewDatabaseServerRepo() biz.DatabaseServerRepo {
return &databaseServerRepo{}
return do.MustInvoke[biz.DatabaseServerRepo](injector)
}
func (r databaseServerRepo) Count() (int64, error) {
@@ -115,11 +119,12 @@ func (r databaseServerRepo) Delete(id uint) error {
}
func (r databaseServerRepo) Sync(id uint) error {
/*server, err := r.Get(id)
server, err := r.Get(id)
if err != nil {
return err
}
dbRepo := NewDatabaseRepo()
switch server.Type {
case biz.DatabaseTypeMysql:
mysql, err := db.NewMySQL(server.Username, server.Password, fmt.Sprintf("%s:%d", server.Host, server.Port))
@@ -131,18 +136,10 @@ func (r databaseServerRepo) Sync(id uint) error {
return err
}
for database := range slices.Values(databases) {
db := &biz.Database{
Name: database.Name,
Username: server.Username,
Password: server.Password,
ServerID: server.ID,
Status: biz.DatabaseStatusInvalid,
}
if err := app.Orm.Where("name = ? AND server_id = ?", database.Name, server.ID).First(db).Error; err != nil {
app.Orm.Create(db)
if err = dbRepo.Add(id, database); err != nil {
return err
}
}
case biz.DatabaseTypePostgresql:
postgres, err := db.NewPostgres(server.Username, server.Password, server.Host, server.Port)
if err != nil {
@@ -152,7 +149,12 @@ func (r databaseServerRepo) Sync(id uint) error {
if err != nil {
return err
}
}*/
for database := range slices.Values(databases) {
if err = dbRepo.Add(id, database.Name); err != nil {
return err
}
}
}
return nil
}

117
internal/data/init.go Normal file
View File

@@ -0,0 +1,117 @@
package data
import (
"context"
"net"
"net/http"
"time"
"github.com/go-rat/utils/hash"
"github.com/go-resty/resty/v2"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/pkg/api"
"github.com/TheTNB/panel/pkg/os"
)
var injector = do.New()
func init() {
do.Provide(injector, func(i do.Injector) (biz.AppRepo, error) {
return &appRepo{
api: api.NewAPI(app.Version),
}, nil
})
do.Provide(injector, func(i do.Injector) (biz.BackupRepo, error) {
return &backupRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.CacheRepo, error) {
return &cacheRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.CertRepo, error) {
return &certRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.CertAccountRepo, error) {
return &certAccountRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.CertDNSRepo, error) {
return &certDNSRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.ContainerRepo, error) {
return &containerRepo{
client: getDockerClient("/var/run/docker.sock"),
}, nil
})
do.Provide(injector, func(i do.Injector) (biz.ContainerImageRepo, error) {
return &containerImageRepo{
client: getDockerClient("/var/run/docker.sock"),
}, nil
})
do.Provide(injector, func(i do.Injector) (biz.ContainerNetworkRepo, error) {
return &containerNetworkRepo{
client: getDockerClient("/var/run/docker.sock"),
}, nil
})
do.Provide(injector, func(i do.Injector) (biz.ContainerVolumeRepo, error) {
return &containerVolumeRepo{
client: getDockerClient("/var/run/docker.sock"),
}, nil
})
do.Provide(injector, func(i do.Injector) (biz.CronRepo, error) {
return &cronRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.DatabaseServerRepo, error) {
return &databaseServerRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.DatabaseRepo, error) {
return &databaseRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.MonitorRepo, error) {
return &monitorRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.SafeRepo, error) {
var ssh string
if os.IsRHEL() {
ssh = "sshd"
} else {
ssh = "ssh"
}
return &safeRepo{
ssh: ssh,
}, nil
})
do.Provide(injector, func(i do.Injector) (biz.SettingRepo, error) {
return &settingRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.SSHRepo, error) {
return &sshRepo{}, nil
})
do.Provide(injector, func(i do.Injector) (biz.TaskRepo, error) {
task := &taskRepo{}
task.DispatchWaiting()
return task, nil
})
do.Provide(injector, func(i do.Injector) (biz.UserRepo, error) {
return &userRepo{
hasher: hash.NewArgon2id(),
}, nil
})
do.Provide(injector, func(i do.Injector) (biz.WebsiteRepo, error) {
return &websiteRepo{}, nil
})
}
func getDockerClient(sock string) *resty.Client {
client := resty.New()
client.SetTimeout(1 * time.Minute)
client.SetRetryCount(2)
client.SetTransport(&http.Transport{
DialContext: func(ctx context.Context, _ string, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", sock)
},
})
client.SetBaseURL("http://d/v1.40")
return client
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"time"
"github.com/samber/do/v2"
"github.com/spf13/cast"
"github.com/TheTNB/panel/internal/app"
@@ -11,22 +12,19 @@ import (
"github.com/TheTNB/panel/internal/http/request"
)
type monitorRepo struct {
settingRepo biz.SettingRepo
}
type monitorRepo struct{}
func NewMonitorRepo() biz.MonitorRepo {
return &monitorRepo{
settingRepo: NewSettingRepo(),
}
return do.MustInvoke[biz.MonitorRepo](injector)
}
func (r monitorRepo) GetSetting() (*request.MonitorSetting, error) {
monitor, err := r.settingRepo.Get(biz.SettingKeyMonitor)
repo := NewSettingRepo()
monitor, err := repo.Get(biz.SettingKeyMonitor)
if err != nil {
return nil, err
}
monitorDays, err := r.settingRepo.Get(biz.SettingKeyMonitorDays)
monitorDays, err := repo.Get(biz.SettingKeyMonitorDays)
if err != nil {
return nil, err
}
@@ -39,10 +37,11 @@ func (r monitorRepo) GetSetting() (*request.MonitorSetting, error) {
}
func (r monitorRepo) UpdateSetting(setting *request.MonitorSetting) error {
if err := r.settingRepo.Set(biz.SettingKeyMonitor, cast.ToString(setting.Enabled)); err != nil {
repo := NewSettingRepo()
if err := repo.Set(biz.SettingKeyMonitor, cast.ToString(setting.Enabled)); err != nil {
return err
}
if err := r.settingRepo.Set(biz.SettingKeyMonitorDays, cast.ToString(setting.Days)); err != nil {
if err := repo.Set(biz.SettingKeyMonitorDays, cast.ToString(setting.Days)); err != nil {
return err
}

View File

@@ -4,11 +4,11 @@ import (
"fmt"
"strings"
"github.com/samber/do/v2"
"github.com/spf13/cast"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/pkg/firewall"
"github.com/TheTNB/panel/pkg/os"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
)
@@ -18,16 +18,7 @@ type safeRepo struct {
}
func NewSafeRepo() biz.SafeRepo {
var ssh string
if os.IsRHEL() {
ssh = "sshd"
} else {
ssh = "ssh"
}
return &safeRepo{
ssh: ssh,
}
return do.MustInvoke[biz.SafeRepo](injector)
}
func (r *safeRepo) GetSSH() (uint, bool, error) {

View File

@@ -8,6 +8,7 @@ import (
"slices"
"github.com/go-rat/utils/hash"
"github.com/samber/do/v2"
"github.com/spf13/cast"
"gopkg.in/yaml.v3"
"gorm.io/gorm"
@@ -24,14 +25,10 @@ import (
"github.com/TheTNB/panel/pkg/types"
)
type settingRepo struct {
taskRepo biz.TaskRepo
}
type settingRepo struct{}
func NewSettingRepo() biz.SettingRepo {
return &settingRepo{
taskRepo: NewTaskRepo(),
}
return do.MustInvoke[biz.SettingRepo](injector)
}
func (r *settingRepo) Get(key biz.SettingKey, defaultValue ...string) (string, error) {
@@ -170,12 +167,13 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P
}
// 下面是需要需要重启的设置
task := NewTaskRepo()
// 面板HTTPS
restartFlag := false
oldCert, _ := io.Read(filepath.Join(app.Root, "panel/storage/cert.pem"))
oldKey, _ := io.Read(filepath.Join(app.Root, "panel/storage/cert.key"))
if oldCert != setting.Cert || oldKey != setting.Key {
if r.taskRepo.HasRunningTask() {
if task.HasRunningTask() {
return false, errors.New("后台任务正在运行,禁止修改部分设置,请稍后再试")
}
restartFlag = true
@@ -232,7 +230,7 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P
return false, err
}
if raw != string(encoded) {
if r.taskRepo.HasRunningTask() {
if task.HasRunningTask() {
return false, errors.New("后台任务正在运行,禁止修改部分设置,请稍后再试")
}
restartFlag = true

View File

@@ -3,20 +3,18 @@ package data
import (
"fmt"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
pkgssh "github.com/TheTNB/panel/pkg/ssh"
)
type sshRepo struct {
settingRepo biz.SettingRepo
}
type sshRepo struct{}
func NewSSHRepo() biz.SSHRepo {
return &sshRepo{
settingRepo: NewSettingRepo(),
}
return do.MustInvoke[biz.SSHRepo](injector)
}
func (r *sshRepo) List(page, limit uint) ([]*biz.SSH, int64, error) {

View File

@@ -3,21 +3,18 @@ package data
import (
"fmt"
"log/slog"
"sync"
"github.com/samber/do/v2"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/queuejob"
)
var taskDispatchOnce sync.Once
type taskRepo struct{}
func NewTaskRepo() biz.TaskRepo {
task := &taskRepo{}
taskDispatchOnce.Do(task.DispatchWaiting)
return &taskRepo{}
return do.MustInvoke[biz.TaskRepo](injector)
}
func (r *taskRepo) HasRunningTask() bool {

View File

@@ -4,6 +4,7 @@ import (
"errors"
"github.com/go-rat/utils/hash"
"github.com/samber/do/v2"
"gorm.io/gorm"
"github.com/TheTNB/panel/internal/app"
@@ -15,9 +16,7 @@ type userRepo struct {
}
func NewUserRepo() biz.UserRepo {
return &userRepo{
hasher: hash.NewArgon2id(),
}
return do.MustInvoke[biz.UserRepo](injector)
}
func (r *userRepo) Create(username, password string) (*biz.User, error) {

View File

@@ -9,6 +9,7 @@ import (
"strings"
"time"
"github.com/samber/do/v2"
"github.com/samber/lo"
"github.com/spf13/cast"
@@ -30,7 +31,7 @@ import (
type websiteRepo struct{}
func NewWebsiteRepo() biz.WebsiteRepo {
return &websiteRepo{}
return do.MustInvoke[biz.WebsiteRepo](injector)
}
func (r *websiteRepo) UpdateDefaultConfig(req *request.WebsiteDefaultConfig) error {

View File

@@ -76,6 +76,7 @@ func Http(r chi.Router) {
r.Post("/", database.Create)
r.Put("/{id}", database.Update)
r.Delete("/{id}", database.Delete)
r.Post("/{id}/sync", database.Sync)
})
r.Route("/backup", func(r chi.Router) {

View File

@@ -156,14 +156,14 @@ func (m *MySQL) Users() ([]types.MySQLUser, error) {
return users, nil
}
func (m *MySQL) Databases() ([]types.MySQLDatabase, error) {
func (m *MySQL) Databases() ([]string, error) {
rows, err := m.Query("SHOW DATABASES")
if err != nil {
return nil, err
}
defer rows.Close()
var databases []types.MySQLDatabase
var databases []string
for rows.Next() {
var database string
if err := rows.Scan(&database); err != nil {
@@ -172,9 +172,7 @@ func (m *MySQL) Databases() ([]types.MySQLDatabase, error) {
if slices.Contains([]string{"information_schema", "performance_schema", "mysql", "sys"}, database) {
continue
}
databases = append(databases, types.MySQLDatabase{
Name: database,
})
databases = append(databases, database)
}
return databases, nil
}

View File

@@ -28,6 +28,7 @@ var (
StrategyAccept Strategy = "accept" // 接受
StrategyDrop Strategy = "drop" // 丢弃
StrategyReject Strategy = "reject" // 拒绝
StrategyMark Strategy = "mark" // 标记
)
type Direction string
@@ -44,7 +45,7 @@ type FireInfo struct {
PortStart uint `json:"port_start"` // 1-65535
PortEnd uint `json:"port_end"` // 1-65535
Protocol Protocol `json:"protocol"` // tcp udp tcp/udp
Strategy Strategy `json:"strategy"` // accept drop reject
Strategy Strategy `json:"strategy"` // accept drop reject mark
Direction Direction `json:"direction"` // in out 入站或出站
}

View File

@@ -23,7 +23,7 @@ type Firewall struct {
func NewFirewall() *Firewall {
firewall := &Firewall{
forwardListRegex: regexp.MustCompile(`^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$`),
richRuleRegex: regexp.MustCompile(`^rule family="([^"]+)"(?: .*?(source|destination) address="([^"]+)")?(?: .*?port port="([^"]+)")?(?: .*?protocol(?: value)?="([^"]+)")?.*?(accept|drop|reject)$`),
richRuleRegex: regexp.MustCompile(`^rule family="([^"]+)"(?: .*?(source|destination) address="([^"]+)")?(?: .*?port port="([^"]+)")?(?: .*?protocol(?: value)?="([^"]+)")?.*?(accept|drop|reject|mark).*?$`),
}
return firewall

View File

@@ -71,3 +71,22 @@ func Disable(name string) error {
_, err := shell.Execf("systemctl disable %s", name)
return err
}
// Logs 获取服务日志
func Logs(name string) (string, error) {
return shell.Execf("journalctl -u %s", name)
}
// LogsTail 获取服务日志
func LogsTail(name string, lines int) (string, error) {
return shell.Execf("journalctl -u %s --lines %d", name, lines)
}
// LogsClear 清空服务日志
func LogsClear(name string) error {
if _, err := shell.Execf("journalctl --rotate -u %s", name); err != nil {
return err
}
_, err := shell.Execf("journalctl --vacuum-time=1s -u %s", name)
return err
}

View File

@@ -5,7 +5,3 @@ type MySQLUser struct {
Host string `json:"host"`
Grants []string `json:"grants"`
}
type MySQLDatabase struct {
Name string `json:"name"`
}

View File

@@ -5,7 +5,11 @@ import type { LogInst } from 'naive-ui'
const props = defineProps({
path: {
type: String,
required: true
required: false
},
service: {
type: String,
required: false
}
})
@@ -14,7 +18,15 @@ const logRef = ref<LogInst | null>(null)
let logWs: WebSocket | null = null
const init = async () => {
const cmd = `tail -n 100 -f '${props.path}'`
let cmd = ''
if (props.path) {
cmd = `tail -n 100 -f '${props.path}'`
} else if (props.service) {
cmd = `journalctl -u '${props.service}' -f`
} else {
window.$message.error('path 或 service 不能为空')
return
}
ws.exec(cmd)
.then((ws: WebSocket) => {
logWs = ws

View File

@@ -13,7 +13,6 @@ const currentTab = ref('status')
const status = ref(false)
const isEnabled = ref(false)
const config = ref('')
const errorLog = ref('')
const slowLog = ref('')
const rootPassword = ref('')
@@ -65,11 +64,6 @@ const getRootPassword = async () => {
})
}
const getErrorLog = async () => {
const { data } = await mysql.errorLog()
return data
}
const getSlowLog = async () => {
const { data } = await mysql.slowLog()
return data
@@ -142,9 +136,6 @@ onMounted(() => {
getLoad().then((res) => {
load.value = res
})
getErrorLog().then((res) => {
errorLog.value = res
})
getSlowLog().then((res) => {
slowLog.value = res
})
@@ -266,7 +257,7 @@ onMounted(() => {
/>
</n-tab-pane>
<n-tab-pane name="error-log" tab="错误日志">
<realtime-log :path="errorLog" />
<realtime-log service="mysqld" />
</n-tab-pane>
<n-tab-pane name="slow-log" tab="慢查询日志">
<realtime-log :path="slowLog" />

View File

@@ -53,7 +53,13 @@ const columns: any = [
NTag,
{
type:
row.strategy === 'accept' ? 'success' : row.strategy === 'drop' ? 'warning' : 'error'
row.strategy === 'accept'
? 'success'
: row.strategy === 'drop'
? 'warning'
: row.strategy === 'reject'
? 'error'
: 'default'
},
{
default: () => {
@@ -64,6 +70,8 @@ const columns: any = [
return '丢弃'
case 'reject':
return '拒绝'
case 'mark':
return '标记'
default:
return '未知'
}

View File

@@ -87,7 +87,13 @@ const columns: any = [
NTag,
{
type:
row.strategy === 'accept' ? 'success' : row.strategy === 'drop' ? 'warning' : 'error'
row.strategy === 'accept'
? 'success'
: row.strategy === 'drop'
? 'warning'
: row.strategy === 'reject'
? 'error'
: 'default'
},
{
default: () => {
@@ -98,6 +104,8 @@ const columns: any = [
return '丢弃'
case 'reject':
return '拒绝'
case 'mark':
return '标记'
default:
return '未知'
}