mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 07:57:21 +08:00
feat: 添加翻译
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
@@ -24,13 +25,15 @@ import (
|
||||
)
|
||||
|
||||
type certRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
client *acme.Client
|
||||
}
|
||||
|
||||
func NewCertRepo(db *gorm.DB, log *slog.Logger) biz.CertRepo {
|
||||
func NewCertRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger) biz.CertRepo {
|
||||
return &certRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
@@ -86,10 +89,10 @@ func (r *certRepo) GetByWebsite(WebsiteID uint) (*biz.Cert, error) {
|
||||
func (r *certRepo) Upload(req *request.CertUpload) (*biz.Cert, error) {
|
||||
info, err := pkgcert.ParseCert(req.Cert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: %v", err)
|
||||
return nil, errors.New(r.t.Get("failed to parse certificate: %v", err))
|
||||
}
|
||||
if _, err = pkgcert.ParseKey(req.Key); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse private key: %v", err)
|
||||
return nil, errors.New(r.t.Get("failed to parse private key: %v", err))
|
||||
}
|
||||
|
||||
cert := &biz.Cert{
|
||||
@@ -126,7 +129,7 @@ func (r *certRepo) Update(req *request.CertUpdate) error {
|
||||
req.Domains = info.DNSNames
|
||||
}
|
||||
if req.Type == "upload" && req.AutoRenew {
|
||||
return errors.New("upload certificate cannot be set to auto renew")
|
||||
return errors.New(r.t.Get("upload certificate cannot be set to auto renew"))
|
||||
}
|
||||
|
||||
return r.db.Model(&biz.Cert{}).Where("id = ?", req.ID).Select("*").Updates(&biz.Cert{
|
||||
@@ -162,11 +165,11 @@ func (r *certRepo) ObtainAuto(id uint) (*acme.Certificate, error) {
|
||||
client.UseDns(cert.DNS.Type, cert.DNS.Data)
|
||||
} else {
|
||||
if cert.Website == nil {
|
||||
return nil, errors.New("this certificate is not associated with a website and cannot be signed. You can try to sign it manually")
|
||||
return nil, errors.New(r.t.Get("this certificate is not associated with a website and cannot be signed. You can try to sign it manually"))
|
||||
} else {
|
||||
for _, domain := range cert.Domains {
|
||||
if strings.Contains(domain, "*") {
|
||||
return nil, errors.New("wildcard domains cannot use HTTP verification")
|
||||
return nil, errors.New(r.t.Get("wildcard domains cannot use HTTP verification"))
|
||||
}
|
||||
}
|
||||
conf := fmt.Sprintf("%s/server/vhost/acme/%s.conf", app.Root, cert.Website.Name)
|
||||
@@ -204,7 +207,7 @@ func (r *certRepo) ObtainManual(id uint) (*acme.Certificate, error) {
|
||||
}
|
||||
|
||||
if r.client == nil {
|
||||
return nil, errors.New("please retry the manual obtain operation")
|
||||
return nil, errors.New(r.t.Get("please retry the manual obtain operation"))
|
||||
}
|
||||
|
||||
ssl, err := r.client.ObtainCertificateManual()
|
||||
@@ -270,18 +273,18 @@ func (r *certRepo) Renew(id uint) (*acme.Certificate, error) {
|
||||
}
|
||||
|
||||
if cert.CertURL == "" {
|
||||
return nil, errors.New("this certificate has not been signed successfully and cannot be renewed")
|
||||
return nil, errors.New(r.t.Get("this certificate has not been signed successfully and cannot be renewed"))
|
||||
}
|
||||
|
||||
if cert.DNS != nil {
|
||||
client.UseDns(cert.DNS.Type, cert.DNS.Data)
|
||||
} else {
|
||||
if cert.Website == nil {
|
||||
return nil, errors.New("this certificate is not associated with a website and cannot be signed. You can try to sign it manually")
|
||||
return nil, errors.New(r.t.Get("this certificate is not associated with a website and cannot be signed. You can try to sign it manually"))
|
||||
} else {
|
||||
for _, domain := range cert.Domains {
|
||||
if strings.Contains(domain, "*") {
|
||||
return nil, errors.New("wildcard domains cannot use HTTP verification")
|
||||
return nil, errors.New(r.t.Get("wildcard domains cannot use HTTP verification"))
|
||||
}
|
||||
}
|
||||
conf := fmt.Sprintf("%s/server/vhost/acme/%s.conf", app.Root, cert.Website.Name)
|
||||
@@ -345,7 +348,7 @@ func (r *certRepo) Deploy(ID, WebsiteID uint) error {
|
||||
}
|
||||
|
||||
if cert.Cert == "" || cert.Key == "" {
|
||||
return errors.New("this certificate has not been signed successfully and cannot be deployed")
|
||||
return errors.New(r.t.Get("this certificate has not been signed successfully and cannot be deployed"))
|
||||
}
|
||||
|
||||
website := new(biz.Website)
|
||||
@@ -400,7 +403,7 @@ func (r *certRepo) runScript(cert *biz.Cert) error {
|
||||
|
||||
func (r *certRepo) getClient(cert *biz.Cert) (*acme.Client, error) {
|
||||
if cert.Account == nil {
|
||||
return nil, errors.New("this certificate is not associated with an ACME account and cannot be signed")
|
||||
return nil, errors.New(r.t.Get("this certificate is not associated with an ACME account and cannot be signed"))
|
||||
}
|
||||
|
||||
var ca string
|
||||
|
||||
@@ -3,11 +3,11 @@ package data
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
@@ -17,13 +17,15 @@ import (
|
||||
)
|
||||
|
||||
type certAccountRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
user biz.UserRepo
|
||||
}
|
||||
|
||||
func NewCertAccountRepo(db *gorm.DB, user biz.UserRepo, log *slog.Logger) biz.CertAccountRepo {
|
||||
func NewCertAccountRepo(t *gotext.Locale, db *gorm.DB, user biz.UserRepo, log *slog.Logger) biz.CertAccountRepo {
|
||||
return &certAccountRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
user: user,
|
||||
@@ -31,7 +33,7 @@ func NewCertAccountRepo(db *gorm.DB, user biz.UserRepo, log *slog.Logger) biz.Ce
|
||||
}
|
||||
|
||||
func (r certAccountRepo) List(page, limit uint) ([]*biz.CertAccount, int64, error) {
|
||||
var accounts []*biz.CertAccount
|
||||
accounts := make([]*biz.CertAccount, 0)
|
||||
var total int64
|
||||
err := r.db.Model(&biz.CertAccount{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&accounts).Error
|
||||
return accounts, total, err
|
||||
@@ -99,16 +101,16 @@ func (r certAccountRepo) Create(req *request.CertAccountCreate) (*biz.CertAccoun
|
||||
case "sslcom":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CASSLcom, &acme.EAB{KeyID: account.Kid, MACKey: account.HmacEncoded}, acme.KeyType(account.KeyType), r.log)
|
||||
default:
|
||||
return nil, errors.New("unsupported CA")
|
||||
return nil, errors.New(r.t.Get("unsupported CA"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to register account: %v", err)
|
||||
return nil, errors.New(r.t.Get("failed to register account: %v", err))
|
||||
}
|
||||
|
||||
privateKey, err := cert.EncodeKey(client.Account.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to get private key")
|
||||
return nil, errors.New(r.t.Get("failed to get private key"))
|
||||
}
|
||||
account.PrivateKey = string(privateKey)
|
||||
|
||||
@@ -158,16 +160,16 @@ func (r certAccountRepo) Update(req *request.CertAccountUpdate) error {
|
||||
case "sslcom":
|
||||
client, err = acme.NewRegisterAccount(context.Background(), account.Email, acme.CASSLcom, &acme.EAB{KeyID: account.Kid, MACKey: account.HmacEncoded}, acme.KeyType(account.KeyType), r.log)
|
||||
default:
|
||||
return errors.New("unsupported CA")
|
||||
return errors.New(r.t.Get("unsupported CA"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.New("failed to register account")
|
||||
return errors.New(r.t.Get("failed to register account: %v", err))
|
||||
}
|
||||
|
||||
privateKey, err := cert.EncodeKey(client.Account.PrivateKey)
|
||||
if err != nil {
|
||||
return errors.New("failed to get private key")
|
||||
return errors.New(r.t.Get("failed to get private key: %v", err))
|
||||
}
|
||||
account.PrivateKey = string(privateKey)
|
||||
|
||||
@@ -193,11 +195,11 @@ func (r certAccountRepo) getGoogleEAB() (*acme.EAB, error) {
|
||||
|
||||
resp, err := client.R().SetResult(&data{}).Get("https://gts.rat.dev/eab")
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return &acme.EAB{}, fmt.Errorf("failed to get Google EAB: %v", err)
|
||||
return &acme.EAB{}, errors.New(r.t.Get("failed to get Google EAB: %v", err))
|
||||
}
|
||||
eab := resp.Result().(*data)
|
||||
if eab.Message != "success" {
|
||||
return &acme.EAB{}, fmt.Errorf("failed to get Google EAB: %s", eab.Message)
|
||||
return &acme.EAB{}, errors.New(r.t.Get("failed to get Google EAB: %s", eab.Message))
|
||||
}
|
||||
|
||||
return &acme.EAB{KeyID: eab.Data.KeyId, MACKey: eab.Data.MacKey}, nil
|
||||
@@ -218,11 +220,11 @@ func (r certAccountRepo) getZeroSSLEAB(email string) (*acme.EAB, error) {
|
||||
"email": email,
|
||||
}).SetResult(&data{}).Post("https://api.zerossl.com/acme/eab-credentials-email")
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return &acme.EAB{}, fmt.Errorf("failed to get ZeroSSL EAB: %v", err)
|
||||
return &acme.EAB{}, errors.New(r.t.Get("failed to get ZeroSSL EAB: %v", err))
|
||||
}
|
||||
eab := resp.Result().(*data)
|
||||
if !eab.Success {
|
||||
return &acme.EAB{}, fmt.Errorf("failed to get ZeroSSL EAB")
|
||||
return &acme.EAB{}, errors.New(r.t.Get("failed to get ZeroSSL EAB"))
|
||||
}
|
||||
|
||||
return &acme.EAB{KeyID: eab.EabKid, MACKey: eab.EabHmacKey}, nil
|
||||
|
||||
@@ -18,7 +18,7 @@ func NewCertDNSRepo(db *gorm.DB) biz.CertDNSRepo {
|
||||
}
|
||||
|
||||
func (r certDNSRepo) List(page, limit uint) ([]*biz.CertDNS, int64, error) {
|
||||
var certDNS []*biz.CertDNS
|
||||
certDNS := make([]*biz.CertDNS, 0)
|
||||
var total int64
|
||||
err := r.db.Model(&biz.CertDNS{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&certDNS).Error
|
||||
return certDNS, total, err
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-rat/utils/str"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
@@ -20,11 +21,13 @@ import (
|
||||
)
|
||||
|
||||
type cronRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewCronRepo(db *gorm.DB) biz.CronRepo {
|
||||
func NewCronRepo(t *gotext.Locale, db *gorm.DB) biz.CronRepo {
|
||||
return &cronRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
@@ -39,7 +42,7 @@ func (r *cronRepo) Count() (int64, error) {
|
||||
}
|
||||
|
||||
func (r *cronRepo) List(page, limit uint) ([]*biz.Cron, int64, error) {
|
||||
var cron []*biz.Cron
|
||||
cron := make([]*biz.Cron, 0)
|
||||
var total int64
|
||||
err := r.db.Model(&biz.Cron{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&cron).Error
|
||||
return cron, total, err
|
||||
@@ -61,8 +64,6 @@ func (r *cronRepo) Create(req *request.CronCreate) error {
|
||||
script = fmt.Sprintf(`#!/bin/bash
|
||||
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
|
||||
|
||||
# 耗子面板 - 网站备份脚本
|
||||
|
||||
panel-cli backup website -n '%s' -p '%s'
|
||||
panel-cli backup clear -t website -f '%s' -s '%d' -p '%s'
|
||||
systemctl reload nginx
|
||||
@@ -72,8 +73,6 @@ systemctl reload nginx
|
||||
script = fmt.Sprintf(`#!/bin/bash
|
||||
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
|
||||
|
||||
# 耗子面板 - 数据库备份脚本
|
||||
|
||||
panel-cli backup database -t '%s' -n '%s' -p '%s'
|
||||
panel-cli backup clear -t '%s' -f '%s' -s '%d' -p '%s'
|
||||
`, req.BackupType, req.Target, req.BackupPath, req.BackupType, req.Target, req.Save, req.BackupPath)
|
||||
@@ -83,9 +82,6 @@ panel-cli backup clear -t '%s' -f '%s' -s '%d' -p '%s'
|
||||
script = fmt.Sprintf(`#!/bin/bash
|
||||
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
|
||||
|
||||
# 耗子面板 - 日志切割脚本
|
||||
|
||||
# 执行切割
|
||||
panel-cli cutoff website -n '%s' -p '%s'
|
||||
panel-cli cutoff clear -t website -f '%s' -s '%d' -p '%s'
|
||||
`, req.Target, req.BackupPath, req.Target, req.Save, req.BackupPath)
|
||||
@@ -97,18 +93,17 @@ panel-cli cutoff clear -t website -f '%s' -s '%d' -p '%s'
|
||||
shellDir := fmt.Sprintf("%s/server/cron/", app.Root)
|
||||
shellLogDir := fmt.Sprintf("%s/server/cron/logs/", app.Root)
|
||||
if !io.Exists(shellDir) {
|
||||
return errors.New("计划任务目录不存在")
|
||||
return errors.New(r.t.Get("cron directory %s not exists", shellDir))
|
||||
}
|
||||
if !io.Exists(shellLogDir) {
|
||||
return errors.New("计划任务日志目录不存在")
|
||||
return errors.New(r.t.Get("cron log directory %s not exists", shellLogDir))
|
||||
}
|
||||
shellFile := strconv.Itoa(int(time.Now().Unix())) + str.Random(16)
|
||||
if err := io.Write(filepath.Join(shellDir, shellFile+".sh"), script, 0700); err != nil {
|
||||
return errors.New(err.Error())
|
||||
}
|
||||
if out, err := shell.Execf("dos2unix %s%s.sh", shellDir, shellFile); err != nil {
|
||||
return errors.New(out)
|
||||
}
|
||||
// 编码转换
|
||||
_, _ = shell.Execf("dos2unix %s%s.sh", shellDir, shellFile)
|
||||
|
||||
cron := new(biz.Cron)
|
||||
cron.Name = req.Name
|
||||
@@ -134,10 +129,6 @@ func (r *cronRepo) Update(req *request.CronUpdate) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !cron.Status {
|
||||
return errors.New("计划任务已禁用")
|
||||
}
|
||||
|
||||
cron.Time = req.Time
|
||||
cron.Name = req.Name
|
||||
if err = r.db.Save(cron).Error; err != nil {
|
||||
@@ -227,5 +218,5 @@ func (r *cronRepo) restartCron() error {
|
||||
return systemctl.Restart("cron")
|
||||
}
|
||||
|
||||
return errors.New("不支持的系统")
|
||||
return errors.New(r.t.Get("unsupported system"))
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
@@ -13,13 +14,15 @@ import (
|
||||
)
|
||||
|
||||
type databaseRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
server biz.DatabaseServerRepo
|
||||
user biz.DatabaseUserRepo
|
||||
}
|
||||
|
||||
func NewDatabaseRepo(db *gorm.DB, server biz.DatabaseServerRepo, user biz.DatabaseUserRepo) biz.DatabaseRepo {
|
||||
func NewDatabaseRepo(t *gotext.Locale, db *gorm.DB, server biz.DatabaseServerRepo, user biz.DatabaseUserRepo) biz.DatabaseRepo {
|
||||
return &databaseRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
server: server,
|
||||
user: user,
|
||||
@@ -179,7 +182,7 @@ func (r databaseRepo) Comment(req *request.DatabaseComment) error {
|
||||
|
||||
switch server.Type {
|
||||
case biz.DatabaseTypeMysql:
|
||||
return errors.New("mysql not support database comment")
|
||||
return errors.New(r.t.Get("mysql not support database comment"))
|
||||
case biz.DatabaseTypePostgresql:
|
||||
postgres, err := db.NewPostgres(server.Username, server.Password, server.Host, server.Port)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
@@ -13,12 +15,14 @@ import (
|
||||
)
|
||||
|
||||
type databaseServerRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewDatabaseServerRepo(db *gorm.DB, log *slog.Logger) biz.DatabaseServerRepo {
|
||||
func NewDatabaseServerRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger) biz.DatabaseServerRepo {
|
||||
return &databaseServerRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
@@ -34,7 +38,7 @@ func (r databaseServerRepo) Count() (int64, error) {
|
||||
}
|
||||
|
||||
func (r databaseServerRepo) List(page, limit uint) ([]*biz.DatabaseServer, int64, error) {
|
||||
var databaseServer []*biz.DatabaseServer
|
||||
databaseServer := make([]*biz.DatabaseServer, 0)
|
||||
var total int64
|
||||
err := r.db.Model(&biz.DatabaseServer{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&databaseServer).Error
|
||||
|
||||
@@ -79,7 +83,7 @@ func (r databaseServerRepo) Create(req *request.DatabaseServerCreate) error {
|
||||
}
|
||||
|
||||
if !r.checkServer(databaseServer) {
|
||||
return fmt.Errorf("check server connection failed")
|
||||
return errors.New(r.t.Get("check server connection failed"))
|
||||
}
|
||||
|
||||
return r.db.Create(databaseServer).Error
|
||||
@@ -99,7 +103,7 @@ func (r databaseServerRepo) Update(req *request.DatabaseServerUpdate) error {
|
||||
server.Remark = req.Remark
|
||||
|
||||
if !r.checkServer(server) {
|
||||
return fmt.Errorf("check server connection failed")
|
||||
return errors.New(r.t.Get("check server connection failed"))
|
||||
}
|
||||
|
||||
return r.db.Save(server).Error
|
||||
@@ -154,7 +158,7 @@ func (r databaseServerRepo) Sync(id uint) error {
|
||||
ServerID: id,
|
||||
Username: user.User,
|
||||
Host: user.Host,
|
||||
Remark: fmt.Sprintf("sync from server %s", server.Name),
|
||||
Remark: r.t.Get("sync from server %s", server.Name),
|
||||
}
|
||||
if err = r.db.Create(newUser).Error; err != nil {
|
||||
r.log.Warn("sync database user failed", slog.Any("err", err))
|
||||
@@ -180,7 +184,7 @@ func (r databaseServerRepo) Sync(id uint) error {
|
||||
newUser := &biz.DatabaseUser{
|
||||
ServerID: id,
|
||||
Username: user.Role,
|
||||
Remark: fmt.Sprintf("sync from server %s", server.Name),
|
||||
Remark: r.t.Get("sync from server %s", server.Name),
|
||||
}
|
||||
r.db.Create(newUser)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
@@ -12,12 +13,14 @@ import (
|
||||
)
|
||||
|
||||
type databaseUserRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
server biz.DatabaseServerRepo
|
||||
}
|
||||
|
||||
func NewDatabaseUserRepo(db *gorm.DB, server biz.DatabaseServerRepo) biz.DatabaseUserRepo {
|
||||
func NewDatabaseUserRepo(t *gotext.Locale, db *gorm.DB, server biz.DatabaseServerRepo) biz.DatabaseUserRepo {
|
||||
return &databaseUserRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
server: server,
|
||||
}
|
||||
@@ -33,7 +36,7 @@ func (r databaseUserRepo) Count() (int64, error) {
|
||||
}
|
||||
|
||||
func (r databaseUserRepo) List(page, limit uint) ([]*biz.DatabaseUser, int64, error) {
|
||||
var user []*biz.DatabaseUser
|
||||
user := make([]*biz.DatabaseUser, 0)
|
||||
var total int64
|
||||
err := r.db.Model(&biz.DatabaseUser{}).Preload("Server").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&user).Error
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
@@ -56,14 +55,10 @@ func (r monitorRepo) Clear() error {
|
||||
}
|
||||
|
||||
func (r monitorRepo) List(start, end time.Time) ([]*biz.Monitor, error) {
|
||||
var monitors []*biz.Monitor
|
||||
monitors := make([]*biz.Monitor, 0)
|
||||
if err := r.db.Where("created_at BETWEEN ? AND ?", start, end).Find(&monitors).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(monitors) == 0 {
|
||||
return nil, errors.New("没有找到数据")
|
||||
}
|
||||
|
||||
return monitors, nil
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package data
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-rat/utils/hash"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/spf13/cast"
|
||||
"gopkg.in/yaml.v3"
|
||||
"gorm.io/gorm"
|
||||
@@ -23,13 +23,15 @@ import (
|
||||
)
|
||||
|
||||
type settingRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
conf *koanf.Koanf
|
||||
task biz.TaskRepo
|
||||
}
|
||||
|
||||
func NewSettingRepo(db *gorm.DB, conf *koanf.Koanf, task biz.TaskRepo) biz.SettingRepo {
|
||||
func NewSettingRepo(t *gotext.Locale, db *gorm.DB, conf *koanf.Koanf, task biz.TaskRepo) biz.SettingRepo {
|
||||
return &settingRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
conf: conf,
|
||||
task: task,
|
||||
@@ -186,15 +188,15 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P
|
||||
oldKey, _ := io.Read(filepath.Join(app.Root, "panel/storage/cert.key"))
|
||||
if oldCert != setting.Cert || oldKey != setting.Key {
|
||||
if r.task.HasRunningTask() {
|
||||
return false, errors.New("后台任务正在运行,禁止修改部分设置,请稍后再试")
|
||||
return false, errors.New(r.t.Get("background task is running, modifying some settings is prohibited, please try again later"))
|
||||
}
|
||||
restartFlag = true
|
||||
}
|
||||
if _, err := cert.ParseCert(setting.Cert); err != nil {
|
||||
return false, fmt.Errorf("failed to parse certificate: %w", err)
|
||||
return false, errors.New(r.t.Get("failed to parse certificate: %w", err))
|
||||
}
|
||||
if _, err := cert.ParseKey(setting.Key); err != nil {
|
||||
return false, fmt.Errorf("failed to parse private key: %w", err)
|
||||
return false, errors.New(r.t.Get("failed to parse private key: %w", err))
|
||||
}
|
||||
if err := io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), setting.Cert, 0644); err != nil {
|
||||
return false, err
|
||||
@@ -215,7 +217,7 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P
|
||||
|
||||
if setting.Port != config.HTTP.Port {
|
||||
if os.TCPPortInUse(setting.Port) {
|
||||
return false, errors.New("端口已被占用")
|
||||
return false, errors.New(r.t.Get("port is already in use"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +245,7 @@ func (r *settingRepo) UpdatePanelSetting(ctx context.Context, setting *request.P
|
||||
}
|
||||
if raw != string(encoded) {
|
||||
if r.task.HasRunningTask() {
|
||||
return false, errors.New("后台任务正在运行,禁止修改部分设置,请稍后再试")
|
||||
return false, errors.New(r.t.Get("background task is running, modifying some settings is prohibited, please try again later"))
|
||||
}
|
||||
restartFlag = true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
@@ -11,17 +13,19 @@ import (
|
||||
)
|
||||
|
||||
type sshRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewSSHRepo(db *gorm.DB) biz.SSHRepo {
|
||||
func NewSSHRepo(t *gotext.Locale, db *gorm.DB) biz.SSHRepo {
|
||||
return &sshRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *sshRepo) List(page, limit uint) ([]*biz.SSH, int64, error) {
|
||||
var ssh []*biz.SSH
|
||||
ssh := make([]*biz.SSH, 0)
|
||||
var total int64
|
||||
err := r.db.Model(&biz.SSH{}).Omit("Hosts").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&ssh).Error
|
||||
return ssh, total, err
|
||||
@@ -46,7 +50,7 @@ func (r *sshRepo) Create(req *request.SSHCreate) error {
|
||||
}
|
||||
_, err := pkgssh.NewSSHClient(conf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check ssh connection: %v", err)
|
||||
return errors.New(r.t.Get("failed to check ssh connection: %v", err))
|
||||
}
|
||||
|
||||
ssh := &biz.SSH{
|
||||
@@ -70,7 +74,7 @@ func (r *sshRepo) Update(req *request.SSHUpdate) error {
|
||||
}
|
||||
_, err := pkgssh.NewSSHClient(conf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check ssh connection: %v", err)
|
||||
return errors.New(r.t.Get("failed to check ssh connection: %v", err))
|
||||
}
|
||||
|
||||
ssh := &biz.SSH{
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
@@ -12,13 +13,15 @@ import (
|
||||
)
|
||||
|
||||
type taskRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
queue *queue.Queue
|
||||
}
|
||||
|
||||
func NewTaskRepo(db *gorm.DB, log *slog.Logger, queue *queue.Queue) biz.TaskRepo {
|
||||
func NewTaskRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger, queue *queue.Queue) biz.TaskRepo {
|
||||
return &taskRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
queue: queue,
|
||||
@@ -32,7 +35,7 @@ func (r *taskRepo) HasRunningTask() bool {
|
||||
}
|
||||
|
||||
func (r *taskRepo) List(page, limit uint) ([]*biz.Task, int64, error) {
|
||||
var tasks []*biz.Task
|
||||
tasks := make([]*biz.Task, 0)
|
||||
var total int64
|
||||
err := r.db.Model(&biz.Task{}).Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&tasks).Error
|
||||
return tasks, total, err
|
||||
@@ -53,12 +56,13 @@ func (r *taskRepo) UpdateStatus(id uint, status biz.TaskStatus) error {
|
||||
}
|
||||
|
||||
func (r *taskRepo) Push(task *biz.Task) error {
|
||||
// 防止有人喜欢酒吧点炒饭
|
||||
var count int64
|
||||
if err := r.db.Model(&biz.Task{}).Where("shell = ? and (status = ? or status = ?)", task.Shell, biz.TaskStatusWaiting, biz.TaskStatusRunning).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return fmt.Errorf("duplicate submission, please wait for the previous task to end")
|
||||
return errors.New(r.t.Get("duplicate submission, please wait for the previous task to end"))
|
||||
}
|
||||
|
||||
if err := r.db.Create(task).Error; err != nil {
|
||||
|
||||
@@ -4,18 +4,21 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-rat/utils/hash"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
)
|
||||
|
||||
type userRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
hasher hash.Hasher
|
||||
}
|
||||
|
||||
func NewUserRepo(db *gorm.DB) biz.UserRepo {
|
||||
func NewUserRepo(t *gotext.Locale, db *gorm.DB) biz.UserRepo {
|
||||
return &userRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
hasher: hash.NewArgon2id(),
|
||||
}
|
||||
@@ -42,14 +45,14 @@ func (r *userRepo) CheckPassword(username, password string) (*biz.User, error) {
|
||||
user := new(biz.User)
|
||||
if err := r.db.Where("username = ?", username).First(user).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errors.New("用户名或密码错误")
|
||||
return nil, errors.New(r.t.Get("username or password error"))
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !r.hasher.Check(password, user.Password) {
|
||||
return nil, errors.New("用户名或密码错误")
|
||||
return nil, errors.New(r.t.Get("username or password error"))
|
||||
}
|
||||
|
||||
return user, nil
|
||||
|
||||
@@ -208,7 +208,7 @@ func (r *websiteRepo) GetByName(name string) (*types.WebsiteSetting, error) {
|
||||
}
|
||||
|
||||
func (r *websiteRepo) List(page, limit uint) ([]*biz.Website, int64, error) {
|
||||
var websites []*biz.Website
|
||||
websites := make([]*biz.Website, 0)
|
||||
var total int64
|
||||
|
||||
if err := r.db.Model(&biz.Website{}).Count(&total).Error; err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
@@ -11,13 +12,15 @@ import (
|
||||
)
|
||||
|
||||
type AppService struct {
|
||||
t *gotext.Locale
|
||||
appRepo biz.AppRepo
|
||||
cacheRepo biz.CacheRepo
|
||||
settingRepo biz.SettingRepo
|
||||
}
|
||||
|
||||
func NewAppService(app biz.AppRepo, cache biz.CacheRepo, setting biz.SettingRepo) *AppService {
|
||||
func NewAppService(t *gotext.Locale, app biz.AppRepo, cache biz.CacheRepo, setting biz.SettingRepo) *AppService {
|
||||
return &AppService{
|
||||
t: t,
|
||||
appRepo: app,
|
||||
cacheRepo: cache,
|
||||
settingRepo: setting,
|
||||
@@ -167,7 +170,7 @@ func (s *AppService) IsInstalled(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *AppService) UpdateCache(w http.ResponseWriter, r *http.Request) {
|
||||
if offline, _ := s.settingRepo.GetBool(biz.SettingKeyOfflineMode); offline {
|
||||
Error(w, http.StatusForbidden, "离线模式下无法更新应用列表缓存")
|
||||
Error(w, http.StatusForbidden, s.t.Get("Unable to update app list cache in offline mode"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"slices"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
@@ -15,11 +16,13 @@ import (
|
||||
)
|
||||
|
||||
type BackupService struct {
|
||||
t *gotext.Locale
|
||||
backupRepo biz.BackupRepo
|
||||
}
|
||||
|
||||
func NewBackupService(backup biz.BackupRepo) *BackupService {
|
||||
func NewBackupService(t *gotext.Locale, backup biz.BackupRepo) *BackupService {
|
||||
return &BackupService{
|
||||
t: t,
|
||||
backupRepo: backup,
|
||||
}
|
||||
}
|
||||
@@ -71,7 +74,7 @@ func (s *BackupService) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// 只允许上传 .sql .zip .tar .gz .tgz .bz2 .xz .7z
|
||||
if !slices.Contains([]string{".sql", ".zip", ".tar", ".gz", ".tgz", ".bz2", ".xz", ".7z"}, filepath.Ext(req.File.Filename)) {
|
||||
Error(w, http.StatusForbidden, "unsupported file type")
|
||||
Error(w, http.StatusForbidden, s.t.Get("unsupported file type"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -81,19 +84,19 @@ func (s *BackupService) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if io.Exists(filepath.Join(path, req.File.Filename)) {
|
||||
Error(w, http.StatusForbidden, "target backup %s already exists", path)
|
||||
Error(w, http.StatusForbidden, s.t.Get("target backup %s already exists", path))
|
||||
return
|
||||
}
|
||||
|
||||
src, _ := req.File.Open()
|
||||
out, err := os.OpenFile(filepath.Join(path, req.File.Filename), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "open file error: %v", err)
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = stdio.Copy(out, src); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "write file error: %v", err)
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
@@ -12,21 +13,19 @@ import (
|
||||
)
|
||||
|
||||
type CertService struct {
|
||||
t *gotext.Locale
|
||||
certRepo biz.CertRepo
|
||||
}
|
||||
|
||||
func NewCertService(cert biz.CertRepo) *CertService {
|
||||
func NewCertService(t *gotext.Locale, cert biz.CertRepo) *CertService {
|
||||
return &CertService{
|
||||
t: t,
|
||||
certRepo: cert,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CertService) CAProviders(w http.ResponseWriter, r *http.Request) {
|
||||
Success(w, []types.LV{
|
||||
{
|
||||
Label: "GoogleCN(推荐)",
|
||||
Value: "googlecn",
|
||||
},
|
||||
{
|
||||
Label: "Let's Encrypt",
|
||||
Value: "letsencrypt",
|
||||
@@ -39,6 +38,10 @@ func (s *CertService) CAProviders(w http.ResponseWriter, r *http.Request) {
|
||||
Label: "SSL.com",
|
||||
Value: "sslcom",
|
||||
},
|
||||
{
|
||||
Label: "GoogleCN",
|
||||
Value: "googlecn",
|
||||
},
|
||||
{
|
||||
Label: "Google",
|
||||
Value: "google",
|
||||
@@ -54,67 +57,67 @@ func (s *CertService) CAProviders(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *CertService) DNSProviders(w http.ResponseWriter, r *http.Request) {
|
||||
Success(w, []types.LV{
|
||||
{
|
||||
Label: "阿里云",
|
||||
Label: s.t.Get("Aliyun"),
|
||||
Value: string(acme.AliYun),
|
||||
},
|
||||
{
|
||||
Label: "腾讯云",
|
||||
Label: s.t.Get("Tencent Cloud"),
|
||||
Value: string(acme.Tencent),
|
||||
},
|
||||
{
|
||||
Label: "华为云",
|
||||
Label: s.t.Get("Huawei Cloud"),
|
||||
Value: string(acme.Huawei),
|
||||
},
|
||||
{
|
||||
Label: "西部数码",
|
||||
Label: s.t.Get("West.cn"),
|
||||
Value: string(acme.Westcn),
|
||||
},
|
||||
{
|
||||
Label: "CloudFlare",
|
||||
Label: s.t.Get("CloudFlare"),
|
||||
Value: string(acme.CloudFlare),
|
||||
},
|
||||
{
|
||||
Label: "Godaddy",
|
||||
Label: s.t.Get("Godaddy"),
|
||||
Value: string(acme.Godaddy),
|
||||
},
|
||||
{
|
||||
Label: "Gcore",
|
||||
Label: s.t.Get("Gcore"),
|
||||
Value: string(acme.Gcore),
|
||||
},
|
||||
{
|
||||
Label: "Porkbun",
|
||||
Label: s.t.Get("Porkbun"),
|
||||
Value: string(acme.Porkbun),
|
||||
},
|
||||
{
|
||||
Label: "Namecheap",
|
||||
Label: s.t.Get("Namecheap"),
|
||||
Value: string(acme.Namecheap),
|
||||
},
|
||||
{
|
||||
Label: "NameSilo",
|
||||
Label: s.t.Get("NameSilo"),
|
||||
Value: string(acme.NameSilo),
|
||||
},
|
||||
{
|
||||
Label: "Name.com",
|
||||
Label: s.t.Get("Name.com"),
|
||||
Value: string(acme.Namecom),
|
||||
},
|
||||
{
|
||||
Label: "ClouDNS",
|
||||
Label: s.t.Get("ClouDNS"),
|
||||
Value: string(acme.ClouDNS),
|
||||
},
|
||||
{
|
||||
Label: "Duck DNS",
|
||||
Label: s.t.Get("Duck DNS"),
|
||||
Value: string(acme.DuckDNS),
|
||||
},
|
||||
{
|
||||
Label: "Hetzner",
|
||||
Label: s.t.Get("Hetzner"),
|
||||
Value: string(acme.Hetzner),
|
||||
},
|
||||
{
|
||||
Label: "Linode",
|
||||
Label: s.t.Get("Linode"),
|
||||
Value: string(acme.Linode),
|
||||
},
|
||||
{
|
||||
Label: "Vercel",
|
||||
Label: s.t.Get("Vercel"),
|
||||
Value: string(acme.Vercel),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/go-rat/utils/hash"
|
||||
"github.com/go-rat/utils/str"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/urfave/cli/v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
|
||||
type CliService struct {
|
||||
hr string
|
||||
t *gotext.Locale
|
||||
api *api.API
|
||||
conf *koanf.Koanf
|
||||
db *gorm.DB
|
||||
@@ -45,10 +47,11 @@ type CliService struct {
|
||||
hash hash.Hasher
|
||||
}
|
||||
|
||||
func NewCliService(conf *koanf.Koanf, db *gorm.DB, appRepo biz.AppRepo, cache biz.CacheRepo, user biz.UserRepo, setting biz.SettingRepo, backup biz.BackupRepo, website biz.WebsiteRepo, databaseServer biz.DatabaseServerRepo) *CliService {
|
||||
func NewCliService(t *gotext.Locale, conf *koanf.Koanf, db *gorm.DB, appRepo biz.AppRepo, cache biz.CacheRepo, user biz.UserRepo, setting biz.SettingRepo, backup biz.BackupRepo, website biz.WebsiteRepo, databaseServer biz.DatabaseServerRepo) *CliService {
|
||||
return &CliService{
|
||||
hr: `+----------------------------------------------------`,
|
||||
api: api.NewAPI(app.Version),
|
||||
t: t,
|
||||
conf: conf,
|
||||
db: db,
|
||||
appRepo: appRepo,
|
||||
@@ -67,7 +70,7 @@ func (s *CliService) Restart(ctx context.Context, cmd *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("面板服务已重启")
|
||||
fmt.Println(s.t.Get("Panel service restarted"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -76,7 +79,7 @@ func (s *CliService) Stop(ctx context.Context, cmd *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("面板服务已停止")
|
||||
fmt.Println(s.t.Get("Panel service stopped"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -85,19 +88,19 @@ func (s *CliService) Start(ctx context.Context, cmd *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("面板服务已启动")
|
||||
fmt.Println(s.t.Get("Panel service started"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CliService) Update(ctx context.Context, cmd *cli.Command) error {
|
||||
panel, err := s.api.LatestVersion()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取最新版本失败:%v", err)
|
||||
return errors.New(s.t.Get("Failed to get latest version: %v", err))
|
||||
}
|
||||
|
||||
download := collect.First(panel.Downloads)
|
||||
if download == nil {
|
||||
return fmt.Errorf("下载地址为空")
|
||||
return errors.New(s.t.Get("Download URL is empty"))
|
||||
}
|
||||
|
||||
return s.backupRepo.UpdatePanel(panel.Version, download.URL, download.Checksum)
|
||||
@@ -105,13 +108,14 @@ func (s *CliService) Update(ctx context.Context, cmd *cli.Command) error {
|
||||
|
||||
func (s *CliService) Sync(ctx context.Context, cmd *cli.Command) error {
|
||||
if err := s.cacheRepo.UpdateApps(); err != nil {
|
||||
return fmt.Errorf("同步应用数据失败:%v", err)
|
||||
return errors.New(s.t.Get("Sync app data failed: %v", err))
|
||||
}
|
||||
if err := s.cacheRepo.UpdateRewrites(); err != nil {
|
||||
return fmt.Errorf("同步伪静态规则失败:%v", err)
|
||||
return errors.New(s.t.Get("Sync rewrite rules failed: %v", err))
|
||||
}
|
||||
|
||||
fmt.Println("数据同步成功")
|
||||
fmt.Println(s.t.Get("Sync data successfully"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -122,13 +126,13 @@ func (s *CliService) Fix(ctx context.Context, cmd *cli.Command) error {
|
||||
func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error {
|
||||
user := new(biz.User)
|
||||
if err := s.db.Where("id", 1).First(user).Error; err != nil {
|
||||
return fmt.Errorf("获取管理员信息失败:%v", err)
|
||||
return errors.New(s.t.Get("Failed to get user info: %v", err))
|
||||
}
|
||||
|
||||
password := str.Random(16)
|
||||
hashed, err := s.hash.Make(password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码生成失败:%v", err)
|
||||
return errors.New(s.t.Get("Failed to generate password: %v", err))
|
||||
}
|
||||
user.Username = str.Random(8)
|
||||
user.Password = hashed
|
||||
@@ -137,7 +141,7 @@ func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error {
|
||||
}
|
||||
|
||||
if err = s.db.Save(user).Error; err != nil {
|
||||
return fmt.Errorf("管理员信息保存失败:%v", err)
|
||||
return errors.New(s.t.Get("Failed to save user info: %v", err))
|
||||
}
|
||||
|
||||
protocol := "http"
|
||||
@@ -147,39 +151,39 @@ func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error {
|
||||
|
||||
port := s.conf.String("http.port")
|
||||
if port == "" {
|
||||
return fmt.Errorf("端口获取失败")
|
||||
return errors.New(s.t.Get("Failed to get port"))
|
||||
}
|
||||
entrance := s.conf.String("http.entrance")
|
||||
if entrance == "" {
|
||||
return fmt.Errorf("入口获取失败")
|
||||
return errors.New(s.t.Get("Failed to get entrance"))
|
||||
}
|
||||
|
||||
fmt.Printf("用户名: %s\n", user.Username)
|
||||
fmt.Printf("密码: %s\n", password)
|
||||
fmt.Printf("端口: %s\n", port)
|
||||
fmt.Printf("入口: %s\n", entrance)
|
||||
fmt.Println(s.t.Get("Username: %s", user.Username))
|
||||
fmt.Println(s.t.Get("Password: %s", password))
|
||||
fmt.Println(s.t.Get("Port: %s", port))
|
||||
fmt.Println(s.t.Get("Entrance: %s", entrance))
|
||||
|
||||
lv4, err := tools.GetLocalIPv4()
|
||||
if err == nil {
|
||||
fmt.Printf("本地IPv4地址: %s://%s:%s%s\n", protocol, lv4, port, entrance)
|
||||
fmt.Println(s.t.Get("Local IPv4: %s://%s:%s%s", protocol, lv4, port, entrance))
|
||||
}
|
||||
lv6, err := tools.GetLocalIPv6()
|
||||
if err == nil {
|
||||
fmt.Printf("本地IPv6地址: %s://[%s]:%s%s\n", protocol, lv6, port, entrance)
|
||||
fmt.Println(s.t.Get("Local IPv6: %s://[%s]:%s%s", protocol, lv6, port, entrance))
|
||||
}
|
||||
rv4, err := tools.GetPublicIPv4()
|
||||
if err == nil {
|
||||
fmt.Printf("公网IPv4地址: %s://%s:%s%s\n", protocol, rv4, port, entrance)
|
||||
fmt.Println(s.t.Get("Public IPv4: %s://%s:%s%s", protocol, rv4, port, entrance))
|
||||
}
|
||||
rv6, err := tools.GetPublicIPv6()
|
||||
if err == nil {
|
||||
fmt.Printf("公网IPv6地址: %s://[%s]:%s%s\n", protocol, rv6, port, entrance)
|
||||
fmt.Println(s.t.Get("Public IPv6: %s://[%s]:%s%s", protocol, rv6, port, entrance))
|
||||
}
|
||||
|
||||
fmt.Println("请根据自身网络情况自行选择合适的地址访问面板")
|
||||
fmt.Printf("如无法访问,请检查服务器运营商安全组和防火墙是否放行%s端口\n", port)
|
||||
fmt.Println("若仍无法访问,可尝试运行 panel-cli https off 关闭面板HTTPS")
|
||||
fmt.Println("警告:关闭面板HTTPS后,面板安全性将大大降低,请谨慎操作")
|
||||
fmt.Println(s.t.Get("Please choose the appropriate address to access the panel based on your network situation"))
|
||||
fmt.Println(s.t.Get("If you cannot access, please check whether the server's security group and firewall allow port %s", port))
|
||||
fmt.Println(s.t.Get("If you still cannot access, try running panel-cli https off to turn off panel HTTPS"))
|
||||
fmt.Println(s.t.Get("Warning: After turning off panel HTTPS, the security of the panel will be greatly reduced, please operate with caution"))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -187,11 +191,11 @@ func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error {
|
||||
func (s *CliService) UserList(ctx context.Context, cmd *cli.Command) error {
|
||||
users := make([]biz.User, 0)
|
||||
if err := s.db.Find(&users).Error; err != nil {
|
||||
return fmt.Errorf("获取用户列表失败:%v", err)
|
||||
return errors.New(s.t.Get("Failed to get user list: %v", err))
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
fmt.Printf("ID: %d, 用户名: %s, 邮箱: %s, 创建日期: %s\n", user.ID, user.Username, user.Email, user.CreatedAt.Format(time.DateTime))
|
||||
fmt.Println(s.t.Get("ID: %d, Username: %s, Email: %s, Created At: %s", user.ID, user.Username, user.Email, user.CreatedAt.Format(time.DateTime)))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -202,10 +206,10 @@ func (s *CliService) UserName(ctx context.Context, cmd *cli.Command) error {
|
||||
oldUsername := cmd.Args().Get(0)
|
||||
newUsername := cmd.Args().Get(1)
|
||||
if oldUsername == "" {
|
||||
return fmt.Errorf("旧用户名不能为空")
|
||||
return errors.New(s.t.Get("Old username cannot be empty"))
|
||||
}
|
||||
if newUsername == "" {
|
||||
return fmt.Errorf("新用户名不能为空")
|
||||
return errors.New(s.t.Get("New username cannot be empty"))
|
||||
}
|
||||
|
||||
if err := s.db.Where("username", oldUsername).First(user).Error; err != nil {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/go-rat/utils/collect"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/host"
|
||||
"github.com/spf13/cast"
|
||||
@@ -26,6 +27,7 @@ import (
|
||||
)
|
||||
|
||||
type DashboardService struct {
|
||||
t *gotext.Locale
|
||||
api *api.API
|
||||
conf *koanf.Koanf
|
||||
taskRepo biz.TaskRepo
|
||||
@@ -36,8 +38,9 @@ type DashboardService struct {
|
||||
backupRepo biz.BackupRepo
|
||||
}
|
||||
|
||||
func NewDashboardService(conf *koanf.Koanf, task biz.TaskRepo, website biz.WebsiteRepo, appRepo biz.AppRepo, setting biz.SettingRepo, cron biz.CronRepo, backupRepo biz.BackupRepo) *DashboardService {
|
||||
func NewDashboardService(t *gotext.Locale, conf *koanf.Koanf, task biz.TaskRepo, website biz.WebsiteRepo, appRepo biz.AppRepo, setting biz.SettingRepo, cron biz.CronRepo, backupRepo biz.BackupRepo) *DashboardService {
|
||||
return &DashboardService{
|
||||
t: t,
|
||||
api: api.NewAPI(app.Version),
|
||||
conf: conf,
|
||||
taskRepo: task,
|
||||
@@ -52,7 +55,7 @@ func NewDashboardService(conf *koanf.Koanf, task biz.TaskRepo, website biz.Websi
|
||||
func (s *DashboardService) Panel(w http.ResponseWriter, r *http.Request) {
|
||||
name, _ := s.settingRepo.Get(biz.SettingKeyName)
|
||||
if name == "" {
|
||||
name = "耗子面板"
|
||||
name = s.t.Get("Rat Panel")
|
||||
}
|
||||
|
||||
Success(w, chix.M{
|
||||
@@ -64,7 +67,7 @@ func (s *DashboardService) Panel(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *DashboardService) HomeApps(w http.ResponseWriter, r *http.Request) {
|
||||
apps, err := s.appRepo.GetHomeShow()
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "获取首页应用失败: %v", err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get home apps: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,7 +87,7 @@ func (s *DashboardService) Current(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *DashboardService) SystemInfo(w http.ResponseWriter, r *http.Request) {
|
||||
hostInfo, err := host.Info()
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "获取系统信息失败")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get system info: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -130,7 +133,7 @@ func (s *DashboardService) SystemInfo(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *DashboardService) CountInfo(w http.ResponseWriter, r *http.Request) {
|
||||
websiteCount, err := s.websiteRepo.Count()
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "获取网站数量失败")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get the total number of websites: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -194,8 +197,8 @@ func (s *DashboardService) InstalledDbAndPhp(w http.ResponseWriter, r *http.Requ
|
||||
|
||||
var phpData []types.LVInt
|
||||
var dbData []types.LV
|
||||
phpData = append(phpData, types.LVInt{Value: 0, Label: "不使用"})
|
||||
dbData = append(dbData, types.LV{Value: "0", Label: "不使用"})
|
||||
phpData = append(phpData, types.LVInt{Value: 0, Label: s.t.Get("Not used")})
|
||||
dbData = append(dbData, types.LV{Value: "0", Label: s.t.Get("Not used")})
|
||||
for _, p := range php {
|
||||
// 过滤 phpmyadmin
|
||||
match := regexp.MustCompile(`php(\d+)`).FindStringSubmatch(p.Slug)
|
||||
@@ -222,25 +225,25 @@ func (s *DashboardService) InstalledDbAndPhp(w http.ResponseWriter, r *http.Requ
|
||||
|
||||
func (s *DashboardService) CheckUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
if offline, _ := s.settingRepo.GetBool(biz.SettingKeyOfflineMode); offline {
|
||||
Error(w, http.StatusForbidden, "离线模式下无法检查更新")
|
||||
Error(w, http.StatusForbidden, s.t.Get("unable to check for updates in offline mode"))
|
||||
return
|
||||
}
|
||||
|
||||
current := app.Version
|
||||
latest, err := s.api.LatestVersion()
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "获取最新版本失败")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get the latest version: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
v1, err := version.NewVersion(current)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "版本号解析失败")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to parse version: %v", err))
|
||||
return
|
||||
}
|
||||
v2, err := version.NewVersion(latest.Version)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "版本号解析失败")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to parse version: %v", err))
|
||||
return
|
||||
}
|
||||
if v1.GreaterThanOrEqual(v2) {
|
||||
@@ -257,35 +260,35 @@ func (s *DashboardService) CheckUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *DashboardService) UpdateInfo(w http.ResponseWriter, r *http.Request) {
|
||||
if offline, _ := s.settingRepo.GetBool(biz.SettingKeyOfflineMode); offline {
|
||||
Error(w, http.StatusForbidden, "离线模式下无法检查更新")
|
||||
Error(w, http.StatusForbidden, s.t.Get("unable to check for updates in offline mode"))
|
||||
return
|
||||
}
|
||||
|
||||
current := app.Version
|
||||
latest, err := s.api.LatestVersion()
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "获取最新版本失败")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get the latest version: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
v1, err := version.NewVersion(current)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "版本号解析失败")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to parse version: %v", err))
|
||||
return
|
||||
}
|
||||
v2, err := version.NewVersion(latest.Version)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "版本号解析失败")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to parse version: %v", err))
|
||||
return
|
||||
}
|
||||
if v1.GreaterThanOrEqual(v2) {
|
||||
Error(w, http.StatusInternalServerError, "当前版本已是最新版本")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("the current version is the latest version"))
|
||||
return
|
||||
}
|
||||
|
||||
versions, err := s.api.IntermediateVersions()
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "获取更新信息失败:%v", err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get the update information: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -294,24 +297,24 @@ func (s *DashboardService) UpdateInfo(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *DashboardService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
if offline, _ := s.settingRepo.GetBool(biz.SettingKeyOfflineMode); offline {
|
||||
Error(w, http.StatusForbidden, "离线模式下无法更新")
|
||||
Error(w, http.StatusForbidden, s.t.Get("unable to update in offline mode"))
|
||||
return
|
||||
}
|
||||
|
||||
if s.taskRepo.HasRunningTask() {
|
||||
Error(w, http.StatusInternalServerError, "后台任务正在运行,禁止更新,请稍后再试")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("background task is running, updating is prohibited, please try again later"))
|
||||
return
|
||||
}
|
||||
|
||||
panel, err := s.api.LatestVersion()
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "获取最新版本失败:%v", err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get the latest version: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
download := collect.First(panel.Downloads)
|
||||
if download == nil {
|
||||
Error(w, http.StatusInternalServerError, "获取下载链接失败")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get the latest version download link"))
|
||||
return
|
||||
}
|
||||
ver, url, checksum := panel.Version, download.URL, download.Checksum
|
||||
@@ -330,7 +333,7 @@ func (s *DashboardService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *DashboardService) Restart(w http.ResponseWriter, r *http.Request) {
|
||||
if s.taskRepo.HasRunningTask() {
|
||||
Error(w, http.StatusInternalServerError, "后台任务正在运行,禁止重启,请稍后再试")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("background task is running, restart is prohibited, please try again later"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/go-rat/utils/file"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
@@ -29,11 +30,13 @@ import (
|
||||
)
|
||||
|
||||
type FileService struct {
|
||||
t *gotext.Locale
|
||||
taskRepo biz.TaskRepo
|
||||
}
|
||||
|
||||
func NewFileService(task biz.TaskRepo) *FileService {
|
||||
func NewFileService(t *gotext.Locale, task biz.TaskRepo) *FileService {
|
||||
return &FileService{
|
||||
t: t,
|
||||
taskRepo: task,
|
||||
}
|
||||
}
|
||||
@@ -74,11 +77,11 @@ func (s *FileService) Content(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if fileInfo.IsDir() {
|
||||
Error(w, http.StatusInternalServerError, "target is a directory")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("target is a directory"))
|
||||
return
|
||||
}
|
||||
if fileInfo.Size() > 10*1024*1024 {
|
||||
Error(w, http.StatusInternalServerError, "file is too large, please download it")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("file is too large, please download it to view"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -129,7 +132,7 @@ func (s *FileService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
banned := []string{"/", app.Root, filepath.Join(app.Root, "server"), filepath.Join(app.Root, "panel")}
|
||||
if slices.Contains(banned, req.Path) {
|
||||
Error(w, http.StatusForbidden, "please don't do this")
|
||||
Error(w, http.StatusForbidden, s.t.Get("please don't do this"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -150,17 +153,17 @@ func (s *FileService) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
path := r.FormValue("path")
|
||||
_, handler, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "upload file error: %v", err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("upload file error: %v", err))
|
||||
return
|
||||
}
|
||||
if io.Exists(path) {
|
||||
Error(w, http.StatusForbidden, "target path %s already exists", path)
|
||||
Error(w, http.StatusForbidden, s.t.Get("target path %s already exists", path))
|
||||
return
|
||||
}
|
||||
|
||||
if !io.Exists(filepath.Dir(path)) {
|
||||
if err = stdos.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "create directory error: %v", err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("create directory error: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -168,12 +171,12 @@ func (s *FileService) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
src, _ := handler.Open()
|
||||
out, err := stdos.OpenFile(path, stdos.O_CREATE|stdos.O_RDWR|stdos.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "open file error: %v", err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("open file error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = stdio.Copy(out, src); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "write file error: %v", err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("write file error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -216,7 +219,7 @@ func (s *FileService) Move(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if io.IsDir(item.Source) && strings.HasPrefix(item.Target, item.Source) {
|
||||
Error(w, http.StatusForbidden, "you can't do this, it will be broken")
|
||||
Error(w, http.StatusForbidden, s.t.Get("you can't do this, it will be broken"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -245,7 +248,7 @@ func (s *FileService) Copy(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if io.IsDir(item.Source) && strings.HasPrefix(item.Target, item.Source) {
|
||||
Error(w, http.StatusForbidden, "you can't do this, it will be broken")
|
||||
Error(w, http.StatusForbidden, s.t.Get("you can't do this, it will be broken"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -271,7 +274,7 @@ func (s *FileService) Download(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if info.IsDir() {
|
||||
Error(w, http.StatusInternalServerError, "can't download a directory")
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("can't download a directory"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -289,7 +292,7 @@ func (s *FileService) RemoteDownload(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
timestamp := time.Now().Format("20060102150405")
|
||||
task := new(biz.Task)
|
||||
task.Name = "下载远程文件"
|
||||
task.Name = s.t.Get("Download remote file %v", filepath.Base(req.Path))
|
||||
task.Status = biz.TaskStatusWaiting
|
||||
task.Shell = fmt.Sprintf(`wget -o /tmp/remote-download-%s.log -O '%s' '%s' && chmod 0755 '%s' && chown www:www '%s'`, timestamp, req.Path, req.URL, req.Path, req.Path)
|
||||
task.Log = fmt.Sprintf("/tmp/remote-download-%s.log", timestamp)
|
||||
|
||||
@@ -3,15 +3,20 @@ package service
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type SystemctlService struct {
|
||||
t *gotext.Locale
|
||||
}
|
||||
|
||||
func NewSystemctlService() *SystemctlService {
|
||||
return &SystemctlService{}
|
||||
func NewSystemctlService(t *gotext.Locale) *SystemctlService {
|
||||
return &SystemctlService{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SystemctlService) Status(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -23,7 +28,7 @@ func (s *SystemctlService) Status(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
status, err := systemctl.Status(req.Service)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "获取 %s 服务运行状态失败: %v", req.Service, err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get %s service running status: %v", req.Service, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -39,7 +44,7 @@ func (s *SystemctlService) IsEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
enabled, err := systemctl.IsEnabled(req.Service)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "获取 %s 服务启用状态失败: %v", req.Service, err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to get %s service enable status: %v", req.Service, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -54,7 +59,7 @@ func (s *SystemctlService) Enable(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = systemctl.Enable(req.Service); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "启用 %s 服务失败: %v", req.Service, err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to enable %s service: %v", req.Service, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,7 +74,7 @@ func (s *SystemctlService) Disable(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = systemctl.Disable(req.Service); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "禁用 %s 服务失败: %v", req.Service, err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to disable %s service: %v", req.Service, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,7 +89,7 @@ func (s *SystemctlService) Restart(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = systemctl.Restart(req.Service); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "重启 %s 服务失败: %v", req.Service, err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to restart %s service: %v", req.Service, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -99,7 +104,7 @@ func (s *SystemctlService) Reload(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = systemctl.Reload(req.Service); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "重载 %s 服务失败: %v", req.Service, err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to reload %s service: %v", req.Service, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -114,7 +119,7 @@ func (s *SystemctlService) Start(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = systemctl.Start(req.Service); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "启动 %s 服务失败: %v", req.Service, err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to start %s service: %v", req.Service, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -129,7 +134,7 @@ func (s *SystemctlService) Stop(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = systemctl.Stop(req.Service); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "停止 %s 服务失败: %v", req.Service, err)
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("failed to stop %s service: %v", req.Service, err))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/go-rat/sessions"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
@@ -20,14 +21,16 @@ import (
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
t *gotext.Locale
|
||||
conf *koanf.Koanf
|
||||
session *sessions.Manager
|
||||
userRepo biz.UserRepo
|
||||
}
|
||||
|
||||
func NewUserService(conf *koanf.Koanf, session *sessions.Manager, user biz.UserRepo) *UserService {
|
||||
func NewUserService(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager, user biz.UserRepo) *UserService {
|
||||
gob.Register(rsa.PrivateKey{}) // 必须注册 rsa.PrivateKey 类型否则无法反序列化 session 中的 key
|
||||
return &UserService{
|
||||
t: t,
|
||||
conf: conf,
|
||||
session: session,
|
||||
userRepo: user,
|
||||
@@ -72,7 +75,7 @@ func (s *UserService) Login(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
key, ok := sess.Get("key").(rsa.PrivateKey)
|
||||
if !ok {
|
||||
Error(w, http.StatusForbidden, "invalid key, please refresh the page")
|
||||
Error(w, http.StatusForbidden, s.t.Get("invalid key, please refresh the page"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
stdssh "golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
@@ -17,12 +18,14 @@ import (
|
||||
)
|
||||
|
||||
type WsService struct {
|
||||
t *gotext.Locale
|
||||
conf *koanf.Koanf
|
||||
sshRepo biz.SSHRepo
|
||||
}
|
||||
|
||||
func NewWsService(conf *koanf.Koanf, ssh biz.SSHRepo) *WsService {
|
||||
func NewWsService(t *gotext.Locale, conf *koanf.Koanf, ssh biz.SSHRepo) *WsService {
|
||||
return &WsService{
|
||||
t: t,
|
||||
conf: conf,
|
||||
sshRepo: ssh,
|
||||
}
|
||||
@@ -97,14 +100,14 @@ func (s *WsService) Exec(w http.ResponseWriter, r *http.Request) {
|
||||
// 第一条消息是命令
|
||||
_, cmd, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
_ = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "failed to read command"))
|
||||
_ = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, s.t.Get("failed to read command: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
out, err := shell.ExecfWithPipe(ctx, string(cmd))
|
||||
if err != nil {
|
||||
_ = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "failed to run command"))
|
||||
_ = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, s.t.Get("failed to run command: %v", err)))
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
@@ -116,7 +119,7 @@ func (s *WsService) Exec(w http.ResponseWriter, r *http.Request) {
|
||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(line))
|
||||
}
|
||||
if err = scanner.Err(); err != nil {
|
||||
_ = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "failed to read command output"))
|
||||
_ = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, s.t.Get("failed to read command output: %v", err)))
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user