feat(cmd): Add support for internationalization of 1Panel commands (#7192)

pull/7203/head
ssongliu 1 week ago committed by GitHub
parent f7dcacab92
commit 519c6bca66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -28,5 +28,6 @@ var (
MonitorCronID cron.EntryID
OneDriveCronID cron.EntryID
I18n *i18n.Localizer
I18n *i18n.Localizer
I18nForCmd *i18n.Localizer
)

@ -2,9 +2,10 @@ package i18n
import (
"embed"
"github.com/1Panel-dev/1Panel/backend/global"
"strings"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/gin-gonic/gin"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
@ -89,9 +90,54 @@ func UseI18n() gin.HandlerFunc {
}
func Init() {
if bundle != nil {
return
}
bundle = i18n.NewBundle(language.Chinese)
bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
_, _ = bundle.LoadMessageFileFS(fs, "lang/zh.yaml")
_, _ = bundle.LoadMessageFileFS(fs, "lang/en.yaml")
_, _ = bundle.LoadMessageFileFS(fs, "lang/zh-Hant.yaml")
_, _ = bundle.LoadMessageFileFS(fs, "lang/fa.yaml")
}
func UseI18nForCmd(lang string) {
if lang == "" {
lang = "zh"
}
if bundle == nil {
Init()
}
global.I18nForCmd = i18n.NewLocalizer(bundle, lang)
}
func GetMsgByKeyForCmd(key string) string {
if global.I18nForCmd == nil {
UseI18nForCmd("")
}
content, _ := global.I18nForCmd.Localize(&i18n.LocalizeConfig{
MessageID: key,
})
return content
}
func GetMsgWithMapForCmd(key string, maps map[string]interface{}) string {
if global.I18nForCmd == nil {
UseI18nForCmd("")
}
var content string
if maps == nil {
content, _ = global.I18nForCmd.Localize(&i18n.LocalizeConfig{
MessageID: key,
})
} else {
content, _ = global.I18nForCmd.Localize(&i18n.LocalizeConfig{
MessageID: key,
TemplateData: maps,
})
}
content = strings.ReplaceAll(content, ": <no value>", "")
if content == "" {
return key
} else {
return content
}
}

@ -215,4 +215,67 @@ ErrAlert: "Alert information format error, please check and try again!"
ErrAlertPush: "Alert push error, please check and try again!"
ErrAlertSave: "Alert save error, please check and try again!"
ErrAlertSync: "Alert sync error, please check and try again!"
ErrAlertRemote: "Remote alert error, please check and try again!"
ErrAlertRemote: "Remote alert error, please check and try again!"
#cmd
AppVersion : "App version"
AppCommands : "App related commands"
AppInit : "Initialize app"
AppKeyVal: "App key (only supports English)"
AppCreateFileErr : "File {{ .name }} creation failed {{ .err }}"
AppCreateDirErr : "Folder {{ .name }} creation failed {{ .err }}"
AppMissKey : "App key missing, use -k to specify"
AppMissVersion : "App version missing, use -v to specify"
AppVersionExist : "Version already exists!"
AppCreateSuccessful : "Creation successful!"
AppWriteErr : "File {{ .name }} write failed {{ .err }}"
SudoHelper : "Please use {{ .cmd }} or switch to root user"
ListenIPCommands : "Switch listening ip"
ListenIPv4 : "Listen on IPv4"
ListenIPv6 : "Listen on IPv6"
ListenChangeSuccessful : "Switch successful! Now listening on {{ .value }}"
ResetCommands : "Reset system info"
ResetMFA : "Cancel 1Panel two-factor authentication"
ResetHttps : "Cancel 1Panel https login"
ResetEntrance : "Cancel 1Panel secure entrance"
ResetIPs : "Cancel 1Panel authorized ip restrictions"
ResetDomain : "Cancel 1Panel domain binding"
RestoreCommands : "Rollback 1Panel service and data"
RestoreNoSuchFile : "No files available for rollback"
RestoreStep1 : "(0/4) Starting rollback of 1Panel service and data from {{ .name }} directory..."
RestoreStep2 : "1/4 1panel binary rollback successful"
RestoreStep3 : "2/4 1panel script rollback successful"
RestoreStep4 : "3/4 1panel service rollback successful"
RestoreStep5 : "4/4 1panel data rollback successful"
RestoreSuccessful : "Rollback successful! Restarting service, please wait..."
UpdateCommands : "Update panel info"
UpdateUser : "Update panel user"
UpdatePassword : "Update panel password"
UpdatePort : "Update panel port"
UpdateUserNull : "Error: panel user is empty!"
UpdateUserBlank : "Error: panel user contains spaces!"
UpdateUserFormat : "Error: Invalid panel user format! Only supports English, Chinese, numbers, and _, length 3-30"
UpdateUserErr : "Error: Failed to update panel user, {{ .err }}"
UpdateSuccessful : "Update successful!"
UpdateUserResult : "Panel user: {{ .name }}"
UpdatePasswordRead : "Error: Failed to read panel password information, {{ .err }}"
UpdatePasswordNull : "Error: Panel password is empty!"
UpdateUPasswordBlank : "Error: Panel password contains spaces!"
UpdatePasswordFormat : "Error: Panel password only supports letters, numbers, special characters !@#$%*_,.?, length 8-30!"
UpdatePasswordLen : "Error: Please enter a password longer than 6 characters!"
UpdatePasswordRe : "Confirm password:"
UpdatePasswordErr : "Error: Failed to update panel password, {{ .err }}"
UpdatePasswordSame : "Error: The two passwords do not match, please check and try again!"
UpdatePasswordResult : "Panel password: {{ .name }}"
UpdatePortFormat : "Error: The input port number must be between 1 and 65535!"
UpdatePortUsed : "Error: The port number is already in use, please check and try again!"
UpdatePortErr : "Error: Failed to update panel port, {{ .err }}"
UpdatePortResult : "Panel Port: {{ .name }}"
UpdatePortFirewallAdd : "Failed to add firewall port rule, {{ .err }}, please manually add the {{ .name }} port to the firewall rules."
UpdatePortFirewallDel : "Error: Failed to delete firewall port, {{ .err }}"
UpdatePortFirewallReload : "Failed to reload the firewall, {{ .err }}, please manually reload the firewall."
UserInfo : "Get panel information"
UserInfoAddr : "Panel address: "
UserInfoPassHelp : "Tip: To change the password, you can execute the command: "
SystemVersion : "Get system version information"
DBConnErr : "Error: Failed to initialize database connection, {{ .err }}"

@ -216,3 +216,66 @@ ErrAlertPush: "告警資訊推送錯誤,請檢查後重試!"
ErrAlertSave: "告警資訊保存錯誤,請檢查後重試!"
ErrAlertSync: "告警資訊同步錯誤,請檢查後重試!"
ErrAlertRemote: "告警資訊遠端錯誤,請檢查後重試!"
#cmd
AppVersion : "應用版本"
AppCommands : "應用相關命令"
AppInit : "初始化應用"
AppKeyVal: "應用的key僅支持英文"
AppCreateFileErr : "文件 {{ .name }} 創建失敗 {{ .err }}"
AppCreateDirErr : "資料夾 {{ .name }} 創建失敗 {{ .err }}"
AppMissKey : "應用的 key 缺失,使用 -k 指定"
AppMissVersion : "應用版本缺失,使用 -v 指定"
AppVersionExist : "版本已存在!"
AppCreateSuccessful : "創建成功!"
AppWriteErr : "文件 {{ .name }} 寫入失敗 {{ .err }}"
SudoHelper : "請使用 {{ .cmd }} 或者切換到 root 用戶"
ListenIPCommands : "切換監聽 IP"
ListenIPv4 : "監聽 IPv4"
ListenIPv6 : "監聽 IPv6"
ListenChangeSuccessful : "切換成功!已切換至監聽 {{ .value }}"
ResetCommands : "重置系統信息"
ResetMFA : "取消 1Panel 兩步驗證"
ResetHttps : "取消 1Panel https 方式登錄"
ResetEntrance : "取消 1Panel 安全入口"
ResetIPs : "取消 1Panel 授權 IP 限制"
ResetDomain : "取消 1Panel 訪問域名綁定"
RestoreCommands : "回滾 1Panel 服務及數據"
RestoreNoSuchFile : "暫無可回滾文件"
RestoreStep1 : "(0/4) 開始從 {{ .name }} 目錄回滾 1Panel 服務及數據... "
RestoreStep2 : "1/4 1panel 二進制回滾成功"
RestoreStep3 : "2/4 1panel 腳本回滾成功"
RestoreStep4 : "3/4 1panel 服務回滾成功"
RestoreStep5 : "4/4 1panel 數據回滾成功"
RestoreSuccessful : "回滾成功!正在重啟服務,請稍候..."
UpdateCommands : "修改面板信息"
UpdateUser : "修改面板用戶"
UpdatePassword : "修改面板密碼"
UpdatePort : "修改面板端口"
UpdateUserNull : "錯誤:輸入面板用戶為空!"
UpdateUserBlank : "錯誤:輸入面板用戶中包含空格字符!"
UpdateUserFormat : "錯誤輸入面板用戶錯誤僅支持英文、中文、數字和_長度3-30"
UpdateUserErr : "錯誤:面板用戶修改失敗,{{ .err }}"
UpdateSuccessful : "修改成功!"
UpdateUserResult : "面板用戶:{{ .name }}"
UpdatePasswordRead : "錯誤:面板密碼信息讀取錯誤,{{ .err }}"
UpdatePasswordNull : "錯誤:輸入面板密碼為空!"
UpdateUPasswordBlank : "錯誤:輸入面板密碼中包含空格字符!"
UpdatePasswordFormat : "錯誤:面板密碼僅支持字母、數字、特殊字符(!@#$%*_,.?),長度 8-30 位!"
UpdatePasswordLen : "錯誤:請輸入 6 位以上密碼!"
UpdatePasswordRe : "確認密碼:"
UpdatePasswordErr : "錯誤:面板密碼修改失敗,{{ .err }}"
UpdatePasswordSame : "錯誤:兩次密碼不匹配,請檢查後重試!"
UpdatePasswordResult : "面板密碼:{{ .name }}"
UpdatePortFormat : "錯誤:輸入的端口號必須在 1 到 65535 之間!"
UpdatePortUsed : "錯誤:該端口號正被佔用,請檢查後重試!"
UpdatePortErr : "錯誤:面板端口修改失敗,{{ .err }}"
UpdatePortResult : "面板端口:{{ .name }}"
UpdatePortFirewallAdd : "添加防火牆端口規則失敗,{{ .err }},請您手動將 {{ .name }} 端口添加至防火牆規則中。"
UpdatePortFirewallDel : "錯誤:防火牆端口刪除失敗,{{ .err }}"
UpdatePortFirewallReload : "防火牆重載失敗,{{ .err }},請您手動重載防火牆。"
UserInfo : "獲取面板信息"
UserInfoAddr : "面板地址:"
UserInfoPassHelp : "提示:修改密碼可執行命令:"
SystemVersion : "獲取系統版本信息"
DBConnErr : "錯誤:初始化資料庫連接失敗, {{ .err }}"

@ -219,3 +219,65 @@ ErrAlertSave: "告警信息保存错误,请检查后重试!"
ErrAlertSync: "告警信息同步错误,请检查后重试!"
ErrAlertRemote: "告警信息远端错误,请检查后重试!"
#cmd
AppVersion: "应用版本"
AppCommands: "应用相关命令"
AppInit: "初始化应用"
AppKeyVal: "应用的key仅支持英文"
AppCreateFileErr: "文件 {{ .name }} 创建失败 {{ .err }}"
AppCreateDirErr: "文件夹 {{ .name }} 创建失败 {{ .err }}"
AppMissKey: "应用的 key 缺失,使用 -k 指定"
AppMissVersion: "应用版本缺失,使用 -v 指定"
AppVersionExist: "版本已存在!"
AppCreateSuccessful: "创建成功!"
AppWriteErr: "文件 {{ .name }} 写入失败 {{ .err }}"
SudoHelper: "请使用 {{ .cmd }} 或者切换到 root 用户"
ListenIPCommands: "切换监听 IP"
ListenIPv4: "监听 IPv4"
ListenIPv6: "监听 IPv6"
ListenChangeSuccessful: "切换成功!已切换至监听 {{ .value }}"
ResetCommands: "重置系统信息"
ResetMFA: "取消 1Panel 两步验证"
ResetHttps: "取消 1Panel https 方式登录"
ResetEntrance: "取消 1Panel 安全入口"
ResetIPs: "取消 1Panel 授权 IP 限制"
ResetDomain: "取消 1Panel 访问域名绑定"
RestoreCommands: "回滚 1Panel 服务及数据"
RestoreNoSuchFile: "暂无可回滚文件"
RestoreStep1: "(0/4) 开始从 {{ .name }} 目录回滚 1Panel 服务及数据... "
RestoreStep2: "(1/4) 1panel 二进制回滚成功"
RestoreStep3: "(2/4) 1panel 脚本回滚成功"
RestoreStep4: "(3/4) 1panel 服务回滚成功"
RestoreStep5: "(4/4) 1panel 数据回滚成功"
RestoreSuccessful: "回滚成功!正在重启服务,请稍候..."
UpdateCommands: "修改面板信息"
UpdateUser: "修改面板用户"
UpdatePassword: "修改面板密码"
UpdatePort: "修改面板端口"
UpdateUserNull: "错误:输入面板用户为空!"
UpdateUserBlank: "错误:输入面板用户中包含空格字符!"
UpdateUserFormat: "错误输入面板用户错误仅支持英文、中文、数字和_,长度3-30"
UpdateUserErr: "错误:面板用户修改失败,{{ .err }}"
UpdateSuccessful: "修改成功!"
UpdateUserResult: "面板用户:{{ .name }}"
UpdatePasswordRead: "错误:面板密码信息读取错误,{{ .err }}"
UpdatePasswordNull: "错误:输入面板密码为空!"
UpdateUPasswordBlank: "错误:输入面板密码中包含空格字符!"
UpdatePasswordFormat: "错误:面板密码仅支持字母、数字、特殊字符(!@#$%*_,.?),长度 8-30 位!"
UpdatePasswordLen: "错误:请输入 6 位以上密码!"
UpdatePasswordRe: "确认密码:"
UpdatePasswordErr: "错误:面板密码修改失败,{{ .err }}"
UpdatePasswordSame: "错误:两次密码不匹配,请检查后重试!"
UpdatePasswordResult: 面板密码:{{ .name }}"
UpdatePortFormat: "错误:输入的端口号必须在 1 到 65535 之间!"
UpdatePortUsed: "错误:该端口号正被占用,请检查后重试!"
UpdatePortErr: "错误:面板端口修改失败,{{ .err }}"
UpdatePortResult: "面板端口:{{ .name }}"
UpdatePortFirewallAdd: "添加防火墙端口规则失败,{{ .err }},请您手动将 {{ .name }} 端口添加至防火墙规则中。"
UpdatePortFirewallDel: "错误:防火墙端口删除失败,{{ .err }}"
UpdatePortFirewallReload: "防火墙重载失败,{{ .err }},请您手动重载防火墙。"
UserInfo: "获取面板信息"
UserInfoAddr: "面板地址:"
UserInfoPassHelp: "提示:修改密码可执行命令:"
SystemVersion: "获取系统版本信息"
DBConnErr: "错误:初始化数据库连接失败, {{ .err }}"

@ -5,6 +5,7 @@ import (
"fmt"
"io"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/cmd/server/app"
"github.com/pkg/errors"
@ -17,23 +18,31 @@ var (
)
func init() {
initCmd.Flags().StringVarP(&appKey, "key", "k", "", "应用的key仅支持英文")
initCmd.Flags().StringVarP(&appVersion, "version", "v", "", "应用版本")
appCmd.SetHelpFunc(func(c *cobra.Command, s []string) {
i18n.UseI18nForCmd(language)
loadAppHelper()
})
initCmd.SetHelpFunc(func(c *cobra.Command, s []string) {
i18n.UseI18nForCmd(language)
loadAppInitHelper()
})
initCmd.Flags().StringVarP(&appKey, "key", "k", "", "")
initCmd.Flags().StringVarP(&appVersion, "version", "v", "", "")
appCmd.AddCommand(initCmd)
RootCmd.AddCommand(appCmd)
}
var appCmd = &cobra.Command{
Use: "app",
Short: "应用相关命令",
Use: "app",
}
var initCmd = &cobra.Command{
Use: "init",
Short: "初始化应用",
Use: "init",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl app init 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl app init"}))
return nil
}
if len(args) > 0 {
@ -43,11 +52,11 @@ var initCmd = &cobra.Command{
}
}
if appKey == "" {
fmt.Println("应用的 key 缺失,使用 -k 指定")
fmt.Println(i18n.GetMsgByKeyForCmd("AppMissKey"))
return nil
}
if appVersion == "" {
fmt.Println("应用版本缺失,使用 -v 指定")
fmt.Println(i18n.GetMsgByKeyForCmd("AppMissVersion"))
return nil
}
fileOp := files.NewFileOp()
@ -75,7 +84,7 @@ var initCmd = &cobra.Command{
}
versionPath := fmt.Sprintf("%s/%s", appKeyPath, appVersion)
if fileOp.Stat(versionPath) {
return errors.New("版本已存在!")
return errors.New(i18n.GetMsgByKeyForCmd("AppVersionExist"))
}
if err := createFolder(fileOp, versionPath); err != nil {
return err
@ -91,7 +100,7 @@ var initCmd = &cobra.Command{
if err := createFile(fileOp, dockerComposeYamlPath); err != nil {
return err
}
fmt.Println("创建成功!")
fmt.Println(i18n.GetMsgByKeyForCmd("AppCreateSuccessful"))
return nil
},
}
@ -101,7 +110,7 @@ func createFile(fileOp files.FileOp, filePath string) error {
return nil
}
if err := fileOp.CreateFile(filePath); err != nil {
fmt.Printf("文件 %s 创建失败 %v", filePath, err)
fmt.Println(i18n.GetMsgWithMapForCmd("AppCreateFileErr", map[string]interface{}{"name": filePath, "err": err.Error()}))
return err
}
return nil
@ -112,7 +121,7 @@ func createFolder(fileOp files.FileOp, dirPath string) error {
return nil
}
if err := fileOp.CreateDir(dirPath, 0755); err != nil {
fmt.Printf("文件夹 %s 创建失败 %v", dirPath, err)
fmt.Println(i18n.GetMsgWithMapForCmd("AppCreateDirErr", map[string]interface{}{"name": dirPath, "err": err.Error()}))
return err
}
return nil
@ -120,8 +129,26 @@ func createFolder(fileOp files.FileOp, dirPath string) error {
func writeFile(fileOp files.FileOp, filePath string, in io.Reader) error {
if err := fileOp.WriteFile(filePath, in, 0755); err != nil {
fmt.Printf("文件 %s 写入失败 %v", filePath, err)
fmt.Println(i18n.GetMsgWithMapForCmd("AppWriteErr", map[string]interface{}{"name": filePath, "err": err.Error()}))
return err
}
return nil
}
func loadAppHelper() {
fmt.Println(i18n.GetMsgByKeyForCmd("AppCommands"))
fmt.Println("\nUsage:\n 1panel app [command]\n\nAvailable Commands:")
fmt.Println("\n init " + i18n.GetMsgByKeyForCmd("AppInit"))
fmt.Println("\nFlags:\n -h, --help help for app")
fmt.Println(" -k, --key string " + i18n.GetMsgByKeyForCmd("AppKeyVal"))
fmt.Println(" -v, --version string " + i18n.GetMsgByKeyForCmd("AppVersion"))
fmt.Println("\nUse \"1panel app [command] --help\" for more information about a command.")
}
func loadAppInitHelper() {
fmt.Println(i18n.GetMsgByKeyForCmd("AppInit"))
fmt.Println("\nUsage:\n 1panel app init [flags]")
fmt.Println("\nFlags:\n -h, --help help for app")
fmt.Println(" -k, --key string " + i18n.GetMsgByKeyForCmd("AppKeyVal"))
fmt.Println(" -v, --version string " + i18n.GetMsgByKeyForCmd("AppVersion"))
}

@ -3,38 +3,48 @@ package cmd
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/spf13/cobra"
)
func init() {
listenCmd.SetHelpFunc(func(c *cobra.Command, s []string) {
i18n.UseI18nForCmd(language)
loadListenIPHelper()
})
RootCmd.AddCommand(listenCmd)
listenCmd.AddCommand(listenIpv4Cmd)
listenCmd.AddCommand(listenIpv6Cmd)
}
var listenCmd = &cobra.Command{
Use: "listen-ip",
Short: "切换监听 IP",
Use: "listen-ip",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
loadListenIPHelper()
return nil
},
}
var listenIpv4Cmd = &cobra.Command{
Use: "ipv4",
Short: "监听 IPv4",
Use: "ipv4",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
return updateBindInfo("ipv4")
},
}
var listenIpv6Cmd = &cobra.Command{
Use: "ipv6",
Short: "监听 IPv6",
Use: "ipv6",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
return updateBindInfo("ipv6")
},
}
func updateBindInfo(protocol string) error {
if !isRoot() {
fmt.Println("请使用 sudo 1pctl listen-ip ipv6 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl listen-ip ipv6"}))
return nil
}
db, err := loadDBConn()
@ -55,6 +65,15 @@ func updateBindInfo(protocol string) error {
if err := setSettingByKey(db, "BindAddress", address); err != nil {
return err
}
fmt.Printf("切换成功!已切换至监听 %s [%s]", tcp, address)
fmt.Println(i18n.GetMsgWithMapForCmd("ListenChangeSuccessful", map[string]interface{}{"value": fmt.Sprintf(" %s [%s]", tcp, address)}))
return nil
}
func loadListenIPHelper() {
fmt.Println(i18n.GetMsgByKeyForCmd("UpdateCommands"))
fmt.Println("\nUsage:\n 1panel listen-ip [command]\n\nAvailable Commands:")
fmt.Println("\n ipv4 " + i18n.GetMsgByKeyForCmd("ListenIPv4"))
fmt.Println(" ipv6 " + i18n.GetMsgByKeyForCmd("ListenIPv6"))
fmt.Println("\nFlags:\n -h, --help help for listen-ip")
fmt.Println("\nUse \"1panel listen-ip [command] --help\" for more information about a command.")
}

