mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 08:19:15 +08:00
feat: 提取分组及快速命令功能 (#6169)
This commit is contained in:
parent
7fb42d7b47
commit
37df602ae8
@ -10,6 +10,21 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) CheckBackupUsed(c *gin.Context) {
|
||||
id, err := helper.GetIntParamByKey(c, "id")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := backupService.CheckUsed(id); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Page backup records
|
||||
// @Description 获取备份记录列表分页
|
||||
|
@ -30,8 +30,6 @@ var (
|
||||
|
||||
cronjobService = service.NewICronjobService()
|
||||
|
||||
hostService = service.NewIHostService()
|
||||
groupService = service.NewIGroupService()
|
||||
fileService = service.NewIFileService()
|
||||
sshService = service.NewISSHService()
|
||||
firewallService = service.NewIFirewallService()
|
||||
@ -45,8 +43,6 @@ var (
|
||||
settingService = service.NewISettingService()
|
||||
backupService = service.NewIBackupService()
|
||||
|
||||
commandService = service.NewICommandService()
|
||||
|
||||
websiteService = service.NewIWebsiteService()
|
||||
websiteDnsAccountService = service.NewIWebsiteDnsAccountService()
|
||||
websiteSSLService = service.NewIWebsiteSSLService()
|
||||
|
@ -12,67 +12,12 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/copier"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/terminal"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
id, err := strconv.Atoi(c.Query("id"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param id in request")) {
|
||||
return
|
||||
}
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
host, err := hostService.GetHostInfo(uint(id))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "load host info by id failed")) {
|
||||
return
|
||||
}
|
||||
var connInfo ssh.ConnInfo
|
||||
_ = copier.Copy(&connInfo, &host)
|
||||
connInfo.PrivateKey = []byte(host.PrivateKey)
|
||||
if len(host.PassPhrase) != 0 {
|
||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||
}
|
||||
|
||||
client, err := connInfo.NewClient()
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
sws, err := terminal.NewLogicSshWsSession(cols, rows, true, connInfo.Client, wsConn)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer sws.Close()
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
sws.Start(quitChan)
|
||||
go sws.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
|
@ -957,3 +957,15 @@ func (b *BaseApi) UpdateLoadBalanceFile(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) ChangeWebsiteGroup(c *gin.Context) {
|
||||
var req dto.UpdateGroup
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := websiteService.ChangeGroup(req.Group, req.NewGroup); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
@ -52,3 +52,8 @@ type OperationWithNameAndType struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type UpdateGroup struct {
|
||||
Group uint `json:"group"`
|
||||
NewGroup uint `json:"newGroup"`
|
||||
}
|
||||
|
@ -14,3 +14,15 @@ type MonitorData struct {
|
||||
Date []time.Time `json:"date"`
|
||||
Value []interface{} `json:"value"`
|
||||
}
|
||||
|
||||
type MonitorSetting struct {
|
||||
MonitorStatus string `json:"monitorStatus"`
|
||||
MonitorStoreDays string `json:"monitorStoreDays"`
|
||||
MonitorInterval string `json:"monitorInterval"`
|
||||
DefaultNetwork string `json:"defaultNetwork"`
|
||||
}
|
||||
|
||||
type MonitorSettingUpdate struct {
|
||||
Key string `json:"key" validate:"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval DefaultNetwork"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
package model
|
||||
|
||||
type Command struct {
|
||||
BaseModel
|
||||
Name string `gorm:"unique;not null" json:"name"`
|
||||
GroupID uint `json:"groupID"`
|
||||
Command string `gorm:"not null" json:"command"`
|
||||
}
|
||||
|
||||
type RedisCommand struct {
|
||||
BaseModel
|
||||
Name string `gorm:"unique;not null" json:"name"`
|
||||
Command string `gorm:"not null" json:"command"`
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package model
|
||||
|
||||
type Group struct {
|
||||
BaseModel
|
||||
IsDefault bool `json:"isDefault"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Type string `gorm:"not null" json:"type"`
|
||||
}
|
@ -21,7 +21,7 @@ type ICronjobRepo interface {
|
||||
Create(cronjob *model.Cronjob) error
|
||||
WithByJobID(id int) DBOption
|
||||
WithByDbName(name string) DBOption
|
||||
WithByDefaultDownload(account string) DBOption
|
||||
WithByDownloadAccountID(id uint) DBOption
|
||||
WithByRecordDropID(id int) DBOption
|
||||
WithByRecordFile(file string) DBOption
|
||||
Save(id uint, cronjob model.Cronjob) error
|
||||
@ -124,9 +124,9 @@ func (c *CronjobRepo) WithByDbName(name string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByDefaultDownload(account string) DBOption {
|
||||
func (c *CronjobRepo) WithByDownloadAccountID(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("default_download = ?", account)
|
||||
return g.Where("download_account_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ type IFtpRepo interface {
|
||||
Create(ftp *model.Ftp) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
WithLikeUser(user string) DBOption
|
||||
WithByUser(user string) DBOption
|
||||
}
|
||||
|
||||
@ -33,6 +34,12 @@ func (u *FtpRepo) Get(opts ...DBOption) (model.Ftp, error) {
|
||||
}
|
||||
|
||||
func (h *FtpRepo) WithByUser(user string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("user = ?", user)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *FtpRepo) WithLikeUser(user string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(user) == 0 {
|
||||
return g
|
||||
|
@ -3,23 +3,11 @@ package repo
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type HostRepo struct{}
|
||||
|
||||
type IHostRepo interface {
|
||||
Get(opts ...DBOption) (model.Host, error)
|
||||
GetList(opts ...DBOption) ([]model.Host, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Host, error)
|
||||
WithByInfo(info string) DBOption
|
||||
WithByPort(port uint) DBOption
|
||||
WithByUser(user string) DBOption
|
||||
WithByAddr(addr string) DBOption
|
||||
Create(host *model.Host) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
|
||||
GetFirewallRecord(opts ...DBOption) (model.Firewall, error)
|
||||
ListFirewallRecord() ([]model.Firewall, error)
|
||||
SaveFirewallRecord(firewall *model.Firewall) error
|
||||
@ -31,88 +19,6 @@ func NewIHostRepo() IHostRepo {
|
||||
return &HostRepo{}
|
||||
}
|
||||
|
||||
func (h *HostRepo) Get(opts ...DBOption) (model.Host, error) {
|
||||
var host model.Host
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&host).Error
|
||||
return host, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) GetList(opts ...DBOption) ([]model.Host, error) {
|
||||
var hosts []model.Host
|
||||
db := global.DB.Model(&model.Host{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&hosts).Error
|
||||
return hosts, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) Page(page, size int, opts ...DBOption) (int64, []model.Host, error) {
|
||||
var users []model.Host
|
||||
db := global.DB.Model(&model.Host{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) WithByInfo(info string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(info) == 0 {
|
||||
return g
|
||||
}
|
||||
infoStr := "%" + info + "%"
|
||||
return g.Where("name LIKE ? OR addr LIKE ?", infoStr, infoStr)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostRepo) WithByPort(port uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("port = ?", port)
|
||||
}
|
||||
}
|
||||
func (h *HostRepo) WithByUser(user string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("user = ?", user)
|
||||
}
|
||||
}
|
||||
func (h *HostRepo) WithByAddr(addr string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("addr = ?", addr)
|
||||
}
|
||||
}
|
||||
func (h *HostRepo) WithByGroup(group string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(group) == 0 {
|
||||
return g
|
||||
}
|
||||
return g.Where("group_belong = ?", group)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostRepo) Create(host *model.Host) error {
|
||||
return global.DB.Create(host).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Host{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.Host{}).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) GetFirewallRecord(opts ...DBOption) (model.Firewall, error) {
|
||||
var firewall model.Firewall
|
||||
db := global.DB
|
||||
|
@ -31,6 +31,8 @@ type IWebsiteRepo interface {
|
||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||
Create(ctx context.Context, app *model.Website) error
|
||||
DeleteAll(ctx context.Context) error
|
||||
|
||||
UpdateGroup(group, newGroup uint) error
|
||||
}
|
||||
|
||||
func NewIWebsiteRepo() IWebsiteRepo {
|
||||
@ -158,3 +160,7 @@ func (w *WebsiteRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||
func (w *WebsiteRepo) DeleteAll(ctx context.Context) error {
|
||||
return getTx(ctx).Where("1 = 1 ").Delete(&model.Website{}).Error
|
||||
}
|
||||
|
||||
func (w *WebsiteRepo) UpdateGroup(group, newGroup uint) error {
|
||||
return global.DB.Model(&model.Website{}).Where("website_group_id = ?", group).Updates(map[string]interface{}{"website_group_id": newGroup}).Error
|
||||
}
|
||||
|
@ -3,17 +3,20 @@ package service
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage"
|
||||
@ -26,6 +29,8 @@ import (
|
||||
type BackupService struct{}
|
||||
|
||||
type IBackupService interface {
|
||||
CheckUsed(id uint) error
|
||||
|
||||
SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)
|
||||
SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error)
|
||||
DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
@ -97,6 +102,22 @@ func (u *BackupService) SearchRecordsByCronjobWithPage(search dto.RecordSearchBy
|
||||
return total, datas, err
|
||||
}
|
||||
|
||||
func (u *BackupService) CheckUsed(id uint) error {
|
||||
cronjobs, _ := cronjobRepo.List()
|
||||
for _, job := range cronjobs {
|
||||
if job.DownloadAccountID == id {
|
||||
return buserr.New(constant.ErrBackupInUsed)
|
||||
}
|
||||
ids := strings.Split(job.SourceAccountIDs, ",")
|
||||
for _, idItem := range ids {
|
||||
if idItem == fmt.Sprintf("%v", id) {
|
||||
return buserr.New(constant.ErrBackupInUsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type loadSizeHelper struct {
|
||||
isOk bool
|
||||
backupPath string
|
||||
@ -309,7 +330,15 @@ func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
|
||||
accounts[i].Credential, _ = encrypt.StringDecryptWithKey(accounts[i].Credential, setting.Value)
|
||||
}
|
||||
} else {
|
||||
bodyItem, err := json.Marshal(ids)
|
||||
var idItems []uint
|
||||
for i := 0; i < len(ids); i++ {
|
||||
item, _ := strconv.Atoi(ids[i])
|
||||
idItems = append(idItems, uint(item))
|
||||
}
|
||||
operateByIDs := struct {
|
||||
IDs []uint `json:"ids"`
|
||||
}{IDs: idItems}
|
||||
bodyItem, err := json.Marshal(operateByIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -327,6 +356,18 @@ func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
|
||||
}
|
||||
clientMap := make(map[string]backupClientHelper)
|
||||
for _, item := range accounts {
|
||||
if !global.IsMaster {
|
||||
accessItem, err := base64.StdEncoding.DecodeString(item.AccessKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.AccessKey = string(accessItem)
|
||||
secretItem, err := base64.StdEncoding.DecodeString(item.Credential)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.Credential = string(secretItem)
|
||||
}
|
||||
backClient, err := newClient(&item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1,183 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type CommandService struct{}
|
||||
|
||||
type ICommandService interface {
|
||||
List() ([]dto.CommandInfo, error)
|
||||
SearchForTree() ([]dto.CommandTree, error)
|
||||
SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error)
|
||||
Create(commandDto dto.CommandOperate) error
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
Delete(ids []uint) error
|
||||
|
||||
SearchRedisCommandWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||
ListRedisCommand() ([]dto.RedisCommand, error)
|
||||
SaveRedisCommand(commandDto dto.RedisCommand) error
|
||||
DeleteRedisCommand(ids []uint) error
|
||||
}
|
||||
|
||||
func NewICommandService() ICommandService {
|
||||
return &CommandService{}
|
||||
}
|
||||
|
||||
func (u *CommandService) List() ([]dto.CommandInfo, error) {
|
||||
commands, err := commandRepo.GetList(commonRepo.WithOrderBy("name"))
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
var dtoCommands []dto.CommandInfo
|
||||
for _, command := range commands {
|
||||
var item dto.CommandInfo
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoCommands = append(dtoCommands, item)
|
||||
}
|
||||
return dtoCommands, err
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) {
|
||||
cmdList, err := commandRepo.GetList(commonRepo.WithOrderBy("name"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groups, err := groupRepo.GetList(commonRepo.WithByType("command"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var lists []dto.CommandTree
|
||||
for _, group := range groups {
|
||||
var data dto.CommandTree
|
||||
data.ID = group.ID + 10000
|
||||
data.Label = group.Name
|
||||
for _, cmd := range cmdList {
|
||||
if cmd.GroupID == group.ID {
|
||||
data.Children = append(data.Children, dto.CommandInfo{ID: cmd.ID, Name: cmd.Name, Command: cmd.Command})
|
||||
}
|
||||
}
|
||||
if len(data.Children) != 0 {
|
||||
lists = append(lists, data)
|
||||
}
|
||||
}
|
||||
return lists, err
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error) {
|
||||
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commandRepo.WithLikeName(search.Name), commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
groups, _ := groupRepo.GetList(commonRepo.WithByType("command"), commonRepo.WithOrderBy("name"))
|
||||
var dtoCommands []dto.CommandInfo
|
||||
for _, command := range commands {
|
||||
var item dto.CommandInfo
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
for _, group := range groups {
|
||||
if command.GroupID == group.ID {
|
||||
item.GroupBelong = group.Name
|
||||
item.GroupID = group.ID
|
||||
}
|
||||
}
|
||||
dtoCommands = append(dtoCommands, item)
|
||||
}
|
||||
return total, dtoCommands, err
|
||||
}
|
||||
|
||||
func (u *CommandService) Create(commandDto dto.CommandOperate) error {
|
||||
command, _ := commandRepo.Get(commonRepo.WithByName(commandDto.Name))
|
||||
if command.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
if err := copier.Copy(&command, &commandDto); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if err := commandRepo.Create(&command); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CommandService) Delete(ids []uint) error {
|
||||
if len(ids) == 1 {
|
||||
command, _ := commandRepo.Get(commonRepo.WithByID(ids[0]))
|
||||
if command.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
return commandRepo.Delete(commonRepo.WithByID(ids[0]))
|
||||
}
|
||||
return commandRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||
}
|
||||
|
||||
func (u *CommandService) Update(id uint, upMap map[string]interface{}) error {
|
||||
return commandRepo.Update(id, upMap)
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchRedisCommandWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
|
||||
total, commands, err := commandRepo.PageRedis(search.Page, search.PageSize, commandRepo.WithLikeName(search.Info))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var dtoCommands []dto.RedisCommand
|
||||
for _, command := range commands {
|
||||
var item dto.RedisCommand
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoCommands = append(dtoCommands, item)
|
||||
}
|
||||
return total, dtoCommands, err
|
||||
}
|
||||
|
||||
func (u *CommandService) ListRedisCommand() ([]dto.RedisCommand, error) {
|
||||
commands, err := commandRepo.GetRedisList()
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
var dtoCommands []dto.RedisCommand
|
||||
for _, command := range commands {
|
||||
var item dto.RedisCommand
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoCommands = append(dtoCommands, item)
|
||||
}
|
||||
return dtoCommands, err
|
||||
}
|
||||
|
||||
func (u *CommandService) SaveRedisCommand(req dto.RedisCommand) error {
|
||||
if req.ID == 0 {
|
||||
command, _ := commandRepo.GetRedis(commonRepo.WithByName(req.Name))
|
||||
if command.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
}
|
||||
var command model.RedisCommand
|
||||
if err := copier.Copy(&command, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if err := commandRepo.SaveRedis(&command); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CommandService) DeleteRedisCommand(ids []uint) error {
|
||||
if len(ids) == 1 {
|
||||
command, _ := commandRepo.GetRedis(commonRepo.WithByID(ids[0]))
|
||||
if command.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
return commandRepo.DeleteRedis(commonRepo.WithByID(ids[0]))
|
||||
}
|
||||
return commandRepo.DeleteRedis(commonRepo.WithIdsIn(ids))
|
||||
}
|
@ -22,8 +22,6 @@ var (
|
||||
cronjobRepo = repo.NewICronjobRepo()
|
||||
|
||||
hostRepo = repo.NewIHostRepo()
|
||||
groupRepo = repo.NewIGroupRepo()
|
||||
commandRepo = repo.NewICommandRepo()
|
||||
ftpRepo = repo.NewIFtpRepo()
|
||||
clamRepo = repo.NewIClamRepo()
|
||||
monitorRepo = repo.NewIMonitorRepo()
|
||||
|
@ -74,7 +74,7 @@ func (u *FtpService) Operate(operation string) error {
|
||||
}
|
||||
|
||||
func (f *FtpService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||
total, lists, err := ftpRepo.Page(req.Page, req.PageSize, ftpRepo.WithByUser(req.Info), commonRepo.WithOrderBy("created_at desc"))
|
||||
total, lists, err := ftpRepo.Page(req.Page, req.PageSize, ftpRepo.WithLikeUser(req.Info), commonRepo.WithOrderBy("created_at desc"))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@ -142,7 +142,7 @@ func (f *FtpService) Create(req dto.FtpCreate) (uint, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
userInDB, _ := ftpRepo.Get(hostRepo.WithByUser(req.User))
|
||||
userInDB, _ := ftpRepo.Get(ftpRepo.WithByUser(req.User))
|
||||
if userInDB.ID != 0 {
|
||||
return 0, constant.ErrRecordExist
|
||||
}
|
||||
|
@ -181,10 +181,6 @@ func (u *SSHService) Update(req dto.SSHUpdate) error {
|
||||
if err := NewIFirewallService().UpdatePortRule(ruleItem); err != nil {
|
||||
global.LOG.Errorf("reset firewall rules %s -> %s failed, err: %v", req.OldValue, req.NewValue, err)
|
||||
}
|
||||
|
||||
if err = NewIHostService().Update(1, map[string]interface{}{"port": req.NewValue}); err != nil {
|
||||
global.LOG.Errorf("reset host port %s -> %s failed, err: %v", req.OldValue, req.NewValue, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = cmd.Execf("%s systemctl restart %s", sudo, serviceName)
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
@ -20,6 +19,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/jinzhu/copier"
|
||||
|
||||
@ -114,6 +115,8 @@ type IWebsiteService interface {
|
||||
DeleteLoadBalance(req request.WebsiteLBDelete) error
|
||||
UpdateLoadBalance(req request.WebsiteLBUpdate) error
|
||||
UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) error
|
||||
|
||||
ChangeGroup(group, newGroup uint) error
|
||||
}
|
||||
|
||||
func NewIWebsiteService() IWebsiteService {
|
||||
@ -3193,3 +3196,7 @@ func (w WebsiteService) UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) e
|
||||
}()
|
||||
return opNginx(nginxInstall.ContainerName, constant.NginxReload)
|
||||
}
|
||||
|
||||
func (w WebsiteService) ChangeGroup(group, newGroup uint) error {
|
||||
return websiteRepo.UpdateGroup(group, newGroup)
|
||||
}
|
||||
|
@ -11,10 +11,8 @@ func Init() {
|
||||
m := gormigrate.New(global.DB, gormigrate.DefaultOptions, []*gormigrate.Migration{
|
||||
migrations.AddTable,
|
||||
migrations.AddMonitorTable,
|
||||
migrations.InitHost,
|
||||
migrations.InitSetting,
|
||||
migrations.InitImageRepo,
|
||||
migrations.InitDefaultGroup,
|
||||
migrations.InitDefaultCA,
|
||||
migrations.InitPHPExtensions,
|
||||
migrations.AddTask,
|
||||
|
@ -28,7 +28,6 @@ var AddTable = &gormigrate.Migration{
|
||||
&model.App{},
|
||||
&model.BackupRecord{},
|
||||
&model.Clam{},
|
||||
&model.Command{},
|
||||
&model.ComposeTemplate{},
|
||||
&model.Compose{},
|
||||
&model.Cronjob{},
|
||||
@ -39,15 +38,12 @@ var AddTable = &gormigrate.Migration{
|
||||
&model.Forward{},
|
||||
&model.Firewall{},
|
||||
&model.Ftp{},
|
||||
&model.Group{},
|
||||
&model.Host{},
|
||||
&model.ImageRepo{},
|
||||
&model.JobRecords{},
|
||||
&model.MonitorBase{},
|
||||
&model.MonitorIO{},
|
||||
&model.MonitorNetwork{},
|
||||
&model.PHPExtensions{},
|
||||
&model.RedisCommand{},
|
||||
&model.Runtime{},
|
||||
&model.Setting{},
|
||||
&model.Snapshot{},
|
||||
@ -74,25 +70,6 @@ var AddMonitorTable = &gormigrate.Migration{
|
||||
},
|
||||
}
|
||||
|
||||
var InitHost = &gormigrate.Migration{
|
||||
ID: "20240722-init-host",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
group := model.Group{
|
||||
Name: "default", Type: "host", IsDefault: true,
|
||||
}
|
||||
if err := tx.Create(&group).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
host := model.Host{
|
||||
Name: "localhost", Addr: "127.0.0.1", User: "root", Port: 22, AuthMode: "password", GroupID: group.ID,
|
||||
}
|
||||
if err := tx.Create(&host).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var InitSetting = &gormigrate.Migration{
|
||||
ID: "20240722-init-setting",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
@ -228,25 +205,6 @@ var InitImageRepo = &gormigrate.Migration{
|
||||
},
|
||||
}
|
||||
|
||||
var InitDefaultGroup = &gormigrate.Migration{
|
||||
ID: "20240722-init-default-group",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
websiteGroup := &model.Group{
|
||||
Name: "默认",
|
||||
IsDefault: true,
|
||||
Type: "website",
|
||||
}
|
||||
if err := tx.Create(websiteGroup).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
commandGroup := &model.Group{IsDefault: true, Name: "默认", Type: "command"}
|
||||
if err := tx.Create(commandGroup).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var InitDefaultCA = &gormigrate.Migration{
|
||||
ID: "20240722-init-default-ca",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
|
24
agent/router/backup.go
Normal file
24
agent/router/backup.go
Normal file
@ -0,0 +1,24 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
v2 "github.com/1Panel-dev/1Panel/agent/app/api/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type BackupRouter struct{}
|
||||
|
||||
func (s *BackupRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
backupRouter := Router.Group("backups")
|
||||
baseApi := v2.ApiGroupApp.BaseApi
|
||||
{
|
||||
backupRouter.GET("/check/:id", baseApi.CheckBackupUsed)
|
||||
backupRouter.POST("/backup", baseApi.Backup)
|
||||
backupRouter.POST("/recover", baseApi.Recover)
|
||||
backupRouter.POST("/recover/byupload", baseApi.RecoverByUpload)
|
||||
backupRouter.POST("/search/files", baseApi.LoadFilesFromBackup)
|
||||
backupRouter.POST("/record/search", baseApi.SearchBackupRecords)
|
||||
backupRouter.POST("/record/search/bycronjob", baseApi.SearchBackupRecordsByCronjob)
|
||||
backupRouter.POST("/record/download", baseApi.DownloadRecord)
|
||||
backupRouter.POST("/record/del", baseApi.DeleteBackupRecord)
|
||||
}
|
||||
}
|
@ -8,12 +8,11 @@ func commonGroups() []CommonRouter {
|
||||
&LogRouter{},
|
||||
&FileRouter{},
|
||||
&ToolboxRouter{},
|
||||
&TerminalRouter{},
|
||||
&CronjobRouter{},
|
||||
&BackupRouter{},
|
||||
&SettingRouter{},
|
||||
&AppRouter{},
|
||||
&WebsiteRouter{},
|
||||
&WebsiteGroupRouter{},
|
||||
&WebsiteDnsAccountRouter{},
|
||||
&WebsiteAcmeAccountRouter{},
|
||||
&WebsiteSSLRouter{},
|
||||
|
@ -11,15 +11,6 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
hostRouter := Router.Group("hosts")
|
||||
baseApi := v2.ApiGroupApp.BaseApi
|
||||
{
|
||||
hostRouter.POST("", baseApi.CreateHost)
|
||||
hostRouter.POST("/del", baseApi.DeleteHost)
|
||||
hostRouter.POST("/update", baseApi.UpdateHost)
|
||||
hostRouter.POST("/update/group", baseApi.UpdateHostGroup)
|
||||
hostRouter.POST("/search", baseApi.SearchHost)
|
||||
hostRouter.POST("/tree", baseApi.HostTree)
|
||||
hostRouter.POST("/test/byinfo", baseApi.TestByInfo)
|
||||
hostRouter.POST("/test/byid/:id", baseApi.TestByID)
|
||||
|
||||
hostRouter.GET("/firewall/base", baseApi.LoadFirewallBaseInfo)
|
||||
hostRouter.POST("/firewall/search", baseApi.SearchFirewallRule)
|
||||
hostRouter.POST("/firewall/operate", baseApi.OperateFirewall)
|
||||
@ -47,18 +38,6 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
hostRouter.POST("/ssh/conffile/update", baseApi.UpdateSSHByfile)
|
||||
hostRouter.POST("/ssh/operate", baseApi.OperateSSH)
|
||||
|
||||
hostRouter.GET("/command", baseApi.ListCommand)
|
||||
hostRouter.POST("/command", baseApi.CreateCommand)
|
||||
hostRouter.POST("/command/del", baseApi.DeleteCommand)
|
||||
hostRouter.POST("/command/search", baseApi.SearchCommand)
|
||||
hostRouter.GET("/command/tree", baseApi.SearchCommandTree)
|
||||
hostRouter.POST("/command/update", baseApi.UpdateCommand)
|
||||
|
||||
hostRouter.GET("/command/redis", baseApi.ListRedisCommand)
|
||||
hostRouter.POST("/command/redis", baseApi.SaveRedisCommand)
|
||||
hostRouter.POST("/command/redis/search", baseApi.SearchRedisCommand)
|
||||
hostRouter.POST("/command/redis/del", baseApi.DeleteRedisCommand)
|
||||
|
||||
hostRouter.POST("/tool", baseApi.GetToolStatus)
|
||||
hostRouter.POST("/tool/init", baseApi.InitToolConfig)
|
||||
hostRouter.POST("/tool/operate", baseApi.OperateTool)
|
||||
|
@ -1,16 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
v2 "github.com/1Panel-dev/1Panel/agent/app/api/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type TerminalRouter struct{}
|
||||
|
||||
func (s *TerminalRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
terminalRouter := Router.Group("terminals")
|
||||
baseApi := v2.ApiGroupApp.BaseApi
|
||||
{
|
||||
terminalRouter.GET("", baseApi.WsSsh)
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
websiteRouter.GET("/:id", baseApi.GetWebsite)
|
||||
websiteRouter.POST("/del", baseApi.DeleteWebsite)
|
||||
websiteRouter.POST("/default/server", baseApi.ChangeDefaultServer)
|
||||
websiteRouter.POST("/group/change", baseApi.ChangeWebsiteGroup)
|
||||
|
||||
websiteRouter.GET("/domains/:websiteId", baseApi.GetWebDomains)
|
||||
websiteRouter.POST("/domains/del", baseApi.DeleteWebDomain)
|
||||
|
@ -1,9 +1,9 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
// @Param request body dto.CommandOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command [post]
|
||||
// @Router /core/commands [post]
|
||||
// @x-panel-log {"bodyKeys":["name","command"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建快捷命令 [name][command]","formatEN":"create quick command [name][command]"}
|
||||
func (b *BaseApi) CreateCommand(c *gin.Context) {
|
||||
var req dto.CommandOperate
|
||||
@ -29,28 +29,6 @@ func (b *BaseApi) CreateCommand(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Redis Command
|
||||
// @Summary Save redis command
|
||||
// @Description 保存 Redis 快速命令
|
||||
// @Accept json
|
||||
// @Param request body dto.RedisCommand true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/redis [post]
|
||||
// @x-panel-log {"bodyKeys":["name","command"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"保存 redis 快捷命令 [name][command]","formatEN":"save quick command for redis [name][command]"}
|
||||
func (b *BaseApi) SaveRedisCommand(c *gin.Context) {
|
||||
var req dto.RedisCommand
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := commandService.SaveRedisCommand(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Command
|
||||
// @Summary Page commands
|
||||
// @Description 获取快速命令列表分页
|
||||
@ -58,7 +36,7 @@ func (b *BaseApi) SaveRedisCommand(c *gin.Context) {
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/search [post]
|
||||
// @Router /core/commands/search [post]
|
||||
func (b *BaseApi) SearchCommand(c *gin.Context) {
|
||||
var req dto.SearchCommandWithPage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@ -77,57 +55,21 @@ func (b *BaseApi) SearchCommand(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Redis Command
|
||||
// @Summary Page redis commands
|
||||
// @Description 获取 redis 快速命令列表分页
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Success 200 {object} dto.PageResult
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/redis/search [post]
|
||||
func (b *BaseApi) SearchRedisCommand(c *gin.Context) {
|
||||
var req dto.SearchWithPage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := commandService.SearchRedisCommandWithPage(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Command
|
||||
// @Summary Tree commands
|
||||
// @Description 获取快速命令树
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByType true "request"
|
||||
// @Success 200 {Array} dto.CommandTree
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/tree [get]
|
||||
// @Router /core/commands/tree [get]
|
||||
func (b *BaseApi) SearchCommandTree(c *gin.Context) {
|
||||
list, err := commandService.SearchForTree()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
var req dto.OperateByType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Redis Command
|
||||
// @Summary List redis commands
|
||||
// @Description 获取 redis 快速命令列表
|
||||
// @Success 200 {Array} dto.RedisCommand
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/redis [get]
|
||||
func (b *BaseApi) ListRedisCommand(c *gin.Context) {
|
||||
list, err := commandService.ListRedisCommand()
|
||||
list, err := commandService.SearchForTree(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@ -139,11 +81,18 @@ func (b *BaseApi) ListRedisCommand(c *gin.Context) {
|
||||
// @Tags Command
|
||||
// @Summary List commands
|
||||
// @Description 获取快速命令列表
|
||||
// @Accept json
|
||||
// @Param request body dto.OperateByType true "request"
|
||||
// @Success 200 {object} dto.CommandInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command [get]
|
||||
// @Router /core/commands/command [get]
|
||||
func (b *BaseApi) ListCommand(c *gin.Context) {
|
||||
list, err := commandService.List()
|
||||
var req dto.OperateByType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
list, err := commandService.List(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
@ -156,40 +105,18 @@ func (b *BaseApi) ListCommand(c *gin.Context) {
|
||||
// @Summary Delete command
|
||||
// @Description 删除快速命令
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchDeleteReq true "request"
|
||||
// @Param request body dto.OperateByIDs true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/del [post]
|
||||
// @Router /core/commands/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"commands","output_column":"name","output_value":"names"}],"formatZH":"删除快捷命令 [names]","formatEN":"delete quick command [names]"}
|
||||
func (b *BaseApi) DeleteCommand(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
var req dto.OperateByIDs
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := commandService.Delete(req.Ids); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Redis Command
|
||||
// @Summary Delete redis command
|
||||
// @Description 删除 redis 快速命令
|
||||
// @Accept json
|
||||
// @Param request body dto.BatchDeleteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/redis/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"redis_commands","output_column":"name","output_value":"names"}],"formatZH":"删除 redis 快捷命令 [names]","formatEN":"delete quick command of redis [names]"}
|
||||
func (b *BaseApi) DeleteRedisCommand(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := commandService.DeleteRedisCommand(req.Ids); err != nil {
|
||||
if err := commandService.Delete(req.IDs); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
@ -203,7 +130,7 @@ func (b *BaseApi) DeleteRedisCommand(c *gin.Context) {
|
||||
// @Param request body dto.CommandOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/command/update [post]
|
||||
// @Router /core/commands/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新快捷命令 [name]","formatEN":"update quick command [name]"}
|
||||
func (b *BaseApi) UpdateCommand(c *gin.Context) {
|
||||
var req dto.CommandOperate
|
@ -9,9 +9,12 @@ type ApiGroup struct {
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
|
||||
var (
|
||||
hostService = service.NewIHostService()
|
||||
authService = service.NewIAuthService()
|
||||
backupService = service.NewIBackupService()
|
||||
settingService = service.NewISettingService()
|
||||
logService = service.NewILogService()
|
||||
upgradeService = service.NewIUpgradeService()
|
||||
groupService = service.NewIGroupService()
|
||||
commandService = service.NewICommandService()
|
||||
)
|
||||
|
@ -1,9 +1,9 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
// @Param request body dto.GroupCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /groups [post]
|
||||
// @Router /core/groups [post]
|
||||
// @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建组 [name][type]","formatEN":"create group [name][type]"}
|
||||
func (b *BaseApi) CreateGroup(c *gin.Context) {
|
||||
var req dto.GroupCreate
|
||||
@ -36,7 +36,7 @@ func (b *BaseApi) CreateGroup(c *gin.Context) {
|
||||
// @Param request body dto.OperateByID true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /groups/del [post]
|
||||
// @Router /core/groups/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"name","output_value":"name"},{"input_column":"id","input_value":"id","isList":false,"db":"groups","output_column":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"}
|
||||
func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
@ -58,7 +58,7 @@ func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
||||
// @Param request body dto.GroupUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /groups/update [post]
|
||||
// @Router /core/groups/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新组 [name][type]","formatEN":"update group [name][type]"}
|
||||
func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
||||
var req dto.GroupUpdate
|
||||
@ -78,11 +78,11 @@ func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
||||
// @Description 查询系统组
|
||||
// @Accept json
|
||||
// @Param request body dto.GroupSearch true "request"
|
||||
// @Success 200 {array} dto.GroupInfo
|
||||
// @Success 200 {array} dto.OperateByType
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /groups/search [post]
|
||||
// @Router /core/groups/search [post]
|
||||
func (b *BaseApi) ListGroup(c *gin.Context) {
|
||||
var req dto.GroupSearch
|
||||
var req dto.OperateByType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
@ -1,11 +1,23 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/copier"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ssh"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/terminal"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// @Tags Host
|
||||
@ -15,7 +27,7 @@ import (
|
||||
// @Param request body dto.HostOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts [post]
|
||||
// @Router /core/hosts [post]
|
||||
// @x-panel-log {"bodyKeys":["name","addr"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建主机 [name][addr]","formatEN":"create host [name][addr]"}
|
||||
func (b *BaseApi) CreateHost(c *gin.Context) {
|
||||
var req dto.HostOperate
|
||||
@ -38,7 +50,7 @@ func (b *BaseApi) CreateHost(c *gin.Context) {
|
||||
// @Param request body dto.HostConnTest true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/test/byinfo [post]
|
||||
// @Router /core/hosts/test/byinfo [post]
|
||||
func (b *BaseApi) TestByInfo(c *gin.Context) {
|
||||
var req dto.HostConnTest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@ -56,15 +68,20 @@ func (b *BaseApi) TestByInfo(c *gin.Context) {
|
||||
// @Param id path integer true "request"
|
||||
// @Success 200 {boolean} connStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/test/byid/:id [post]
|
||||
// @Router /core/hosts/test/byid/:id [post]
|
||||
func (b *BaseApi) TestByID(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
idParam, ok := c.Params.Get("id")
|
||||
if !ok {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("no such params find in request"))
|
||||
return
|
||||
}
|
||||
intNum, err := strconv.Atoi(idParam)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
connStatus := hostService.TestLocalConn(id)
|
||||
connStatus := hostService.TestLocalConn(uint(intNum))
|
||||
helper.SuccessWithData(c, connStatus)
|
||||
}
|
||||
|
||||
@ -75,10 +92,10 @@ func (b *BaseApi) TestByID(c *gin.Context) {
|
||||
// @Param request body dto.SearchForTree true "request"
|
||||
// @Success 200 {array} dto.HostTree
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/tree [post]
|
||||
// @Router /core/hosts/tree [post]
|
||||
func (b *BaseApi) HostTree(c *gin.Context) {
|
||||
var req dto.SearchForTree
|
||||
if err := helper.CheckBind(&req, c); err != nil {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -98,7 +115,7 @@ func (b *BaseApi) HostTree(c *gin.Context) {
|
||||
// @Param request body dto.SearchHostWithPage true "request"
|
||||
// @Success 200 {array} dto.HostTree
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/search [post]
|
||||
// @Router /core/hosts/search [post]
|
||||
func (b *BaseApi) SearchHost(c *gin.Context) {
|
||||
var req dto.SearchHostWithPage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@ -124,15 +141,15 @@ func (b *BaseApi) SearchHost(c *gin.Context) {
|
||||
// @Param request body dto.BatchDeleteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/del [post]
|
||||
// @Router /core/hosts/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"hosts","output_column":"addr","output_value":"addrs"}],"formatZH":"删除主机 [addrs]","formatEN":"delete host [addrs]"}
|
||||
func (b *BaseApi) DeleteHost(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
var req dto.OperateByIDs
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := hostService.Delete(req.Ids); err != nil {
|
||||
if err := hostService.Delete(req.IDs); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
@ -146,7 +163,7 @@ func (b *BaseApi) DeleteHost(c *gin.Context) {
|
||||
// @Param request body dto.HostOperate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/update [post]
|
||||
// @Router /core/hosts/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name","addr"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新主机信息 [name][addr]","formatEN":"update host [name][addr]"}
|
||||
func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
var req dto.HostOperate
|
||||
@ -212,7 +229,7 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
// @Param request body dto.ChangeHostGroup true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /hosts/update/group [post]
|
||||
// @Router /core/hosts/update/group [post]
|
||||
// @x-panel-log {"bodyKeys":["id","group"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"hosts","output_column":"addr","output_value":"addr"}],"formatZH":"切换主机[addr]分组 => [group]","formatEN":"change host [addr] group => [group]"}
|
||||
func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
|
||||
var req dto.ChangeHostGroup
|
||||
@ -228,3 +245,84 @@ func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
id, err := strconv.Atoi(c.Query("id"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param id in request")) {
|
||||
return
|
||||
}
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
host, err := hostService.GetHostInfo(uint(id))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "load host info by id failed")) {
|
||||
return
|
||||
}
|
||||
var connInfo ssh.ConnInfo
|
||||
_ = copier.Copy(&connInfo, &host)
|
||||
connInfo.PrivateKey = []byte(host.PrivateKey)
|
||||
if len(host.PassPhrase) != 0 {
|
||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||
}
|
||||
|
||||
client, err := connInfo.NewClient()
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
sws, err := terminal.NewLogicSshWsSession(cols, rows, true, connInfo.Client, wsConn)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer sws.Close()
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
sws.Start(quitChan)
|
||||
go sws.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var upGrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024 * 1024 * 10,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
func wshandleError(ws *websocket.Conn, err error) bool {
|
||||
if err != nil {
|
||||
global.LOG.Errorf("handler ws faled:, err: %v", err)
|
||||
dt := time.Now().Add(time.Second)
|
||||
if ctlerr := ws.WriteControl(websocket.CloseMessage, []byte(err.Error()), dt); ctlerr != nil {
|
||||
wsData, err := json.Marshal(terminal.WsMsg{
|
||||
Type: terminal.WsMsgCmd,
|
||||
Data: base64.StdEncoding.EncodeToString([]byte(err.Error())),
|
||||
})
|
||||
if err != nil {
|
||||
_ = ws.WriteMessage(websocket.TextMessage, []byte("{\"type\":\"cmd\",\"data\":\"failed to encoding to json\"}"))
|
||||
} else {
|
||||
_ = ws.WriteMessage(websocket.TextMessage, wsData)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
38
core/app/dto/command.go
Normal file
38
core/app/dto/command.go
Normal file
@ -0,0 +1,38 @@
|
||||
package dto
|
||||
|
||||
type SearchCommandWithPage struct {
|
||||
PageInfo
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=name command created_at"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
GroupID uint `json:"groupID"`
|
||||
Type string `josn:"type" validate:"required,oneof=redis command"`
|
||||
Info string `json:"info"`
|
||||
}
|
||||
|
||||
type CommandOperate struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `josn:"type"`
|
||||
GroupID uint `json:"groupID"`
|
||||
GroupBelong string `json:"groupBelong"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
}
|
||||
|
||||
type CommandInfo struct {
|
||||
ID uint `json:"id"`
|
||||
GroupID uint `json:"groupID"`
|
||||
Name string `json:"name"`
|
||||
Command string `json:"command"`
|
||||
GroupBelong string `json:"groupBelong"`
|
||||
}
|
||||
|
||||
type CommandTree struct {
|
||||
ID uint `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Children []CommandInfo `json:"children"`
|
||||
}
|
||||
|
||||
type CommandDelete struct {
|
||||
Type string `json:"type" validate:"required,oneof=redis command"`
|
||||
IDs []uint `json:"ids"`
|
||||
}
|
@ -31,6 +31,10 @@ type Options struct {
|
||||
Option string `json:"option"`
|
||||
}
|
||||
|
||||
type OperateByType struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type OperateByID struct {
|
||||
ID uint `json:"id"`
|
||||
}
|
||||
|
@ -73,15 +73,3 @@ type TreeChild struct {
|
||||
ID uint `json:"id"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
type MonitorSetting struct {
|
||||
MonitorStatus string `json:"monitorStatus"`
|
||||
MonitorStoreDays string `json:"monitorStoreDays"`
|
||||
MonitorInterval string `json:"monitorInterval"`
|
||||
DefaultNetwork string `json:"defaultNetwork"`
|
||||
}
|
||||
|
||||
type MonitorSettingUpdate struct {
|
||||
Key string `json:"key" validate:"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval DefaultNetwork"`
|
||||
Value string `json:"value"`
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type BackupAccount struct {
|
||||
BaseModel
|
||||
Name string `gorm:"unique;not null" json:"name"`
|
||||
@ -11,6 +13,7 @@ type BackupAccount struct {
|
||||
Vars string `json:"vars"`
|
||||
|
||||
RememberAuth bool `json:"rememberAuth"`
|
||||
InUsed bool `json:"inUsed"`
|
||||
EntryID uint `json:"entryID"`
|
||||
|
||||
DeletedAt time.Time `json:"deletedAt"`
|
||||
}
|
||||
|
9
core/app/model/command.go
Normal file
9
core/app/model/command.go
Normal file
@ -0,0 +1,9 @@
|
||||
package model
|
||||
|
||||
type Command struct {
|
||||
BaseModel
|
||||
Type string `gorm:"not null" json:"type"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
GroupID uint `gorm:"not null" json:"groupID"`
|
||||
Command string `gorm:"not null" json:"command"`
|
||||
}
|
8
core/app/model/group.go
Normal file
8
core/app/model/group.go
Normal file
@ -0,0 +1,8 @@
|
||||
package model
|
||||
|
||||
type Group struct {
|
||||
BaseModel
|
||||
IsDefault bool `json:"isDefault"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -14,15 +14,10 @@ type ICommandRepo interface {
|
||||
WithByInfo(info string) DBOption
|
||||
Create(command *model.Command) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
UpdateGroup(group, newGroup uint) error
|
||||
Delete(opts ...DBOption) error
|
||||
Get(opts ...DBOption) (model.Command, error)
|
||||
WithLikeName(name string) DBOption
|
||||
|
||||
PageRedis(limit, offset int, opts ...DBOption) (int64, []model.RedisCommand, error)
|
||||
GetRedis(opts ...DBOption) (model.RedisCommand, error)
|
||||
GetRedisList(opts ...DBOption) ([]model.RedisCommand, error)
|
||||
SaveRedis(command *model.RedisCommand) error
|
||||
DeleteRedis(opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewICommandRepo() ICommandRepo {
|
||||
@ -39,16 +34,6 @@ func (u *CommandRepo) Get(opts ...DBOption) (model.Command, error) {
|
||||
return command, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) GetRedis(opts ...DBOption) (model.RedisCommand, error) {
|
||||
var command model.RedisCommand
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&command).Error
|
||||
return command, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) Page(page, size int, opts ...DBOption) (int64, []model.Command, error) {
|
||||
var users []model.Command
|
||||
db := global.DB.Model(&model.Command{})
|
||||
@ -61,18 +46,6 @@ func (u *CommandRepo) Page(page, size int, opts ...DBOption) (int64, []model.Com
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) PageRedis(page, size int, opts ...DBOption) (int64, []model.RedisCommand, error) {
|
||||
var users []model.RedisCommand
|
||||
db := global.DB.Model(&model.RedisCommand{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) GetList(opts ...DBOption) ([]model.Command, error) {
|
||||
var commands []model.Command
|
||||
db := global.DB.Model(&model.Command{})
|
||||
@ -83,16 +56,6 @@ func (u *CommandRepo) GetList(opts ...DBOption) ([]model.Command, error) {
|
||||
return commands, err
|
||||
}
|
||||
|
||||
func (u *CommandRepo) GetRedisList(opts ...DBOption) ([]model.RedisCommand, error) {
|
||||
var commands []model.RedisCommand
|
||||
db := global.DB.Model(&model.RedisCommand{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&commands).Error
|
||||
return commands, err
|
||||
}
|
||||
|
||||
func (c *CommandRepo) WithByInfo(info string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(info) == 0 {
|
||||
@ -107,13 +70,12 @@ func (u *CommandRepo) Create(command *model.Command) error {
|
||||
return global.DB.Create(command).Error
|
||||
}
|
||||
|
||||
func (u *CommandRepo) SaveRedis(command *model.RedisCommand) error {
|
||||
return global.DB.Save(command).Error
|
||||
}
|
||||
|
||||
func (u *CommandRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Command{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
func (h *CommandRepo) UpdateGroup(group, newGroup uint) error {
|
||||
return global.DB.Model(&model.Command{}).Where("group_id = ?", group).Updates(map[string]interface{}{"group_id": newGroup}).Error
|
||||
}
|
||||
|
||||
func (u *CommandRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
@ -123,14 +85,6 @@ func (u *CommandRepo) Delete(opts ...DBOption) error {
|
||||
return db.Delete(&model.Command{}).Error
|
||||
}
|
||||
|
||||
func (u *CommandRepo) DeleteRedis(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.RedisCommand{}).Error
|
||||
}
|
||||
|
||||
func (a CommandRepo) WithLikeName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(name) == 0 {
|
@ -1,6 +1,9 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -12,6 +15,8 @@ type ICommonRepo interface {
|
||||
WithByIDs(ids []uint) DBOption
|
||||
WithByType(ty string) DBOption
|
||||
WithOrderBy(orderStr string) DBOption
|
||||
|
||||
WithOrderRuleBy(orderBy, order string) DBOption
|
||||
}
|
||||
|
||||
type CommonRepo struct{}
|
||||
@ -51,3 +56,18 @@ func (c *CommonRepo) WithOrderBy(orderStr string) DBOption {
|
||||
return g.Order(orderStr)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommonRepo) WithOrderRuleBy(orderBy, order string) DBOption {
|
||||
switch order {
|
||||
case constant.OrderDesc:
|
||||
order = "desc"
|
||||
case constant.OrderAsc:
|
||||
order = "asc"
|
||||
default:
|
||||
orderBy = "created_at"
|
||||
order = "desc"
|
||||
}
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Order(fmt.Sprintf("%s %s", orderBy, order))
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -14,14 +14,42 @@ type IGroupRepo interface {
|
||||
Create(group *model.Group) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
|
||||
WithByID(id uint) DBOption
|
||||
WithByGroupID(id uint) DBOption
|
||||
WithByGroupType(ty string) DBOption
|
||||
WithByDefault(isDefalut bool) DBOption
|
||||
CancelDefault(groupType string) error
|
||||
WithByHostDefault() DBOption
|
||||
}
|
||||
|
||||
func NewIGroupRepo() IGroupRepo {
|
||||
return &GroupRepo{}
|
||||
}
|
||||
|
||||
func (c *GroupRepo) WithByID(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GroupRepo) WithByGroupID(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("group_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GroupRepo) WithByGroupType(ty string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("`type` = ?", ty)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GroupRepo) WithByDefault(isDefalut bool) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("is_default = ?", isDefalut)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *GroupRepo) Get(opts ...DBOption) (model.Group, error) {
|
||||
var group model.Group
|
||||
db := global.DB
|
||||
@ -50,12 +78,6 @@ func (u *GroupRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Group{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (u *GroupRepo) WithByHostDefault() DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("is_default = ? AND type = ?", 1, "host")
|
||||
}
|
||||
}
|
||||
|
||||
func (u *GroupRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
@ -65,5 +87,7 @@ func (u *GroupRepo) Delete(opts ...DBOption) error {
|
||||
}
|
||||
|
||||
func (u *GroupRepo) CancelDefault(groupType string) error {
|
||||
return global.DB.Model(&model.Group{}).Where("is_default = ? AND type = ?", 1, groupType).Updates(map[string]interface{}{"is_default": 0}).Error
|
||||
return global.DB.Model(&model.Group{}).
|
||||
Where("is_default = ? AND type = ?", 1, groupType).
|
||||
Updates(map[string]interface{}{"is_default": 0}).Error
|
||||
}
|
113
core/app/repo/host.go
Normal file
113
core/app/repo/host.go
Normal file
@ -0,0 +1,113 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type HostRepo struct{}
|
||||
|
||||
type IHostRepo interface {
|
||||
Get(opts ...DBOption) (model.Host, error)
|
||||
GetList(opts ...DBOption) ([]model.Host, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Host, error)
|
||||
WithByInfo(info string) DBOption
|
||||
WithByPort(port uint) DBOption
|
||||
WithByUser(user string) DBOption
|
||||
WithByAddr(addr string) DBOption
|
||||
Create(host *model.Host) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
UpdateGroup(group, newGroup uint) error
|
||||
Delete(opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIHostRepo() IHostRepo {
|
||||
return &HostRepo{}
|
||||
}
|
||||
|
||||
func (h *HostRepo) Get(opts ...DBOption) (model.Host, error) {
|
||||
var host model.Host
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&host).Error
|
||||
return host, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) GetList(opts ...DBOption) ([]model.Host, error) {
|
||||
var hosts []model.Host
|
||||
db := global.DB.Model(&model.Host{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&hosts).Error
|
||||
return hosts, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) Page(page, size int, opts ...DBOption) (int64, []model.Host, error) {
|
||||
var users []model.Host
|
||||
db := global.DB.Model(&model.Host{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) WithByInfo(info string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(info) == 0 {
|
||||
return g
|
||||
}
|
||||
infoStr := "%" + info + "%"
|
||||
return g.Where("name LIKE ? OR addr LIKE ?", infoStr, infoStr)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostRepo) WithByPort(port uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("port = ?", port)
|
||||
}
|
||||
}
|
||||
func (h *HostRepo) WithByUser(user string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("user = ?", user)
|
||||
}
|
||||
}
|
||||
func (h *HostRepo) WithByAddr(addr string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("addr = ?", addr)
|
||||
}
|
||||
}
|
||||
func (h *HostRepo) WithByGroup(group string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(group) == 0 {
|
||||
return g
|
||||
}
|
||||
return g.Where("group_belong = ?", group)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostRepo) Create(host *model.Host) error {
|
||||
return global.DB.Create(host).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Host{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) UpdateGroup(group, newGroup uint) error {
|
||||
return global.DB.Model(&model.Host{}).Where("group_id = ?", group).Updates(map[string]interface{}{"group_id": newGroup}).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.Host{}).Error
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@ -19,6 +20,8 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/core/utils/cloud_storage/client"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
|
||||
fileUtils "github.com/1Panel-dev/1Panel/core/utils/files"
|
||||
httpUtils "github.com/1Panel-dev/1Panel/core/utils/http"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/robfig/cron/v3"
|
||||
@ -271,12 +274,17 @@ func (u *BackupService) Delete(id uint) error {
|
||||
if backup.Type == constant.Local {
|
||||
return buserr.New(constant.ErrBackupLocalDelete)
|
||||
}
|
||||
if backup.InUsed {
|
||||
return buserr.New(constant.ErrBackupInUsed)
|
||||
}
|
||||
if backup.Type == constant.OneDrive {
|
||||
global.Cron.Remove(cron.EntryID(backup.EntryID))
|
||||
}
|
||||
if _, err := httpUtils.NewLocalClient(fmt.Sprintf("/api/v2/backups/check/%v", id), http.MethodGet, nil); err != nil {
|
||||
global.LOG.Errorf("check used of local cronjob failed, err: %v", err)
|
||||
return buserr.New(constant.ErrBackupInUsed)
|
||||
}
|
||||
if err := xpack.CheckBackupUsed(id); err != nil {
|
||||
global.LOG.Errorf("check used of node cronjob failed, err: %v", err)
|
||||
return buserr.New(constant.ErrBackupInUsed)
|
||||
}
|
||||
|
||||
return backupRepo.Delete(commonRepo.WithByID(id))
|
||||
}
|
||||
|
128
core/app/service/command.go
Normal file
128
core/app/service/command.go
Normal file
@ -0,0 +1,128 @@
|
||||
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/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type CommandService struct{}
|
||||
|
||||
type ICommandService interface {
|
||||
List(req dto.OperateByType) ([]dto.CommandInfo, error)
|
||||
SearchForTree(req dto.OperateByType) ([]dto.CommandTree, error)
|
||||
SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error)
|
||||
Create(commandDto dto.CommandOperate) error
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
Delete(ids []uint) error
|
||||
}
|
||||
|
||||
func NewICommandService() ICommandService {
|
||||
return &CommandService{}
|
||||
}
|
||||
|
||||
func (u *CommandService) List(req dto.OperateByType) ([]dto.CommandInfo, error) {
|
||||
commands, err := commandRepo.GetList(commonRepo.WithOrderBy("name"), commonRepo.WithByType(req.Type))
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
var dtoCommands []dto.CommandInfo
|
||||
for _, command := range commands {
|
||||
var item dto.CommandInfo
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoCommands = append(dtoCommands, item)
|
||||
}
|
||||
return dtoCommands, err
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchForTree(req dto.OperateByType) ([]dto.CommandTree, error) {
|
||||
cmdList, err := commandRepo.GetList(commonRepo.WithOrderBy("name"), commonRepo.WithByType(req.Type))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groups, err := groupRepo.GetList(commonRepo.WithByType(req.Type))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var lists []dto.CommandTree
|
||||
for _, group := range groups {
|
||||
var data dto.CommandTree
|
||||
data.ID = group.ID + 10000
|
||||
data.Label = group.Name
|
||||
for _, cmd := range cmdList {
|
||||
if cmd.GroupID == group.ID {
|
||||
data.Children = append(data.Children, dto.CommandInfo{ID: cmd.ID, Name: cmd.Name, Command: cmd.Command})
|
||||
}
|
||||
}
|
||||
if len(data.Children) != 0 {
|
||||
lists = append(lists, data)
|
||||
}
|
||||
}
|
||||
return lists, err
|
||||
}
|
||||
|
||||
func (u *CommandService) SearchWithPage(req dto.SearchCommandWithPage) (int64, interface{}, error) {
|
||||
options := []repo.DBOption{
|
||||
commonRepo.WithOrderRuleBy(req.OrderBy, req.Order),
|
||||
commonRepo.WithByType(req.Type),
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
options = append(options, commandRepo.WithLikeName(req.Info))
|
||||
}
|
||||
if req.GroupID != 0 {
|
||||
options = append(options, groupRepo.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))
|
||||
var dtoCommands []dto.CommandInfo
|
||||
for _, command := range commands {
|
||||
var item dto.CommandInfo
|
||||
if err := copier.Copy(&item, &command); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
for _, group := range groups {
|
||||
if command.GroupID == group.ID {
|
||||
item.GroupBelong = group.Name
|
||||
item.GroupID = group.ID
|
||||
}
|
||||
}
|
||||
dtoCommands = append(dtoCommands, item)
|
||||
}
|
||||
return total, dtoCommands, err
|
||||
}
|
||||
|
||||
func (u *CommandService) Create(commandDto dto.CommandOperate) error {
|
||||
command, _ := commandRepo.Get(commonRepo.WithByName(commandDto.Name))
|
||||
if command.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
if err := copier.Copy(&command, &commandDto); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
if err := commandRepo.Create(&command); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *CommandService) Delete(ids []uint) error {
|
||||
if len(ids) == 1 {
|
||||
command, _ := commandRepo.Get(commonRepo.WithByID(ids[0]))
|
||||
if command.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
return commandRepo.Delete(commonRepo.WithByID(ids[0]))
|
||||
}
|
||||
return commandRepo.Delete(commonRepo.WithByIDs(ids))
|
||||
}
|
||||
|
||||
func (u *CommandService) Update(id uint, upMap map[string]interface{}) error {
|
||||
return commandRepo.Update(id, upMap)
|
||||
}
|
@ -3,8 +3,11 @@ package service
|
||||
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()
|
||||
groupRepo = repo.NewIGroupRepo()
|
||||
)
|
||||
|
@ -1,9 +1,17 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"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"
|
||||
httpUtils "github.com/1Panel-dev/1Panel/core/utils/http"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -11,7 +19,7 @@ import (
|
||||
type GroupService struct{}
|
||||
|
||||
type IGroupService interface {
|
||||
List(req dto.GroupSearch) ([]dto.GroupInfo, error)
|
||||
List(req dto.OperateByType) ([]dto.GroupInfo, error)
|
||||
Create(req dto.GroupCreate) error
|
||||
Update(req dto.GroupUpdate) error
|
||||
Delete(id uint) error
|
||||
@ -21,8 +29,17 @@ func NewIGroupService() IGroupService {
|
||||
return &GroupService{}
|
||||
}
|
||||
|
||||
func (u *GroupService) List(req dto.GroupSearch) ([]dto.GroupInfo, error) {
|
||||
groups, err := groupRepo.GetList(commonRepo.WithByType(req.Type), commonRepo.WithOrderBy("is_default desc"), commonRepo.WithOrderBy("created_at desc"))
|
||||
func (u *GroupService) List(req dto.OperateByType) ([]dto.GroupInfo, error) {
|
||||
options := []repo.DBOption{
|
||||
commonRepo.WithByType(req.Type),
|
||||
commonRepo.WithOrderBy("is_default desc"),
|
||||
commonRepo.WithOrderBy("created_at desc"),
|
||||
}
|
||||
var (
|
||||
groups []model.Group
|
||||
err error
|
||||
)
|
||||
groups, err = groupRepo.GetList(options...)
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
@ -56,22 +73,33 @@ func (u *GroupService) Delete(id uint) error {
|
||||
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))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch group.Type {
|
||||
case "website":
|
||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithGroupID(id))
|
||||
if len(websites) > 0 {
|
||||
return buserr.New(constant.ErrGroupIsUsed)
|
||||
}
|
||||
case "command":
|
||||
commands, _ := commandRepo.GetList(commonRepo.WithByGroupID(id))
|
||||
if len(commands) > 0 {
|
||||
return buserr.New(constant.ErrGroupIsUsed)
|
||||
}
|
||||
case "host":
|
||||
hosts, _ := hostRepo.GetList(commonRepo.WithByGroupID(id))
|
||||
if len(hosts) > 0 {
|
||||
return buserr.New(constant.ErrGroupIsUsed)
|
||||
err = hostRepo.UpdateGroup(id, defaultGroup.ID)
|
||||
case "command":
|
||||
err = commandRepo.UpdateGroup(id, defaultGroup.ID)
|
||||
case "node":
|
||||
err = xpack.UpdateGroup("node", id, defaultGroup.ID)
|
||||
case "website":
|
||||
bodyItem := []byte(fmt.Sprintf(`{"Group":%v, "NewGroup":%v}`, id, defaultGroup.ID))
|
||||
if _, err := httpUtils.NewLocalClient("/api/v2/websites/group/change", http.MethodPost, bytes.NewReader(bodyItem)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xpack.UpdateGroup("node", id, defaultGroup.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return constant.ErrNotSupportType
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return groupRepo.Delete(commonRepo.WithByID(id))
|
||||
}
|
@ -4,11 +4,12 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
||||
"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/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ssh"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -149,8 +150,15 @@ func (u *HostService) GetHostInfo(id uint) (*model.Host, error) {
|
||||
return &host, err
|
||||
}
|
||||
|
||||
func (u *HostService) SearchWithPage(search dto.SearchHostWithPage) (int64, interface{}, error) {
|
||||
total, hosts, err := hostRepo.Page(search.Page, search.PageSize, hostRepo.WithByInfo(search.Info), commonRepo.WithByGroupID(search.GroupID))
|
||||
func (u *HostService) SearchWithPage(req dto.SearchHostWithPage) (int64, interface{}, error) {
|
||||
var options []repo.DBOption
|
||||
if len(req.Info) != 0 {
|
||||
options = append(options, commandRepo.WithLikeName(req.Info))
|
||||
}
|
||||
if req.GroupID != 0 {
|
||||
options = append(options, groupRepo.WithByGroupID(req.GroupID))
|
||||
}
|
||||
total, hosts, err := hostRepo.Page(req.Page, req.PageSize, options...)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@ -249,7 +257,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(groupRepo.WithByHostDefault())
|
||||
group, err := groupRepo.Get(commonRepo.WithByType("host"), groupRepo.WithByDefault(true))
|
||||
if err != nil {
|
||||
return nil, errors.New("get default group failed")
|
||||
}
|
||||
@ -299,7 +307,7 @@ func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
|
||||
}
|
||||
|
||||
func (u *HostService) Delete(ids []uint) error {
|
||||
hosts, _ := hostRepo.GetList(commonRepo.WithIdsIn(ids))
|
||||
hosts, _ := hostRepo.GetList(commonRepo.WithByIDs(ids))
|
||||
for _, host := range hosts {
|
||||
if host.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
@ -308,7 +316,7 @@ func (u *HostService) Delete(ids []uint) error {
|
||||
return errors.New("the local connection information cannot be deleted!")
|
||||
}
|
||||
}
|
||||
return hostRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||
return hostRepo.Delete(commonRepo.WithByIDs(ids))
|
||||
}
|
||||
|
||||
func (u *HostService) Update(id uint, upMap map[string]interface{}) error {
|
@ -37,6 +37,7 @@ var (
|
||||
ErrPortInUsed = "ErrPortInUsed"
|
||||
ErrCmdTimeout = "ErrCmdTimeout"
|
||||
ErrGroupIsUsed = "ErrGroupIsUsed"
|
||||
ErrGroupIsDefault = "ErrGroupIsDefault"
|
||||
)
|
||||
|
||||
// api
|
||||
|
@ -14,6 +14,7 @@ ErrProxy: "请求错误,请检查该节点状态: {{ .detail }}"
|
||||
ErrDemoEnvironment: "演示服务器,禁止此操作!"
|
||||
ErrCmdTimeout: "命令执行超时!"
|
||||
ErrEntrance: "安全入口信息错误,请检查后重试!"
|
||||
ErrGroupIsDefault: '默认分组,无法删除'
|
||||
ErrGroupIsUsed: "分组正在使用中,无法删除"
|
||||
ErrLocalDelete: "无法删除本地节点!"
|
||||
ErrMasterAddr: "当前未设置主节点地址,无法添加节点!"
|
||||
|
@ -13,6 +13,7 @@ func Init() {
|
||||
migrations.InitSetting,
|
||||
migrations.InitOneDrive,
|
||||
migrations.InitMasterAddr,
|
||||
migrations.InitHost,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
@ -15,13 +15,16 @@ import (
|
||||
)
|
||||
|
||||
var AddTable = &gormigrate.Migration{
|
||||
ID: "20240808-add-table",
|
||||
ID: "20240819-add-table",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
&model.OperationLog{},
|
||||
&model.LoginLog{},
|
||||
&model.Setting{},
|
||||
&model.BackupAccount{},
|
||||
&model.Group{},
|
||||
&model.Host{},
|
||||
&model.Command{},
|
||||
)
|
||||
},
|
||||
}
|
||||
@ -144,6 +147,35 @@ var InitSetting = &gormigrate.Migration{
|
||||
},
|
||||
}
|
||||
|
||||
var InitHost = &gormigrate.Migration{
|
||||
ID: "20240816-init-host",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
hostGroup := &model.Group{Name: "default", Type: "host", IsDefault: true}
|
||||
if err := tx.Create(hostGroup).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Group{Name: "default", Type: "node", IsDefault: true}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Group{Name: "default", Type: "command", IsDefault: true}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Group{Name: "default", Type: "website", IsDefault: true}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Group{Name: "default", Type: "redis", IsDefault: true}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
host := model.Host{
|
||||
Name: "localhost", Addr: "127.0.0.1", User: "root", Port: 22, AuthMode: "password", GroupID: hostGroup.ID,
|
||||
}
|
||||
if err := tx.Create(&host).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var InitOneDrive = &gormigrate.Migration{
|
||||
ID: "20240808-init-one-drive",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
|
21
core/router/command.go
Normal file
21
core/router/command.go
Normal file
@ -0,0 +1,21 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
v2 "github.com/1Panel-dev/1Panel/core/app/api/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type CommandRouter struct{}
|
||||
|
||||
func (s *CommandRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
commandRouter := Router.Group("commands")
|
||||
baseApi := v2.ApiGroupApp.BaseApi
|
||||
{
|
||||
commandRouter.POST("/list", baseApi.ListCommand)
|
||||
commandRouter.POST("", baseApi.CreateCommand)
|
||||
commandRouter.POST("/del", baseApi.DeleteCommand)
|
||||
commandRouter.POST("/search", baseApi.SearchCommand)
|
||||
commandRouter.POST("/tree", baseApi.SearchCommandTree)
|
||||
commandRouter.POST("/update", baseApi.UpdateCommand)
|
||||
}
|
||||
}
|
@ -6,5 +6,8 @@ func commonGroups() []CommonRouter {
|
||||
&BackupRouter{},
|
||||
&LogRouter{},
|
||||
&SettingRouter{},
|
||||
&CommandRouter{},
|
||||
&HostRouter{},
|
||||
&GroupRouter{},
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
v2 "github.com/1Panel-dev/1Panel/agent/app/api/v2"
|
||||
v2 "github.com/1Panel-dev/1Panel/core/app/api/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type WebsiteGroupRouter struct {
|
||||
type GroupRouter struct {
|
||||
}
|
||||
|
||||
func (a *WebsiteGroupRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
func (a *GroupRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
groupRouter := Router.Group("groups")
|
||||
|
||||
baseApi := v2.ApiGroupApp.BaseApi
|
25
core/router/ro_host.go
Normal file
25
core/router/ro_host.go
Normal file
@ -0,0 +1,25 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
v2 "github.com/1Panel-dev/1Panel/core/app/api/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type HostRouter struct{}
|
||||
|
||||
func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
hostRouter := Router.Group("hosts")
|
||||
baseApi := v2.ApiGroupApp.BaseApi
|
||||
{
|
||||
hostRouter.POST("", baseApi.CreateHost)
|
||||
hostRouter.POST("/del", baseApi.DeleteHost)
|
||||
hostRouter.POST("/update", baseApi.UpdateHost)
|
||||
hostRouter.POST("/update/group", baseApi.UpdateHostGroup)
|
||||
hostRouter.POST("/search", baseApi.SearchHost)
|
||||
hostRouter.POST("/tree", baseApi.HostTree)
|
||||
hostRouter.POST("/test/byinfo", baseApi.TestByInfo)
|
||||
hostRouter.POST("/test/byid/:id", baseApi.TestByID)
|
||||
|
||||
hostRouter.GET("/terminal", baseApi.WsSsh)
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -31,7 +30,7 @@ func StringDecryptWithBase64(text string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base32.StdEncoding.EncodeToString([]byte(decryptItem)), nil
|
||||
return base64.StdEncoding.EncodeToString([]byte(decryptItem)), nil
|
||||
}
|
||||
|
||||
func StringEncrypt(text string) (string, error) {
|
||||
|
69
core/utils/http/new.go
Normal file
69
core/utils/http/new.go
Normal file
@ -0,0 +1,69 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
)
|
||||
|
||||
func NewLocalClient(reqUrl, reqMethod string, body io.Reader) (interface{}, error) {
|
||||
sockPath := "/tmp/agent.sock"
|
||||
if _, err := os.Stat(sockPath); err != nil {
|
||||
return nil, fmt.Errorf("no such agent.sock find in localhost, err: %v", err)
|
||||
}
|
||||
dialUnix := func() (conn net.Conn, err error) {
|
||||
return net.Dial("unix", sockPath)
|
||||
}
|
||||
transport := &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return dialUnix()
|
||||
},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
parsedURL, err := url.Parse("http://unix")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("handle url Parse failed, err: %v \n", err)
|
||||
}
|
||||
rURL := &url.URL{
|
||||
Scheme: "http",
|
||||
Path: reqUrl,
|
||||
Host: parsedURL.Host,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(reqMethod, rURL.String(), body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating request failed, err: %v", err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client do request failed, err: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("do request failed, err: %v", resp.Status)
|
||||
}
|
||||
bodyByte, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read resp body from request failed, err: %v", err)
|
||||
}
|
||||
var respJson dto.Response
|
||||
if err := json.Unmarshal(bodyByte, &respJson); err != nil {
|
||||
return nil, fmt.Errorf("json umarshal resp data failed, err: %v", err)
|
||||
}
|
||||
if respJson.Code != http.StatusOK {
|
||||
return nil, fmt.Errorf("do request success but handle failed, err: %v", respJson.Message)
|
||||
}
|
||||
|
||||
return respJson.Data, nil
|
||||
}
|
93
core/utils/terminal/local_cmd.go
Normal file
93
core/utils/terminal/local_cmd.go
Normal file
@ -0,0 +1,93 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/creack/pty"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultCloseSignal = syscall.SIGINT
|
||||
DefaultCloseTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
type LocalCommand struct {
|
||||
closeSignal syscall.Signal
|
||||
closeTimeout time.Duration
|
||||
|
||||
cmd *exec.Cmd
|
||||
pty *os.File
|
||||
}
|
||||
|
||||
func NewCommand(commands []string) (*LocalCommand, error) {
|
||||
cmd := exec.Command("docker", commands...)
|
||||
|
||||
pty, err := pty.Start(cmd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to start command")
|
||||
}
|
||||
|
||||
lcmd := &LocalCommand{
|
||||
closeSignal: DefaultCloseSignal,
|
||||
closeTimeout: DefaultCloseTimeout,
|
||||
|
||||
cmd: cmd,
|
||||
pty: pty,
|
||||
}
|
||||
|
||||
return lcmd, nil
|
||||
}
|
||||
|
||||
func (lcmd *LocalCommand) Read(p []byte) (n int, err error) {
|
||||
return lcmd.pty.Read(p)
|
||||
}
|
||||
|
||||
func (lcmd *LocalCommand) Write(p []byte) (n int, err error) {
|
||||
return lcmd.pty.Write(p)
|
||||
}
|
||||
|
||||
func (lcmd *LocalCommand) Close() error {
|
||||
if lcmd.cmd != nil && lcmd.cmd.Process != nil {
|
||||
_ = lcmd.cmd.Process.Kill()
|
||||
}
|
||||
_ = lcmd.pty.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lcmd *LocalCommand) ResizeTerminal(width int, height int) error {
|
||||
window := struct {
|
||||
row uint16
|
||||
col uint16
|
||||
x uint16
|
||||
y uint16
|
||||
}{
|
||||
uint16(height),
|
||||
uint16(width),
|
||||
0,
|
||||
0,
|
||||
}
|
||||
_, _, errno := syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
lcmd.pty.Fd(),
|
||||
syscall.TIOCSWINSZ,
|
||||
uintptr(unsafe.Pointer(&window)),
|
||||
)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (lcmd *LocalCommand) Wait(quitChan chan bool) {
|
||||
if err := lcmd.cmd.Wait(); err != nil {
|
||||
global.LOG.Errorf("ssh session wait failed, err: %v", err)
|
||||
setQuit(quitChan)
|
||||
}
|
||||
}
|
122
core/utils/terminal/ws_local_session.go
Normal file
122
core/utils/terminal/ws_local_session.go
Normal file
@ -0,0 +1,122 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type LocalWsSession struct {
|
||||
slave *LocalCommand
|
||||
wsConn *websocket.Conn
|
||||
|
||||
allowCtrlC bool
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewLocalWsSession(cols, rows int, wsConn *websocket.Conn, slave *LocalCommand, allowCtrlC bool) (*LocalWsSession, error) {
|
||||
if err := slave.ResizeTerminal(cols, rows); err != nil {
|
||||
global.LOG.Errorf("ssh pty change windows size failed, err: %v", err)
|
||||
}
|
||||
|
||||
return &LocalWsSession{
|
||||
slave: slave,
|
||||
wsConn: wsConn,
|
||||
|
||||
allowCtrlC: allowCtrlC,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sws *LocalWsSession) Start(quitChan chan bool) {
|
||||
go sws.handleSlaveEvent(quitChan)
|
||||
go sws.receiveWsMsg(quitChan)
|
||||
}
|
||||
|
||||
func (sws *LocalWsSession) handleSlaveEvent(exitCh chan bool) {
|
||||
defer setQuit(exitCh)
|
||||
defer global.LOG.Debug("thread of handle slave event has exited now")
|
||||
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return
|
||||
default:
|
||||
n, _ := sws.slave.Read(buffer)
|
||||
_ = sws.masterWrite(buffer[:n])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LocalWsSession) masterWrite(data []byte) error {
|
||||
sws.writeMutex.Lock()
|
||||
defer sws.writeMutex.Unlock()
|
||||
wsData, err := json.Marshal(WsMsg{
|
||||
Type: WsMsgCmd,
|
||||
Data: base64.StdEncoding.EncodeToString(data),
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to encoding to json")
|
||||
}
|
||||
err = sws.wsConn.WriteMessage(websocket.TextMessage, wsData)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to write to master")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
global.LOG.Errorf("A panic occurred during receive ws message, error message: %v", r)
|
||||
}
|
||||
}()
|
||||
wsConn := sws.wsConn
|
||||
defer setQuit(exitCh)
|
||||
defer global.LOG.Debug("thread of receive ws msg has exited now")
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return
|
||||
default:
|
||||
_, wsData, err := wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("reading webSocket message failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
msgObj := WsMsg{}
|
||||
_ = json.Unmarshal(wsData, &msgObj)
|
||||
switch msgObj.Type {
|
||||
case WsMsgResize:
|
||||
if msgObj.Cols > 0 && msgObj.Rows > 0 {
|
||||
if err := sws.slave.ResizeTerminal(msgObj.Cols, msgObj.Rows); err != nil {
|
||||
global.LOG.Errorf("ssh pty change windows size failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
case WsMsgCmd:
|
||||
decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Data)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("websock cmd string base64 decoding failed, err: %v", err)
|
||||
}
|
||||
if string(decodeBytes) != "\x03" || sws.allowCtrlC {
|
||||
sws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes)
|
||||
}
|
||||
case WsMsgHeartbeat:
|
||||
err = wsConn.WriteMessage(websocket.TextMessage, wsData)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("ssh sending heartbeat to webSocket failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LocalWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) {
|
||||
if _, err := sws.slave.Write(cmdBytes); err != nil {
|
||||
global.LOG.Errorf("ws cmd bytes write to ssh.stdin pipe failed, err: %v", err)
|
||||
}
|
||||
}
|
218
core/utils/terminal/ws_session.go
Normal file
218
core/utils/terminal/ws_session.go
Normal file
@ -0,0 +1,218 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/gorilla/websocket"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type safeBuffer struct {
|
||||
buffer bytes.Buffer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (w *safeBuffer) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.buffer.Write(p)
|
||||
}
|
||||
func (w *safeBuffer) Bytes() []byte {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.buffer.Bytes()
|
||||
}
|
||||
func (w *safeBuffer) Reset() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
w.buffer.Reset()
|
||||
}
|
||||
|
||||
const (
|
||||
WsMsgCmd = "cmd"
|
||||
WsMsgResize = "resize"
|
||||
WsMsgHeartbeat = "heartbeat"
|
||||
)
|
||||
|
||||
type WsMsg struct {
|
||||
Type string `json:"type"`
|
||||
Data string `json:"data,omitempty"` // WsMsgCmd
|
||||
Cols int `json:"cols,omitempty"` // WsMsgResize
|
||||
Rows int `json:"rows,omitempty"` // WsMsgResize
|
||||
Timestamp int `json:"timestamp,omitempty"` // WsMsgHeartbeat
|
||||
}
|
||||
|
||||
type LogicSshWsSession struct {
|
||||
stdinPipe io.WriteCloser
|
||||
comboOutput *safeBuffer
|
||||
logBuff *safeBuffer
|
||||
inputFilterBuff *safeBuffer
|
||||
session *ssh.Session
|
||||
wsConn *websocket.Conn
|
||||
isAdmin bool
|
||||
IsFlagged bool
|
||||
}
|
||||
|
||||
func NewLogicSshWsSession(cols, rows int, isAdmin bool, sshClient *ssh.Client, wsConn *websocket.Conn) (*LogicSshWsSession, error) {
|
||||
sshSession, err := sshClient.NewSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdinP, err := sshSession.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comboWriter := new(safeBuffer)
|
||||
logBuf := new(safeBuffer)
|
||||
inputBuf := new(safeBuffer)
|
||||
sshSession.Stdout = comboWriter
|
||||
sshSession.Stderr = comboWriter
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 1,
|
||||
ssh.TTY_OP_ISPEED: 14400,
|
||||
ssh.TTY_OP_OSPEED: 14400,
|
||||
}
|
||||
if err := sshSession.RequestPty("xterm", rows, cols, modes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := sshSession.Shell(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LogicSshWsSession{
|
||||
stdinPipe: stdinP,
|
||||
comboOutput: comboWriter,
|
||||
logBuff: logBuf,
|
||||
inputFilterBuff: inputBuf,
|
||||
session: sshSession,
|
||||
wsConn: wsConn,
|
||||
isAdmin: isAdmin,
|
||||
IsFlagged: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) Close() {
|
||||
if sws.session != nil {
|
||||
sws.session.Close()
|
||||
}
|
||||
if sws.logBuff != nil {
|
||||
sws.logBuff = nil
|
||||
}
|
||||
if sws.comboOutput != nil {
|
||||
sws.comboOutput = nil
|
||||
}
|
||||
}
|
||||
func (sws *LogicSshWsSession) Start(quitChan chan bool) {
|
||||
go sws.receiveWsMsg(quitChan)
|
||||
go sws.sendComboOutput(quitChan)
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
global.LOG.Errorf("[A panic occurred during receive ws message, error message: %v", r)
|
||||
}
|
||||
}()
|
||||
wsConn := sws.wsConn
|
||||
defer setQuit(exitCh)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return
|
||||
default:
|
||||
_, wsData, err := wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
msgObj := WsMsg{}
|
||||
_ = json.Unmarshal(wsData, &msgObj)
|
||||
switch msgObj.Type {
|
||||
case WsMsgResize:
|
||||
if msgObj.Cols > 0 && msgObj.Rows > 0 {
|
||||
if err := sws.session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {
|
||||
global.LOG.Errorf("ssh pty change windows size failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
case WsMsgCmd:
|
||||
decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Data)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("websock cmd string base64 decoding failed, err: %v", err)
|
||||
}
|
||||
sws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes)
|
||||
case WsMsgHeartbeat:
|
||||
// 接收到心跳包后将心跳包原样返回,可以用于网络延迟检测等情况
|
||||
err = wsConn.WriteMessage(websocket.TextMessage, wsData)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("ssh sending heartbeat to webSocket failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) {
|
||||
if _, err := sws.stdinPipe.Write(cmdBytes); err != nil {
|
||||
global.LOG.Errorf("ws cmd bytes write to ssh.stdin pipe failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) {
|
||||
wsConn := sws.wsConn
|
||||
defer setQuit(exitCh)
|
||||
|
||||
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
||||
defer tick.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
if sws.comboOutput == nil {
|
||||
return
|
||||
}
|
||||
bs := sws.comboOutput.Bytes()
|
||||
if len(bs) > 0 {
|
||||
wsData, err := json.Marshal(WsMsg{
|
||||
Type: WsMsgCmd,
|
||||
Data: base64.StdEncoding.EncodeToString(bs),
|
||||
})
|
||||
if err != nil {
|
||||
global.LOG.Errorf("encoding combo output to json failed, err: %v", err)
|
||||
continue
|
||||
}
|
||||
err = wsConn.WriteMessage(websocket.TextMessage, wsData)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("ssh sending combo output to webSocket failed, err: %v", err)
|
||||
}
|
||||
_, err = sws.logBuff.Write(bs)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("combo output to log buffer failed, err: %v", err)
|
||||
}
|
||||
sws.comboOutput.buffer.Reset()
|
||||
}
|
||||
if string(bs) == string([]byte{13, 10, 108, 111, 103, 111, 117, 116, 13, 10}) {
|
||||
sws.Close()
|
||||
return
|
||||
}
|
||||
|
||||
case <-exitCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) Wait(quitChan chan bool) {
|
||||
if err := sws.session.Wait(); err != nil {
|
||||
setQuit(quitChan)
|
||||
}
|
||||
}
|
||||
|
||||
func setQuit(ch chan bool) {
|
||||
ch <- true
|
||||
}
|
@ -9,3 +9,11 @@ import (
|
||||
func Proxy(c *gin.Context, currentNode string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateGroup(name string, group, newGroup uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckBackupUsed(id uint) error {
|
||||
return nil
|
||||
}
|
||||
|
@ -1,19 +1,16 @@
|
||||
export namespace Command {
|
||||
export interface CommandInfo {
|
||||
id: number;
|
||||
type: string;
|
||||
name: string;
|
||||
groupID: number;
|
||||
command: string;
|
||||
}
|
||||
export interface CommandOperate {
|
||||
id: number;
|
||||
type: string;
|
||||
name: string;
|
||||
groupID: number;
|
||||
command: string;
|
||||
}
|
||||
export interface RedisCommand {
|
||||
id: number;
|
||||
name: string;
|
||||
command: string;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ export namespace Group {
|
||||
name: string;
|
||||
type: string;
|
||||
isDefault: boolean;
|
||||
isDelete: boolean;
|
||||
}
|
||||
export interface GroupCreate {
|
||||
id: number;
|
||||
@ -15,7 +16,4 @@ export namespace Group {
|
||||
name: string;
|
||||
isDefault: boolean;
|
||||
}
|
||||
export interface GroupSearch {
|
||||
type: string;
|
||||
}
|
||||
}
|
||||
|
82
frontend/src/api/modules/backup.ts
Normal file
82
frontend/src/api/modules/backup.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import http from '@/api';
|
||||
import { deepCopy } from '@/utils/util';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { ResPage } from '../interface';
|
||||
import { Backup } from '../interface/backup';
|
||||
import { TimeoutEnum } from '@/enums/http-enum';
|
||||
|
||||
// backup-agent
|
||||
export const handleBackup = (params: Backup.Backup) => {
|
||||
return http.post(`/backups/backup`, params, TimeoutEnum.T_1H);
|
||||
};
|
||||
export const handleRecover = (params: Backup.Recover) => {
|
||||
return http.post(`/backups/recover`, params, TimeoutEnum.T_1D);
|
||||
};
|
||||
export const handleRecoverByUpload = (params: Backup.Recover) => {
|
||||
return http.post(`/backups/recover/byupload`, params, TimeoutEnum.T_1D);
|
||||
};
|
||||
export const downloadBackupRecord = (params: Backup.RecordDownload) => {
|
||||
return http.post<string>(`/backups/record/download`, params, TimeoutEnum.T_10M);
|
||||
};
|
||||
export const deleteBackupRecord = (params: { ids: number[] }) => {
|
||||
return http.post(`/backups/record/del`, params);
|
||||
};
|
||||
export const searchBackupRecords = (params: Backup.SearchBackupRecord) => {
|
||||
return http.post<ResPage<Backup.RecordInfo>>(`/backups/record/search`, params, TimeoutEnum.T_5M);
|
||||
};
|
||||
export const searchBackupRecordsByCronjob = (params: Backup.SearchBackupRecordByCronjob) => {
|
||||
return http.post<ResPage<Backup.RecordInfo>>(`/backups/record/search/bycronjob`, params, TimeoutEnum.T_5M);
|
||||
};
|
||||
export const getFilesFromBackup = (type: string) => {
|
||||
return http.post<Array<any>>(`/backups/search/files`, { type: type });
|
||||
};
|
||||
|
||||
// backup-core
|
||||
export const refreshOneDrive = () => {
|
||||
return http.post(`/core/backup/refresh/onedrive`, {});
|
||||
};
|
||||
export const getBackupList = () => {
|
||||
return http.get<Array<Backup.BackupOption>>(`/core/backup/options`);
|
||||
};
|
||||
export const getLocalBackupDir = () => {
|
||||
return http.get<string>(`/core/backup/local`);
|
||||
};
|
||||
export const searchBackup = (params: Backup.SearchWithType) => {
|
||||
return http.post<ResPage<Backup.BackupInfo>>(`/core/backup/search`, params);
|
||||
};
|
||||
export const getOneDriveInfo = () => {
|
||||
return http.get<Backup.OneDriveInfo>(`/core/backup/onedrive`);
|
||||
};
|
||||
export const addBackup = (params: Backup.BackupOperate) => {
|
||||
let request = deepCopy(params) as Backup.BackupOperate;
|
||||
if (request.accessKey) {
|
||||
request.accessKey = Base64.encode(request.accessKey);
|
||||
}
|
||||
if (request.credential) {
|
||||
request.credential = Base64.encode(request.credential);
|
||||
}
|
||||
return http.post<Backup.BackupOperate>(`/core/backup`, request, TimeoutEnum.T_60S);
|
||||
};
|
||||
export const editBackup = (params: Backup.BackupOperate) => {
|
||||
let request = deepCopy(params) as Backup.BackupOperate;
|
||||
if (request.accessKey) {
|
||||
request.accessKey = Base64.encode(request.accessKey);
|
||||
}
|
||||
if (request.credential) {
|
||||
request.credential = Base64.encode(request.credential);
|
||||
}
|
||||
return http.post(`/core/backup/update`, request);
|
||||
};
|
||||
export const deleteBackup = (params: { id: number }) => {
|
||||
return http.post(`/core/backup/del`, params);
|
||||
};
|
||||
export const listBucket = (params: Backup.ForBucket) => {
|
||||
let request = deepCopy(params) as Backup.BackupOperate;
|
||||
if (request.accessKey) {
|
||||
request.accessKey = Base64.encode(request.accessKey);
|
||||
}
|
||||
if (request.credential) {
|
||||
request.credential = Base64.encode(request.credential);
|
||||
}
|
||||
return http.post(`/core/backup/buckets`, request);
|
||||
};
|
22
frontend/src/api/modules/command.ts
Normal file
22
frontend/src/api/modules/command.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import http from '@/api';
|
||||
import { ResPage, SearchWithPage } from '../interface';
|
||||
import { Command } from '../interface/command';
|
||||
|
||||
export const getCommandList = (type: string) => {
|
||||
return http.post<Array<Command.CommandInfo>>(`/core/commands/list`, { type: type });
|
||||
};
|
||||
export const getCommandPage = (params: SearchWithPage) => {
|
||||
return http.post<ResPage<Command.CommandInfo>>(`/core/commands/search`, params);
|
||||
};
|
||||
export const getCommandTree = (type: string) => {
|
||||
return http.post<any>(`/core/commands/tree`, { type: type });
|
||||
};
|
||||
export const addCommand = (params: Command.CommandOperate) => {
|
||||
return http.post<Command.CommandOperate>(`/core/commands`, params);
|
||||
};
|
||||
export const editCommand = (params: Command.CommandOperate) => {
|
||||
return http.post(`/core/commands/update`, params);
|
||||
};
|
||||
export const deleteCommand = (params: { ids: number[] }) => {
|
||||
return http.post(`/core/commands/del`, params);
|
||||
};
|
@ -1,15 +1,15 @@
|
||||
import { Group } from '../interface/group';
|
||||
import http from '@/api';
|
||||
|
||||
export const GetGroupList = (params: Group.GroupSearch) => {
|
||||
return http.post<Array<Group.GroupInfo>>(`/groups/search`, params);
|
||||
export const GetGroupList = (type: string) => {
|
||||
return http.post<Array<Group.GroupInfo>>(`/core/groups/search`, { type: type });
|
||||
};
|
||||
export const CreateGroup = (params: Group.GroupCreate) => {
|
||||
return http.post<Group.GroupCreate>(`/groups`, params);
|
||||
return http.post<Group.GroupCreate>(`/core/groups`, params);
|
||||
};
|
||||
export const UpdateGroup = (params: Group.GroupUpdate) => {
|
||||
return http.post(`/groups/update`, params);
|
||||
return http.post(`/core/groups/update`, params);
|
||||
};
|
||||
export const DeleteGroup = (id: number) => {
|
||||
return http.post(`/groups/del`, { id: id });
|
||||
return http.post(`/core/groups/del`, { id: id });
|
||||
};
|
||||
|
@ -1,90 +1,8 @@
|
||||
import http from '@/api';
|
||||
import { ResPage, SearchWithPage } from '../interface';
|
||||
import { Command } from '../interface/command';
|
||||
import { ResPage } from '../interface';
|
||||
import { Host } from '../interface/host';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { deepCopy } from '@/utils/util';
|
||||
import { TimeoutEnum } from '@/enums/http-enum';
|
||||
|
||||
export const searchHosts = (params: Host.SearchWithPage) => {
|
||||
return http.post<ResPage<Host.Host>>(`/hosts/search`, params);
|
||||
};
|
||||
export const getHostTree = (params: Host.ReqSearch) => {
|
||||
return http.post<Array<Host.HostTree>>(`/hosts/tree`, params);
|
||||
};
|
||||
export const addHost = (params: Host.HostOperate) => {
|
||||
let request = deepCopy(params) as Host.HostOperate;
|
||||
if (request.password) {
|
||||
request.password = Base64.encode(request.password);
|
||||
}
|
||||
if (request.privateKey) {
|
||||
request.privateKey = Base64.encode(request.privateKey);
|
||||
}
|
||||
return http.post<Host.HostOperate>(`/hosts`, request);
|
||||
};
|
||||
export const testByInfo = (params: Host.HostConnTest) => {
|
||||
let request = deepCopy(params) as Host.HostOperate;
|
||||
if (request.password) {
|
||||
request.password = Base64.encode(request.password);
|
||||
}
|
||||
if (request.privateKey) {
|
||||
request.privateKey = Base64.encode(request.privateKey);
|
||||
}
|
||||
return http.post<boolean>(`/hosts/test/byinfo`, request);
|
||||
};
|
||||
export const testByID = (id: number) => {
|
||||
return http.post<boolean>(`/hosts/test/byid/${id}`);
|
||||
};
|
||||
export const editHost = (params: Host.HostOperate) => {
|
||||
let request = deepCopy(params) as Host.HostOperate;
|
||||
if (request.password) {
|
||||
request.password = Base64.encode(request.password);
|
||||
}
|
||||
if (request.privateKey) {
|
||||
request.privateKey = Base64.encode(request.privateKey);
|
||||
}
|
||||
return http.post(`/hosts/update`, request);
|
||||
};
|
||||
export const editHostGroup = (params: Host.GroupChange) => {
|
||||
return http.post(`/hosts/update/group`, params);
|
||||
};
|
||||
export const deleteHost = (params: { ids: number[] }) => {
|
||||
return http.post(`/hosts/del`, params);
|
||||
};
|
||||
|
||||
// command
|
||||
export const getCommandList = () => {
|
||||
return http.get<Array<Command.CommandInfo>>(`/hosts/command`, {});
|
||||
};
|
||||
export const getCommandPage = (params: SearchWithPage) => {
|
||||
return http.post<ResPage<Command.CommandInfo>>(`/hosts/command/search`, params);
|
||||
};
|
||||
export const getCommandTree = () => {
|
||||
return http.get<any>(`/hosts/command/tree`);
|
||||
};
|
||||
export const addCommand = (params: Command.CommandOperate) => {
|
||||
return http.post<Command.CommandOperate>(`/hosts/command`, params);
|
||||
};
|
||||
export const editCommand = (params: Command.CommandOperate) => {
|
||||
return http.post(`/hosts/command/update`, params);
|
||||
};
|
||||
export const deleteCommand = (params: { ids: number[] }) => {
|
||||
return http.post(`/hosts/command/del`, params);
|
||||
};
|
||||
|
||||
export const getRedisCommandList = () => {
|
||||
return http.get<Array<Command.RedisCommand>>(`/hosts/command/redis`, {});
|
||||
};
|
||||
export const getRedisCommandPage = (params: SearchWithPage) => {
|
||||
return http.post<ResPage<Command.RedisCommand>>(`/hosts/command/redis/search`, params);
|
||||
};
|
||||
export const saveRedisCommand = (params: Command.RedisCommand) => {
|
||||
return http.post(`/hosts/command/redis`, params);
|
||||
};
|
||||
export const deleteRedisCommand = (params: { ids: number[] }) => {
|
||||
return http.post(`/hosts/command/redis/del`, params);
|
||||
};
|
||||
|
||||
// firewall
|
||||
export const loadFireBaseInfo = () => {
|
||||
return http.get<Host.FirewallBase>(`/hosts/firewall/base`);
|
||||
|
51
frontend/src/api/modules/terminal.ts
Normal file
51
frontend/src/api/modules/terminal.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import http from '@/api';
|
||||
import { ResPage } from '../interface';
|
||||
import { Host } from '../interface/host';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { deepCopy } from '@/utils/util';
|
||||
|
||||
export const searchHosts = (params: Host.SearchWithPage) => {
|
||||
return http.post<ResPage<Host.Host>>(`/core/hosts/search`, params);
|
||||
};
|
||||
export const getHostTree = (params: Host.ReqSearch) => {
|
||||
return http.post<Array<Host.HostTree>>(`/core/hosts/tree`, params);
|
||||
};
|
||||
export const addHost = (params: Host.HostOperate) => {
|
||||
let request = deepCopy(params) as Host.HostOperate;
|
||||
if (request.password) {
|
||||
request.password = Base64.encode(request.password);
|
||||
}
|
||||
if (request.privateKey) {
|
||||
request.privateKey = Base64.encode(request.privateKey);
|
||||
}
|
||||
return http.post<Host.HostOperate>(`/core/hosts`, request);
|
||||
};
|
||||
export const testByInfo = (params: Host.HostConnTest) => {
|
||||
let request = deepCopy(params) as Host.HostOperate;
|
||||
if (request.password) {
|
||||
request.password = Base64.encode(request.password);
|
||||
}
|
||||
if (request.privateKey) {
|
||||
request.privateKey = Base64.encode(request.privateKey);
|
||||
}
|
||||
return http.post<boolean>(`/core/hosts/test/byinfo`, request);
|
||||
};
|
||||
export const testByID = (id: number) => {
|
||||
return http.post<boolean>(`/core/hosts/test/byid/${id}`);
|
||||
};
|
||||
export const editHost = (params: Host.HostOperate) => {
|
||||
let request = deepCopy(params) as Host.HostOperate;
|
||||
if (request.password) {
|
||||
request.password = Base64.encode(request.password);
|
||||
}
|
||||
if (request.privateKey) {
|
||||
request.privateKey = Base64.encode(request.privateKey);
|
||||
}
|
||||
return http.post(`/core/hosts/update`, request);
|
||||
};
|
||||
export const editHostGroup = (params: Host.GroupChange) => {
|
||||
return http.post(`/core/hosts/update/group`, params);
|
||||
};
|
||||
export const deleteHost = (params: { ids: number[] }) => {
|
||||
return http.post(`/core/hosts/del`, params);
|
||||
};
|
@ -1,9 +1,9 @@
|
||||
@font-face {
|
||||
font-family: "panel"; /* Project id 3575356 */
|
||||
src: url('iconfont.woff2?t=1717570629440') format('woff2'),
|
||||
url('iconfont.woff?t=1717570629440') format('woff'),
|
||||
url('iconfont.ttf?t=1717570629440') format('truetype'),
|
||||
url('iconfont.svg?t=1717570629440#panel') format('svg');
|
||||
src: url('iconfont.woff2?t=1723798525783') format('woff2'),
|
||||
url('iconfont.woff?t=1723798525783') format('woff'),
|
||||
url('iconfont.ttf?t=1723798525783') format('truetype'),
|
||||
url('iconfont.svg?t=1723798525783#panel') format('svg');
|
||||
}
|
||||
|
||||
.panel {
|
||||
@ -14,6 +14,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.p-zhongduan:before {
|
||||
content: "\e731";
|
||||
}
|
||||
|
||||
.p-docker1:before {
|
||||
content: "\e76a";
|
||||
}
|
||||
@ -178,18 +182,10 @@
|
||||
content: "\e856";
|
||||
}
|
||||
|
||||
.p-webdav:before {
|
||||
content: "\e622";
|
||||
}
|
||||
|
||||
.p-xiangqing:before {
|
||||
content: "\e677";
|
||||
}
|
||||
|
||||
.p-onedrive:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.p-caidan:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
@ -198,14 +194,6 @@
|
||||
content: "\e744";
|
||||
}
|
||||
|
||||
.p-tengxunyun1:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
||||
.p-qiniuyun:before {
|
||||
content: "\e62c";
|
||||
}
|
||||
|
||||
.p-file-png:before {
|
||||
content: "\e7ae";
|
||||
}
|
||||
@ -250,10 +238,6 @@
|
||||
content: "\e60f";
|
||||
}
|
||||
|
||||
.p-aws:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.p-taolun:before {
|
||||
content: "\e602";
|
||||
}
|
||||
@ -262,22 +246,10 @@
|
||||
content: "\e616";
|
||||
}
|
||||
|
||||
.p-SFTP:before {
|
||||
content: "\e647";
|
||||
}
|
||||
|
||||
.p-huaban88:before {
|
||||
content: "\e67c";
|
||||
}
|
||||
|
||||
.p-oss:before {
|
||||
content: "\e607";
|
||||
}
|
||||
|
||||
.p-minio:before {
|
||||
content: "\e63c";
|
||||
}
|
||||
|
||||
.p-logout:before {
|
||||
content: "\e8fe";
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -5,6 +5,13 @@
|
||||
"css_prefix_text": "p-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "30053592",
|
||||
"name": "终端",
|
||||
"font_class": "zhongduan",
|
||||
"unicode": "e731",
|
||||
"unicode_decimal": 59185
|
||||
},
|
||||
{
|
||||
"icon_id": "1064806",
|
||||
"name": "docker",
|
||||
@ -292,13 +299,6 @@
|
||||
"unicode": "e856",
|
||||
"unicode_decimal": 59478
|
||||
},
|
||||
{
|
||||
"icon_id": "23044673",
|
||||
"name": "webdav",
|
||||
"font_class": "webdav",
|
||||
"unicode": "e622",
|
||||
"unicode_decimal": 58914
|
||||
},
|
||||
{
|
||||
"icon_id": "10293150",
|
||||
"name": "详情",
|
||||
@ -306,13 +306,6 @@
|
||||
"unicode": "e677",
|
||||
"unicode_decimal": 58999
|
||||
},
|
||||
{
|
||||
"icon_id": "13015332",
|
||||
"name": "onedrive",
|
||||
"font_class": "onedrive",
|
||||
"unicode": "e601",
|
||||
"unicode_decimal": 58881
|
||||
},
|
||||
{
|
||||
"icon_id": "7708032",
|
||||
"name": "菜单",
|
||||
@ -327,20 +320,6 @@
|
||||
"unicode": "e744",
|
||||
"unicode_decimal": 59204
|
||||
},
|
||||
{
|
||||
"icon_id": "12959160",
|
||||
"name": "腾讯云",
|
||||
"font_class": "tengxunyun1",
|
||||
"unicode": "e651",
|
||||
"unicode_decimal": 58961
|
||||
},
|
||||
{
|
||||
"icon_id": "24877229",
|
||||
"name": "七牛云",
|
||||
"font_class": "qiniuyun",
|
||||
"unicode": "e62c",
|
||||
"unicode_decimal": 58924
|
||||
},
|
||||
{
|
||||
"icon_id": "19671162",
|
||||
"name": "png-1",
|
||||
@ -418,13 +397,6 @@
|
||||
"unicode": "e60f",
|
||||
"unicode_decimal": 58895
|
||||
},
|
||||
{
|
||||
"icon_id": "32101973",
|
||||
"name": "Amazon_Web_Services_Logo",
|
||||
"font_class": "aws",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "1760690",
|
||||
"name": "讨论",
|
||||
@ -439,13 +411,6 @@
|
||||
"unicode": "e616",
|
||||
"unicode_decimal": 58902
|
||||
},
|
||||
{
|
||||
"icon_id": "13532955",
|
||||
"name": "SFTP",
|
||||
"font_class": "SFTP",
|
||||
"unicode": "e647",
|
||||
"unicode_decimal": 58951
|
||||
},
|
||||
{
|
||||
"icon_id": "15337722",
|
||||
"name": "Logo GitHub",
|
||||
@ -453,20 +418,6 @@
|
||||
"unicode": "e67c",
|
||||
"unicode_decimal": 59004
|
||||
},
|
||||
{
|
||||
"icon_id": "16268521",
|
||||
"name": "oss",
|
||||
"font_class": "oss",
|
||||
"unicode": "e607",
|
||||
"unicode_decimal": 58887
|
||||
},
|
||||
{
|
||||
"icon_id": "20290513",
|
||||
"name": "minio",
|
||||
"font_class": "minio",
|
||||
"unicode": "e63c",
|
||||
"unicode_decimal": 58940
|
||||
},
|
||||
{
|
||||
"icon_id": "924436",
|
||||
"name": "logout",
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 157 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -72,7 +72,7 @@ const rules = reactive({
|
||||
});
|
||||
|
||||
const loadGroups = async (groupName: string) => {
|
||||
const res = await GetGroupList({ type: dialogData.value.groupType });
|
||||
const res = await GetGroupList(dialogData.value.groupType);
|
||||
groupList.value = res.data;
|
||||
for (const group of groupList.value) {
|
||||
if (group.name === groupName) {
|
||||
|
@ -21,7 +21,13 @@
|
||||
{{ $t('commons.table.default') }}
|
||||
</span>
|
||||
<span v-if="row.name !== 'default'">{{ row.name }}</span>
|
||||
<span v-if="row.isDefault">({{ $t('commons.table.default') }})</span>
|
||||
<el-tag v-if="row.isDefault" type="success" class="ml-2" size="small">
|
||||
({{ $t('commons.table.default') }})
|
||||
</el-tag>
|
||||
|
||||
<el-tag type="warning" size="small" class="ml-4" v-if="row.isDelete">
|
||||
{{ $t('app.takeDown') }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<el-form @submit.prevent ref="groupForm" v-if="row.edit" :model="row">
|
||||
@ -31,6 +37,24 @@
|
||||
</el-form>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('commons.table.status')" prop="status" :min-width="60">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.status === 'Enable'"
|
||||
@click="onChangeStatus(row.id, 'disable')"
|
||||
link
|
||||
icon="VideoPlay"
|
||||
type="success"
|
||||
>
|
||||
{{ $t('commons.status.enabled') }}
|
||||
</el-button>
|
||||
<el-button v-else icon="VideoPause" link type="danger" @click="onChangeStatus(row.id, 'enable')">
|
||||
{{ $t('commons.status.disabled') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('commons.table.operate')">
|
||||
<template #default="{ row, $index }">
|
||||
<div>
|
||||
@ -52,7 +76,12 @@
|
||||
<el-button link v-if="row.edit" type="primary" @click="search()">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button link v-if="!row.edit && !row.isDefault" type="primary" @click="setDefault(row)">
|
||||
<el-button
|
||||
link
|
||||
v-if="!row.edit && !row.isDefault && !row.isDelete"
|
||||
type="primary"
|
||||
@click="setDefault(row)"
|
||||
>
|
||||
{{ $t('website.setDefault') }}
|
||||
</el-button>
|
||||
</div>
|
||||
@ -92,17 +121,8 @@ const acceptParams = (params: DialogProps): void => {
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const search = () => {
|
||||
data.value = [];
|
||||
GetGroupList({ type: type.value }).then((res) => {
|
||||
for (const d of res.data) {
|
||||
const g = {
|
||||
id: d.id,
|
||||
name: d.name,
|
||||
isDefault: d.isDefault,
|
||||
edit: false,
|
||||
};
|
||||
data.value.push(g);
|
||||
}
|
||||
GetGroupList(type.value).then((res) => {
|
||||
data.value = res.data || [];
|
||||
});
|
||||
};
|
||||
|
||||
@ -136,6 +156,18 @@ const setDefault = (group: Group.GroupInfo) => {
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeStatus = async (row: any, status: string) => {
|
||||
ElMessageBox.confirm(i18n.global.t('cronjob.' + status + 'Msg'), i18n.global.t('cronjob.changeStatus'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
}).then(async () => {
|
||||
row.status = status === 'enable' ? 'Enable' : 'Disable';
|
||||
await UpdateGroup(row);
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
});
|
||||
};
|
||||
|
||||
const openCreate = () => {
|
||||
for (const d of data.value) {
|
||||
if (d.name == '') {
|
||||
@ -150,6 +182,7 @@ const openCreate = () => {
|
||||
name: '',
|
||||
isDefault: false,
|
||||
edit: true,
|
||||
status: 'Enable',
|
||||
};
|
||||
data.value.unshift(g);
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Layout } from '@/routers/constant';
|
||||
|
||||
const cronRouter = {
|
||||
sort: 8,
|
||||
sort: 9,
|
||||
path: '/cronjobs',
|
||||
component: Layout,
|
||||
redirect: '/cronjobs',
|
||||
|
@ -40,17 +40,6 @@ const hostRouter = {
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/hosts/terminal',
|
||||
name: 'Terminal',
|
||||
props: true,
|
||||
component: () => import('@/views/host/terminal/index.vue'),
|
||||
meta: {
|
||||
title: 'menu.terminal',
|
||||
keepAlive: true,
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/hosts/firewall/port',
|
||||
name: 'FirewallPort',
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Layout } from '@/routers/constant';
|
||||
|
||||
const logsRouter = {
|
||||
sort: 8,
|
||||
sort: 11,
|
||||
path: '/logs',
|
||||
component: Layout,
|
||||
redirect: '/logs/operation',
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Layout } from '@/routers/constant';
|
||||
|
||||
const settingRouter = {
|
||||
sort: 10,
|
||||
sort: 12,
|
||||
path: '/settings',
|
||||
component: Layout,
|
||||
redirect: '/settings/panel',
|
||||
|
26
frontend/src/routers/modules/terminal.ts
Normal file
26
frontend/src/routers/modules/terminal.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Layout } from '@/routers/constant';
|
||||
|
||||
const terminalRouter = {
|
||||
sort: 8,
|
||||
path: '/terminal',
|
||||
component: Layout,
|
||||
redirect: '/terminal',
|
||||
meta: {
|
||||
icon: 'p-zhongduan',
|
||||
title: 'menu.terminal',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/terminal',
|
||||
name: 'Terminal',
|
||||
props: true,
|
||||
component: () => import('@/views/terminal/index.vue'),
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
requiresAuth: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default terminalRouter;
|
@ -27,7 +27,7 @@
|
||||
<el-table-column min-width="40">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.lineStatus === 'create'"
|
||||
v-if="scope.row.lineStatus === 'create' || scope.row.lineStatus === 'edit'"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleCmdSave(scope.row)"
|
||||
@ -38,15 +38,20 @@
|
||||
v-if="!scope.row.lineStatus || scope.row.lineStatus === 'saved'"
|
||||
link
|
||||
type="primary"
|
||||
@click="scope.row.lineStatus = 'create'"
|
||||
@click="scope.row.lineStatus = 'edit'"
|
||||
>
|
||||
{{ $t('commons.button.edit') }}
|
||||
</el-button>
|
||||
<el-button v-if="scope.row.lineStatus === 'create'" link type="primary" @click="search()">
|
||||
<el-button
|
||||
v-if="scope.row.lineStatus === 'create' || scope.row.lineStatus === 'edit'"
|
||||
link
|
||||
type="primary"
|
||||
@click="search()"
|
||||
>
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.lineStatus !== 'create'"
|
||||
v-if="scope.row.lineStatus !== 'create' && scope.row.lineStatus !== 'edit'"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleCmdDelete(scope.$index)"
|
||||
@ -68,7 +73,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Command } from '@/api/interface/command';
|
||||
import { saveRedisCommand, deleteRedisCommand, getRedisCommandPage } from '@/api/modules/host';
|
||||
import { deleteCommand, getCommandPage, addCommand, editCommand } from '@/api/modules/command';
|
||||
import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
@ -81,6 +86,8 @@ const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
orderBy: 'name',
|
||||
order: 'ascending',
|
||||
});
|
||||
const opRef = ref();
|
||||
|
||||
@ -99,6 +106,7 @@ const handleCmdAdd = () => {
|
||||
let item = {
|
||||
name: '',
|
||||
command: '',
|
||||
type: 'redis',
|
||||
lineStatus: 'create',
|
||||
};
|
||||
data.value.push(item);
|
||||
@ -113,7 +121,20 @@ const handleCmdSave = async (row: any) => {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
await saveRedisCommand(row)
|
||||
if (row.lineStatus === 'create') {
|
||||
await addCommand(row)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
row.lineStatus = 'saved';
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
await editCommand(row)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
row.lineStatus = 'saved';
|
||||
@ -123,10 +144,9 @@ const handleCmdSave = async (row: any) => {
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
||||
const batchDelete = async (row: Command.RedisCommand | null) => {
|
||||
const batchDelete = async (row: Command.CommandInfo | null) => {
|
||||
let names = [];
|
||||
let ids = [];
|
||||
if (row) {
|
||||
@ -145,19 +165,24 @@ const batchDelete = async (row: Command.RedisCommand | null) => {
|
||||
i18n.global.t('terminal.quickCommand'),
|
||||
i18n.global.t('commons.button.delete'),
|
||||
]),
|
||||
api: deleteRedisCommand,
|
||||
api: deleteCommand,
|
||||
params: { ids: ids },
|
||||
});
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
const search = async (column?: any) => {
|
||||
paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;
|
||||
paginationConfig.order = column?.order ? column.order : paginationConfig.order;
|
||||
let params = {
|
||||
page: paginationConfig.currentPage,
|
||||
pageSize: paginationConfig.pageSize,
|
||||
orderBy: paginationConfig.orderBy,
|
||||
order: paginationConfig.order,
|
||||
info: '',
|
||||
type: 'redis',
|
||||
};
|
||||
loading.value = true;
|
||||
await getRedisCommandPage(params)
|
||||
await getCommandPage(params)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
data.value = res.data.items || [];
|
||||
|
@ -152,7 +152,7 @@ import { listDatabases, checkRedisCli, installRedisCli } from '@/api/modules/dat
|
||||
import { Database } from '@/api/interface/database';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import i18n from '@/lang';
|
||||
import { getRedisCommandList } from '@/api/modules/host';
|
||||
import { getCommandList } from '@/api/modules/command';
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const loading = ref(false);
|
||||
@ -364,7 +364,7 @@ const installCli = async () => {
|
||||
};
|
||||
|
||||
const loadQuickCmd = async () => {
|
||||
const res = await getRedisCommandList();
|
||||
const res = await getCommandList('redis');
|
||||
quickCmdList.value = res.data || [];
|
||||
};
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
<el-option :value="item.id" :label="item.name" />
|
||||
</div>
|
||||
</el-select>
|
||||
<TableSearch @search="search()" v-model:searchName="commandReq.name" />
|
||||
<TableSearch @search="search()" v-model:searchName="info" />
|
||||
</template>
|
||||
<template #main>
|
||||
<ComplexTable
|
||||
@ -109,7 +109,7 @@
|
||||
import { Command } from '@/api/interface/command';
|
||||
import GroupDialog from '@/components/group/index.vue';
|
||||
import GroupChangeDialog from '@/components/group/change.vue';
|
||||
import { addCommand, editCommand, deleteCommand, getCommandPage } from '@/api/modules/host';
|
||||
import { addCommand, editCommand, deleteCommand, getCommandPage } from '@/api/modules/command';
|
||||
import { reactive, ref } from 'vue';
|
||||
import type { ElForm } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
@ -151,19 +151,16 @@ const acceptParams = () => {
|
||||
const defaultGroupID = ref();
|
||||
let commandInfo = reactive<Command.CommandOperate>({
|
||||
id: 0,
|
||||
type: 'command',
|
||||
name: '',
|
||||
groupID: 0,
|
||||
command: '',
|
||||
});
|
||||
|
||||
const commandReq = reactive({
|
||||
name: '',
|
||||
});
|
||||
|
||||
const cmdVisible = ref<boolean>(false);
|
||||
|
||||
const loadGroups = async () => {
|
||||
const res = await GetGroupList({ type: 'command' });
|
||||
const res = await GetGroupList('command');
|
||||
groupList.value = res.data;
|
||||
for (const group of groupList.value) {
|
||||
if (group.isDefault) {
|
||||
@ -274,7 +271,7 @@ const search = async (column?: any) => {
|
||||
info: info.value,
|
||||
orderBy: paginationConfig.orderBy,
|
||||
order: paginationConfig.order,
|
||||
name: commandReq.name,
|
||||
type: 'command',
|
||||
};
|
||||
loading.value = true;
|
||||
await getCommandPage(params)
|
@ -31,7 +31,7 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import type { ElForm } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { editHostGroup } from '@/api/modules/host';
|
||||
import { editHostGroup } from '@/api/modules/terminal';
|
||||
import { GetGroupList } from '@/api/modules/group';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
@ -66,7 +66,7 @@ const rules = reactive({
|
||||
});
|
||||
|
||||
const loadGroups = async (groupName: string) => {
|
||||
const res = await GetGroupList({ type: 'host' });
|
||||
const res = await GetGroupList('host');
|
||||
groupList.value = res.data;
|
||||
for (const group of groupList.value) {
|
||||
if (group.name === groupName) {
|
@ -60,8 +60,8 @@
|
||||
<script setup lang="ts">
|
||||
import GroupDialog from '@/components/group/index.vue';
|
||||
import GroupChangeDialog from '@/components/group/change.vue';
|
||||
import OperateDialog from '@/views/host/terminal/host/operate/index.vue';
|
||||
import { deleteHost, editHostGroup, searchHosts } from '@/api/modules/host';
|
||||
import OperateDialog from '@/views/terminal/host/operate/index.vue';
|
||||
import { deleteHost, editHostGroup, searchHosts } from '@/api/modules/terminal';
|
||||
import { GetGroupList } from '@/api/modules/group';
|
||||
import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
@ -138,7 +138,7 @@ const onBatchDelete = async (row: Host.Host | null) => {
|
||||
};
|
||||
|
||||
const loadGroups = async () => {
|
||||
const res = await GetGroupList({ type: 'host' });
|
||||
const res = await GetGroupList('host');
|
||||
groupList.value = res.data;
|
||||
};
|
||||
|
@ -81,7 +81,7 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import type { ElForm } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { addHost, editHost, testByInfo } from '@/api/modules/host';
|
||||
import { addHost, editHost, testByInfo } from '@/api/modules/terminal';
|
||||
import { GetGroupList } from '@/api/modules/group';
|
||||
import i18n from '@/lang';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
@ -122,7 +122,7 @@ const rules = reactive({
|
||||
});
|
||||
|
||||
const loadGroups = async () => {
|
||||
const res = await GetGroupList({ type: 'host' });
|
||||
const res = await GetGroupList('host');
|
||||
groupList.value = res.data;
|
||||
if (dialogData.value.title === 'create') {
|
||||
for (const item of groupList.value) {
|
@ -27,9 +27,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import HostTab from '@/views/host/terminal/host/index.vue';
|
||||
import CommandTab from '@/views/host/terminal/command/index.vue';
|
||||
import TerminalTab from '@/views/host/terminal/terminal/index.vue';
|
||||
import HostTab from '@/views/terminal/host/index.vue';
|
||||
import CommandTab from '@/views/terminal/command/index.vue';
|
||||
import TerminalTab from '@/views/terminal/terminal/index.vue';
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
const activeNames = ref<string>('terminal');
|
@ -68,7 +68,7 @@
|
||||
import { ElForm } from 'element-plus';
|
||||
import { Host } from '@/api/interface/host';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { addHost, testByInfo } from '@/api/modules/host';
|
||||
import { addHost, testByInfo } from '@/api/modules/terminal';
|
||||
import i18n from '@/lang';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
@ -143,15 +143,16 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, getCurrentInstance, watch, nextTick, computed, onMounted } from 'vue';
|
||||
import Terminal from '@/components/terminal/index.vue';
|
||||
import HostDialog from '@/views/host/terminal/terminal/host-create.vue';
|
||||
import HostDialog from '@/views/terminal/terminal/host-create.vue';
|
||||
import type Node from 'element-plus/es/components/tree/src/model/node';
|
||||
import { ElTree } from 'element-plus';
|
||||
import screenfull from 'screenfull';
|
||||
import i18n from '@/lang';
|
||||
import { Host } from '@/api/interface/host';
|
||||
import { getCommandTree, getHostTree, testByID } from '@/api/modules/host';
|
||||
import { getHostTree, testByID } from '@/api/modules/terminal';
|
||||
import { GlobalStore } from '@/store';
|
||||
import router from '@/routers';
|
||||
import { getCommandTree } from '@/api/modules/command';
|
||||
|
||||
const dialogRef = ref();
|
||||
const ctx = getCurrentInstance() as any;
|
||||
@ -287,7 +288,7 @@ const filterHost = (value: string, data: any) => {
|
||||
return data.label.includes(value);
|
||||
};
|
||||
const loadCommandTree = async () => {
|
||||
const res = await getCommandTree();
|
||||
const res = await getCommandTree('command');
|
||||
commandTree.value = res.data || [];
|
||||
};
|
||||
|
||||
@ -348,7 +349,7 @@ const onReconnect = async (item: any) => {
|
||||
nextTick(() => {
|
||||
ctx.refs[`t-${item.index}`] &&
|
||||
ctx.refs[`t-${item.index}`][0].acceptParams({
|
||||
endpoint: '/api/v2/terminals',
|
||||
endpoint: '/api/v2/core/hosts/terminal',
|
||||
args: `id=${item.wsID}`,
|
||||
error: res.data ? '' : 'Failed to set up the connection. Please check the host information',
|
||||
});
|
||||
@ -380,7 +381,7 @@ const onConnTerminal = async (title: string, wsID: number, isLocal?: boolean) =>
|
||||
nextTick(() => {
|
||||
ctx.refs[`t-${terminalValue.value}`] &&
|
||||
ctx.refs[`t-${terminalValue.value}`][0].acceptParams({
|
||||
endpoint: '/api/v2/terminals',
|
||||
endpoint: '/api/v2/core/hosts/terminal',
|
||||
args: `id=${wsID}`,
|
||||
initCmd: initCmd.value,
|
||||
error: res.data ? '' : 'Authentication failed. Please check the host information !',
|
@ -88,7 +88,7 @@ const submit = async (formEl: FormInstance | undefined) => {
|
||||
});
|
||||
};
|
||||
const search = async () => {
|
||||
const res = await GetGroupList({ type: 'website' });
|
||||
const res = await GetGroupList('website');
|
||||
groups.value = res.data;
|
||||
|
||||
GetWebsite(websiteId.value).then((res) => {
|
||||
|
@ -829,7 +829,7 @@ const acceptParams = async (installPath: string) => {
|
||||
}
|
||||
staticPath.value = installPath + '/www/sites/';
|
||||
|
||||
const res = await GetGroupList({ type: 'website' });
|
||||
const res = await GetGroupList('website');
|
||||
groups.value = res.data;
|
||||
website.value.webSiteGroupId = res.data[0].id;
|
||||
website.value.type = 'deployment';
|
||||
|
@ -334,7 +334,7 @@ const search = async () => {
|
||||
};
|
||||
|
||||
const listGroup = async () => {
|
||||
const res = await GetGroupList({ type: 'website' });
|
||||
const res = await GetGroupList('website');
|
||||
groups.value = res.data;
|
||||
};
|
||||
|
||||
|
2
go.mod
2
go.mod
@ -5,6 +5,7 @@ go 1.22.4
|
||||
require (
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
github.com/aws/aws-sdk-go v1.55.5
|
||||
github.com/creack/pty v1.1.9
|
||||
github.com/dgraph-io/badger/v4 v4.2.0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gin-contrib/gzip v1.0.1
|
||||
@ -15,6 +16,7 @@ require (
|
||||
github.com/goh-chunlin/go-onedrive v1.1.1
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/minio/minio-go/v7 v7.0.74
|
||||
github.com/mojocn/base64Captcha v1.3.6
|
||||
|
3
go.sum
3
go.sum
@ -68,6 +68,7 @@ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -218,6 +219,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
|
||||
github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4=
|
||||
github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
|
Loading…
x
Reference in New Issue
Block a user