2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 06:47:20 +08:00
Files
panel/internal/service/cli.go
2026-01-04 04:57:03 +08:00

929 lines
26 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"bufio"
"context"
"errors"
"fmt"
"math/rand/v2"
stdos "os"
"path/filepath"
"strings"
"time"
"github.com/leonelquinteros/gotext"
"github.com/libtnb/utils/collect"
"github.com/libtnb/utils/hash"
"github.com/libtnb/utils/str"
"github.com/spf13/cast"
"github.com/urfave/cli/v3"
"gorm.io/gorm"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/biz"
"github.com/acepanel/panel/internal/http/request"
"github.com/acepanel/panel/pkg/api"
"github.com/acepanel/panel/pkg/cert"
"github.com/acepanel/panel/pkg/config"
"github.com/acepanel/panel/pkg/firewall"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/ntp"
"github.com/acepanel/panel/pkg/os"
"github.com/acepanel/panel/pkg/systemctl"
"github.com/acepanel/panel/pkg/tools"
)
type CliService struct {
hr string
t *gotext.Locale
api *api.API
conf *config.Config
db *gorm.DB
appRepo biz.AppRepo
cacheRepo biz.CacheRepo
userRepo biz.UserRepo
settingRepo biz.SettingRepo
backupRepo biz.BackupRepo
websiteRepo biz.WebsiteRepo
databaseServerRepo biz.DatabaseServerRepo
hash hash.Hasher
}
func NewCliService(t *gotext.Locale, conf *config.Config, 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, app.Locale),
t: t,
conf: conf,
db: db,
appRepo: appRepo,
cacheRepo: cache,
userRepo: user,
settingRepo: setting,
backupRepo: backup,
websiteRepo: website,
databaseServerRepo: databaseServer,
hash: hash.NewArgon2id(),
}
}
func (s *CliService) Restart(ctx context.Context, cmd *cli.Command) error {
if err := systemctl.Restart("panel"); err != nil {
return err
}
fmt.Println(s.t.Get("Panel service restarted"))
return nil
}
func (s *CliService) Stop(ctx context.Context, cmd *cli.Command) error {
if err := systemctl.Stop("panel"); err != nil {
return err
}
fmt.Println(s.t.Get("Panel service stopped"))
return nil
}
func (s *CliService) Start(ctx context.Context, cmd *cli.Command) error {
if err := systemctl.Start("panel"); err != nil {
return err
}
fmt.Println(s.t.Get("Panel service started"))
return nil
}
func (s *CliService) Update(ctx context.Context, cmd *cli.Command) error {
channel, _ := s.settingRepo.Get(biz.SettingKeyChannel)
panel, err := s.api.LatestVersion(channel)
if err != nil {
return errors.New(s.t.Get("Failed to get latest version: %v", err))
}
download := collect.First(panel.Downloads)
if download == nil {
return errors.New(s.t.Get("Download URL is empty"))
}
return s.backupRepo.UpdatePanel(panel.Version, download.URL, download.Checksum)
}
func (s *CliService) Sync(ctx context.Context, cmd *cli.Command) error {
if err := s.cacheRepo.UpdateApps(); err != nil {
return errors.New(s.t.Get("Failed to synchronize app data: %v", err))
}
if err := s.cacheRepo.UpdateRewrites(); err != nil {
return errors.New(s.t.Get("Failed to synchronize rewrite rules: %v", err))
}
fmt.Println(s.t.Get("Data synchronized successfully"))
return nil
}
func (s *CliService) Fix(ctx context.Context, cmd *cli.Command) error {
return s.backupRepo.FixPanel()
}
func (s *CliService) Info(ctx context.Context, cmd *cli.Command) error {
// TODO 未来加权限设置之后这里需要优化
user := new(biz.User)
if err := s.db.First(user).Error; err != nil {
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 errors.New(s.t.Get("Failed to generate password: %v", err))
}
user.Username = str.Random(8)
user.Password = hashed
if err = s.db.Save(user).Error; err != nil {
return errors.New(s.t.Get("Failed to save user info: %v", err))
}
protocol := "http"
if s.conf.HTTP.TLS {
protocol = "https"
}
port := s.conf.HTTP.Port
if port == 0 {
return errors.New(s.t.Get("Failed to get port"))
}
entrance := s.conf.HTTP.Entrance
if entrance == "" {
return errors.New(s.t.Get("Failed to get entrance"))
}
fmt.Println(s.t.Get("Username: %s", user.Username))
fmt.Println(s.t.Get("Password: %s", password))
fmt.Println(s.t.Get("Port: %d", port))
fmt.Println(s.t.Get("Entrance: %s", entrance))
lv4, err := tools.GetLocalIPv4()
if err == nil {
fmt.Println(s.t.Get("Local IPv4: %s://%s:%d%s", protocol, lv4, port, entrance))
}
lv6, err := tools.GetLocalIPv6()
if err == nil {
fmt.Println(s.t.Get("Local IPv6: %s://[%s]:%d%s", protocol, lv6, port, entrance))
}
rv4, err := tools.GetPublicIPv4()
if err == nil {
fmt.Println(s.t.Get("Public IPv4: %s://%s:%d%s", protocol, rv4, port, entrance))
}
rv6, err := tools.GetPublicIPv6()
if err == nil {
fmt.Println(s.t.Get("Public IPv6: %s://[%s]:%d%s", protocol, rv6, port, entrance))
}
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 %d", 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
}
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 errors.New(s.t.Get("Failed to get user list: %v", err))
}
for _, user := range users {
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
}
func (s *CliService) UserName(ctx context.Context, cmd *cli.Command) error {
user := new(biz.User)
oldUsername := cmd.Args().Get(0)
newUsername := cmd.Args().Get(1)
if oldUsername == "" {
return errors.New(s.t.Get("Old username cannot be empty"))
}
if newUsername == "" {
return errors.New(s.t.Get("New username cannot be empty"))
}
if err := s.db.Where("username", oldUsername).First(user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New(s.t.Get("User not exists"))
} else {
return errors.New(s.t.Get("Failed to get user: %v", err))
}
}
user.Username = newUsername
if err := s.db.Save(user).Error; err != nil {
return errors.New(s.t.Get("Failed to change username: %v", err))
}
fmt.Println(s.t.Get("Username %s changed to %s successfully", oldUsername, newUsername))
return nil
}
func (s *CliService) UserPassword(ctx context.Context, cmd *cli.Command) error {
user := new(biz.User)
username := cmd.Args().Get(0)
password := cmd.Args().Get(1)
if username == "" || password == "" {
return errors.New(s.t.Get("Username and password cannot be empty"))
}
if len(password) < 6 {
return errors.New(s.t.Get("Password length cannot be less than 6"))
}
if err := s.db.Where("username", username).First(user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New(s.t.Get("User not exists"))
} else {
return errors.New(s.t.Get("Failed to get user: %v", err))
}
}
hashed, err := s.hash.Make(password)
if err != nil {
return errors.New(s.t.Get("Failed to generate password: %v", err))
}
user.Password = hashed
if err = s.db.Save(user).Error; err != nil {
return errors.New(s.t.Get("Failed to change password: %v", err))
}
fmt.Println(s.t.Get("Password for user %s changed successfully", username))
return nil
}
func (s *CliService) UserTwoFA(ctx context.Context, cmd *cli.Command) error {
user := new(biz.User)
username := cmd.Args().Get(0)
if username == "" {
return errors.New(s.t.Get("Username cannot be empty"))
}
if err := s.db.Where("username", username).First(user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New(s.t.Get("User not exists"))
} else {
return errors.New(s.t.Get("Failed to get user: %v", err))
}
}
// 已开启关闭2FA
if user.TwoFA != "" {
user.TwoFA = ""
if err := s.db.Save(user).Error; err != nil {
return errors.New(s.t.Get("Failed to change 2FA status: %v", err))
}
fmt.Println(s.t.Get("2FA disabled for user %s", username))
return nil
}
// 未开启开启2FA
_, url, secret, err := s.userRepo.GenerateTwoFA(user.ID)
if err != nil {
return errors.New(s.t.Get("Failed to generate 2FA: %v", err))
}
fmt.Println(s.t.Get("2FA url: %s", url))
reader := bufio.NewReader(stdos.Stdin)
fmt.Print(s.t.Get("Please enter the 2FA code: "))
code, err := reader.ReadString('\n')
if err != nil {
return errors.New(s.t.Get("Failed to read input: %v", err))
}
if err = s.userRepo.UpdateTwoFA(user.ID, strings.TrimSpace(code), secret); err != nil {
return errors.New(s.t.Get("Failed to update 2FA: %v", err))
}
return nil
}
func (s *CliService) HTTPSOn(ctx context.Context, cmd *cli.Command) error {
conf, err := config.Load()
if err != nil {
return err
}
conf.HTTP.TLS = true
if err = config.Save(conf); err != nil {
return err
}
fmt.Println(s.t.Get("HTTPS enabled"))
return s.Restart(ctx, cmd)
}
func (s *CliService) HTTPSOff(ctx context.Context, cmd *cli.Command) error {
conf, err := config.Load()
if err != nil {
return err
}
conf.HTTP.TLS = false
if err = config.Save(conf); err != nil {
return err
}
fmt.Println(s.t.Get("HTTPS disabled"))
return s.Restart(ctx, cmd)
}
func (s *CliService) HTTPSGenerate(ctx context.Context, cmd *cli.Command) error {
var names []string
if lv4, err := tools.GetLocalIPv4(); err == nil {
names = append(names, lv4)
}
if lv6, err := tools.GetLocalIPv6(); err == nil {
names = append(names, lv6)
}
if rv4, err := tools.GetPublicIPv4(); err == nil {
names = append(names, rv4)
}
if rv6, err := tools.GetPublicIPv6(); err == nil {
names = append(names, rv6)
}
crt, key, err := cert.GenerateSelfSigned(names)
if err != nil {
return err
}
if err = io.Write(filepath.Join(app.Root, "panel/storage/cert.pem"), string(crt), 0644); err != nil {
return err
}
if err = io.Write(filepath.Join(app.Root, "panel/storage/cert.key"), string(key), 0644); err != nil {
return err
}
fmt.Println(s.t.Get("HTTPS certificate generated"))
return s.Restart(ctx, cmd)
}
func (s *CliService) EntranceOn(ctx context.Context, cmd *cli.Command) error {
conf, err := config.Load()
if err != nil {
return err
}
conf.HTTP.Entrance = "/" + str.Random(6)
if err = config.Save(conf); err != nil {
return err
}
fmt.Println(s.t.Get("Entrance enabled"))
fmt.Println(s.t.Get("Entrance: %s", conf.HTTP.Entrance))
return s.Restart(ctx, cmd)
}
func (s *CliService) EntranceOff(ctx context.Context, cmd *cli.Command) error {
conf, err := config.Load()
if err != nil {
return err
}
conf.HTTP.Entrance = "/"
if err = config.Save(conf); err != nil {
return err
}
fmt.Println(s.t.Get("Entrance disabled"))
return s.Restart(ctx, cmd)
}
func (s *CliService) BindDomainOff(ctx context.Context, cmd *cli.Command) error {
conf, err := config.Load()
if err != nil {
return err
}
conf.HTTP.BindDomain = nil
if err = config.Save(conf); err != nil {
return err
}
fmt.Println(s.t.Get("Bind domain disabled"))
return s.Restart(ctx, cmd)
}
func (s *CliService) BindIPOff(ctx context.Context, cmd *cli.Command) error {
conf, err := config.Load()
if err != nil {
return err
}
conf.HTTP.BindIP = nil
if err = config.Save(conf); err != nil {
return err
}
fmt.Println(s.t.Get("Bind IP disabled"))
return s.Restart(ctx, cmd)
}
func (s *CliService) BindUAOff(ctx context.Context, cmd *cli.Command) error {
conf, err := config.Load()
if err != nil {
return err
}
conf.HTTP.BindUA = nil
if err = config.Save(conf); err != nil {
return err
}
fmt.Println(s.t.Get("Bind UA disabled"))
return s.Restart(ctx, cmd)
}
func (s *CliService) Port(ctx context.Context, cmd *cli.Command) error {
port := cast.ToUint(cmd.Args().First())
if port < 1 || port > 65535 {
return errors.New(s.t.Get("Port range error"))
}
conf, err := config.Load()
if err != nil {
return err
}
if port != conf.HTTP.Port {
if os.TCPPortInUse(port) {
return errors.New(s.t.Get("Port already in use"))
}
}
conf.HTTP.Port = port
// 放行端口
if ok, _ := systemctl.IsEnabled("firewalld"); ok {
fw := firewall.NewFirewall()
err = fw.Port(firewall.FireInfo{
Type: firewall.TypeNormal,
PortStart: port,
PortEnd: port,
Direction: firewall.DirectionIn,
Strategy: firewall.StrategyAccept,
}, firewall.OperationAdd)
if err != nil {
return err
}
}
if err = config.Save(conf); err != nil {
return err
}
fmt.Println(s.t.Get("Port changed to %d", port))
return s.Restart(ctx, cmd)
}
func (s *CliService) WebsiteCreate(ctx context.Context, cmd *cli.Command) error {
req := &request.WebsiteCreate{
Name: cmd.String("name"),
Domains: cmd.StringSlice("domains"),
Listens: cmd.StringSlice("listens"),
Path: cmd.String("path"),
PHP: cmd.Uint("php"),
DB: false,
}
website, err := s.websiteRepo.Create(req)
if err != nil {
return err
}
fmt.Println(s.t.Get("Website %s created successfully", website.Name))
return nil
}
func (s *CliService) WebsiteRemove(ctx context.Context, cmd *cli.Command) error {
website, err := s.websiteRepo.GetByName(cmd.String("name"))
if err != nil {
return err
}
req := &request.WebsiteDelete{
ID: website.ID,
}
if err = s.websiteRepo.Delete(req); err != nil {
return err
}
fmt.Println(s.t.Get("Website %s removed successfully", website.Name))
return nil
}
func (s *CliService) WebsiteDelete(ctx context.Context, cmd *cli.Command) error {
website, err := s.websiteRepo.GetByName(cmd.String("name"))
if err != nil {
return err
}
req := &request.WebsiteDelete{
ID: website.ID,
Path: true,
DB: true,
}
if err = s.websiteRepo.Delete(req); err != nil {
return err
}
fmt.Println(s.t.Get("Website %s deleted successfully", website.Name))
return nil
}
func (s *CliService) WebsiteWrite(ctx context.Context, cmd *cli.Command) error {
fmt.Println(s.t.Get("Not supported"))
return nil
}
func (s *CliService) DatabaseAddServer(ctx context.Context, cmd *cli.Command) error {
req := &request.DatabaseServerCreate{
Type: cmd.String("type"),
Name: cmd.String("name"),
Host: cmd.String("host"),
Port: uint(cmd.Uint("port")),
Username: cmd.String("username"),
Password: cmd.String("password"),
Remark: cmd.String("remark"),
}
if err := s.databaseServerRepo.Create(req); err != nil {
return err
}
fmt.Println(s.t.Get("Database server %s added successfully", cmd.String("name")))
return nil
}
func (s *CliService) DatabaseDeleteServer(ctx context.Context, cmd *cli.Command) error {
server, err := s.databaseServerRepo.GetByName(cmd.String("name"))
if err != nil {
return err
}
if err = s.databaseServerRepo.Delete(server.ID); err != nil {
return err
}
fmt.Println(s.t.Get("Database server %s deleted successfully", server.Name))
return nil
}
func (s *CliService) BackupWebsite(ctx context.Context, cmd *cli.Command) error {
fmt.Println(s.hr)
fmt.Println(s.t.Get("★ Start backup [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
fmt.Println(s.t.Get("|-Backup type: website"))
fmt.Println(s.t.Get("|-Backup target: %s", cmd.String("name")))
if err := s.backupRepo.Create(biz.BackupTypeWebsite, cmd.String("name"), cmd.String("path")); err != nil {
return errors.New(s.t.Get("Backup failed: %v", err))
}
fmt.Println(s.hr)
fmt.Println(s.t.Get("☆ Backup successful [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
return nil
}
func (s *CliService) BackupDatabase(ctx context.Context, cmd *cli.Command) error {
fmt.Println(s.hr)
fmt.Println(s.t.Get("★ Start backup [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
fmt.Println(s.t.Get("|-Backup type: database"))
fmt.Println(s.t.Get("|-Database: %s", cmd.String("type")))
fmt.Println(s.t.Get("|-Backup target: %s", cmd.String("name")))
if err := s.backupRepo.Create(biz.BackupType(cmd.String("type")), cmd.String("name"), cmd.String("path")); err != nil {
return errors.New(s.t.Get("Backup failed: %v", err))
}
fmt.Println(s.hr)
fmt.Println(s.t.Get("☆ Backup successful [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
return nil
}
func (s *CliService) BackupPanel(ctx context.Context, cmd *cli.Command) error {
fmt.Println(s.hr)
fmt.Println(s.t.Get("★ Start backup [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
fmt.Println(s.t.Get("|-Backup type: panel"))
if err := s.backupRepo.Create(biz.BackupTypePanel, "", cmd.String("path")); err != nil {
return errors.New(s.t.Get("Backup failed: %v", err))
}
fmt.Println(s.hr)
fmt.Println(s.t.Get("☆ Backup successful [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
return nil
}
func (s *CliService) BackupClear(ctx context.Context, cmd *cli.Command) error {
path, err := s.backupRepo.GetPath(biz.BackupType(cmd.String("type")))
if err != nil {
return err
}
if cmd.String("path") != "" {
path = cmd.String("path")
}
fmt.Println(s.hr)
fmt.Println(s.t.Get("★ Start cleaning [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
fmt.Println(s.t.Get("|-Cleaning type: %s", cmd.String("type")))
fmt.Println(s.t.Get("|-Cleaning target: %s", cmd.String("file")))
fmt.Println(s.t.Get("|-Keep count: %d", cmd.Int("save")))
if err = s.backupRepo.ClearExpired(path, cmd.String("file"), int(cmd.Int("save"))); err != nil {
return errors.New(s.t.Get("Cleaning failed: %v", err))
}
fmt.Println(s.hr)
fmt.Println(s.t.Get("☆ Cleaning successful [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
return nil
}
func (s *CliService) CutoffWebsite(ctx context.Context, cmd *cli.Command) error {
website, err := s.websiteRepo.GetByName(cmd.String("name"))
if err != nil {
return err
}
path := filepath.Join(app.Root, "wwwlogs")
if cmd.String("path") != "" {
path = cmd.String("path")
}
fmt.Println(s.hr)
fmt.Println(s.t.Get("★ Start log rotation [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
fmt.Println(s.t.Get("|-Rotation type: website"))
fmt.Println(s.t.Get("|-Rotation target: %s", website.Name))
if err = s.backupRepo.CutoffLog(path, filepath.Join(app.Root, "wwwlogs", website.Name+".log")); err != nil {
return err
}
fmt.Println(s.hr)
fmt.Println(s.t.Get("☆ Rotation successful [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
return nil
}
func (s *CliService) CutoffClear(ctx context.Context, cmd *cli.Command) error {
if cmd.String("type") != "website" {
return errors.New(s.t.Get("Currently only website log rotation is supported"))
}
path := filepath.Join(app.Root, "wwwlogs")
if cmd.String("path") != "" {
path = cmd.String("path")
}
fmt.Println(s.hr)
fmt.Println(s.t.Get("★ Start cleaning rotated logs [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
fmt.Println(s.t.Get("|-Cleaning type: %s", cmd.String("type")))
fmt.Println(s.t.Get("|-Cleaning target: %s", cmd.String("file")))
fmt.Println(s.t.Get("|-Keep count: %d", cmd.Int("save")))
if err := s.backupRepo.ClearExpired(path, cmd.String("file"), int(cmd.Int("save"))); err != nil {
return err
}
fmt.Println(s.hr)
fmt.Println(s.t.Get("☆ Cleaning successful [%s]", time.Now().Format(time.DateTime)))
fmt.Println(s.hr)
return nil
}
func (s *CliService) AppInstall(ctx context.Context, cmd *cli.Command) error {
slug := cmd.Args().First()
channel := cmd.Args().Get(1)
if channel == "" || slug == "" {
return errors.New(s.t.Get("Parameters cannot be empty"))
}
if err := s.appRepo.Install(channel, slug); err != nil {
return errors.New(s.t.Get("App install failed: %v", err))
}
fmt.Println(s.t.Get("App %s installed successfully", slug))
return nil
}
func (s *CliService) AppUnInstall(ctx context.Context, cmd *cli.Command) error {
slug := cmd.Args().First()
if slug == "" {
return errors.New(s.t.Get("Parameters cannot be empty"))
}
if err := s.appRepo.UnInstall(slug); err != nil {
return errors.New(s.t.Get("App uninstall failed: %v", err))
}
fmt.Println(s.t.Get("App %s uninstalled successfully", slug))
return nil
}
func (s *CliService) AppUpdate(ctx context.Context, cmd *cli.Command) error {
slug := cmd.Args().First()
if slug == "" {
return errors.New(s.t.Get("Parameters cannot be empty"))
}
if err := s.appRepo.Update(slug); err != nil {
return errors.New(s.t.Get("App update failed: %v", err))
}
fmt.Println(s.t.Get("App %s updated successfully", slug))
return nil
}
func (s *CliService) AppWrite(ctx context.Context, cmd *cli.Command) error {
slug := cmd.Args().Get(0)
channel := cmd.Args().Get(1)
version := cmd.Args().Get(2)
if slug == "" || channel == "" || version == "" {
return errors.New(s.t.Get("Parameters cannot be empty"))
}
newApp := new(biz.App)
if err := s.db.Where("slug", slug).First(newApp).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New(s.t.Get("Failed to get app: %v", err))
}
}
newApp.Slug = slug
newApp.Channel = channel
newApp.Version = version
if err := s.db.Save(newApp).Error; err != nil {
return errors.New(s.t.Get("Failed to save app: %v", err))
}
return nil
}
func (s *CliService) AppRemove(ctx context.Context, cmd *cli.Command) error {
slug := cmd.Args().First()
if slug == "" {
return errors.New(s.t.Get("Parameters cannot be empty"))
}
if err := s.db.Where("slug", slug).Delete(&biz.App{}).Error; err != nil {
return errors.New(s.t.Get("Failed to delete app: %v", err))
}
return nil
}
func (s *CliService) SyncTime(ctx context.Context, cmd *cli.Command) error {
now, err := ntp.Now()
if err != nil {
return err
}
if err = ntp.UpdateSystemTime(now); err != nil {
return err
}
fmt.Println(s.t.Get("Time synchronized successfully"))
return nil
}
func (s *CliService) ClearTask(ctx context.Context, cmd *cli.Command) error {
if err := s.db.Model(&biz.Task{}).
Where("status", biz.TaskStatusRunning).Or("status", biz.TaskStatusWaiting).
Update("status", biz.TaskStatusFailed).
Error; err != nil {
return errors.New(s.t.Get("Failed to clear tasks: %v", err))
}
fmt.Println(s.t.Get("Tasks cleared successfully"))
return nil
}
func (s *CliService) GetSetting(ctx context.Context, cmd *cli.Command) error {
key := cmd.Args().First()
if key == "" {
return errors.New(s.t.Get("Parameters cannot be empty"))
}
setting := new(biz.Setting)
if err := s.db.Where("key", key).First(setting).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New(s.t.Get("Setting not exists"))
}
return errors.New(s.t.Get("Failed to get setting: %v", err))
}
fmt.Print(setting.Value)
return nil
}
func (s *CliService) WriteSetting(ctx context.Context, cmd *cli.Command) error {
key := cmd.Args().Get(0)
value := cmd.Args().Get(1)
if key == "" || value == "" {
return errors.New(s.t.Get("Parameters cannot be empty"))
}
setting := new(biz.Setting)
if err := s.db.Where("key", key).First(setting).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New(s.t.Get("Failed to get setting: %v", err))
}
}
setting.Key = biz.SettingKey(key)
setting.Value = value
if err := s.db.Save(setting).Error; err != nil {
return errors.New(s.t.Get("Failed to save setting: %v", err))
}
return nil
}
func (s *CliService) RemoveSetting(ctx context.Context, cmd *cli.Command) error {
key := cmd.Args().First()
if key == "" {
return errors.New(s.t.Get("Parameters cannot be empty"))
}
if err := s.db.Where("key", key).Delete(&biz.Setting{}).Error; err != nil {
return errors.New(s.t.Get("Failed to delete setting: %v", err))
}
return nil
}
func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error {
var check biz.User
if err := s.db.First(&check).Error; err == nil {
return errors.New(s.t.Get("Already initialized"))
}
settings := []biz.Setting{
{Key: biz.SettingKeyName, Value: "AcePanel"},
{Key: biz.SettingKeyChannel, Value: "stable"},
{Key: biz.SettingKeyVersion, Value: app.Version},
{Key: biz.SettingKeyMonitor, Value: "true"},
{Key: biz.SettingKeyMonitorDays, Value: "30"},
{Key: biz.SettingKeyBackupPath, Value: filepath.Join(app.Root, "backup")},
{Key: biz.SettingKeyWebsitePath, Value: filepath.Join(app.Root, "wwwroot")},
{Key: biz.SettingKeyOfflineMode, Value: "false"},
{Key: biz.SettingKeyAutoUpdate, Value: "true"},
}
if err := s.db.Create(&settings).Error; err != nil {
return errors.New(s.t.Get("Initialization failed: %v", err))
}
value, err := hash.NewArgon2id().Make(str.Random(32))
if err != nil {
return errors.New(s.t.Get("Initialization failed: %v", err))
}
_, err = s.userRepo.Create("admin", value, str.Random(8)+"@yourdomain.com")
if err != nil {
return errors.New(s.t.Get("Initialization failed: %v", err))
}
if err = s.HTTPSGenerate(ctx, cmd); err != nil {
return errors.New(s.t.Get("Initialization failed: %v", err))
}
conf, err := config.Load()
if err != nil {
return err
}
conf.App.Key = str.Random(32)
conf.HTTP.Entrance = "/" + str.Random(6)
// 随机默认端口
checkPort:
port := uint(rand.IntN(50000) + 10000) // 10000-60000
if os.TCPPortInUse(port) {
goto checkPort
}
conf.HTTP.Port = port
// 放行端口
fw := firewall.NewFirewall()
_ = fw.Port(firewall.FireInfo{
Type: firewall.TypeNormal,
PortStart: port,
PortEnd: port,
Direction: firewall.DirectionIn,
Strategy: firewall.StrategyAccept,
}, firewall.OperationAdd)
if err = config.Save(conf); err != nil {
return err
}
return nil
}