1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: Add support for multiple host license (#7296)

This commit is contained in:
ssongliu 2024-12-10 13:57:38 +08:00 committed by GitHub
parent e5660a0d91
commit a8170a499a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 870 additions and 544 deletions

View File

@ -9,7 +9,7 @@ import (
)
func (b *BaseApi) CheckHealth(c *gin.Context) {
_, err := xpack.RequestToMaster("/api/v2/agent/health", http.MethodGet, nil)
_, err := xpack.RequestToMaster("/api/v2/agent/xpack/health", http.MethodGet, nil)
if err != nil {
helper.InternalServer(c, err)
return

View File

@ -293,7 +293,7 @@ func NewBackupClientWithID(id uint) (*model.BackupAccount, cloud_storage.CloudSt
if err != nil {
return nil, nil, err
}
data, err := xpack.RequestToMaster("/api/v2/agent/backup", http.MethodPost, bytes.NewReader(bodyItem))
data, err := xpack.RequestToMaster("/api/v2/agent/xpack/backup", http.MethodPost, bytes.NewReader(bodyItem))
if err != nil {
return nil, nil, err
}
@ -358,7 +358,7 @@ func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
if err != nil {
return nil, err
}
data, err := xpack.RequestToMaster("/api/v2/agent/backup/list", http.MethodPost, bytes.NewReader(bodyItem))
data, err := xpack.RequestToMaster("/api/v2/agent/xpack/backup/list", http.MethodPost, bytes.NewReader(bodyItem))
if err != nil {
return nil, err
}

View File

@ -5,6 +5,7 @@ type System struct {
Version string `mapstructure:"version"`
BaseDir string `mapstructure:"base_dir"`
CurrentNode string `mapstructure:"base_dir"`
MasterAddr string `mapstructure:"master_addr"`
EncryptKey string `mapstructure:"encrypt_key"`

View File

@ -30,7 +30,7 @@ var (
ErrDemoEnvironment = "ErrDemoEnvironment"
ErrCmdIllegal = "ErrCmdIllegal"
ErrXpackNotFound = "ErrXpackNotFound"
ErrXpackNotActive = "ErrXpackNotActive"
ErrXpackExceptional = "ErrXpackExceptional"
ErrXpackOutOfDate = "ErrXpackOutOfDate"
)
@ -134,11 +134,3 @@ var (
var (
ErrNotExistUser = "ErrNotExistUser"
)
// license
var (
ErrLicense = "ErrLicense"
ErrLicenseCheck = "ErrLicenseCheck"
ErrLicenseSave = "ErrLicenseSave"
ErrLicenseSync = "ErrLicenseSync"
)

View File

@ -240,16 +240,6 @@ cc: 'Access Frequency Limit'
defaultUrlBlack: 'URL Rules'
sqlInject: 'SQL Injection'
#license
ErrLicense: "License format error, please check and try again!"
ErrLicenseCheck: "License verification failed, please check and try again!"
ErrLicenseSave: "Failed to save license information, error {{ .err }}, please try again!"
ErrLicenseSync: "Failed to sync license information, no license information detected in the database!"
ErrXpackNotFound: "This section is a professional edition feature, please import the license first in Panel Settings-License interface"
ErrXpackNotActive: "This section is a professional edition feature, please synchronize the license status first in Panel Settings-License interface"
ErrXpackOutOfDate: "The current license has expired, please re-import the license in Panel Settings-License interface"
#task
TaskStart: "{{.name}} Start [START]"
TaskEnd: "{{.name}} End [COMPLETED]"

View File

@ -241,16 +241,6 @@ cc: '訪問頻率限制'
defaultUrlBlack: 'URL 規則'
sqlInject: 'SQL 注入'
#license
ErrLicense: "許可證格式錯誤,請檢查後重試!"
ErrLicenseCheck: "許可證校驗失敗,請檢查後重試!"
ErrLicenseSave: "許可證信息保存失敗,錯誤 {{ .err }}, 請重試!"
ErrLicenseSync: "許可證信息同步失敗,資料庫中未檢測到許可證信息!"
ErrXpackNotFound: "該部分為專業版功能,請先在 面板設置-許可證 界面導入許可證"
ErrXpackNotActive: "該部分為專業版功能,請先在 面板設置-許可證 界面同步許可證狀態"
ErrXpackOutOfDate: "當前許可證已過期,請重新在 面板設置-許可證 界面導入許可證"
#task
TaskStart: "{{.name}} 開始 [START]"
TaskEnd: "{{.name}} 結束 [COMPLETED]"

View File

@ -239,16 +239,6 @@ cc: '访问频率限制'
defaultUrlBlack: 'URL 规则'
sqlInject: 'SQL 注入'
#license
ErrLicense: "许可证格式错误,请检查后重试!"
ErrLicenseCheck: "许可证校验失败,请检查后重试!"
ErrLicenseSave: "许可证信息保存失败,错误 {{ .err }},请重试!"
ErrLicenseSync: "许可证信息同步失败,数据库中未检测到许可证信息!"
ErrXpackNotFound: "该部分为专业版功能,请先在 面板设置-许可证 界面导入许可证"
ErrXpackNotActive: "该部分为专业版功能,请先在 面板设置-许可证 界面同步许可证状态"
ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可证 界面导入许可证"
#task
TaskStart: "{{.name}} 任务开始 [START]"
TaskEnd: "{{.name}} 任务结束 [COMPLETED]"

View File

@ -33,9 +33,9 @@ func initGlobalData() {
global.CONF.System.BaseDir, _ = settingRepo.GetValueByKey("BaseDir")
global.CONF.System.Version, _ = settingRepo.GetValueByKey("SystemVersion")
global.CONF.System.EncryptKey, _ = settingRepo.GetValueByKey("EncryptKey")
currentNode, _ := settingRepo.GetValueByKey("CurrentNode")
global.CONF.System.CurrentNode, _ = settingRepo.GetValueByKey("CurrentNode")
global.IsMaster = currentNode == "127.0.0.1" || len(currentNode) == 0
global.IsMaster = global.CONF.System.CurrentNode == "127.0.0.1" || len(global.CONF.System.CurrentNode) == 0
if global.IsMaster {
global.CoreDB = common.LoadDBConnByPath(path.Join(global.CONF.System.DbPath, "core.db"), "core")
} else {

View File

@ -3,6 +3,10 @@ package server
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"os"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/cron"
"github.com/1Panel-dev/1Panel/agent/global"
@ -18,12 +22,10 @@ import (
"github.com/1Panel-dev/1Panel/agent/init/validator"
"github.com/1Panel-dev/1Panel/agent/init/viper"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"net"
"net/http"
"os"
_ "net/http/pprof"
"github.com/gin-gonic/gin"
_ "net/http/pprof"
)
func Start() {
@ -47,12 +49,13 @@ func Start() {
}
go func() {
http.ListenAndServe("0.0.0.0:6060", nil)
_ = http.ListenAndServe("0.0.0.0:6060", nil)
}()
if global.IsMaster {
_ = os.Remove("/tmp/agent.sock")
listener, err := net.Listen("unix", "/tmp/agent.sock")
_ = os.Remove("/etc/1panel/agent.sock")
_ = os.Mkdir("/etc/1panel", 0755)
listener, err := net.Listen("unix", "/etc/1panel/agent.sock")
if err != nil {
panic(err)
}

View File

@ -49,7 +49,6 @@ func LoadNodeInfo() (bool, model.NodeInfo, error) {
var info model.NodeInfo
info.BaseDir = loadParams("BASE_DIR")
info.Version = loadParams("ORIGINAL_VERSION")
info.CurrentNode = "127.0.0.1"
info.EncryptKey = common.RandStr(16)
return false, info, nil
}

View File

@ -8,78 +8,66 @@ import (
"gorm.io/gorm"
)
type ICommonRepo interface {
WithByID(id uint) global.DBOption
WithByGroupID(id uint) global.DBOption
WithByName(name string) global.DBOption
WithByType(ty string) global.DBOption
WithByKey(key string) global.DBOption
WithOrderBy(orderStr string) global.DBOption
WithByStatus(status string) global.DBOption
WithByGroupBelong(group string) global.DBOption
WithByIDs(ids []uint) global.DBOption
WithOrderRuleBy(orderBy, order string) global.DBOption
}
type CommonRepo struct{}
func NewICommonRepo() ICommonRepo {
return &CommonRepo{}
}
func (c *CommonRepo) WithByID(id uint) global.DBOption {
func WithByID(id uint) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id = ?", id)
}
}
func (c *CommonRepo) WithByGroupID(id uint) global.DBOption {
func WithByGroupID(id uint) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("group_id = ?", id)
}
}
func (c *CommonRepo) WithByIDs(ids []uint) global.DBOption {
func WithByIDs(ids []uint) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id in (?)", ids)
}
}
func (c *CommonRepo) WithByName(name string) global.DBOption {
func WithByName(name string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("`name` = ?", name)
}
}
func WithoutByName(name string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("`name` != ?", name)
}
}
func (c *CommonRepo) WithByType(ty string) global.DBOption {
func WithByType(ty string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("`type` = ?", ty)
}
}
func (c *CommonRepo) WithByKey(key string) global.DBOption {
func WithByAddr(addr string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("addr = ?", addr)
}
}
func WithByKey(key string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("key = ?", key)
}
}
func (c *CommonRepo) WithByStatus(status string) global.DBOption {
func WithByStatus(status string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("status = ?", status)
}
}
func (c *CommonRepo) WithByGroupBelong(group string) global.DBOption {
func WithByGroupBelong(group string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("group_belong = ?", group)
}
}
func (c *CommonRepo) WithOrderBy(orderStr string) global.DBOption {
func WithOrderBy(orderStr string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Order(orderStr)
}
}
func (c *CommonRepo) WithOrderRuleBy(orderBy, order string) global.DBOption {
func WithOrderRuleBy(orderBy, order string) global.DBOption {
switch order {
case constant.OrderDesc:
order = "desc"

View File

@ -2,6 +2,7 @@ package service
import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/constant"
)
@ -17,7 +18,7 @@ func NewIAppLauncher() IAppLauncher {
}
func (u *LauncherService) Search() ([]string, error) {
launchers, err := launcherRepo.List(commonRepo.WithOrderBy("created_at"))
launchers, err := launcherRepo.List(repo.WithOrderBy("created_at"))
if err != nil {
return nil, err
}
@ -29,7 +30,7 @@ func (u *LauncherService) Search() ([]string, error) {
}
func (u *LauncherService) ChangeShow(req dto.SettingUpdate) error {
launcher, _ := launcherRepo.Get(commonRepo.WithByKey(req.Key))
launcher, _ := launcherRepo.Get(repo.WithByKey(req.Key))
if req.Value == constant.StatusEnable {
if launcher.ID != 0 {
return nil
@ -41,5 +42,5 @@ func (u *LauncherService) ChangeShow(req dto.SettingUpdate) error {
return nil
}
return launcherRepo.Delete(commonRepo.WithByKey(req.Key))
return launcherRepo.Delete(repo.WithByKey(req.Key))
}

View File

@ -5,6 +5,7 @@ import (
"strconv"
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
@ -31,11 +32,11 @@ func NewIAuthService() IAuthService {
}
func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error) {
nameSetting, err := settingRepo.Get(commonRepo.WithByKey("UserName"))
nameSetting, err := settingRepo.Get(repo.WithByKey("UserName"))
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
passwordSetting, err := settingRepo.Get(commonRepo.WithByKey("Password"))
passwordSetting, err := settingRepo.Get(repo.WithByKey("Password"))
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
@ -46,14 +47,14 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
return nil, constant.ErrAuth
}
entranceSetting, err := settingRepo.Get(commonRepo.WithByKey("SecurityEntrance"))
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
if err != nil {
return nil, err
}
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
return nil, buserr.New(constant.ErrEntrance)
}
mfa, err := settingRepo.Get(commonRepo.WithByKey("MFAStatus"))
mfa, err := settingRepo.Get(repo.WithByKey("MFAStatus"))
if err != nil {
return nil, err
}
@ -67,11 +68,11 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
}
func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, error) {
nameSetting, err := settingRepo.Get(commonRepo.WithByKey("UserName"))
nameSetting, err := settingRepo.Get(repo.WithByKey("UserName"))
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
passwordSetting, err := settingRepo.Get(commonRepo.WithByKey("Password"))
passwordSetting, err := settingRepo.Get(repo.WithByKey("Password"))
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
@ -82,18 +83,18 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
return nil, constant.ErrAuth
}
entranceSetting, err := settingRepo.Get(commonRepo.WithByKey("SecurityEntrance"))
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
if err != nil {
return nil, err
}
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
return nil, buserr.New(constant.ErrEntrance)
}
mfaSecret, err := settingRepo.Get(commonRepo.WithByKey("MFASecret"))
mfaSecret, err := settingRepo.Get(repo.WithByKey("MFASecret"))
if err != nil {
return nil, err
}
mfaInterval, err := settingRepo.Get(commonRepo.WithByKey("MFAInterval"))
mfaInterval, err := settingRepo.Get(repo.WithByKey("MFAInterval"))
if err != nil {
return nil, err
}
@ -106,11 +107,11 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
}
func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (*dto.UserLoginInfo, error) {
setting, err := settingRepo.Get(commonRepo.WithByKey("SessionTimeout"))
setting, err := settingRepo.Get(repo.WithByKey("SessionTimeout"))
if err != nil {
return nil, err
}
httpsSetting, err := settingRepo.Get(commonRepo.WithByKey("SSL"))
httpsSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
if err != nil {
return nil, err
}
@ -147,7 +148,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
}
func (u *AuthService) LogOut(c *gin.Context) error {
httpsSetting, err := settingRepo.Get(commonRepo.WithByKey("SSL"))
httpsSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
if err != nil {
return err
}
@ -163,7 +164,7 @@ func (u *AuthService) LogOut(c *gin.Context) error {
}
func (u *AuthService) VerifyCode(code string) (bool, error) {
setting, err := settingRepo.Get(commonRepo.WithByKey("SecurityEntrance"))
setting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
if err != nil {
return false, err
}
@ -171,7 +172,7 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
}
func (u *AuthService) CheckIsSafety(code string) (string, error) {
status, err := settingRepo.Get(commonRepo.WithByKey("SecurityEntrance"))
status, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
if err != nil {
return "", err
}
@ -185,7 +186,7 @@ func (u *AuthService) CheckIsSafety(code string) (string, error) {
}
func (u *AuthService) GetResponsePage() (string, error) {
pageCode, err := settingRepo.Get(commonRepo.WithByKey("NoAuthSetting"))
pageCode, err := settingRepo.Get(repo.WithByKey("NoAuthSetting"))
if err != nil {
return "", err
}

View File

@ -13,6 +13,7 @@ import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
@ -51,7 +52,7 @@ func NewIBackupService() IBackupService {
func (u *BackupService) Get(req dto.OperateByID) (dto.BackupInfo, error) {
var data dto.BackupInfo
account, err := backupRepo.Get(commonRepo.WithByID(req.ID))
account, err := backupRepo.Get(repo.WithByID(req.ID))
if err != nil {
return data, err
}
@ -70,7 +71,7 @@ func (u *BackupService) Get(req dto.OperateByID) (dto.BackupInfo, error) {
}
func (u *BackupService) List(req dto.OperateByIDs) ([]dto.BackupInfo, error) {
accounts, err := backupRepo.List(commonRepo.WithByIDs(req.IDs), commonRepo.WithOrderBy("created_at desc"))
accounts, err := backupRepo.List(repo.WithByIDs(req.IDs), repo.WithOrderBy("created_at desc"))
if err != nil {
return nil, err
}
@ -94,7 +95,7 @@ func (u *BackupService) List(req dto.OperateByIDs) ([]dto.BackupInfo, error) {
}
func (u *BackupService) GetLocalDir() (string, error) {
account, err := backupRepo.Get(commonRepo.WithByType(constant.Local))
account, err := backupRepo.Get(repo.WithByType(constant.Local))
if err != nil {
return "", err
}
@ -106,7 +107,7 @@ func (u *BackupService) GetLocalDir() (string, error) {
}
func (u *BackupService) LoadBackupOptions() ([]dto.BackupOption, error) {
accounts, err := backupRepo.List(commonRepo.WithOrderBy("created_at desc"))
accounts, err := backupRepo.List(repo.WithOrderBy("created_at desc"))
if err != nil {
return nil, err
}
@ -125,9 +126,9 @@ func (u *BackupService) SearchWithPage(req dto.SearchPageWithType) (int64, inter
count, accounts, err := backupRepo.Page(
req.Page,
req.PageSize,
commonRepo.WithByType(req.Type),
commonRepo.WithByName(req.Info),
commonRepo.WithOrderBy("created_at desc"),
repo.WithByType(req.Type),
repo.WithByName(req.Info),
repo.WithOrderBy("created_at desc"),
)
if err != nil {
return 0, nil, err
@ -181,7 +182,7 @@ func (u *BackupService) LoadBackupClientInfo(clientType string) (dto.BackupClien
} else {
data.RedirectUri = constant.OneDriveRedirectURI
}
clientID, err := settingRepo.Get(commonRepo.WithByKey(clientIDKey))
clientID, err := settingRepo.Get(repo.WithByKey(clientIDKey))
if err != nil {
return data, err
}
@ -190,7 +191,7 @@ func (u *BackupService) LoadBackupClientInfo(clientType string) (dto.BackupClien
return data, err
}
data.ClientID = string(idItem)
clientSecret, err := settingRepo.Get(commonRepo.WithByKey(clientIDSc))
clientSecret, err := settingRepo.Get(repo.WithByKey(clientIDSc))
if err != nil {
return data, err
}
@ -204,7 +205,7 @@ func (u *BackupService) LoadBackupClientInfo(clientType string) (dto.BackupClien
}
func (u *BackupService) Create(req dto.BackupOperate) error {
backup, _ := backupRepo.Get(commonRepo.WithByName(req.Name))
backup, _ := backupRepo.Get(repo.WithByName(req.Name))
if backup.ID != 0 {
return constant.ErrRecordExist
}
@ -279,7 +280,7 @@ func (u *BackupService) GetBuckets(req dto.ForBuckets) ([]interface{}, error) {
}
func (u *BackupService) Delete(id uint) error {
backup, _ := backupRepo.Get(commonRepo.WithByID(id))
backup, _ := backupRepo.Get(repo.WithByID(id))
if backup.ID == 0 {
return constant.ErrRecordNotFound
}
@ -295,11 +296,11 @@ func (u *BackupService) Delete(id uint) error {
return buserr.New(constant.ErrBackupInUsed)
}
return backupRepo.Delete(commonRepo.WithByID(id))
return backupRepo.Delete(repo.WithByID(id))
}
func (u *BackupService) Update(req dto.BackupOperate) error {
backup, _ := backupRepo.Get(commonRepo.WithByID(req.ID))
backup, _ := backupRepo.Get(repo.WithByID(req.ID))
if backup.ID == 0 {
return constant.ErrRecordNotFound
}

View File

@ -2,6 +2,7 @@ package service
import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/jinzhu/copier"
@ -24,7 +25,7 @@ func NewICommandService() ICommandService {
}
func (u *CommandService) List(req dto.OperateByType) ([]dto.CommandInfo, error) {
commands, err := commandRepo.List(commonRepo.WithOrderBy("name"), commonRepo.WithByType(req.Type))
commands, err := commandRepo.List(repo.WithOrderBy("name"), repo.WithByType(req.Type))
if err != nil {
return nil, constant.ErrRecordNotFound
}
@ -40,11 +41,11 @@ func (u *CommandService) List(req dto.OperateByType) ([]dto.CommandInfo, error)
}
func (u *CommandService) SearchForTree(req dto.OperateByType) ([]dto.CommandTree, error) {
cmdList, err := commandRepo.List(commonRepo.WithOrderBy("name"), commonRepo.WithByType(req.Type))
cmdList, err := commandRepo.List(repo.WithOrderBy("name"), repo.WithByType(req.Type))
if err != nil {
return nil, err
}
groups, err := groupRepo.GetList(commonRepo.WithByType(req.Type))
groups, err := groupRepo.GetList(repo.WithByType(req.Type))
if err != nil {
return nil, err
}
@ -67,20 +68,20 @@ func (u *CommandService) SearchForTree(req dto.OperateByType) ([]dto.CommandTree
func (u *CommandService) SearchWithPage(req dto.SearchCommandWithPage) (int64, interface{}, error) {
options := []global.DBOption{
commonRepo.WithOrderRuleBy(req.OrderBy, req.Order),
commonRepo.WithByType(req.Type),
repo.WithOrderRuleBy(req.OrderBy, req.Order),
repo.WithByType(req.Type),
}
if len(req.Info) != 0 {
options = append(options, commandRepo.WithByInfo(req.Info))
}
if req.GroupID != 0 {
options = append(options, commonRepo.WithByGroupID(req.GroupID))
options = append(options, repo.WithByGroupID(req.GroupID))
}
total, commands, err := commandRepo.Page(req.Page, req.PageSize, options...)
if err != nil {
return 0, nil, err
}
groups, _ := groupRepo.GetList(commonRepo.WithByType(req.Type))
groups, _ := groupRepo.GetList(repo.WithByType(req.Type))
var dtoCommands []dto.CommandInfo
for _, command := range commands {
var item dto.CommandInfo
@ -99,7 +100,7 @@ func (u *CommandService) SearchWithPage(req dto.SearchCommandWithPage) (int64, i
}
func (u *CommandService) Create(commandDto dto.CommandOperate) error {
command, _ := commandRepo.Get(commonRepo.WithByName(commandDto.Name))
command, _ := commandRepo.Get(repo.WithByName(commandDto.Name))
if command.ID != 0 {
return constant.ErrRecordExist
}
@ -114,13 +115,13 @@ func (u *CommandService) Create(commandDto dto.CommandOperate) error {
func (u *CommandService) Delete(ids []uint) error {
if len(ids) == 1 {
command, _ := commandRepo.Get(commonRepo.WithByID(ids[0]))
command, _ := commandRepo.Get(repo.WithByID(ids[0]))
if command.ID == 0 {
return constant.ErrRecordNotFound
}
return commandRepo.Delete(commonRepo.WithByID(ids[0]))
return commandRepo.Delete(repo.WithByID(ids[0]))
}
return commandRepo.Delete(commonRepo.WithByIDs(ids))
return commandRepo.Delete(repo.WithByIDs(ids))
}
func (u *CommandService) Update(id uint, upMap map[string]interface{}) error {

View File

@ -5,7 +5,6 @@ import "github.com/1Panel-dev/1Panel/core/app/repo"
var (
hostRepo = repo.NewIHostRepo()
commandRepo = repo.NewICommandRepo()
commonRepo = repo.NewICommonRepo()
settingRepo = repo.NewISettingRepo()
backupRepo = repo.NewIBackupRepo()
logRepo = repo.NewILogRepo()

View File

@ -7,6 +7,7 @@ import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
@ -31,11 +32,11 @@ func NewIGroupService() IGroupService {
func (u *GroupService) List(req dto.OperateByType) ([]dto.GroupInfo, error) {
options := []global.DBOption{
commonRepo.WithOrderBy("is_default desc"),
commonRepo.WithOrderBy("created_at desc"),
repo.WithOrderBy("is_default desc"),
repo.WithOrderBy("created_at desc"),
}
if len(req.Type) != 0 {
options = append(options, commonRepo.WithByType(req.Type))
options = append(options, repo.WithByType(req.Type))
}
var (
groups []model.Group
@ -57,7 +58,7 @@ func (u *GroupService) List(req dto.OperateByType) ([]dto.GroupInfo, error) {
}
func (u *GroupService) Create(req dto.GroupCreate) error {
group, _ := groupRepo.Get(commonRepo.WithByName(req.Name), commonRepo.WithByType(req.Type))
group, _ := groupRepo.Get(repo.WithByName(req.Name), repo.WithByType(req.Type))
if group.ID != 0 {
return constant.ErrRecordExist
}
@ -71,14 +72,14 @@ func (u *GroupService) Create(req dto.GroupCreate) error {
}
func (u *GroupService) Delete(id uint) error {
group, _ := groupRepo.Get(commonRepo.WithByID(id))
group, _ := groupRepo.Get(repo.WithByID(id))
if group.ID == 0 {
return constant.ErrRecordNotFound
}
if group.IsDefault {
return buserr.New(constant.ErrGroupIsDefault)
}
defaultGroup, err := groupRepo.Get(commonRepo.WithByType(group.Type), groupRepo.WithByDefault(true))
defaultGroup, err := groupRepo.Get(repo.WithByType(group.Type), groupRepo.WithByDefault(true))
if err != nil {
return err
}
@ -103,7 +104,7 @@ func (u *GroupService) Delete(id uint) error {
if err != nil {
return err
}
return groupRepo.Delete(commonRepo.WithByID(id))
return groupRepo.Delete(repo.WithByID(id))
}
func (u *GroupService) Update(req dto.GroupUpdate) error {

View File

@ -6,6 +6,7 @@ import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
@ -84,7 +85,7 @@ func (u *HostService) TestLocalConn(id uint) bool {
return false
}
} else {
host, err = hostRepo.Get(commonRepo.WithByID(id))
host, err = hostRepo.Get(repo.WithByID(id))
if err != nil {
return false
}
@ -124,7 +125,7 @@ func (u *HostService) TestLocalConn(id uint) bool {
}
func (u *HostService) GetHostInfo(id uint) (*model.Host, error) {
host, err := hostRepo.Get(commonRepo.WithByID(id))
host, err := hostRepo.Get(repo.WithByID(id))
if err != nil {
return nil, constant.ErrRecordNotFound
}
@ -156,7 +157,7 @@ func (u *HostService) SearchWithPage(req dto.SearchHostWithPage) (int64, interfa
options = append(options, hostRepo.WithByInfo(req.Info))
}
if req.GroupID != 0 {
options = append(options, commonRepo.WithByGroupID(req.GroupID))
options = append(options, repo.WithByGroupID(req.GroupID))
}
total, hosts, err := hostRepo.Page(req.Page, req.PageSize, options...)
if err != nil {
@ -168,7 +169,7 @@ func (u *HostService) SearchWithPage(req dto.SearchHostWithPage) (int64, interfa
if err := copier.Copy(&item, &host); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
group, _ := groupRepo.Get(commonRepo.WithByID(host.GroupID))
group, _ := groupRepo.Get(repo.WithByID(host.GroupID))
item.GroupBelong = group.Name
if !item.RememberPassword {
item.Password = ""
@ -204,7 +205,7 @@ func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, e
if err != nil {
return nil, err
}
groups, err := groupRepo.GetList(commonRepo.WithByType("host"))
groups, err := groupRepo.GetList(repo.WithByType("host"))
if err != nil {
return nil, err
}
@ -257,7 +258,7 @@ func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if req.GroupID == 0 {
group, err := groupRepo.Get(commonRepo.WithByType("host"), groupRepo.WithByDefault(true))
group, err := groupRepo.Get(repo.WithByType("host"), groupRepo.WithByDefault(true))
if err != nil {
return nil, errors.New("get default group failed")
}
@ -307,7 +308,7 @@ func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
}
func (u *HostService) Delete(ids []uint) error {
hosts, _ := hostRepo.GetList(commonRepo.WithByIDs(ids))
hosts, _ := hostRepo.GetList(repo.WithByIDs(ids))
for _, host := range hosts {
if host.ID == 0 {
return constant.ErrRecordNotFound
@ -316,7 +317,7 @@ func (u *HostService) Delete(ids []uint) error {
return errors.New("the local connection information cannot be deleted!")
}
}
return hostRepo.Delete(commonRepo.WithByIDs(ids))
return hostRepo.Delete(repo.WithByIDs(ids))
}
func (u *HostService) Update(id uint, upMap map[string]interface{}) error {

View File

@ -10,6 +10,7 @@ import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
@ -75,13 +76,13 @@ func (u *LogService) ListSystemLogFile() ([]string, error) {
func (u *LogService) PageLoginLog(req dto.SearchLgLogWithPage) (int64, interface{}, error) {
options := []global.DBOption{
commonRepo.WithOrderBy("created_at desc"),
repo.WithOrderBy("created_at desc"),
}
if len(req.IP) != 0 {
options = append(options, logRepo.WithByIP(req.IP))
}
if len(req.Status) != 0 {
options = append(options, commonRepo.WithByStatus(req.Status))
options = append(options, repo.WithByStatus(req.Status))
}
total, ops, err := logRepo.PageLoginLog(
req.Page,
@ -105,14 +106,14 @@ func (u *LogService) CreateOperationLog(operation *model.OperationLog) error {
func (u *LogService) PageOperationLog(req dto.SearchOpLogWithPage) (int64, interface{}, error) {
options := []global.DBOption{
commonRepo.WithOrderBy("created_at desc"),
repo.WithOrderBy("created_at desc"),
logRepo.WithByLikeOperation(req.Operation),
}
if len(req.Source) != 0 {
options = append(options, logRepo.WithBySource(req.Source))
}
if len(req.Status) != 0 {
options = append(options, commonRepo.WithByStatus(req.Status))
options = append(options, repo.WithByStatus(req.Status))
}
total, ops, err := logRepo.PageOperationLog(

View File

@ -14,6 +14,7 @@ import (
"time"
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
@ -74,7 +75,7 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
func (u *SettingService) Update(key, value string) error {
switch key {
case "AppStoreLastModified":
exist, _ := settingRepo.Get(commonRepo.WithByKey("AppStoreLastModified"))
exist, _ := settingRepo.Get(repo.WithByKey("AppStoreLastModified"))
if exist.ID == 0 {
_ = settingRepo.Create("AppStoreLastModified", value)
return nil
@ -270,14 +271,14 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
}
func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
ssl, err := settingRepo.Get(commonRepo.WithByKey("SSL"))
ssl, err := settingRepo.Get(repo.WithByKey("SSL"))
if err != nil {
return nil, err
}
if ssl.Value == "disable" {
return &dto.SSLInfo{}, nil
}
sslType, err := settingRepo.Get(commonRepo.WithByKey("SSLType"))
sslType, err := settingRepo.Get(repo.WithByKey("SSLType"))
if err != nil {
return nil, err
}
@ -311,7 +312,7 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
}
func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error {
setting, err := settingRepo.Get(commonRepo.WithByKey("Password"))
setting, err := settingRepo.Get(repo.WithByKey("Password"))
if err != nil {
return err
}
@ -328,7 +329,7 @@ func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string)
return err
}
expiredSetting, err := settingRepo.Get(commonRepo.WithByKey("ExpirationDays"))
expiredSetting, err := settingRepo.Get(repo.WithByKey("ExpirationDays"))
if err != nil {
return err
}

View File

@ -2,6 +2,7 @@ package service
import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/global"
)
@ -17,13 +18,13 @@ func NewITaskService() ITaskLogService {
func (u *TaskLogService) Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, error) {
opts := []global.DBOption{
commonRepo.WithOrderBy("created_at desc"),
repo.WithOrderBy("created_at desc"),
}
if req.Status != "" {
opts = append(opts, commonRepo.WithByStatus(req.Status))
opts = append(opts, repo.WithByStatus(req.Status))
}
if req.Type != "" {
opts = append(opts, commonRepo.WithByType(req.Type))
opts = append(opts, repo.WithByType(req.Type))
}
total, tasks, err := taskRepo.Page(

View File

@ -12,6 +12,7 @@ import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
@ -35,11 +36,11 @@ func NewIUpgradeService() IUpgradeService {
func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
var upgrade dto.UpgradeInfo
currentVersion, err := settingRepo.Get(commonRepo.WithByKey("SystemVersion"))
currentVersion, err := settingRepo.Get(repo.WithByKey("SystemVersion"))
if err != nil {
return nil, err
}
DeveloperMode, err := settingRepo.Get(commonRepo.WithByKey("DeveloperMode"))
DeveloperMode, err := settingRepo.Get(repo.WithByKey("DeveloperMode"))
if err != nil {
return nil, err
}
@ -167,7 +168,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
}
func (u *UpgradeService) Rollback(req dto.OperateByID) error {
log, _ := upgradeLogRepo.Get(commonRepo.WithByID(req.ID))
log, _ := upgradeLogRepo.Get(repo.WithByID(req.ID))
if log.ID == 0 {
return constant.ErrRecordNotFound
}

View File

@ -38,6 +38,18 @@ func New(Key string) BusinessError {
}
}
func WithErr(Key string, err error) BusinessError {
paramMap := map[string]interface{}{}
if err != nil {
paramMap["err"] = err
}
return BusinessError{
Msg: Key,
Map: paramMap,
Err: err,
}
}
func WithDetail(Key string, detail interface{}, err error) BusinessError {
return BusinessError{
Msg: Key,

View File

@ -50,6 +50,14 @@ var (
ErrEntrance = "ErrEntrance"
ErrProxy = "ErrProxy"
ErrLocalDelete = "ErrLocalDelete"
ErrXpackNotFound = "ErrXpackNotFound"
ErrXpackExceptional = "ErrXpackExceptional"
ErrXpackLost = "ErrXpackLost"
ErrXpackTimeout = "ErrXpackTimeout"
ErrXpackOutOfDate = "ErrXpackOutOfDate"
ErrNoSuchNode = "ErrNoSuchNode"
ErrNodeUnbind = "ErrNodeUnbind"
)
// backup
@ -58,3 +66,14 @@ var (
ErrBackupLocalDelete = "ErrBackupLocalDelete"
ErrMasterAddr = "ErrMasterAddr"
)
var (
ErrLicense = "ErrLicense"
ErrLicenseCheck = "ErrLicenseCheck"
ErrLicenseSave = "ErrLicenseSave"
ErrLicenseSync = "ErrLicenseSync"
ErrUnbindMaster = "ErrUnbindMaster"
ErrFreeNodeLimit = "ErrFreeNodeLimit"
ErrNodeBound = "ErrNodeBound"
ErrNodeBind = "ErrNodeBind"
)

View File

@ -14,4 +14,9 @@ const (
StatusUnhealthy = "unhealthy"
StatusUpgrading = "upgrading"
StatusRunning = "running"
StatusFree = "free"
StatusBound = "bound"
StatusExceptional = "exceptional"
StatusRetrying = "retrying"
StatusLost = "lost"
)

View File

@ -30,6 +30,23 @@ ErrBackupInUsed: "The backup account is currently in use in a scheduled task and
ErrBackupCheck: "Backup account test connection failed {{.err}}"
ErrBackupLocalDelete: "Deleting local server backup accounts is not currently supported."
#license
ErrLicense: "License format error, please check and try again!"
ErrLicenseCheck: "License verification failed, please check and try again!"
ErrLicenseSave: "Failed to save license information, error {{ .err }}, please try again!"
ErrLicenseSync: "Failed to sync license information, no license information detected in the database!"
ErrXpackNotFound: "This section is a professional edition feature, please import the license first in Panel Settings-License interface"
ErrXpackExceptional: "This section is a professional edition feature, please synchronize the license status first in Panel Settings-License interface"
ErrXpackOutOfDate: "The current license has expired, please re-import the license in Panel Settings-License interface"
ErrXpackLost: "The license has reached the maximum number of retry attempts. Please go to the [Settings] [License] page and manually click the sync button to ensure that the professional version features are functioning properly."
ErrXpackTimeout: "Request timed out, the network connection may be unstable, please try again later!"
ErrUnbindMaster: "Detected that there are nodes in the node management, unable to unbind the current license. Please remove them first and try again!"
ErrFreeNodeLimit: "The number of nodes for the community edition has reached the free limit. Please visit www.lxware.cn/1panel to purchase and try again!"
ErrNodeBound: "This license is already bound to another node. Please check and try again!"
ErrNoSuchNode: "Failed to find the node information. Please check and try again!"
ErrNodeUnbind: "Detected that this node is no longer within the license binding range. Please check and try again!"
ErrNodeBind: "The node is already bound to a license. Please check and try again!"
#task
TaskStart: "{{.name}} Task Start [START]"
TaskEnd: "{{.name}} Task End [COMPLETED]"

View File

@ -30,6 +30,23 @@ ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除"
ErrBackupCheck: "備份帳號測試連接失敗 {{.err}}"
ErrBackupLocalDelete: "暫不支持刪除本地伺服器備份帳號"
#license
ErrLicense: "許可證格式錯誤,請檢查後重試!"
ErrLicenseCheck: "許可證校驗失敗,請檢查後重試!"
ErrLicenseSave: "許可證信息保存失敗,錯誤 {{ .err }}, 請重試!"
ErrLicenseSync: "許可證信息同步失敗,資料庫中未檢測到許可證信息!"
ErrXpackNotFound: "該部分為專業版功能,請先在 面板設置-許可證 界面導入許可證"
ErrXpackExceptional: "該部分為專業版功能,請先在 面板設置-許可證 界面同步許可證狀態"
ErrXpackOutOfDate: "當前許可證已過期,請重新在 面板設置-許可證 界面導入許可證"
ErrXpackLost: "許可證已達到最大重試次數,請進入【面板設定】【許可證】頁面手動點擊同步按鈕,以確保專業版功能正常使用"
ErrXpackTimeout: "請求超時,網絡連接可能不穩定,請稍後再試!"
ErrUnbindMaster: "檢測到節點管理內存在節點,無法解綁當前許可證,請先移除後重試!"
ErrFreeNodeLimit: "社區版節點數量已達到免費上限,請前往 www.lxware.cn/1panel 購買後重試!"
ErrNodeBound: "該許可證已綁定到其他節點,請檢查後重試!"
ErrNoSuchNode: "未能找到該節點信息,請檢查後重試!"
ErrNodeUnbind: "檢測到該節點未在許可證綁定範圍內,請檢查後重試!"
ErrNodeBind: "檢測到該節點已綁定許可證,請檢查後重試!"
#task
TaskStart: "{{.name}} 任務開始 [START]"
TaskEnd: "{{.name}} 任務結束 [COMPLETED]"

View File

@ -32,7 +32,23 @@ ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
ErrBackupCheck: "备份账号测试连接失败 {{ .err}}"
ErrBackupLocalDelete: "暂不支持删除本地服务器备份账号"
#task
#license
ErrLicense: "许可证格式错误,请检查后重试!"
ErrLicenseCheck: "许可证校验失败,请检查后重试!"
ErrLicenseSave: "许可证信息保存失败,错误 {{ .err }},请重试!"
ErrLicenseSync: "许可证信息同步失败,数据库中未检测到许可证信息!"
ErrXpackNotFound: "该部分为专业版功能,请先在 面板设置-许可证 界面导入许可证"
ErrXpackExceptional: "该部分为专业版功能,请先在 面板设置-许可证 界面同步许可证状态"
ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可证 界面导入许可证"
ErrXpackLost: "许可证已达到最大重试次数,请进入【面板设置】【许可证】页面手动点击同步按钮,以确保专业版功能正常使用"
ErrXpackTimeout: "请求超时,网络连接可能不稳定,请稍后再试!"
ErrUnbindMaster: "检测到节点管理内存在节点,无法解绑当前许可证,请先移除后重试!"
ErrFreeNodeLimit: "社区版节点数量已达到免费上限,请前往 www.lxware.cn/1panel 购买后重试!"
ErrNodeBound: "该许可证已绑定到其他节点,请检查后重试!"
ErrNoSuchNode: "未能找到该节点信息,请检查后重试!"
ErrNodeUnbind: "检测到该节点未在许可证绑定范围内,请检查后重试!"
ErrNodeBind: "检测到该节点已绑定许可证,请检查后重试!"
#task
TaskStart: "{{.name}} 任务开始 [START]"
TaskEnd: "{{.name}} 任务结束 [COMPLETED]"

View File

@ -1,89 +1,13 @@
package db
import (
"fmt"
"log"
"os"
"path"
"time"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"github.com/1Panel-dev/1Panel/core/utils/common"
)
func Init() {
initDB()
initTaskDB()
}
func initDB() {
dbPath := path.Join(global.CONF.System.BaseDir, "1panel/db")
if _, err := os.Stat(dbPath); err != nil {
if err := os.MkdirAll(dbPath, os.ModePerm); err != nil {
panic(fmt.Errorf("init db dir failed, err: %v", err))
}
}
fullPath := path.Join(dbPath, global.CONF.System.DbCoreFile)
if _, err := os.Stat(fullPath); err != nil {
f, err := os.Create(fullPath)
if err != nil {
panic(fmt.Errorf("init db file failed, err: %v", err))
}
_ = f.Close()
}
db, err := NewDBWithPath(fullPath)
if err != nil {
panic(err)
}
global.DB = db
global.LOG.Info("init db successfully")
}
func initTaskDB() {
fullPath := path.Join(global.CONF.System.BaseDir, "1panel/db/task.db")
if _, err := os.Stat(fullPath); err != nil {
f, err := os.Create(fullPath)
if err != nil {
panic(fmt.Errorf("init task db file failed, err: %v", err))
}
_ = f.Close()
}
db, err := NewDBWithPath(fullPath)
if err != nil {
panic(err)
}
global.TaskDB = db
global.LOG.Info("init task db successfully")
}
func NewDBWithPath(dbPath string) (*gorm.DB, error) {
db, _ := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: getLogger(),
})
sqlDB, dbError := db.DB()
if dbError != nil {
return nil, dbError
}
sqlDB.SetConnMaxIdleTime(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
return db, nil
}
func getLogger() logger.Interface {
return logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Silent,
IgnoreRecordNotFoundError: true,
Colorful: false,
},
)
global.DB = common.LoadDBConnByPath(path.Join(global.CONF.System.BaseDir, "1panel/db/core.db"), "core")
global.TaskDB = common.LoadDBConnByPath(path.Join(global.CONF.System.BaseDir, "1panel/db/task.db"), "task")
}

View File

@ -14,39 +14,38 @@ import (
func Init() {
settingRepo := repo.NewISettingRepo()
commonRepo := repo.NewICommonRepo()
masterSetting, err := settingRepo.Get(commonRepo.WithByKey("MasterAddr"))
masterSetting, err := settingRepo.Get(repo.WithByKey("MasterAddr"))
if err != nil {
global.LOG.Errorf("load master addr from setting failed, err: %v", err)
}
global.CONF.System.MasterAddr = masterSetting.Value
portSetting, err := settingRepo.Get(commonRepo.WithByKey("ServerPort"))
portSetting, err := settingRepo.Get(repo.WithByKey("ServerPort"))
if err != nil {
global.LOG.Errorf("load service port from setting failed, err: %v", err)
}
global.CONF.System.Port = portSetting.Value
ipv6Setting, err := settingRepo.Get(commonRepo.WithByKey("Ipv6"))
ipv6Setting, err := settingRepo.Get(repo.WithByKey("Ipv6"))
if err != nil {
global.LOG.Errorf("load ipv6 status from setting failed, err: %v", err)
}
global.CONF.System.Ipv6 = ipv6Setting.Value
bindAddressSetting, err := settingRepo.Get(commonRepo.WithByKey("BindAddress"))
bindAddressSetting, err := settingRepo.Get(repo.WithByKey("BindAddress"))
if err != nil {
global.LOG.Errorf("load bind address from setting failed, err: %v", err)
}
global.CONF.System.BindAddress = bindAddressSetting.Value
sslSetting, err := settingRepo.Get(commonRepo.WithByKey("SSL"))
sslSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
if err != nil {
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
}
global.CONF.System.SSL = sslSetting.Value
versionSetting, err := settingRepo.Get(commonRepo.WithByKey("SystemVersion"))
versionSetting, err := settingRepo.Get(repo.WithByKey("SystemVersion"))
if err != nil {
global.LOG.Errorf("load version from setting failed, err: %v", err)
}
global.CONF.System.Version = versionSetting.Value
if _, err := settingRepo.Get(commonRepo.WithByKey("SystemStatus")); err != nil {
if _, err := settingRepo.Get(repo.WithByKey("SystemStatus")); err != nil {
_ = settingRepo.Create("SystemStatus", "Free")
}
if err := settingRepo.Update("SystemStatus", "Free"); err != nil {

View File

@ -13,8 +13,7 @@ import (
func BindDomain() gin.HandlerFunc {
return func(c *gin.Context) {
settingRepo := repo.NewISettingRepo()
commonRepo := repo.NewICommonRepo()
status, err := settingRepo.Get(commonRepo.WithByKey("BindDomain"))
status, err := settingRepo.Get(repo.WithByKey("BindDomain"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return

View File

@ -8,8 +8,7 @@ import (
func LoadErrCode(errInfo string) int {
settingRepo := repo.NewISettingRepo()
commonRepo := repo.NewICommonRepo()
codeVal, err := settingRepo.Get(commonRepo.WithByKey("NoAuthSetting"))
codeVal, err := settingRepo.Get(repo.WithByKey("NoAuthSetting"))
if err != nil {
return 500
}

View File

@ -15,8 +15,7 @@ import (
func WhiteAllow() gin.HandlerFunc {
return func(c *gin.Context) {
settingRepo := repo.NewISettingRepo()
commonRepo := repo.NewICommonRepo()
status, err := settingRepo.Get(commonRepo.WithByKey("AllowIPs"))
status, err := settingRepo.Get(repo.WithByKey("AllowIPs"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return

View File

@ -10,8 +10,7 @@ import (
func GlobalLoading() gin.HandlerFunc {
return func(c *gin.Context) {
settingRepo := repo.NewISettingRepo()
commonRepo := repo.NewICommonRepo()
status, err := settingRepo.Get(commonRepo.WithByKey("SystemStatus"))
status, err := settingRepo.Get(repo.WithByKey("SystemStatus"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return

View File

@ -21,8 +21,7 @@ func PasswordExpired() gin.HandlerFunc {
return
}
settingRepo := repo.NewISettingRepo()
commonRepo := repo.NewICommonRepo()
setting, err := settingRepo.Get(commonRepo.WithByKey("ExpirationDays"))
setting, err := settingRepo.Get(repo.WithByKey("ExpirationDays"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err)
return
@ -33,7 +32,7 @@ func PasswordExpired() gin.HandlerFunc {
return
}
extime, err := settingRepo.Get(commonRepo.WithByKey("ExpirationTime"))
extime, err := settingRepo.Get(repo.WithByKey("ExpirationTime"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err)
return

View File

@ -16,24 +16,18 @@ import (
func Proxy() gin.HandlerFunc {
return func(c *gin.Context) {
if strings.HasPrefix(c.Request.URL.Path, "/api/v2/core") || strings.HasPrefix(c.Request.URL.Path, "/1panel/swagger") {
if strings.HasPrefix(c.Request.URL.Path, "/1panel/swagger") || !strings.HasPrefix(c.Request.URL.Path, "/api/v2") {
c.Next()
return
}
if !strings.HasPrefix(c.Request.URL.Path, "/api/v2") {
if strings.HasPrefix(c.Request.URL.Path, "/api/v2/core") && !strings.HasPrefix(c.Request.URL.Path, "/api/v2/core/xpack") {
c.Next()
return
}
currentNode := c.Request.Header.Get("CurrentNode")
if len(currentNode) != 0 && currentNode != "127.0.0.1" {
if err := xpack.Proxy(c, currentNode); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrProxy, err)
return
}
c.Abort()
return
}
sockPath := "/tmp/agent.sock"
if !strings.HasPrefix(c.Request.URL.Path, "/api/v2/core") && (currentNode == "local" || len(currentNode) == 0) {
sockPath := "/etc/1panel/agent.sock"
if _, err := os.Stat(sockPath); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrProxy, err)
return
@ -55,5 +49,10 @@ func Proxy() gin.HandlerFunc {
}
proxy.ServeHTTP(c.Writer, c.Request)
c.Abort()
return
}
xpack.Proxy(c, currentNode)
c.Abort()
return
}
}

View File

@ -27,14 +27,13 @@ func SessionAuth() gin.HandlerFunc {
return
}
settingRepo := repo.NewISettingRepo()
commonRepo := repo.NewICommonRepo()
setting, err := settingRepo.Get(commonRepo.WithByKey("SessionTimeout"))
setting, err := settingRepo.Get(repo.WithByKey("SessionTimeout"))
if err != nil {
global.LOG.Errorf("create operation record failed, err: %v", err)
return
}
lifeTime, _ := strconv.Atoi(setting.Value)
httpsSetting, err := settingRepo.Get(commonRepo.WithByKey("SSL"))
httpsSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
if err != nil {
global.LOG.Errorf("create operation record failed, err: %v", err)
return

View File

@ -4,10 +4,13 @@ import (
"fmt"
mathRand "math/rand"
"net"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/cmd"
)
@ -120,3 +123,23 @@ func LoadArch() (string, error) {
}
return "", fmt.Errorf("unsupported such arch: %s", std)
}
func Clean(str []byte) {
for i := 0; i < len(str); i++ {
str[i] = 0
}
}
func CreateDirWhenNotExist(isDir bool, pathItem string) (string, error) {
checkPath := pathItem
if !isDir {
checkPath = path.Dir(pathItem)
}
if _, err := os.Stat(checkPath); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(checkPath, os.ModePerm); err != nil {
global.LOG.Errorf("mkdir %s failed, err: %v", checkPath, err)
return pathItem, err
}
}
return pathItem, nil
}

View File

@ -0,0 +1,86 @@
package common
import (
"fmt"
"log"
"os"
"path"
"time"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func LoadDBConnByPath(fullPath, dbName string) *gorm.DB {
if _, err := CreateDirWhenNotExist(true, path.Dir(fullPath)); err != nil {
panic(fmt.Errorf("init db dir failed, err: %v", err))
}
if _, err := os.Stat(fullPath); err != nil {
f, err := os.Create(fullPath)
if err != nil {
panic(fmt.Errorf("init %s db file failed, err: %v", dbName, err))
}
_ = f.Close()
}
db, err := GetDBWithPath(fullPath)
if err != nil {
panic(err)
}
return db
}
func LoadDBConnByPathWithErr(fullPath, dbName string) (*gorm.DB, error) {
if _, err := CreateDirWhenNotExist(true, path.Dir(fullPath)); err != nil {
return nil, fmt.Errorf("init db dir failed, err: %v", err)
}
if _, err := os.Stat(fullPath); err != nil {
f, err := os.Create(fullPath)
if err != nil {
return nil, fmt.Errorf("init %s db file failed, err: %v", dbName, err)
}
_ = f.Close()
}
db, err := GetDBWithPath(fullPath)
if err != nil {
return nil, fmt.Errorf("init %s db failed, err: %v", dbName, err)
}
return db, nil
}
func CloseDB(db *gorm.DB) {
sqlDB, err := db.DB()
if err != nil {
return
}
_ = sqlDB.Close()
}
func GetDBWithPath(dbPath string) (*gorm.DB, error) {
db, _ := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: newLogger(),
})
sqlDB, dbError := db.DB()
if dbError != nil {
return nil, dbError
}
sqlDB.SetConnMaxIdleTime(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
return db, nil
}
func newLogger() logger.Interface {
return logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Silent,
IgnoreRecordNotFoundError: true,
Colorful: false,
},
)
}

View File

@ -14,7 +14,7 @@ import (
)
func NewLocalClient(reqUrl, reqMethod string, body io.Reader) (interface{}, error) {
sockPath := "/tmp/agent.sock"
sockPath := "/etc/1panel/agent.sock"
if _, err := os.Stat(sockPath); err != nil {
return nil, fmt.Errorf("no such agent.sock find in localhost, err: %v", err)
}

View File

@ -27,8 +27,7 @@ type BaseClaims struct {
func NewJWT() *JWT {
settingRepo := repo.NewISettingRepo()
commonRepo := repo.NewICommonRepo()
jwtSign, _ := settingRepo.Get(commonRepo.WithByKey("JWTSigningKey"))
jwtSign, _ := settingRepo.Get(repo.WithByKey("JWTSigningKey"))
return &JWT{
[]byte(jwtSign.Value),
}

View File

@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin"
)
func Proxy(c *gin.Context, currentNode string) error {
return nil
func Proxy(c *gin.Context, currentNode string) {
return
}
func UpdateGroup(name string, group, newGroup uint) error {

View File

@ -224,4 +224,9 @@ export namespace Setting {
productPro: string;
status: string;
}
export interface NodeItem {
id: number;
addr: string;
status: string;
}
}

View File

@ -1,24 +1,39 @@
import http from '@/api';
import { deepCopy } from '@/utils/util';
import { Base64 } from 'js-base64';
import { ResPage, SearchWithPage, DescriptionUpdate } from '../interface';
import { ResPage, SearchWithPage, DescriptionUpdate, ReqPage } from '../interface';
import { Setting } from '../interface/setting';
// license
export const UploadFileData = (params: FormData) => {
return http.upload('/xpack/licenses/upload', params);
return http.upload('/core/licenses/upload', params);
};
export const getLicense = () => {
return http.get<Setting.License>(`/xpack/licenses/get`);
export const SearchLicense = (params: ReqPage) => {
return http.post<ResPage<Setting.License>>('/core/licenses/search', params);
};
export const DeleteLicense = (id: number, force: boolean) => {
return http.post('/core/licenses/del', { id: id, force: force });
};
export const getLicenseStatus = () => {
return http.get<Setting.LicenseStatus>(`/xpack/licenses/get/status`);
return http.get<Setting.LicenseStatus>(`/core/licenses/status`);
};
export const syncLicense = () => {
return http.post(`/xpack/licenses/sync`);
export const getMasterLicenseStatus = () => {
return http.get<Setting.LicenseStatus>(`/core/licenses/master/status`);
};
export const unbindLicense = () => {
return http.post(`/xpack/licenses/unbind`);
export const syncLicense = (id: number) => {
return http.post(`/core/licenses/sync`, { id: id });
};
export const bindLicense = (id: number, nodeID: number) => {
return http.post(`/core/licenses/bind`, { nodeID: nodeID, licenseID: id });
};
export const unbindLicense = (id: number) => {
return http.post(`/core/licenses/unbind`, { id: id });
};
export const loadLicenseOptions = () => {
return http.get(`/core/licenses/options`);
};
export const listNodeOptions = () => {
return http.get<Array<Setting.NodeItem>>(`/core/nodes/list`);
};
// agent

View File

@ -15,7 +15,7 @@
<el-divider direction="vertical" />
</span>
<el-button type="primary" link @click="toHalo">
<span class="font-normal">{{ isProductPro ? $t('license.pro') : $t('license.community') }}</span>
<span class="font-normal">{{ isMasterProductPro ? $t('license.pro') : $t('license.community') }}</span>
</el-button>
<span class="version">{{ version }}</span>
<el-badge is-dot style="margin-top: -3px" v-if="version !== 'Waiting' && globalStore.hasNewVersion">
@ -49,7 +49,7 @@ const globalStore = GlobalStore();
const upgradeRef = ref();
const version = ref<string>('');
const isProductPro = ref();
const isMasterProductPro = ref();
const loading = ref(false);
const upgradeInfo = ref();
const upgradeVersion = ref();
@ -112,7 +112,7 @@ const onLoadUpgradeInfo = async () => {
};
onMounted(() => {
isProductPro.value = globalStore.isProductPro;
isMasterProductPro.value = globalStore.isMasterProductPro;
search();
});
</script>

View File

@ -3,7 +3,7 @@ import { GlobalStore } from '@/store';
export const useTheme = () => {
const globalStore = GlobalStore();
const switchTheme = () => {
if (globalStore.themeConfig.isGold && globalStore.isProductPro) {
if (globalStore.themeConfig.isGold && globalStore.isMasterProductPro) {
const body = document.documentElement as HTMLElement;
body.setAttribute('class', 'dark-gold');
return;

View File

@ -72,6 +72,8 @@ const message = {
createNewFolder: 'Create new folder',
createNewFile: 'Create new file',
helpDoc: 'Help Document',
bind: 'Bind',
unbind: 'Unbind',
},
search: {
timeStart: 'Time start',
@ -1731,30 +1733,34 @@ const message = {
community: 'Community Edition: ',
community2: 'Community Edition',
pro: 'Professional Edition: ',
trial: 'Trial Edition',
office: 'Official Edition',
xpack: 'Professional Edition',
trial: 'Trial Version',
forceDelete: 'Force delete, will ignore errors during the deletion process and eventually remove metadata',
deleteHelper: 'Deleting the license may cause child nodes to be unable to switch, please proceed with caution!',
office: 'Official Version',
trialInfo: 'Version',
authorizationId: 'Subscription Authorization ID',
authorizedUser: 'Authorized User',
expiresAt: 'Expiry Date',
expiresAt: 'Expiration Date',
productName: 'Product Name',
productStatus: 'Product Status',
Lost01: 'Lost * 1',
Lost02: 'Lost * 2',
Lost03: 'Lost',
Enable: 'Enabled',
Disable: 'Disabled',
lost: 'Lost Contact',
bound: 'Bound',
exceptional: 'Exceptional',
free: 'Free',
lostHelper:
'The License needs to be periodically synchronized for availability. Please ensure normal external network access. After three losses of connection, the License binding will be released.',
'The license has reached the maximum number of retry attempts. Please manually click the sync button to ensure the professional version functions properly.',
exceptionalHelper:
'License synchronization verification is abnormal. Please manually click the sync button to ensure the professional version functions properly.',
quickUpdate: 'Quick Update',
import: 'Import',
power: 'Authorize',
unbind: 'Unbind License',
unbindHelper: 'All Pro related Settings will be cleared after unbinding. Do you want to continue? ',
power: 'Authorization',
unbind: 'Unbind',
unbindHelper: 'Unbinding will clear all professional edition settings for this node. Do you wish to continue?',
unbindMasterHelper:
'There are currently other nodes besides the master node. The {0} operation is not supported. Please delete the nodes in the node management and try again.',
importLicense: 'Import License',
importHelper: 'Please click or drag the license file here',
technicalAdvice: 'Technical Advice',
advice: 'Consultation',
indefinitePeriod: 'Indefinite Period',
levelUpPro: 'Upgrade to Professional Edition',
licenseSync: 'License Sync',

View File

@ -71,6 +71,8 @@ const message = {
createNewFolder: '新建資料夾',
createNewFile: '新建檔案',
helpDoc: '幫助文档',
bind: '綁定',
unbind: '解除綁定',
},
search: {
timeStart: '開始時間',
@ -1612,7 +1614,10 @@ const message = {
community: '社區版',
community2: '社區版',
pro: '專業版',
xpack: '專業版',
trial: '試用版',
forceDelete: '強制刪除會忽略刪除過程中產生的錯誤並最終刪除元數據',
deleteHelper: '刪除許可證可能導致子節點無法切換請謹慎操作',
office: '正式版',
trialInfo: '版本',
authorizationId: '訂閱授權 ID',
@ -1620,22 +1625,21 @@ const message = {
expiresAt: '到期時間',
productName: '產品名稱',
productStatus: '產品狀態',
Lost01: '失聯 * 1',
Lost02: '失聯 * 2',
Lost03: '已失聯',
Enable: '已啟用',
Disable: '未啟用',
lostHelper: '許可證需要定時同步是否可用請保證正常外網訪問失聯三次後將解除許可證綁定',
lost: '已失聯',
bound: '已綁定',
exceptional: '異常',
free: '空閒',
lostHelper: '許可證已達到最大重試次數請手動點擊同步按鈕以確保專業版功能正常使用',
exceptionalHelper: '許可證同步驗證異常請手動點擊同步按鈕以確保專業版功能正常使用',
quickUpdate: '快速更新',
import: '導入',
power: ' ',
power: '',
unbind: '解除綁定',
unbindHelper: '解除綁定後將清除所有專業版相關設置是否繼續',
unbindHelper: '解除綁定後將清除該節點所有專業版相關設置是否繼續',
unbindMasterHelper: '當前已存在除主節點外其他節點不支持 {0} 操作請在節點管理中刪除節點後重試',
importLicense: '導入許可證',
importHelper: '請點擊或拖動許可文件到此處',
technicalAdvice: '技術諮詢',
advice: '諮詢',
indefinitePeriod: '無限期',
indefinitePeriod: '無期限',
levelUpPro: '升級專業版',
licenseSync: '許可證同步',
knowMorePro: '了解更多',

View File

@ -71,6 +71,8 @@ const message = {
createNewFolder: '新建文件夹',
createNewFile: '新建文件',
helpDoc: '帮助文档',
bind: '绑定',
unbind: '解绑',
},
search: {
timeStart: '开始时间',
@ -1597,6 +1599,8 @@ const message = {
currentVersion: '当前运行版本',
license: '许可证',
bind: '绑定',
bindNode: '绑定节点',
advancedMenuHide: '高级功能菜单隐藏',
showMainAdvancedMenu: '如果只保留 1 个菜单则侧边栏只会显示高级功能主菜单',
showAll: '全部显示',
@ -1611,7 +1615,10 @@ const message = {
community: '社区版',
community2: '社区版',
pro: '专业版',
xpack: '专业版',
trial: '试用版',
forceDelete: '强制删除会忽略删除过程中产生的错误并最终删除元数据',
deleteHelper: '删除许可证可能导致子节点无法切换请谨慎操作',
office: '正式版',
trialInfo: '版本',
authorizationId: '订阅授权 ID',
@ -1619,21 +1626,20 @@ const message = {
expiresAt: '到期时间',
productName: '产品名称',
productStatus: '产品状态',
Lost01: '失联 * 1',
Lost02: '失联 * 2',
Lost03: '已失联',
Enable: '已激活',
Disable: '未激活',
lostHelper: '许可证需要定时同步是否可用请保证正常外网访问失联三次后将解除许可证绑定',
lost: '已失联',
bound: '已绑定',
exceptional: '异常',
free: '空闲',
lostHelper: '许可证已达到最大重试次数请手动点击同步按钮以确保专业版功能正常使用',
exceptionalHelper: '许可证同步验证异常请手动点击同步按钮以确保专业版功能正常使用',
quickUpdate: '快速更新',
import: '导入',
power: ' ',
unbind: '解除绑定',
unbindHelper: '解除绑定后将清除所有专业版相关设置是否继续',
unbindHelper: '解除绑定后将清除该节点所有专业版相关设置是否继续',
unbindMasterHelper: '当前已存在除主节点外其他节点不支持 {0} 操作请在节点管理中删除节点后重试',
importLicense: '导入许可证',
importHelper: '请点击或拖动许可文件到此处',
technicalAdvice: '技术咨询',
advice: '咨询',
indefinitePeriod: '无限期',
levelUpPro: '升级专业版',
licenseSync: '许可证同步',

View File

@ -8,13 +8,16 @@
>
<Logo :isCollapse="isCollapse" />
<span v-if="nodes.length !== 1" class="el-dropdown-link">
{{ globalStore.currentNode || '127.0.0.1' }}
<span v-if="nodes.length !== 0" class="el-dropdown-link">
{{ loadCurrentName() }}
</span>
<el-dropdown v-if="nodes.length !== 1" placement="right-start" @command="changeNode">
<el-dropdown v-if="nodes.length !== 0" placement="right-start" @command="changeNode">
<el-icon class="ico"><Switch /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="local">
{{ $t('terminal.local') }}
</el-dropdown-item>
<el-dropdown-item v-for="item in nodes" :key="item.name" :command="item.name">
{{ item.name }}
</el-dropdown-item>
@ -60,7 +63,7 @@ import { ElMessageBox } from 'element-plus';
import { GlobalStore, MenuStore } from '@/store';
import { MsgSuccess } from '@/utils/message';
import { isString } from '@vueuse/core';
import { getSettingInfo } from '@/api/modules/setting';
import { getSettingInfo, listNodeOptions } from '@/api/modules/setting';
const route = useRoute();
const menuStore = MenuStore();
@ -83,6 +86,13 @@ let routerMenus = computed((): RouteRecordRaw[] => {
return menuStore.menuList.filter((route) => route.meta && !route.meta.hideInSidebar);
});
const loadCurrentName = () => {
if (globalStore.currentNode) {
return globalStore.currentNode === 'local' ? i18n.global.t('terminal.local') : globalStore.currentNode;
}
return i18n.global.t('terminal.local');
};
const screenWidth = ref(0);
interface Node {
@ -127,25 +137,23 @@ const systemLogOut = async () => {
};
const loadNodes = async () => {
let listXNodes;
const xpackModules = import.meta.glob('../../../xpack/api/modules/node.ts', { eager: true });
if (xpackModules['../../../xpack/api/modules/node.ts']) {
listXNodes = xpackModules['../../../xpack/api/modules/node.ts']['listNodes'] || {};
const res = await listXNodes();
await listNodeOptions()
.then((res) => {
if (!res) {
nodes.value = [];
return;
}
nodes.value = res.data;
if (nodes.value.length === 1) {
globalStore.currentNode = nodes.value[0].name;
}
return;
if (nodes.value.length === 0) {
globalStore.currentNode = 'local';
}
})
.catch(() => {
nodes.value = [];
});
};
const changeNode = (command: string) => {
globalStore.currentNode = command || '127.0.0.1';
globalStore.currentNode = command || 'local';
location.reload();
};

View File

@ -22,7 +22,7 @@ import { GlobalStore, MenuStore, TabsStore } from '@/store';
import { DeviceType } from '@/enums/app';
import { getSystemAvailable } from '@/api/modules/setting';
import { useRoute, useRouter } from 'vue-router';
import { loadProductProFromDB } from '@/utils/xpack';
import { loadMasterProductProFromDB, loadProductProFromDB } from '@/utils/xpack';
import { useTheme } from '@/global/use-theme';
const { switchTheme } = useTheme();
useResize();
@ -100,6 +100,7 @@ onMounted(() => {
loadStatus();
loadProductProFromDB();
loadMasterProductProFromDB();
const mqList = window.matchMedia('(prefers-color-scheme: dark)');
if (mqList.addEventListener) {

View File

@ -35,6 +35,7 @@ export interface GlobalState {
isProductPro: boolean;
productProExpires: number;
isMasterProductPro: boolean;
errStatus: string;

View File

@ -39,17 +39,18 @@ const GlobalStore = defineStore({
isProductPro: false,
productProExpires: 0,
isMasterProductPro: false,
errStatus: '',
currentNode: '127.0.0.1',
currentNode: 'local',
}),
getters: {
isDarkTheme: (state) =>
state.themeConfig.theme === 'dark' ||
state.themeConfig.isGold ||
(state.themeConfig.theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches),
isDarkGoldTheme: (state) => state.themeConfig.isGold && state.isProductPro,
isDarkGoldTheme: (state) => state.themeConfig.isGold && state.isMasterProductPro,
},
actions: {
setOpenMenuTabs(openMenuTabs: boolean) {

View File

@ -1,4 +1,4 @@
import { getLicenseStatus, getSettingInfo } from '@/api/modules/setting';
import { getLicenseStatus, getMasterLicenseStatus, getSettingInfo } from '@/api/modules/setting';
import { useTheme } from '@/global/use-theme';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
@ -53,36 +53,39 @@ const loadDataFromDB = async () => {
export async function loadProductProFromDB() {
const res = await getLicenseStatus();
if (!res || !res.data) {
resetXSetting();
globalStore.isProductPro = false;
} else {
globalStore.isProductPro =
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
globalStore.isProductPro = res.data.status === 'bound';
if (globalStore.isProductPro) {
globalStore.productProExpires = Number(res.data.productPro);
}
}
switchTheme();
initFavicon();
loadDataFromDB();
}
export async function loadMasterProductProFromDB() {
const res = await getMasterLicenseStatus();
if (!res || !res.data) {
globalStore.isMasterProductPro = false;
} else {
globalStore.isMasterProductPro = res.data.status === 'bound';
}
switchTheme();
initFavicon();
}
export async function getXpackSettingForTheme() {
const res = await getLicenseStatus();
const res = await getMasterLicenseStatus();
if (!res.data) {
globalStore.isProductPro = false;
globalStore.isMasterProductPro = false;
resetXSetting();
switchTheme();
initFavicon();
return;
}
globalStore.isProductPro =
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
if (globalStore.isProductPro) {
globalStore.productProExpires = Number(res.data.productPro);
}
if (!globalStore.isProductPro) {
globalStore.isProductPro = false;
globalStore.isMasterProductPro = res.data.status === 'bound';
if (!globalStore.isMasterProductPro) {
globalStore.isMasterProductPro = false;
resetXSetting();
switchTheme();
initFavicon();

View File

@ -0,0 +1,97 @@
<template>
<DrawerPro
v-model="drawerVisible"
:header="$t('commons.button.bind')"
:resource="licenseName"
:back="handleClose"
size="small"
>
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
<el-form-item :label="$t('setting.bindNode')" prop="nodeID" :rules="Rules.requiredSelect">
<el-select filterable v-model="form.nodeID" style="width: 100%">
<el-option v-for="item in freeNodes" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button :disabled="loading" type="primary" @click="onBind(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</template>
</DrawerPro>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { bindLicense, listNodeOptions } from '@/api/modules/setting';
import { FormInstance } from 'element-plus';
import { GlobalStore } from '@/store';
import { Rules } from '@/global/form-rules';
const globalStore = GlobalStore();
interface DialogProps {
licenseName: string;
licenseID: number;
}
const drawerVisible = ref();
const loading = ref();
const licenseName = ref();
const freeNodes = ref([]);
const form = reactive({
nodeID: null,
licenseID: null,
});
const formRef = ref<FormInstance>();
const acceptParams = (params: DialogProps): void => {
licenseName.value = params.licenseName;
form.licenseID = params.licenseID;
loadNodes();
drawerVisible.value = true;
};
const onBind = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
await bindLicense(form.licenseID, form.nodeID)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
globalStore.isProductPro = false;
globalStore.themeConfig.isGold = false;
window.location.reload();
})
.catch(() => {
loading.value = false;
});
});
};
const loadNodes = async () => {
if (!globalStore.isMasterProductPro) {
freeNodes.value = [{ id: 0, name: i18n.global.t('terminal.local') }];
return;
}
await listNodeOptions()
.then((res) => {
freeNodes.value = res.data || [];
})
.catch(() => {
freeNodes.value = [];
});
};
const handleClose = () => {
drawerVisible.value = false;
};
defineExpose({
acceptParams,
});
</script>

View File

@ -0,0 +1,74 @@
<template>
<el-dialog v-model="dialogVisible" :title="$t('commons.button.delete')" width="30%" :close-on-click-modal="false">
<el-form ref="deleteRef" v-loading="loading" @submit.prevent>
<el-form-item>
<el-alert :title="$t('license.deleteHelper')" :closable="false" type="warning" />
</el-form-item>
<el-form-item>
<el-checkbox v-model="form.forceDelete" :label="$t('database.unBindForce')" />
<span class="input-help">
{{ $t('license.forceDelete') }}
</span>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false" :disabled="loading">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button type="primary" @click="submit" :disabled="loading">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { FormInstance } from 'element-plus';
import { ref } from 'vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { DeleteLicense } from '@/api/modules/setting';
let form = reactive({
id: 0,
licenseName: '',
forceDelete: false,
});
let dialogVisible = ref(false);
let loading = ref(false);
const deleteRef = ref<FormInstance>();
interface DialogProps {
id: number;
name: string;
database: string;
}
const emit = defineEmits<{ (e: 'search'): void }>();
const acceptParams = async (prop: DialogProps) => {
form.id = prop.id;
form.licenseName = prop.name;
form.forceDelete = false;
dialogVisible.value = true;
};
const submit = async () => {
loading.value = true;
DeleteLicense(form.id, form.forceDelete)
.then(() => {
loading.value = false;
emit('search');
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
dialogVisible.value = false;
})
.catch(() => {
loading.value = false;
});
};
defineExpose({
acceptParams,
});
</script>

View File

@ -1,144 +1,133 @@
<template>
<div>
<LayoutContent v-loading="loading" :title="$t('setting.license')" :divider="true">
<LayoutContent v-loading="loading" :title="$t('setting.license')">
<template #leftToolBar>
<el-button type="primary" @click="toUpload()">
{{ $t('commons.button.add') }}
</el-button>
</template>
<template #main>
<el-row :gutter="20" class="mt-5; mb-10">
<el-col :xs="24" :sm="24" :md="15" :lg="15" :xl="15">
<div class="descriptions" v-if="hasLicense">
<el-descriptions :column="1" direction="horizontal" size="large" border>
<el-descriptions-item :label="$t('license.authorizationId')">
{{ license.licenseName || '-' }}
<el-button
type="primary"
class="ml-3"
plain
@click="onSync"
size="small"
v-if="showSync()"
>
{{ $t('commons.button.sync') }}
</el-button>
<el-button type="primary" class="ml-3" plain @click="onUnBind()" size="small">
{{ $t('license.unbind') }}
</el-button>
</el-descriptions-item>
<el-descriptions-item :label="$t('license.authorizedUser')">
{{ license.assigneeName || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('license.productName')">
{{ license.productName || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('license.trialInfo')">
{{ license.trial ? $t('license.trial') : $t('license.office') }}
</el-descriptions-item>
<el-descriptions-item :label="$t('license.expiresAt')">
{{ license.expiresAt || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('license.productStatus')">
<div v-if="license.status">
<ComplexTable :pagination-config="paginationConfig" @sort-change="search" @search="search" :data="data">
<el-table-column
:label="$t('license.authorizationId')"
:min-width="80"
prop="licenseName"
show-overflow-tooltip
/>
<el-table-column :label="$t('license.authorizedUser')" prop="assigneeName" show-overflow-tooltip>
<template #default="{ row }">
{{ row.assigneeName || '-' }}
</template>
</el-table-column>
<el-table-column :label="$t('license.expiresAt')" prop="expiresAt" show-overflow-tooltip>
<template #default="{ row }">
{{ row.expiresAt || '-' }}
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.status')" prop="status" show-overflow-tooltip>
<template #default="{ row }">
<div v-if="row.status">
<el-tooltip
v-if="license.status.indexOf('Lost') !== -1"
:content="$t('license.lostHelper')"
v-if="row.status === 'exceptional'"
:content="$t('license.exceptionalHelper')"
>
<el-tag type="info">
{{ $t('license.' + license.status) }}
<el-tag type="danger">
{{ $t('license.' + row.status) }}
</el-tag>
</el-tooltip>
<el-tag v-else>{{ $t('license.' + license.status) }}</el-tag>
<el-tooltip v-if="row.status === 'lost'" :content="$t('license.lostHelper')">
<el-tag type="info">
{{ $t('license.' + row.status) }}
</el-tag>
</el-tooltip>
<el-tag v-if="row.status !== 'exceptional' && row.status !== 'lost'">
{{ $t('license.' + row.status) }}
</el-tag>
</div>
<span v-else>-</span>
</el-descriptions-item>
<el-descriptions-item class="descriptions" :label="$t('commons.table.message')">
{{ license.message }}
</el-descriptions-item>
</el-descriptions>
</template>
</el-table-column>
<el-table-column :label="$t('setting.bindNode')">
<template #default="{ row }">
<span v-if="row.freeCount !== 0 && (row.status === 'free' || row.status === 'exceptional')">
-
</span>
<div v-else>
<span v-if="row.freeCount === 0">{{ row.bindNode || '-' }}</span>
<div v-else>
<el-popover
placement="bottom"
:width="120"
trigger="hover"
v-if="row.freeNodes && row.freeNodes.length != 0"
>
<div v-for="(item, index) of row.freeNodes" :key="index">
<el-tag>{{ item.name }}</el-tag>
</div>
<template #reference>
<el-button link type="primary">
({{ row.bindCount }} / {{ row.freeCount }})
</el-button>
</template>
</el-popover>
<span v-else link type="primary">({{ row.bindCount }} / {{ row.freeCount }})</span>
</div>
<CardWithHeader :header="$t('home.overview')" height="160px" v-if="!hasLicense">
<template #body>
<div class="h-app-card">
<el-row>
<el-col :span="6">
<span>{{ $t('setting.license') }}</span>
</el-col>
<el-col :span="6">
<span>{{ $t('license.community2') }}</span>
</el-col>
</el-row>
</div>
</template>
</CardWithHeader>
</el-col>
<el-col :xs="24" :sm="24" :md="9" :lg="9" :xl="9">
<CardWithHeader :header="$t('license.quickUpdate')" height="160px">
<template #body>
<div class="h-app-card">
<el-row>
<el-col :span="15">
<div class="h-app-content">{{ $t('license.importLicense') }}</div>
</el-col>
<el-col :span="5">
<el-button type="primary" plain round size="small" @click="toUpload">
{{ $t('license.import') }}
</el-button>
</el-col>
</el-row>
</div>
<div class="h-app-card">
<el-row>
<el-col :span="15">
<div class="h-app-content">{{ $t('license.technicalAdvice') }}</div>
</el-col>
<el-col :span="5">
<el-button type="primary" plain round size="small" @click="toHalo()">
{{ $t('license.advice') }}
</el-button>
</el-col>
</el-row>
</div>
</template>
</CardWithHeader>
</el-col>
</el-row>
</el-table-column>
<el-table-column
prop="createdAt"
:label="$t('commons.table.date')"
:formatter="dateFormat"
show-overflow-tooltip
/>
<fu-table-operations
width="300px"
:buttons="buttons"
:ellipsis="10"
:label="$t('commons.table.operate')"
fix
/>
</ComplexTable>
</template>
</LayoutContent>
<LicenseImport ref="licenseRef" />
<LicenseBind ref="bindRef" />
<LicenseDelete ref="delRef" @search="search" />
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { getLicense, syncLicense, unbindLicense } from '@/api/modules/setting';
import CardWithHeader from '@/components/card-with-header/index.vue';
import { SearchLicense, syncLicense, unbindLicense } from '@/api/modules/setting';
import LicenseImport from '@/components/license-import/index.vue';
import LicenseDelete from '@/views/setting/license/delete/index.vue';
import LicenseBind from '@/views/setting/license/bind/index.vue';
import { dateFormat } from '@/utils/util';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { MsgError, MsgSuccess } from '@/utils/message';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const loading = ref();
const licenseRef = ref();
const globalStore = GlobalStore();
const hasLicense = ref();
const delRef = ref();
const bindRef = ref();
const license = reactive({
licenseName: '',
trial: true,
expiresAt: '',
assigneeName: '',
productName: '',
status: '',
message: '',
const data = ref();
const paginationConfig = reactive({
cacheSizeKey: 'backup-page-size',
currentPage: 1,
pageSize: 10,
total: 0,
type: '',
name: '',
});
const toHalo = () => {
window.open('https://www.lxware.cn/1panel' + '', '_blank', 'noopener,noreferrer');
};
const onSync = async () => {
const onSync = async (row: any) => {
loading.value = true;
await syncLicense()
await syncLicense(row.id)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
@ -149,20 +138,24 @@ const onSync = async () => {
});
};
const onUnBind = async () => {
const onUnbind = async (row: any) => {
ElMessageBox.confirm(i18n.global.t('license.unbindHelper'), i18n.global.t('license.unbind'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
loading.value = true;
await unbindLicense()
await unbindLicense(row.id)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
if (row.freeCount !== 0) {
globalStore.isProductPro = false;
globalStore.themeConfig.isGold = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
window.location.reload();
return;
}
search();
})
.catch(() => {
loading.value = false;
@ -170,6 +163,29 @@ const onUnBind = async () => {
});
};
const search = async () => {
loading.value = true;
let params = {
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
};
await SearchLicense(params)
.then((res) => {
loading.value = false;
data.value = res.data.items || [];
for (const item of data.value) {
item.productName = 'product-1panel-pro';
item.expiresAt =
item.productPro === '0'
? i18n.global.t('license.indefinitePeriod')
: timestampToDate(Number(item.productPro));
}
})
.catch(() => {
loading.value = false;
});
};
const timestampToDate = (timestamp: number) => {
const date = new Date(timestamp * 1000);
const y = date.getFullYear();
@ -186,47 +202,64 @@ const timestampToDate = (timestamp: number) => {
return `${y}-${m}-${d} ${h}:${minute}:${second}`;
};
const search = async () => {
loading.value = true;
await getLicense()
.then((res) => {
loading.value = false;
license.status = res.data.status;
globalStore.isProductPro =
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
if (res.data.status === '') {
hasLicense.value = false;
return;
}
hasLicense.value = true;
if (globalStore.isProductPro) {
globalStore.productProExpires = Number(res.data.productPro);
}
license.licenseName = res.data.licenseName;
license.message = res.data.message;
license.assigneeName = res.data.assigneeName;
license.trial = res.data.trial;
if (res.data.productPro) {
license.productName = 'product-1panel-pro';
license.expiresAt =
res.data.productPro === '0'
? i18n.global.t('license.indefinitePeriod')
: timestampToDate(Number(res.data.productPro));
}
})
.catch(() => {
loading.value = false;
});
};
const showSync = () => {
return license.status.indexOf('Lost') !== -1 || license.status === 'Disable';
};
const toUpload = () => {
licenseRef.value.acceptParams();
};
const buttons = [
{
label: i18n.global.t('commons.button.bind'),
disabled: (row: any) => {
return row.status !== 'free';
},
click: (row: any) => {
bindRef.value.acceptParams({ licenseID: row.id, licenseName: row.licenseName });
},
},
{
label: i18n.global.t('commons.button.unbind'),
disabled: (row: any) => {
return row.status === 'free';
},
click: (row: any) => {
if (row.freeCount != 0) {
if (row.freeNodes) {
MsgError(i18n.global.t('license.unbindMasterHelper', [i18n.global.t('commons.button.unbind')]));
return;
}
for (const item of data.value) {
if (item.bindNode && item.freeCount == 0) {
MsgError(i18n.global.t('license.unbindMasterHelper', [i18n.global.t('commons.button.unbind')]));
return;
}
}
}
onUnbind(row);
},
},
{
label: i18n.global.t('commons.button.sync'),
disabled: (row: any) => {
return row.status.indexOf('Lost') !== -1 || row.status === 'Disable';
},
click: (row: any) => {
onSync(row);
},
},
{
label: i18n.global.t('commons.button.delete'),
click: (row: any) => {
for (const item of data.value) {
if (item.bindNode && row.freeCount != 0) {
MsgError(i18n.global.t('license.unbindMasterHelper', [i18n.global.t('commons.button.delete')]));
return;
}
}
delRef.value.acceptParams({ id: row.id, name: row.licenseName });
},
},
];
onMounted(() => {
search();
});

View File

@ -28,7 +28,7 @@
<el-form-item :label="$t('setting.theme')" prop="theme">
<el-radio-group @change="onSave('Theme', form.theme)" v-model="form.theme">
<el-radio-button v-if="isProductPro" value="dark-gold">
<el-radio-button v-if="isMasterProductPro" value="dark-gold">
<span>{{ $t('setting.darkGold') }}</span>
</el-radio-button>
<el-radio-button value="light">
@ -159,7 +159,7 @@ const loading = ref(false);
const i18n = useI18n();
const globalStore = GlobalStore();
const { isProductPro } = storeToRefs(globalStore);
const { isMasterProductPro } = storeToRefs(globalStore);
const { switchTheme } = useTheme();
@ -230,7 +230,7 @@ const search = async () => {
const checkedTitles = getCheckedTitles(json);
form.proHideMenus = checkedTitles.toString();
if (isProductPro.value) {
if (isMasterProductPro.value) {
const xpackRes = await getXpackSetting();
if (xpackRes) {
form.theme = xpackRes.data.theme === 'dark-gold' ? 'dark-gold' : res.data.theme;
@ -305,7 +305,7 @@ const onSave = async (key: string, val: any) => {
globalStore.themeConfig.theme = val;
}
switchTheme();
if (globalStore.isProductPro) {
if (globalStore.isMasterProductPro) {
updateXpackSettingByKey('Theme', val === 'dark-gold' ? 'dark-gold' : '');
if (val === 'dark-gold') {
MsgSuccess(i18n.t('commons.msg.operationSuccess'));

View File

@ -5,8 +5,8 @@
<template #default>
{{ $t('setting.proxyHelper') }}
<ul class="-ml-5">
<li v-if="isProductPro">{{ $t('setting.proxyHelper1') }}</li>
<li v-if="isProductPro">{{ $t('setting.proxyHelper2') }}</li>
<li v-if="isMasterProductPro">{{ $t('setting.proxyHelper1') }}</li>
<li v-if="isMasterProductPro">{{ $t('setting.proxyHelper2') }}</li>
<li>{{ $t('setting.proxyHelper3') }}</li>
</ul>
</template>
@ -69,7 +69,7 @@ import { storeToRefs } from 'pinia';
const globalStore = GlobalStore();
const emit = defineEmits<{ (e: 'search'): void }>();
const { isProductPro } = storeToRefs(globalStore);
const { isMasterProductPro } = storeToRefs(globalStore);
const formRef = ref<FormInstance>();
const rules = reactive({