mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 05:31:44 +08:00
feat: 添加翻译
This commit is contained in:
@@ -2,6 +2,7 @@ package toolbox
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -88,7 +89,7 @@ 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 := io.FileInfo(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
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/expr-lang/expr"
|
||||
"github.com/go-rat/utils/collect"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/spf13/cast"
|
||||
"gorm.io/gorm"
|
||||
|
||||
@@ -19,13 +20,15 @@ import (
|
||||
)
|
||||
|
||||
type appRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
cache biz.CacheRepo
|
||||
task biz.TaskRepo
|
||||
}
|
||||
|
||||
func NewAppRepo(db *gorm.DB, cache biz.CacheRepo, task biz.TaskRepo) biz.AppRepo {
|
||||
func NewAppRepo(t *gotext.Locale, db *gorm.DB, cache biz.CacheRepo, task biz.TaskRepo) biz.AppRepo {
|
||||
return &appRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
cache: cache,
|
||||
task: task,
|
||||
@@ -50,7 +53,7 @@ func (r *appRepo) Get(slug string) (*api.App, error) {
|
||||
return item, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("应用不存在")
|
||||
return nil, errors.New(r.t.Get("app %s not found", slug))
|
||||
}
|
||||
|
||||
func (r *appRepo) UpdateExist(slug string) bool {
|
||||
@@ -153,7 +156,7 @@ func (r *appRepo) Install(channel, slug string) error {
|
||||
}
|
||||
|
||||
if installed, _ := r.IsInstalled(slug); installed {
|
||||
return errors.New("应用已安装")
|
||||
return errors.New(r.t.Get("app %s already installed", slug))
|
||||
}
|
||||
|
||||
shellUrl, shellChannel, shellVersion := "", "", ""
|
||||
@@ -164,7 +167,7 @@ func (r *appRepo) Install(channel, slug string) error {
|
||||
}
|
||||
if ch.Slug == channel {
|
||||
if vs.GreaterThan(panel) {
|
||||
return fmt.Errorf("应用 %s 需要面板版本 %s,当前版本 %s", item.Name, ch.Panel, app.Version)
|
||||
return errors.New(r.t.Get("app %s requires panel version %s, current version %s", item.Name, ch.Panel, app.Version))
|
||||
}
|
||||
shellUrl = ch.Install
|
||||
shellChannel = ch.Slug
|
||||
@@ -173,7 +176,7 @@ func (r *appRepo) Install(channel, slug string) error {
|
||||
}
|
||||
}
|
||||
if shellUrl == "" {
|
||||
return fmt.Errorf("应用 %s 不支持当前面板版本", item.Name)
|
||||
return errors.New(r.t.Get("app %s not support current panel version", item.Name))
|
||||
}
|
||||
|
||||
if err = r.preCheck(item); err != nil {
|
||||
@@ -185,7 +188,7 @@ func (r *appRepo) Install(channel, slug string) error {
|
||||
}
|
||||
|
||||
task := new(biz.Task)
|
||||
task.Name = "安装应用 " + item.Name
|
||||
task.Name = r.t.Get("Install app %s", item.Name)
|
||||
task.Status = biz.TaskStatusWaiting
|
||||
task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
@@ -204,7 +207,7 @@ func (r *appRepo) UnInstall(slug string) error {
|
||||
}
|
||||
|
||||
if installed, _ := r.IsInstalled(slug); !installed {
|
||||
return errors.New("应用未安装")
|
||||
return errors.New(r.t.Get("app %s not installed", item.Name))
|
||||
}
|
||||
installed, err := r.GetInstalled(slug)
|
||||
if err != nil {
|
||||
@@ -219,7 +222,7 @@ func (r *appRepo) UnInstall(slug string) error {
|
||||
}
|
||||
if ch.Slug == installed.Channel {
|
||||
if vs.GreaterThan(panel) {
|
||||
return fmt.Errorf("应用 %s 需要面板版本 %s,当前版本 %s", item.Name, ch.Panel, app.Version)
|
||||
return errors.New(r.t.Get("app %s requires panel version %s, current version %s", item.Name, ch.Panel, app.Version))
|
||||
}
|
||||
shellUrl = ch.Uninstall
|
||||
shellChannel = ch.Slug
|
||||
@@ -228,7 +231,7 @@ func (r *appRepo) UnInstall(slug string) error {
|
||||
}
|
||||
}
|
||||
if shellUrl == "" {
|
||||
return fmt.Errorf("无法获取应用 %s 的卸载脚本", item.Name)
|
||||
return errors.New(r.t.Get("failed to get uninstall script for app %s", item.Name))
|
||||
}
|
||||
|
||||
if err = r.preCheck(item); err != nil {
|
||||
@@ -240,7 +243,7 @@ func (r *appRepo) UnInstall(slug string) error {
|
||||
}
|
||||
|
||||
task := new(biz.Task)
|
||||
task.Name = "卸载应用 " + item.Name
|
||||
task.Name = r.t.Get("Uninstall app %s", item.Name)
|
||||
task.Status = biz.TaskStatusWaiting
|
||||
task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
@@ -259,7 +262,7 @@ func (r *appRepo) Update(slug string) error {
|
||||
}
|
||||
|
||||
if installed, _ := r.IsInstalled(slug); !installed {
|
||||
return errors.New("应用未安装")
|
||||
return errors.New(r.t.Get("app %s not installed", item.Name))
|
||||
}
|
||||
installed, err := r.GetInstalled(slug)
|
||||
if err != nil {
|
||||
@@ -274,7 +277,7 @@ func (r *appRepo) Update(slug string) error {
|
||||
}
|
||||
if ch.Slug == installed.Channel {
|
||||
if vs.GreaterThan(panel) {
|
||||
return fmt.Errorf("应用 %s 需要面板版本 %s,当前版本 %s", item.Name, ch.Panel, app.Version)
|
||||
return errors.New(r.t.Get("app %s requires panel version %s, current version %s", item.Name, ch.Panel, app.Version))
|
||||
}
|
||||
shellUrl = ch.Update
|
||||
shellChannel = ch.Slug
|
||||
@@ -283,7 +286,7 @@ func (r *appRepo) Update(slug string) error {
|
||||
}
|
||||
}
|
||||
if shellUrl == "" {
|
||||
return fmt.Errorf("应用 %s 不支持当前面板版本", item.Name)
|
||||
return errors.New(r.t.Get("app %s not support current panel version", item.Name))
|
||||
}
|
||||
|
||||
if err = r.preCheck(item); err != nil {
|
||||
@@ -295,7 +298,7 @@ func (r *appRepo) Update(slug string) error {
|
||||
}
|
||||
|
||||
task := new(biz.Task)
|
||||
task.Name = "更新应用 " + item.Name
|
||||
task.Name = r.t.Get("Update app %s", item.Name)
|
||||
task.Status = biz.TaskStatusWaiting
|
||||
task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
@@ -341,7 +344,7 @@ func (r *appRepo) preCheck(app *api.App) error {
|
||||
|
||||
result := cast.ToString(output)
|
||||
if result != "ok" {
|
||||
return fmt.Errorf("应用 %s %s", app.Name, result)
|
||||
return errors.New(r.t.Get("App %s %s", app.Name, result))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"gorm.io/gorm"
|
||||
|
||||
@@ -22,13 +23,15 @@ import (
|
||||
)
|
||||
|
||||
type backupRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
setting biz.SettingRepo
|
||||
website biz.WebsiteRepo
|
||||
}
|
||||
|
||||
func NewBackupRepo(db *gorm.DB, setting biz.SettingRepo, website biz.WebsiteRepo) biz.BackupRepo {
|
||||
func NewBackupRepo(t *gotext.Locale, db *gorm.DB, setting biz.SettingRepo, website biz.WebsiteRepo) biz.BackupRepo {
|
||||
return &backupRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
setting: setting,
|
||||
website: website,
|
||||
@@ -89,7 +92,7 @@ func (r *backupRepo) Create(typ biz.BackupType, target string, path ...string) e
|
||||
|
||||
}
|
||||
|
||||
return errors.New("未知备份类型")
|
||||
return errors.New(r.t.Get("unknown backup type"))
|
||||
}
|
||||
|
||||
// Delete 删除备份
|
||||
@@ -125,7 +128,7 @@ func (r *backupRepo) Restore(typ biz.BackupType, backup, target string) error {
|
||||
return r.restorePostgres(backup, target)
|
||||
}
|
||||
|
||||
return errors.New("未知备份类型")
|
||||
return errors.New(r.t.Get("unknown backup type"))
|
||||
}
|
||||
|
||||
// CutoffLog 切割日志
|
||||
@@ -133,7 +136,7 @@ func (r *backupRepo) Restore(typ biz.BackupType, backup, target string) error {
|
||||
// target 待切割日志文件绝对路径
|
||||
func (r *backupRepo) CutoffLog(path, target string) error {
|
||||
if !io.Exists(target) {
|
||||
return errors.New("日志文件不存在")
|
||||
return errors.New(r.t.Get("log file %s not exists", target))
|
||||
}
|
||||
|
||||
to := filepath.Join(path, fmt.Sprintf("%s_%s.zip", time.Now().Format("20060102150405"), filepath.Base(target)))
|
||||
@@ -184,14 +187,10 @@ func (r *backupRepo) ClearExpired(path, prefix string, save int) error {
|
||||
for _, file := range toDelete {
|
||||
filePath := filepath.Join(path, file.Name())
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-清理过期文件:%s\n", filePath)
|
||||
fmt.Println(r.t.Get("|-Cleaning expired file: %s", filePath))
|
||||
}
|
||||
if err = os.Remove(filePath); err != nil {
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-清理失败:%v\n", err)
|
||||
} else {
|
||||
return fmt.Errorf("清理失败:%v", err)
|
||||
}
|
||||
return errors.New(r.t.Get("Cleanup failed: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +204,7 @@ func (r *backupRepo) GetPath(typ biz.BackupType) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
if !slices.Contains([]biz.BackupType{biz.BackupTypePath, biz.BackupTypeWebsite, biz.BackupTypeMySQL, biz.BackupTypePostgres, biz.BackupTypeRedis, biz.BackupTypePanel}, typ) {
|
||||
return "", errors.New("未知备份类型")
|
||||
return "", errors.New(r.t.Get("unknown backup type"))
|
||||
}
|
||||
|
||||
backupPath = filepath.Join(backupPath, string(typ))
|
||||
@@ -236,8 +235,8 @@ func (r *backupRepo) createWebsite(to string, name string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-备份耗时:%s\n", time.Since(start).String())
|
||||
fmt.Printf("|-已备份至文件:%s\n", filepath.Base(backup))
|
||||
fmt.Println(r.t.Get("|-Backup time: %s", time.Since(start).String()))
|
||||
fmt.Println(r.t.Get("|-Backed up to file: %s", filepath.Base(backup)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -256,7 +255,7 @@ func (r *backupRepo) createMySQL(to string, name string) error {
|
||||
_ = mysql.Close()
|
||||
}(mysql)
|
||||
if exist, _ := mysql.DatabaseExists(name); !exist {
|
||||
return fmt.Errorf("数据库不存在:%s", name)
|
||||
return errors.New(r.t.Get("database does not exist: %s", name))
|
||||
}
|
||||
size, err := mysql.DatabaseSize(name)
|
||||
if err != nil {
|
||||
@@ -286,8 +285,8 @@ func (r *backupRepo) createMySQL(to string, name string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-备份耗时:%s\n", time.Since(start).String())
|
||||
fmt.Printf("|-已备份至文件:%s\n", filepath.Base(backup+".zip"))
|
||||
fmt.Println(r.t.Get("|-Backup time: %s", time.Since(start).String()))
|
||||
fmt.Println(r.t.Get("|-Backed up to file: %s", filepath.Base(backup+".zip")))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -302,7 +301,7 @@ func (r *backupRepo) createPostgres(to string, name string) error {
|
||||
_ = postgres.Close()
|
||||
}(postgres)
|
||||
if exist, _ := postgres.DatabaseExist(name); !exist {
|
||||
return fmt.Errorf("数据库不存在:%s", name)
|
||||
return errors.New(r.t.Get("database does not exist: %s", name))
|
||||
}
|
||||
size, err := postgres.DatabaseSize(name)
|
||||
if err != nil {
|
||||
@@ -326,8 +325,8 @@ func (r *backupRepo) createPostgres(to string, name string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-备份耗时:%s\n", time.Since(start).String())
|
||||
fmt.Printf("|-已备份至文件:%s\n", filepath.Base(backup+".zip"))
|
||||
fmt.Println(r.t.Get("|-Backup time: %s", time.Since(start).String()))
|
||||
fmt.Println(r.t.Get("|-Backed up to file: %s", filepath.Base(backup+".zip")))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -366,8 +365,8 @@ func (r *backupRepo) createPanel(to string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-备份耗时:%s\n", time.Since(start).String())
|
||||
fmt.Printf("|-已备份至文件:%s\n", filepath.Base(backup))
|
||||
fmt.Println(r.t.Get("|-Backup time: %s", time.Since(start).String()))
|
||||
fmt.Println(r.t.Get("|-Backed up to file: %s", filepath.Base(backup)))
|
||||
}
|
||||
|
||||
return io.Remove(temp)
|
||||
@@ -376,7 +375,7 @@ func (r *backupRepo) createPanel(to string) error {
|
||||
// restoreWebsite 恢复网站备份
|
||||
func (r *backupRepo) restoreWebsite(backup, target string) error {
|
||||
if !io.Exists(backup) {
|
||||
return errors.New("备份文件不存在")
|
||||
return errors.New(r.t.Get("backup file %s not exists", backup))
|
||||
}
|
||||
|
||||
website, err := r.website.GetByName(target)
|
||||
@@ -403,7 +402,7 @@ func (r *backupRepo) restoreWebsite(backup, target string) error {
|
||||
// restoreMySQL 恢复 MySQL 备份
|
||||
func (r *backupRepo) restoreMySQL(backup, target string) error {
|
||||
if !io.Exists(backup) {
|
||||
return errors.New("备份文件不存在")
|
||||
return errors.New(r.t.Get("backup file %s not exists", backup))
|
||||
}
|
||||
|
||||
rootPassword, err := r.setting.Get(biz.SettingKeyMySQLRootPassword)
|
||||
@@ -418,7 +417,7 @@ func (r *backupRepo) restoreMySQL(backup, target string) error {
|
||||
_ = mysql.Close()
|
||||
}(mysql)
|
||||
if exist, _ := mysql.DatabaseExists(target); !exist {
|
||||
return fmt.Errorf("数据库不存在:%s", target)
|
||||
return errors.New(r.t.Get("database does not exist: %s", target))
|
||||
}
|
||||
if err = os.Setenv("MYSQL_PWD", rootPassword); err != nil {
|
||||
return err
|
||||
@@ -449,7 +448,7 @@ func (r *backupRepo) restoreMySQL(backup, target string) error {
|
||||
// restorePostgres 恢复 PostgreSQL 备份
|
||||
func (r *backupRepo) restorePostgres(backup, target string) error {
|
||||
if !io.Exists(backup) {
|
||||
return errors.New("备份文件不存在")
|
||||
return errors.New(r.t.Get("backup file %s not exists", backup))
|
||||
}
|
||||
|
||||
postgres, err := db.NewPostgres("postgres", "", "127.0.0.1", 5432)
|
||||
@@ -460,7 +459,7 @@ func (r *backupRepo) restorePostgres(backup, target string) error {
|
||||
_ = postgres.Close()
|
||||
}(postgres)
|
||||
if exist, _ := postgres.DatabaseExist(target); !exist {
|
||||
return fmt.Errorf("数据库不存在:%s", target)
|
||||
return errors.New(r.t.Get("database does not exist: %s", target))
|
||||
}
|
||||
|
||||
clean := false
|
||||
@@ -501,18 +500,18 @@ func (r *backupRepo) preCheckPath(to, path string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-目标大小:%s\n", tools.FormatBytes(float64(size)))
|
||||
fmt.Printf("|-目标文件数:%d\n", files)
|
||||
fmt.Printf("|-备份目录可用空间:%s\n", tools.FormatBytes(float64(usage.Free)))
|
||||
fmt.Printf("|-备份目录可用Inode:%d\n", usage.InodesFree)
|
||||
fmt.Println(r.t.Get("|-Target size: %s", tools.FormatBytes(float64(size))))
|
||||
fmt.Println(r.t.Get("|-Target file count: %d", files))
|
||||
fmt.Println(r.t.Get("|-Backup directory available space: %s", tools.FormatBytes(float64(usage.Free))))
|
||||
fmt.Println(r.t.Get("|-Backup directory available Inode: %d", usage.InodesFree))
|
||||
}
|
||||
|
||||
if uint64(size) > usage.Free {
|
||||
return errors.New("备份目录空间不足")
|
||||
return errors.New(r.t.Get("Insufficient backup directory space"))
|
||||
}
|
||||
// 对于 fuse 等文件系统,可能没有 inode 的概念
|
||||
/*if uint64(files) > usage.InodesFree {
|
||||
return errors.New("备份目录Inode不足")
|
||||
return errors.New(r.t.Get("Insufficient backup directory inode"))
|
||||
}*/
|
||||
|
||||
return nil
|
||||
@@ -528,13 +527,13 @@ func (r *backupRepo) preCheckDB(to string, size int64) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-目标大小:%s\n", tools.FormatBytes(float64(size)))
|
||||
fmt.Printf("|-备份目录可用空间:%s\n", tools.FormatBytes(float64(usage.Free)))
|
||||
fmt.Printf("|-备份目录可用Inode:%d\n", usage.InodesFree)
|
||||
fmt.Println(r.t.Get("|-Target size: %s", tools.FormatBytes(float64(size))))
|
||||
fmt.Println(r.t.Get("|-Backup directory available space: %s", tools.FormatBytes(float64(usage.Free))))
|
||||
fmt.Println(r.t.Get("|-Backup directory available Inode: %d", usage.InodesFree))
|
||||
}
|
||||
|
||||
if uint64(size) > usage.Free {
|
||||
return errors.New("备份目录空间不足")
|
||||
return errors.New(r.t.Get("Insufficient backup directory space"))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -554,7 +553,7 @@ func (r *backupRepo) autoUnCompressSQL(backup string) (string, error) {
|
||||
backup = "" // 置空,防止干扰后续判断
|
||||
if files, err := os.ReadDir(temp); err == nil {
|
||||
if len(files) != 1 {
|
||||
return "", fmt.Errorf("压缩文件中包含的文件数量不为1,实际为%d", len(files))
|
||||
return "", errors.New(r.t.Get("The number of files contained in the compressed file is not 1, actual %d", len(files)))
|
||||
}
|
||||
if strings.HasSuffix(files[0].Name(), ".sql") {
|
||||
backup = filepath.Join(temp, files[0].Name())
|
||||
@@ -562,7 +561,7 @@ func (r *backupRepo) autoUnCompressSQL(backup string) (string, error) {
|
||||
}
|
||||
|
||||
if backup == "" {
|
||||
return "", errors.New("无法找到.sql备份文件")
|
||||
return "", errors.New(r.t.Get("could not find .sql backup file"))
|
||||
}
|
||||
|
||||
return backup, nil
|
||||
@@ -570,7 +569,7 @@ func (r *backupRepo) autoUnCompressSQL(backup string) (string, error) {
|
||||
|
||||
func (r *backupRepo) FixPanel() error {
|
||||
if app.IsCli {
|
||||
fmt.Println("|-开始修复面板...")
|
||||
fmt.Println(r.t.Get("|-Start fixing the panel..."))
|
||||
}
|
||||
|
||||
// 检查关键文件是否正常
|
||||
@@ -586,7 +585,7 @@ func (r *backupRepo) FixPanel() error {
|
||||
flag = true
|
||||
}
|
||||
if !flag {
|
||||
return fmt.Errorf("文件正常无需修复,请运行 panel-cli update 更新面板")
|
||||
return errors.New(r.t.Get("Files are normal and do not need to be repaired, please run panel-cli update to update the panel"))
|
||||
}
|
||||
|
||||
// 再次确认是否需要修复
|
||||
@@ -596,10 +595,10 @@ func (r *backupRepo) FixPanel() error {
|
||||
io.Exists(filepath.Join(app.Root, "panel", "storage", "app.db")) &&
|
||||
io.Exists("/usr/local/etc/panel/config.yml") {
|
||||
if err := io.Remove("/tmp/panel-storage.zip"); err != nil {
|
||||
return fmt.Errorf("清理临时文件失败:%w", err)
|
||||
return errors.New(r.t.Get("failed to clean temporary files: %w", err))
|
||||
}
|
||||
if app.IsCli {
|
||||
fmt.Println("|-已清理临时文件,请运行 panel-cli update 更新面板")
|
||||
fmt.Println(r.t.Get("|-Cleaned up temporary files, please run panel-cli update to update the panel"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -614,57 +613,57 @@ func (r *backupRepo) FixPanel() error {
|
||||
return int(b.Time.Unix() - a.Time.Unix())
|
||||
})
|
||||
if len(list) == 0 {
|
||||
return fmt.Errorf("未找到备份文件,无法自动修复")
|
||||
return errors.New(r.t.Get("No backup file found, unable to automatically repair"))
|
||||
}
|
||||
latest := list[0]
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-使用备份文件:%s\n", latest.Name)
|
||||
fmt.Println(r.t.Get("|-Backup file used: %s", latest.Name))
|
||||
}
|
||||
|
||||
// 解压备份文件
|
||||
if app.IsCli {
|
||||
fmt.Println("|-解压备份文件...")
|
||||
fmt.Println(r.t.Get("|-Unzip backup file..."))
|
||||
}
|
||||
if err = io.Remove("/tmp/panel-fix"); err != nil {
|
||||
return fmt.Errorf("清理临时目录失败:%w", err)
|
||||
return errors.New(r.t.Get("Cleaning temporary directory failed: %w", err))
|
||||
}
|
||||
if err = io.UnCompress(latest.Path, "/tmp/panel-fix"); err != nil {
|
||||
return fmt.Errorf("解压备份文件失败:%w", err)
|
||||
return errors.New(r.t.Get("Unzip backup file failed: %w", err))
|
||||
}
|
||||
|
||||
// 移动文件到对应位置
|
||||
if app.IsCli {
|
||||
fmt.Println("|-移动备份文件...")
|
||||
fmt.Println(r.t.Get("|-Move backup file..."))
|
||||
}
|
||||
if io.Exists(filepath.Join("/tmp/panel-fix", "panel")) && io.IsDir(filepath.Join("/tmp/panel-fix", "panel")) {
|
||||
if err = io.Remove(filepath.Join(app.Root, "panel")); err != nil {
|
||||
return fmt.Errorf("删除目录失败:%w", err)
|
||||
return errors.New(r.t.Get("Remove panel file failed: %w", err))
|
||||
}
|
||||
if err = io.Mv(filepath.Join("/tmp/panel-fix", "panel"), filepath.Join(app.Root)); err != nil {
|
||||
return fmt.Errorf("移动目录失败:%w", err)
|
||||
return errors.New(r.t.Get("Move panel file failed: %w", err))
|
||||
}
|
||||
}
|
||||
if io.Exists(filepath.Join("/tmp/panel-fix", "config.yml")) {
|
||||
if err = io.Mv(filepath.Join("/tmp/panel-fix", "config.yml"), "/usr/local/etc/panel/config.yml"); err != nil {
|
||||
return fmt.Errorf("移动文件失败:%w", err)
|
||||
return errors.New(r.t.Get("Move panel config failed: %w", err))
|
||||
}
|
||||
}
|
||||
if io.Exists(filepath.Join("/tmp/panel-fix", "panel-cli")) {
|
||||
if err = io.Mv(filepath.Join("/tmp/panel-fix", "panel-cli"), "/usr/local/sbin/panel-cli"); err != nil {
|
||||
return fmt.Errorf("移动文件失败:%w", err)
|
||||
return errors.New(r.t.Get("Move panel-cli file failed: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
// tmp 目录下如果有 storage 备份,则解压回去
|
||||
if app.IsCli {
|
||||
fmt.Println("|-恢复面板数据...")
|
||||
fmt.Println(r.t.Get("|-Restore panel data..."))
|
||||
}
|
||||
if io.Exists("/tmp/panel-storage.zip") {
|
||||
if err = io.UnCompress("/tmp/panel-storage.zip", filepath.Join(app.Root, "panel")); err != nil {
|
||||
return fmt.Errorf("恢复面板数据失败:%w", err)
|
||||
return errors.New(r.t.Get("Unzip panel data failed: %w", err))
|
||||
}
|
||||
if err = io.Remove("/tmp/panel-storage.zip"); err != nil {
|
||||
return fmt.Errorf("清理临时文件失败:%w", err)
|
||||
return errors.New(r.t.Get("Cleaning temporary file failed: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +676,7 @@ func (r *backupRepo) FixPanel() error {
|
||||
|
||||
// 处理权限
|
||||
if app.IsCli {
|
||||
fmt.Println("|-设置关键文件权限...")
|
||||
fmt.Println(r.t.Get("|-Set key file permissions..."))
|
||||
}
|
||||
if err = io.Chmod("/usr/local/etc/panel/config.yml", 0600); err != nil {
|
||||
return err
|
||||
@@ -697,7 +696,7 @@ func (r *backupRepo) FixPanel() error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-修复完成")
|
||||
fmt.Println(r.t.Get("|-Fix completed"))
|
||||
}
|
||||
|
||||
tools.RestartPanel()
|
||||
@@ -715,119 +714,107 @@ func (r *backupRepo) UpdatePanel(version, url, checksum string) error {
|
||||
|
||||
name := filepath.Base(url)
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-目标版本:%s\n", version)
|
||||
fmt.Printf("|-下载链接:%s\n", url)
|
||||
fmt.Printf("|-文件名:%s\n", name)
|
||||
fmt.Println(r.t.Get("|-Target version: %s", version))
|
||||
fmt.Println(r.t.Get("|-Download link: %s", url))
|
||||
fmt.Println(r.t.Get("|-File name: %s", name))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-正在下载...")
|
||||
fmt.Println(r.t.Get("|-Downloading..."))
|
||||
}
|
||||
if _, err := shell.Execf("wget -T 120 -t 3 -O /tmp/%s %s", name, url); err != nil {
|
||||
return fmt.Errorf("下载失败:%w", err)
|
||||
return errors.New(r.t.Get("Download failed: %w", err))
|
||||
}
|
||||
if _, err := shell.Execf("wget -T 20 -t 3 -O /tmp/%s %s", name+".sha256", checksum); err != nil {
|
||||
return fmt.Errorf("下载失败:%w", err)
|
||||
return errors.New(r.t.Get("Download failed: %w", err))
|
||||
}
|
||||
if !io.Exists(filepath.Join("/tmp", name)) || !io.Exists(filepath.Join("/tmp", name+".sha256")) {
|
||||
return errors.New("下载文件检查失败")
|
||||
return errors.New(r.t.Get("Download file check failed"))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-校验下载文件...")
|
||||
fmt.Println(r.t.Get("|-Verify download file..."))
|
||||
}
|
||||
if check, err := shell.Execf("cd /tmp && sha256sum -c %s --ignore-missing", name+".sha256"); check != name+": OK" || err != nil {
|
||||
return errors.New("下载文件校验失败")
|
||||
return errors.New(r.t.Get("Verify download file failed: %w", err))
|
||||
}
|
||||
if err := io.Remove(filepath.Join("/tmp", name+".sha256")); err != nil {
|
||||
if app.IsCli {
|
||||
fmt.Println("|-清理校验文件失败:", err)
|
||||
}
|
||||
return fmt.Errorf("清理校验文件失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Clean up verification file failed: %w", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-前置检查...")
|
||||
}
|
||||
if io.Exists("/tmp/panel-storage.zip") {
|
||||
return errors.New("检测到 /tmp 存在临时文件,可能是上次更新失败所致,请运行 panel-cli fix 修复后重试")
|
||||
return errors.New(r.t.Get("Temporary file detected in /tmp, this may be caused by the last update failure, please run panel-cli fix to repair and try again"))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-备份面板数据...")
|
||||
fmt.Println(r.t.Get("|-Backup panel data..."))
|
||||
}
|
||||
// 备份面板
|
||||
if err := r.Create(biz.BackupTypePanel, ""); err != nil {
|
||||
if app.IsCli {
|
||||
fmt.Println("|-备份面板失败:", err)
|
||||
}
|
||||
return fmt.Errorf("备份面板失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Backup panel data failed: %w", err))
|
||||
}
|
||||
if err := io.Compress(filepath.Join(app.Root, "panel/storage"), nil, "/tmp/panel-storage.zip"); err != nil {
|
||||
if app.IsCli {
|
||||
fmt.Println("|-备份面板数据失败:", err)
|
||||
}
|
||||
return fmt.Errorf("备份面板数据失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Backup panel data failed: %w", err))
|
||||
}
|
||||
if !io.Exists("/tmp/panel-storage.zip") {
|
||||
return errors.New("已备份面板数据检查失败")
|
||||
return errors.New(r.t.Get("|-Backup panel data failed, missing file"))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-清理旧版本...")
|
||||
fmt.Println(r.t.Get("|-Cleaning old version..."))
|
||||
}
|
||||
if _, err := shell.Execf("rm -rf %s/panel/*", app.Root); err != nil {
|
||||
return fmt.Errorf("清理旧版本失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Cleaning old version failed: %w", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-解压新版本...")
|
||||
fmt.Println(r.t.Get("|-Unzip new version..."))
|
||||
}
|
||||
if err := io.UnCompress(filepath.Join("/tmp", name), filepath.Join(app.Root, "panel")); err != nil {
|
||||
return fmt.Errorf("解压失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Unzip new version failed: %w", err))
|
||||
}
|
||||
if !io.Exists(filepath.Join(app.Root, "panel", "web")) {
|
||||
return errors.New("解压失败,缺失文件")
|
||||
return errors.New(r.t.Get("|-Unzip new version failed, missing file"))
|
||||
}
|
||||
if err := io.Remove(filepath.Join("/tmp", name)); err != nil {
|
||||
return fmt.Errorf("清理临时文件失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Clean up temporary file failed: %w", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-恢复面板数据...")
|
||||
fmt.Println(r.t.Get("|-Restore panel data..."))
|
||||
}
|
||||
if err := io.UnCompress("/tmp/panel-storage.zip", filepath.Join(app.Root, "panel", "storage")); err != nil {
|
||||
return fmt.Errorf("恢复面板数据失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Restore panel data failed: %w", err))
|
||||
}
|
||||
if !io.Exists(filepath.Join(app.Root, "panel/storage/app.db")) {
|
||||
return errors.New("恢复面板数据失败")
|
||||
return errors.New(r.t.Get("|-Restore panel data failed, missing file"))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-运行更新后脚本...")
|
||||
fmt.Println(r.t.Get("|-Run post-update script..."))
|
||||
}
|
||||
if _, err := shell.Execf("curl -fsLm 10 https://dl.cdn.haozi.net/panel/auto_update.sh | bash"); err != nil {
|
||||
return fmt.Errorf("运行面板更新后脚本失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Run post-update script failed: %w", err))
|
||||
}
|
||||
if _, err := shell.Execf(`wget -O /etc/systemd/system/panel.service https://dl.cdn.haozi.net/panel/panel.service && sed -i "s|/www|%s|g" /etc/systemd/system/panel.service`, app.Root); err != nil {
|
||||
return fmt.Errorf("下载面板服务文件失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Download panel service file failed: %w", err))
|
||||
}
|
||||
if _, err := shell.Execf("panel-cli setting write version %s", version); err != nil {
|
||||
return fmt.Errorf("写入面板版本号失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Write new panel version failed: %w", err))
|
||||
}
|
||||
if err := io.Mv(filepath.Join(app.Root, "panel/cli"), "/usr/local/sbin/panel-cli"); err != nil {
|
||||
return fmt.Errorf("移动面板命令行工具失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Move panel-cli tool failed: %w", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-设置关键文件权限...")
|
||||
fmt.Println(r.t.Get("|-Set key file permissions..."))
|
||||
}
|
||||
_ = io.Chmod("/usr/local/sbin/panel-cli", 0700)
|
||||
_ = io.Chmod("/etc/systemd/system/panel.service", 0644)
|
||||
_ = io.Chmod(filepath.Join(app.Root, "panel"), 0700)
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-更新完成")
|
||||
fmt.Println(r.t.Get("|-Update completed"))
|
||||
}
|
||||
|
||||
_, _ = shell.Execf("systemctl daemon-reload")
|
||||
|
||||
@@ -276,14 +276,24 @@ func (r *websiteRepo) Create(req *request.WebsiteCreate) (*biz.Website, error) {
|
||||
if err = os.MkdirAll(req.Path, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
index, err := embed.WebsiteFS.ReadFile(filepath.Join("website", "index.html"))
|
||||
var index []byte
|
||||
if app.Locale == "zh_CN" {
|
||||
index, err = embed.WebsiteFS.ReadFile(filepath.Join("website", "index_zh.html"))
|
||||
} else {
|
||||
index, err = embed.WebsiteFS.ReadFile(filepath.Join("website", "index.html"))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取index模板文件失败: %w", err)
|
||||
}
|
||||
if err = io.Write(filepath.Join(req.Path, "index.html"), string(index), 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
notFound, err := embed.WebsiteFS.ReadFile(filepath.Join("website", "404.html"))
|
||||
var notFound []byte
|
||||
if app.Locale == "zh_CN" {
|
||||
notFound, err = embed.WebsiteFS.ReadFile(filepath.Join("website", "404_zh.html"))
|
||||
} else {
|
||||
notFound, err = embed.WebsiteFS.ReadFile(filepath.Join("website", "404.html"))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取404模板文件失败: %w", err)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (s *FileService) Content(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
fileInfo, err := io.FileInfo(req.Path)
|
||||
fileInfo, err := stdos.Stat(req.Path)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -82,7 +82,7 @@ func (s *FileService) Content(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := io.ReadBytes(req.Path)
|
||||
content, err := stdos.ReadFile(req.Path)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -106,7 +106,7 @@ func (s *FileService) Save(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
fileInfo, err := io.FileInfo(req.Path)
|
||||
fileInfo, err := stdos.Stat(req.Path)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -265,7 +265,7 @@ func (s *FileService) Download(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
info, err := io.FileInfo(req.Path)
|
||||
info, err := stdos.Stat(req.Path)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -309,7 +309,7 @@ func (s *FileService) Info(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
info, err := io.FileInfo(req.Path)
|
||||
info, err := stdos.Stat(req.Path)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
|
||||
@@ -114,7 +114,7 @@ func generatePrivateKey(keyType KeyType) (crypto.Signer, error) {
|
||||
return rsa.GenerateKey(rand.Reader, 4096)
|
||||
}
|
||||
|
||||
return nil, errors.New("未知的密钥类型")
|
||||
return nil, errors.New("unsupported key type")
|
||||
}
|
||||
|
||||
func getClient(CA string, log *slog.Logger) (acmez.Client, error) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
package acme
|
||||
@@ -54,11 +54,7 @@ func ParseKey(key string) (crypto.Signer, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if parse, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil {
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("解析私钥失败")
|
||||
return x509.ParseECPrivateKey(keyBlockDER.Bytes)
|
||||
}
|
||||
|
||||
func EncodeCert(cert x509.Certificate) ([]byte, error) {
|
||||
@@ -88,7 +84,7 @@ func EncodeKey(key crypto.Signer) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("未知的密钥类型 %T", key)
|
||||
return nil, fmt.Errorf("unsupported key type %T", key)
|
||||
}
|
||||
pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes}
|
||||
return pem.EncodeToMemory(&pemKey), nil
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
@@ -12,21 +11,21 @@ import (
|
||||
func MySQLResetRootPassword(password string) error {
|
||||
_ = systemctl.Stop("mysqld")
|
||||
if run, err := systemctl.Status("mysqld"); err != nil || run {
|
||||
return fmt.Errorf("停止MySQL失败: %w", err)
|
||||
return fmt.Errorf("failed to stop MySQL: %w", err)
|
||||
}
|
||||
_, _ = shell.Execf(`systemctl set-environment MYSQLD_OPTS="--skip-grant-tables --skip-networking"`)
|
||||
if err := systemctl.Start("mysqld"); err != nil {
|
||||
return fmt.Errorf("以安全模式启动MySQL失败: %w", err)
|
||||
return fmt.Errorf("failed to start MySQL in safe mode: %w", err)
|
||||
}
|
||||
if _, err := shell.Execf(`mysql -uroot -e "FLUSH PRIVILEGES;UPDATE mysql.user SET authentication_string=null WHERE user='root' AND host='localhost';ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';FLUSH PRIVILEGES;"`, password); err != nil {
|
||||
return errors.New("设置root密码失败")
|
||||
return fmt.Errorf("failed to reset MySQL root password: %w", err)
|
||||
}
|
||||
if err := systemctl.Stop("mysqld"); err != nil {
|
||||
return fmt.Errorf("停止MySQL失败: %w", err)
|
||||
return fmt.Errorf("failed to stop MySQL: %w", err)
|
||||
}
|
||||
_, _ = shell.Execf(`systemctl unset-environment MYSQLD_OPTS`)
|
||||
if err := systemctl.Start("mysqld"); err != nil {
|
||||
return fmt.Errorf("启动MySQL失败: %w", err)
|
||||
return fmt.Errorf("failed to start MySQL: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -205,11 +205,11 @@ func (r *Postgres) Users() ([]types.PostgresUser, error) {
|
||||
}
|
||||
|
||||
permissions := map[string]bool{
|
||||
"超级用户": super,
|
||||
"创建角色": canCreateRole,
|
||||
"创建数据库": canCreateDb,
|
||||
"可以复制": replication,
|
||||
"绕过行级安全": bypassRls,
|
||||
"Super": super,
|
||||
"CreateRole": canCreateRole,
|
||||
"CreateDB": canCreateDb,
|
||||
"Replication": replication,
|
||||
"BypassRLS": bypassRls,
|
||||
}
|
||||
for perm, enabled := range permissions {
|
||||
if enabled {
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>404 Not Found</h1>
|
||||
<p>由 <a target="_blank" href="https://panel.haozi.net">耗子面板</a> 强力驱动</p>
|
||||
<p>Powered by <a target="_blank" href="https://panel.haozi.net">Rat Panel</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
55
pkg/embed/website/404_zh.html
Normal file
55
pkg/embed/website/404_zh.html
Normal file
@@ -0,0 +1,55 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>404 Not Found</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #f9f9f9;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 2em auto;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #ddd;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
p {
|
||||
color: #555;
|
||||
line-height: 1.8;
|
||||
text-align: center;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #007bff;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
margin: 2em 15px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>404 Not Found</h1>
|
||||
<p>由 <a target="_blank" href="https://panel.haozi.net">耗子面板</a> 强力驱动</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>耗子面板</title>
|
||||
<title>Rat Panel</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #f9f9f9;
|
||||
@@ -49,10 +49,10 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>耗子面板</h1>
|
||||
<p>这是耗子面板的网站默认页面!</p>
|
||||
<p>当您看到此页面,说明您的网站已创建成功。</p>
|
||||
<p>由 <a target="_blank" href="https://panel.haozi.net">耗子面板</a> 强力驱动</p>
|
||||
<h1>Rat Panel</h1>
|
||||
<p>This is the default page for the Rat Panel website!</p>
|
||||
<p>When you see this page, it means your website has been created successfully.</p>
|
||||
<p>Powered by <a target="_blank" href="https://panel.haozi.net">Rat Panel</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
58
pkg/embed/website/index_zh.html
Normal file
58
pkg/embed/website/index_zh.html
Normal file
@@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>耗子面板</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #f9f9f9;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 2em auto;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #ddd;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
p {
|
||||
color: #555;
|
||||
line-height: 1.8;
|
||||
text-align: center;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #007bff;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
margin: 2em 15px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>耗子面板</h1>
|
||||
<p>这是耗子面板的网站默认页面!</p>
|
||||
<p>当您看到此页面,说明您的网站已创建成功。</p>
|
||||
<p>由 <a target="_blank" href="https://panel.haozi.net">耗子面板</a> 强力驱动</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -95,16 +95,6 @@ func Read(path string) (string, error) {
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
// ReadBytes 读取文件
|
||||
func ReadBytes(path string) ([]byte, error) {
|
||||
return os.ReadFile(path)
|
||||
}
|
||||
|
||||
// FileInfo 获取文件大小
|
||||
func FileInfo(path string) (os.FileInfo, error) {
|
||||
return os.Stat(path)
|
||||
}
|
||||
|
||||
// IsSymlink 判读是否为软链接
|
||||
func IsSymlink(mode os.FileMode) bool {
|
||||
return mode&os.ModeSymlink != 0
|
||||
|
||||
@@ -31,3 +31,31 @@ const defaultConf = `server {
|
||||
error_log /www/wwwlogs/default.log;
|
||||
}
|
||||
`
|
||||
|
||||
const defaultConfEn = `server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
index index.php index.html index.htm;
|
||||
root /www/wwwroot/default;
|
||||
# Error page configuration
|
||||
error_page 404 /404.html;
|
||||
include enable-php-0.conf;
|
||||
# Do not log static files
|
||||
location ~ .*\.(bmp|jpg|jpeg|png|gif|svg|ico|tiff|webp|avif|heif|heic|jxl)$ {
|
||||
expires 30d;
|
||||
access_log /dev/null;
|
||||
error_log /dev/null;
|
||||
}
|
||||
location ~ .*\.(js|css|ttf|otf|woff|woff2|eot)$ {
|
||||
expires 6h;
|
||||
access_log /dev/null;
|
||||
error_log /dev/null;
|
||||
}
|
||||
# Deny access to sensitive directories
|
||||
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) {
|
||||
return 404;
|
||||
}
|
||||
access_log /www/wwwlogs/default.log;
|
||||
error_log /www/wwwlogs/default.log;
|
||||
}
|
||||
`
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
)
|
||||
|
||||
var ErrNotReachable = errors.New("无法连接到 NTP 服务器")
|
||||
var ErrNotReachable = errors.New("failed to reach NTP server")
|
||||
|
||||
var ErrNoAvailableServer = errors.New("无可用的 NTP 服务器")
|
||||
var ErrNoAvailableServer = errors.New("no available NTP server found")
|
||||
|
||||
var defaultAddresses = []string{
|
||||
//"ntp.ntsc.ac.cn", // 中科院国家授时中心的服务器很快,但是多刷几次就会被封
|
||||
|
||||
Reference in New Issue
Block a user