@ -3,10 +3,16 @@ package cmd
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/spf13/cobra"
)
func init() {
resetCmd.SetHelpFunc(func(c *cobra.Command, s []string) {
i18n.UseI18nForCmd(language)
loadResetHelper()
})
RootCmd.AddCommand(resetCmd)
resetCmd.AddCommand(resetMFACmd)
resetCmd.AddCommand(resetSSLCmd)
@ -16,16 +22,20 @@ func init() {
}
var resetCmd = &cobra.Command{
Use: "reset",
Short: "重置系统信息",
Use: "reset",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
loadResetHelper()
return nil
},
}
var resetMFACmd = &cobra.Command{
Use: "mfa",
Short: "取消 1Panel 两步验证",
Use: "mfa",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset mfa 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl reset mfa"}))
return nil
}
db, err := loadDBConn()
@ -37,11 +47,11 @@ var resetMFACmd = &cobra.Command{
},
}
var resetSSLCmd = &cobra.Command{
Use: "https",
Short: "取消 1Panel https 方式登录",
Use: "https",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset https 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl reset https"}))
return nil
}
db, err := loadDBConn()
@ -53,11 +63,11 @@ var resetSSLCmd = &cobra.Command{
},
}
var resetEntranceCmd = &cobra.Command{
Use: "entrance",
Short: "取消 1Panel 安全入口",
Use: "entrance",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset entrance 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl reset entrance"}))
return nil
}
db, err := loadDBConn()
@ -69,11 +79,11 @@ var resetEntranceCmd = &cobra.Command{
},
}
var resetBindIpsCmd = &cobra.Command{
Use: "ips",
Short: "取消 1Panel 授权 IP 限制",
Use: "ips",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset ips 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl reset ips"}))
return nil
}
db, err := loadDBConn()
@ -85,11 +95,11 @@ var resetBindIpsCmd = &cobra.Command{
},
}
var resetDomainCmd = &cobra.Command{
Use: "domain",
Short: "取消 1Panel 访问域名绑定",
Use: "domain",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset domain 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl reset domain"}))
return nil
}
db, err := loadDBConn()
@ -100,3 +110,15 @@ var resetDomainCmd = &cobra.Command{
return setSettingByKey(db, "BindDomain", "")
},
}
func loadResetHelper() {
fmt.Println(i18n.GetMsgByKeyForCmd("ResetCommands"))
fmt.Println("\nUsage:\n 1panel reset [command]\n\nAvailable Commands:")
fmt.Println("\n domain " + i18n.GetMsgByKeyForCmd("ResetDomain"))
fmt.Println(" entrance " + i18n.GetMsgByKeyForCmd("ResetEntrance"))
fmt.Println(" https " + i18n.GetMsgByKeyForCmd("ResetHttps"))
fmt.Println(" ips " + i18n.GetMsgByKeyForCmd("ResetIPs"))
fmt.Println(" mfa " + i18n.GetMsgByKeyForCmd("ResetMFA"))
fmt.Println("\nFlags:\n -h, --help help for reset")
fmt.Println("\nUse \"1panel reset [command] --help\" for more information about a command.")
}

