mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 06:47:20 +08:00
339 lines
9.3 KiB
Go
339 lines
9.3 KiB
Go
package toolbox
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-rat/chix"
|
|
"github.com/leonelquinteros/gotext"
|
|
"github.com/spf13/cast"
|
|
|
|
"github.com/tnb-labs/panel/internal/app"
|
|
"github.com/tnb-labs/panel/internal/service"
|
|
"github.com/tnb-labs/panel/pkg/io"
|
|
"github.com/tnb-labs/panel/pkg/ntp"
|
|
"github.com/tnb-labs/panel/pkg/shell"
|
|
"github.com/tnb-labs/panel/pkg/tools"
|
|
"github.com/tnb-labs/panel/pkg/types"
|
|
)
|
|
|
|
type App struct {
|
|
t *gotext.Locale
|
|
}
|
|
|
|
func NewApp(t *gotext.Locale) *App {
|
|
return &App{
|
|
t: t,
|
|
}
|
|
}
|
|
|
|
func (s *App) Route(r chi.Router) {
|
|
r.Get("/dns", s.GetDNS)
|
|
r.Post("/dns", s.UpdateDNS)
|
|
r.Get("/swap", s.GetSWAP)
|
|
r.Post("/swap", s.UpdateSWAP)
|
|
r.Get("/timezone", s.GetTimezone)
|
|
r.Post("/timezone", s.UpdateTimezone)
|
|
r.Post("/time", s.UpdateTime)
|
|
r.Post("/syncTime", s.SyncTime)
|
|
r.Get("/hostname", s.GetHostname)
|
|
r.Post("/hostname", s.UpdateHostname)
|
|
r.Get("/hosts", s.GetHosts)
|
|
r.Post("/hosts", s.UpdateHosts)
|
|
r.Post("/rootPassword", s.UpdateRootPassword)
|
|
}
|
|
|
|
// GetDNS 获取 DNS 信息
|
|
func (s *App) GetDNS(w http.ResponseWriter, r *http.Request) {
|
|
raw, err := io.Read("/etc/resolv.conf")
|
|
if err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
|
|
match := regexp.MustCompile(`nameserver\s+(\S+)`).FindAllStringSubmatch(raw, -1)
|
|
dns := make([]string, 0)
|
|
for _, m := range match {
|
|
dns = append(dns, m[1])
|
|
}
|
|
|
|
service.Success(w, dns)
|
|
}
|
|
|
|
// UpdateDNS 设置 DNS 信息
|
|
func (s *App) UpdateDNS(w http.ResponseWriter, r *http.Request) {
|
|
req, err := service.Bind[DNS](r)
|
|
if err != nil {
|
|
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
|
return
|
|
}
|
|
|
|
var dns string
|
|
dns += "nameserver " + req.DNS1 + "\n"
|
|
dns += "nameserver " + req.DNS2 + "\n"
|
|
|
|
if err := io.Write("/etc/resolv.conf", dns, 0644); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to update DNS: %v", err))
|
|
return
|
|
}
|
|
|
|
service.Success(w, nil)
|
|
}
|
|
|
|
// GetSWAP 获取 SWAP 信息
|
|
func (s *App) GetSWAP(w http.ResponseWriter, r *http.Request) {
|
|
var total, used, free string
|
|
var size int64
|
|
if io.Exists(filepath.Join(app.Root, "swap")) {
|
|
file, err := os.Stat(filepath.Join(app.Root, "swap"))
|
|
if err != nil {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get SWAP: %v", err))
|
|
return
|
|
}
|
|
|
|
size = file.Size() / 1024 / 1024
|
|
total = tools.FormatBytes(float64(file.Size()))
|
|
} else {
|
|
size = 0
|
|
total = "0.00 B"
|
|
}
|
|
|
|
raw, err := shell.Execf("free | grep Swap")
|
|
if err != nil {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get SWAP: %v", err))
|
|
return
|
|
}
|
|
|
|
match := regexp.MustCompile(`Swap:\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
|
|
if len(match) >= 4 {
|
|
used = tools.FormatBytes(cast.ToFloat64(match[2]) * 1024)
|
|
free = tools.FormatBytes(cast.ToFloat64(match[3]) * 1024)
|
|
}
|
|
|
|
service.Success(w, chix.M{
|
|
"total": total,
|
|
"size": size,
|
|
"used": used,
|
|
"free": free,
|
|
})
|
|
}
|
|
|
|
// UpdateSWAP 设置 SWAP 信息
|
|
func (s *App) UpdateSWAP(w http.ResponseWriter, r *http.Request) {
|
|
req, err := service.Bind[SWAP](r)
|
|
if err != nil {
|
|
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
|
return
|
|
}
|
|
|
|
if io.Exists(filepath.Join(app.Root, "swap")) {
|
|
if _, err = shell.Execf("swapoff '%s'", filepath.Join(app.Root, "swap")); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
if _, err = shell.Execf("rm -f '%s'", filepath.Join(app.Root, "swap")); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
if _, err = shell.Execf(`sed -i "\|^%s|d" /etc/fstab`, filepath.Join(app.Root, "swap")); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
if req.Size > 1 {
|
|
var free string
|
|
free, err = shell.Execf("df -k %s | awk '{print $4}' | tail -n 1", app.Root)
|
|
if err != nil {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get disk space: %v", err))
|
|
return
|
|
}
|
|
if cast.ToInt64(free)*1024 < req.Size*1024*1024 {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("disk space is insufficient, current free %s", tools.FormatBytes(cast.ToFloat64(free))))
|
|
return
|
|
}
|
|
|
|
btrfsCheck, _ := shell.Execf("df -T %s | awk '{print $2}' | tail -n 1", app.Root)
|
|
if strings.Contains(btrfsCheck, "btrfs") {
|
|
if _, err = shell.Execf("btrfs filesystem mkswapfile --size %dM --uuid clear %s", req.Size, filepath.Join(app.Root, "swap")); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
} else {
|
|
if _, err = shell.Execf("dd if=/dev/zero of=%s bs=1M count=%d", filepath.Join(app.Root, "swap"), req.Size); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
if _, err = shell.Execf("mkswap -f '%s'", filepath.Join(app.Root, "swap")); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
if err = io.Chmod(filepath.Join(app.Root, "swap"), 0600); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set SWAP permission: %v", err))
|
|
return
|
|
}
|
|
}
|
|
if _, err = shell.Execf("swapon '%s'", filepath.Join(app.Root, "swap")); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
if _, err = shell.Execf("echo '%s swap swap defaults 0 0' >> /etc/fstab", filepath.Join(app.Root, "swap")); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
service.Success(w, nil)
|
|
}
|
|
|
|
// GetTimezone 获取时区
|
|
func (s *App) GetTimezone(w http.ResponseWriter, r *http.Request) {
|
|
raw, err := shell.Execf("timedatectl | grep zone")
|
|
if err != nil {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get timezone: %v", err))
|
|
return
|
|
}
|
|
|
|
match := regexp.MustCompile(`zone:\s+(\S+)`).FindStringSubmatch(raw)
|
|
if len(match) == 0 {
|
|
match = append(match, "")
|
|
}
|
|
|
|
zonesRaw, err := shell.Execf("timedatectl list-timezones")
|
|
if err != nil {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get available timezones: %v", err))
|
|
return
|
|
}
|
|
zones := strings.Split(zonesRaw, "\n")
|
|
|
|
var zonesList []types.LV
|
|
for _, z := range zones {
|
|
zonesList = append(zonesList, types.LV{
|
|
Label: z,
|
|
Value: z,
|
|
})
|
|
}
|
|
|
|
service.Success(w, chix.M{
|
|
"timezone": match[1],
|
|
"timezones": zonesList,
|
|
})
|
|
}
|
|
|
|
// UpdateTimezone 设置时区
|
|
func (s *App) UpdateTimezone(w http.ResponseWriter, r *http.Request) {
|
|
req, err := service.Bind[Timezone](r)
|
|
if err != nil {
|
|
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
|
return
|
|
}
|
|
|
|
if _, err = shell.Execf("timedatectl set-timezone '%s'", req.Timezone); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
|
|
service.Success(w, nil)
|
|
}
|
|
|
|
// UpdateTime 设置时间
|
|
func (s *App) UpdateTime(w http.ResponseWriter, r *http.Request) {
|
|
req, err := service.Bind[Time](r)
|
|
if err != nil {
|
|
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
|
return
|
|
}
|
|
|
|
if err = ntp.UpdateSystemTime(req.Time); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
|
|
service.Success(w, nil)
|
|
|
|
}
|
|
|
|
// SyncTime 同步时间
|
|
func (s *App) SyncTime(w http.ResponseWriter, r *http.Request) {
|
|
now, err := ntp.Now()
|
|
if err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
|
|
if err = ntp.UpdateSystemTime(now); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", err)
|
|
return
|
|
}
|
|
|
|
service.Success(w, nil)
|
|
}
|
|
|
|
// GetHostname 获取主机名
|
|
func (s *App) GetHostname(w http.ResponseWriter, r *http.Request) {
|
|
hostname, _ := io.Read("/etc/hostname")
|
|
service.Success(w, strings.TrimSpace(hostname))
|
|
}
|
|
|
|
// UpdateHostname 设置主机名
|
|
func (s *App) UpdateHostname(w http.ResponseWriter, r *http.Request) {
|
|
req, err := service.Bind[Hostname](r)
|
|
if err != nil {
|
|
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
|
return
|
|
}
|
|
|
|
if _, err = shell.Execf("hostnamectl set-hostname '%s'", req.Hostname); err != nil {
|
|
// 直接写 /etc/hostname
|
|
if err = io.Write("/etc/hostname", req.Hostname, 0644); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set hostname: %v", err))
|
|
return
|
|
}
|
|
}
|
|
|
|
service.Success(w, nil)
|
|
}
|
|
|
|
// GetHosts 获取 hosts 信息
|
|
func (s *App) GetHosts(w http.ResponseWriter, r *http.Request) {
|
|
hosts, _ := io.Read("/etc/hosts")
|
|
service.Success(w, hosts)
|
|
}
|
|
|
|
// UpdateHosts 设置 hosts 信息
|
|
func (s *App) UpdateHosts(w http.ResponseWriter, r *http.Request) {
|
|
req, err := service.Bind[Hosts](r)
|
|
if err != nil {
|
|
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
|
return
|
|
}
|
|
|
|
if err = io.Write("/etc/hosts", req.Hosts, 0644); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set hosts: %v", err))
|
|
return
|
|
}
|
|
|
|
service.Success(w, nil)
|
|
}
|
|
|
|
// UpdateRootPassword 设置 root 密码
|
|
func (s *App) UpdateRootPassword(w http.ResponseWriter, r *http.Request) {
|
|
req, err := service.Bind[Password](r)
|
|
if err != nil {
|
|
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
|
return
|
|
}
|
|
|
|
req.Password = strings.ReplaceAll(req.Password, `'`, `\'`)
|
|
if _, err = shell.Execf(`yes '%s' | passwd root`, req.Password); err != nil {
|
|
service.Error(w, http.StatusInternalServerError, "%v", s.t.Get("failed to set root password: %v", err))
|
|
return
|
|
}
|
|
|
|
service.Success(w, nil)
|
|
}
|