@ -8,6 +8,7 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/i18n"
cmdUtils "github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/pkg/errors"
@ -20,11 +21,11 @@ func init() {
}
var restoreCmd = &cobra.Command{
Use: "restore",
Short: "回滚 1Panel 服务及数据",
Use: "restore",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl restore 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl restore"}))
return nil
}
stdout, err := cmdUtils.Exec("grep '^BASE_DIR=' /usr/local/bin/1pctl | cut -d'=' -f2")
@ -38,25 +39,25 @@ var restoreCmd = &cobra.Command{
if err != nil {
return err
}
if tmpPath == "暂无可回滚文件" {
fmt.Println("暂无可回滚文件")
if tmpPath == "no such file" {
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreNoSuchFile"))
return nil
}
tmpPath = path.Join(upgradeDir, tmpPath, "original")
fmt.Printf("(0/4) 开始从 %s 目录回滚 1Panel 服务及数据... \n", tmpPath)
fmt.Println(i18n.GetMsgWithMapForCmd("RestoreStep1", map[string]interface{}{"name": tmpPath}))
if err := common.CopyFile(path.Join(tmpPath, "1panel"), "/usr/local/bin"); err != nil {
return err
}
fmt.Println("(1/4) 1panel 二进制回滚成功")
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep2"))
if err := common.CopyFile(path.Join(tmpPath, "1pctl"), "/usr/local/bin"); err != nil {
return err
}
fmt.Println("(2/4) 1panel 脚本回滚成功")
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep3"))
if err := common.CopyFile(path.Join(tmpPath, "1panel.service"), "/etc/systemd/system"); err != nil {
return err
}
fmt.Println("(3/4) 1panel 服务回滚成功")
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep4"))
checkPointOfWal()
if _, err := os.Stat(path.Join(tmpPath, "1Panel.db")); err == nil {
if err := common.CopyFile(path.Join(tmpPath, "1Panel.db"), path.Join(baseDir, "1panel/db")); err != nil {
@ -68,9 +69,8 @@ var restoreCmd = &cobra.Command{
return err
}
}
fmt.Printf("(4/4) 1panel 数据回滚成功 \n\n")
fmt.Println("回滚成功!正在重启服务,请稍候...")
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep5"))
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreSuccessful"))
return nil
},
}
@ -85,7 +85,7 @@ func checkPointOfWal() {
func loadRestorePath(upgradeDir string) (string, error) {
if _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) {
return "暂无可回滚文件", nil
return "no such file", nil
}
files, err := os.ReadDir(upgradeDir)
if err != nil {
@ -98,7 +98,7 @@ func loadRestorePath(upgradeDir string) (string, error) {
}
}
if len(folders) == 0 {
return "暂无可回滚文件", nil
return "no such file", nil
}
sort.Slice(folders, func(i, j int) bool {
return folders[i] > folders[j]

@ -13,11 +13,14 @@ import (
"gorm.io/gorm"
)
func init() {}
var language string
func init() {
RootCmd.PersistentFlags().StringVarP(&language, "language", "l", "zh", "Set the language (default is 'zh')")
}
var RootCmd = &cobra.Command{
Use: "1panel",
Short: "1Panel ,一款现代化的 Linux 面板",
Use: "1panel",
RunE: func(cmd *cobra.Command, args []string) error {
server.Start()
return nil

@ -10,6 +10,7 @@ import (
"unicode"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
@ -20,6 +21,11 @@ import (
)
func init() {
updateCmd.SetHelpFunc(func(c *cobra.Command, s []string) {
i18n.UseI18nForCmd(language)
loadUpdateHelper()
})
RootCmd.AddCommand(updateCmd)
updateCmd.AddCommand(updateUserName)
updateCmd.AddCommand(updatePassword)
@ -27,16 +33,21 @@ func init() {
}
var updateCmd = &cobra.Command{
Use: "update",
Short: "修改面板信息",
Use: "update",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
loadUpdateHelper()
return nil
},
}
var updateUserName = &cobra.Command{
Use: "username",
Short: "修改面板用户",
Short: i18n.GetMsgByKeyForCmd("UpdateUser"),
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl update username 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl update username"}))
return nil
}
username()
@ -45,10 +56,11 @@ var updateUserName = &cobra.Command{
}
var updatePassword = &cobra.Command{
Use: "password",
Short: "修改面板密码",
Short: i18n.GetMsgByKeyForCmd("UpdatePassword"),
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl update password 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl update password"}))
return nil
}
password()
@ -57,10 +69,11 @@ var updatePassword = &cobra.Command{
}
var updatePort = &cobra.Command{
Use: "port",
Short: "修改面板端口",
Short: i18n.GetMsgByKeyForCmd("UpdatePort"),
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl update port 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl update port"}))
return nil
}
port()
@ -70,83 +83,83 @@ var updatePort = &cobra.Command{
func username() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("修改面板用户: ")
fmt.Print(i18n.GetMsgByKeyForCmd("UpdateUser") + ": ")
newUsername, _ := reader.ReadString('\n')
newUsername = strings.Trim(newUsername, "\n")
if len(newUsername) == 0 {
fmt.Println("错误:输入面板用户为空!")
fmt.Println(i18n.GetMsgByKeyForCmd("UpdateUserNull"))
return
}
if strings.Contains(newUsername, " ") {
fmt.Println("错误:输入面板用户中包含空格字符!")
fmt.Println(i18n.GetMsgByKeyForCmd("UpdateUserBlank"))
return
}
result, err := regexp.MatchString("^[a-zA-Z0-9_\u4e00-\u9fa5]{3,30}$", newUsername)
if !result || err != nil {
fmt.Println("错误输入面板用户错误仅支持英文、中文、数字和_,长度3-30")
fmt.Println(i18n.GetMsgByKeyForCmd("UpdateUserFormat"))
return
}
db, err := loadDBConn()
if err != nil {
fmt.Printf("错误:初始化数据库连接失败,%v\n", err)
fmt.Println(i18n.GetMsgWithMapForCmd("DBConnErr", map[string]interface{}{"err": err.Error()}))
return
}
if err := setSettingByKey(db, "UserName", newUsername); err != nil {
fmt.Printf("错误:面板用户修改失败,%v\n", err)
fmt.Println(i18n.GetMsgWithMapForCmd("UpdateUserErr", map[string]interface{}{"err": err.Error()}))
return
}
fmt.Printf("修改成功!\n\n")
fmt.Printf("面板用户:%s\n", newUsername)
fmt.Println("\n" + i18n.GetMsgByKeyForCmd("UpdateSuccessful"))
fmt.Println(i18n.GetMsgWithMapForCmd("UpdateUserResult", map[string]interface{}{"name": newUsername}))
}
func password() {
fmt.Print("修改面板密码:")
fmt.Print(i18n.GetMsgByKeyForCmd("UpdatePassword") + ": ")
bytePassword, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
fmt.Printf("\n错误面板密码信息读取错误%v\n", err)
fmt.Println("\n" + i18n.GetMsgWithMapForCmd("UpdatePasswordRead", map[string]interface{}{"err": err.Error()}))
return
}
newPassword := string(bytePassword)
newPassword = strings.Trim(newPassword, "\n")
if len(newPassword) == 0 {
fmt.Println("\n错误:输入面板密码为空!")
fmt.Println("\n", i18n.GetMsgByKeyForCmd("UpdatePasswordNull"))
return
}
if strings.Contains(newPassword, " ") {
fmt.Println("\n错误:输入面板密码中包含空格字符!")
fmt.Println("\n" + i18n.GetMsgByKeyForCmd("UpdateUPasswordBlank"))
return
}
db, err := loadDBConn()
if err != nil {
fmt.Printf("\n错误初始化数据库连接失败%v\n", err)
fmt.Println("\n" + i18n.GetMsgWithMapForCmd("DBConnErr", map[string]interface{}{"err": err.Error()}))
return
}
complexSetting := getSettingByKey(db, "ComplexityVerification")
if complexSetting == "enable" {
if isValidPassword("newPassword") {
fmt.Println("\n错误:面板密码仅支持字母、数字、特殊字符(!@#$%*_,.?),长度 8-30 位!")
fmt.Println("\n" + i18n.GetMsgByKeyForCmd("UpdatePasswordFormat"))
return
}
}
if len(newPassword) < 6 {
fmt.Println("错误:请输入 6 位以上密码!")
fmt.Println(i18n.GetMsgByKeyForCmd("UpdatePasswordLen"))
return
}
fmt.Print("\n确认密码:")
fmt.Print("\n" + i18n.GetMsgByKeyForCmd("UpdatePasswordRe"))
byteConfirmPassword, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
fmt.Printf("\n错误面板密码信息读取错误%v\n", err)
fmt.Println("\n" + i18n.GetMsgWithMapForCmd("UpdatePasswordRead", map[string]interface{}{"err": err.Error()}))
return
}
confirmPassword := string(byteConfirmPassword)
confirmPassword = strings.Trim(confirmPassword, "\n")
if newPassword != confirmPassword {
fmt.Printf("\n错误两次密码不匹配请检查后重试%v\n", err)
fmt.Println("\n", i18n.GetMsgByKeyForCmd("UpdatePasswordSame"))
return
}
@ -159,54 +172,54 @@ func password() {
p = newPassword
}
if err := setSettingByKey(db, "Password", p); err != nil {
fmt.Printf("\n错误面板密码修改失败%v\n", err)
fmt.Println("\n", i18n.GetMsgWithMapForCmd("UpdatePortErr", map[string]interface{}{"err": err.Error()}))
return
}
username := getSettingByKey(db, "UserName")
fmt.Printf("\n修改成功\n\n")
fmt.Printf("面板用户:%s\n", username)
fmt.Printf("面板密码:%s\n", string(newPassword))
fmt.Println("\n" + i18n.GetMsgByKeyForCmd("UpdateSuccessful"))
fmt.Println(i18n.GetMsgWithMapForCmd("UpdateUserResult", map[string]interface{}{"name": username}))
fmt.Println(i18n.GetMsgWithMapForCmd("UpdatePasswordResult", map[string]interface{}{"name": string(newPassword)}))
}
func port() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("修改面板端口:")
fmt.Print(i18n.GetMsgByKeyForCmd("UpdatePort") + ": ")
newPortStr, _ := reader.ReadString('\n')
newPortStr = strings.Trim(newPortStr, "\n")
newPort, err := strconv.Atoi(strings.TrimSpace(newPortStr))
if err != nil || newPort < 1 || newPort > 65535 {
fmt.Println("错误:输入的端口号必须在 1 到 65535 之间!")
fmt.Println(i18n.GetMsgByKeyForCmd("UpdatePortFormat"))
return
}
if common.ScanPort(newPort) {
fmt.Println("错误:该端口号正被占用,请检查后重试!")
fmt.Println(i18n.GetMsgByKeyForCmd("UpdatePortUsed"))
return
}
db, err := loadDBConn()
if err != nil {
fmt.Printf("错误:初始化数据库连接失败,%v\n", err)
fmt.Println(i18n.GetMsgWithMapForCmd("DBConnErr", map[string]interface{}{"err": err.Error()}))
return
}
oldPortStr := getSettingByKey(db, "ServerPort")
if err := setSettingByKey(db, "ServerPort", newPortStr); err != nil {
fmt.Printf("错误:面板端口修改失败,%v\n", err)
fmt.Println(i18n.GetMsgWithMapForCmd("UpdatePortErr", map[string]interface{}{"err": err.Error()}))
return
}
fmt.Printf("修改成功!\n\n")
fmt.Printf("面板端口:%s\n", newPortStr)
fmt.Println("\n" + i18n.GetMsgByKeyForCmd("UpdateSuccessful"))
fmt.Println(i18n.GetMsgWithMapForCmd("UpdatePortResult", map[string]interface{}{"name": newPortStr}))
if client, err := firewall.NewFirewallClient(); err == nil {
if err := client.Port(fireClient.FireInfo{Port: newPortStr, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
fmt.Printf("添加防火墙端口规则失败,%v请您手动将 %s 端口添加至防火墙规则中。\n", newPortStr, err)
fmt.Println(i18n.GetMsgWithMapForCmd("UpdatePortFirewallAdd", map[string]interface{}{"name": newPortStr, "err": err.Error()}))
}
if err := client.Port(fireClient.FireInfo{Port: oldPortStr, Protocol: "tcp", Strategy: "accept"}, "remove"); err != nil {
fmt.Printf("错误:防火墙端口删除失败,%v\n", err)
fmt.Println(i18n.GetMsgWithMapForCmd("UpdatePortFirewallDel", map[string]interface{}{"err": err.Error()}))
}
if err := client.Reload(); err != nil {
fmt.Printf("防火墙重载失败,%v请您手动重载防火墙。\n", err)
fmt.Println(i18n.GetMsgWithMapForCmd("UpdatePortFirewallReload", map[string]interface{}{"err": err.Error()}))
}
}
@ -253,3 +266,13 @@ func contains(specialChars string, char rune) bool {
}
return false
}
func loadUpdateHelper() {
fmt.Println(i18n.GetMsgByKeyForCmd("UpdateCommands"))
fmt.Println("\nUsage:\n 1panel update [command]\n\nAvailable Commands:")
fmt.Println("\n password " + i18n.GetMsgByKeyForCmd("UpdatePassword"))
fmt.Println(" port " + i18n.GetMsgByKeyForCmd("UpdatePort"))
fmt.Println(" username " + i18n.GetMsgByKeyForCmd("UpdateUser"))
fmt.Println("\nFlags:\n -h, --help help for update")
fmt.Println("\nUse \"1panel update [command] --help\" for more information about a command.")
}

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/spf13/cobra"
)
@ -13,11 +14,11 @@ func init() {
}
var userinfoCmd = &cobra.Command{
Use: "user-info",
Short: "获取面板信息",
Use: "user-info",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl user-info 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl user-info"}))
return nil
}
db, err := loadDBConn()
@ -47,10 +48,10 @@ var userinfoCmd = &cobra.Command{
address = "$LOCAL_IP"
}
fmt.Printf("面板地址: %s://%s:%s/%s \n", protocol, address, port, entrance)
fmt.Println("面板用户: ", user)
fmt.Println("面板密码: ", pass)
fmt.Println("提示:修改密码可执行命令:1pctl update password")
fmt.Println(i18n.GetMsgByKeyForCmd("UserInfoAddr") + fmt.Sprintf("%s://%s:%s/%s ", protocol, address, port, entrance))
fmt.Println(i18n.GetMsgWithMapForCmd("UpdateUserResult", map[string]interface{}{"name": user}))
fmt.Println(i18n.GetMsgWithMapForCmd("UpdatePasswordResult", map[string]interface{}{"name": pass}))
fmt.Println(i18n.GetMsgByKeyForCmd("UserInfoPassHelp") + "1pctl update password")
return nil
},
}

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/configs"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/cmd/server/conf"
"gopkg.in/yaml.v3"
@ -15,11 +16,11 @@ func init() {
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "获取系统版本信息",
Use: "version",
RunE: func(cmd *cobra.Command, args []string) error {
i18n.UseI18nForCmd(language)
if !isRoot() {
fmt.Println("请使用 sudo 1pctl version 或者切换到 root 用户")
fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl version"}))
return nil
}
db, err := loadDBConn()

Loading…
Cancel
Save