From 3c72a66c1f44c9ae8279d1c6230f26f7dd48ab8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Wed, 23 Oct 2024 02:39:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20ssh=E5=A4=9A=E6=9C=BA=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/setting.go | 4 - internal/biz/ssh.go | 15 +- internal/data/cron.go | 9 - internal/data/ssh.go | 97 ++++-- internal/http/request/ssh.go | 26 +- internal/migration/v1.go | 13 + internal/route/http.go | 7 +- internal/service/ssh.go | 84 +++++- internal/service/ws.go | 20 +- pkg/ssh/ssh.go | 38 +-- web/package.json | 2 + web/pnpm-lock.yaml | 30 ++ web/src/api/panel/cron/index.ts | 2 - web/src/api/panel/ssh/index.ts | 20 +- web/src/api/ws/index.ts | 8 +- web/src/main.ts | 3 +- web/src/styles/index.scss | 6 +- web/src/views/app/IndexView.vue | 2 +- web/src/views/apps/benchmark/IndexView.vue | 2 +- web/src/views/apps/phpmyadmin/IndexView.vue | 2 +- web/src/views/apps/pureftpd/IndexView.vue | 2 +- web/src/views/home/IndexView.vue | 4 +- web/src/views/ssh/CreateModal.vue | 92 ++++++ web/src/views/ssh/IndexView.vue | 319 ++++++++++++++------ web/src/views/ssh/UpdateModal.vue | 108 +++++++ web/src/views/website/EditView.vue | 2 +- 26 files changed, 695 insertions(+), 222 deletions(-) create mode 100644 web/src/views/ssh/CreateModal.vue create mode 100644 web/src/views/ssh/UpdateModal.vue diff --git a/internal/biz/setting.go b/internal/biz/setting.go index 8f447358..72253956 100644 --- a/internal/biz/setting.go +++ b/internal/biz/setting.go @@ -17,10 +17,6 @@ const ( SettingKeyBackupPath SettingKey = "backup_path" SettingKeyWebsitePath SettingKey = "website_path" SettingKeyMySQLRootPassword SettingKey = "mysql_root_password" - SettingKeySshHost SettingKey = "ssh_host" - SettingKeySshPort SettingKey = "ssh_port" - SettingKeySshUser SettingKey = "ssh_user" - SettingKeySshPassword SettingKey = "ssh_password" SettingKeyOfflineMode SettingKey = "offline_mode" ) diff --git a/internal/biz/ssh.go b/internal/biz/ssh.go index e8d18bc1..af44683d 100644 --- a/internal/biz/ssh.go +++ b/internal/biz/ssh.go @@ -9,14 +9,19 @@ import ( type SSH struct { ID uint `gorm:"primaryKey" json:"id"` - Host string `json:"host"` - Port uint `json:"port"` - Config ssh.ClientConfig `json:"config"` + Name string `gorm:"not null" json:"name"` + Host string `gorm:"not null" json:"host"` + Port uint `gorm:"not null" json:"port"` + Config ssh.ClientConfig `gorm:"not null;serializer:json" json:"config"` + Remark string `gorm:"not null" json:"remark"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type SSHRepo interface { - GetInfo() (map[string]any, error) - UpdateInfo(req *request.SSHUpdateInfo) error + List(page, limit uint) ([]*SSH, int64, error) + Get(id uint) (*SSH, error) + Create(req *request.SSHCreate) error + Update(req *request.SSHUpdate) error + Delete(id uint) error } diff --git a/internal/data/cron.go b/internal/data/cron.go index a346a29f..e6483763 100644 --- a/internal/data/cron.go +++ b/internal/data/cron.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "path/filepath" - "regexp" "strconv" "time" @@ -54,10 +53,6 @@ func (r *cronRepo) Get(id uint) (*biz.Cron, error) { } func (r *cronRepo) Create(req *request.CronCreate) error { - if !regexp.MustCompile(`^((\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+)(,(\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+))*\s?){5}$`).MatchString(req.Time) { - return errors.New("时间格式错误") - } - var script string if req.Type == "backup" { if req.BackupType == "website" { @@ -136,10 +131,6 @@ func (r *cronRepo) Update(req *request.CronUpdate) error { return err } - if !regexp.MustCompile(`^((\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+)(,(\*|\d+|\d+-\d+|\d+/\d+|\d+-\d+/\d+|\*/\d+))*\s?){5}$`).MatchString(req.Time) { - return errors.New("时间格式错误") - } - if !cron.Status { return errors.New("计划任务已禁用") } diff --git a/internal/data/ssh.go b/internal/data/ssh.go index 24a521da..970b6b30 100644 --- a/internal/data/ssh.go +++ b/internal/data/ssh.go @@ -1,12 +1,12 @@ package data import ( - "errors" - - "github.com/spf13/cast" + "fmt" + "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/http/request" + pkgssh "github.com/TheTNB/panel/pkg/ssh" ) type sshRepo struct { @@ -19,36 +19,71 @@ func NewSSHRepo() biz.SSHRepo { } } -func (r *sshRepo) GetInfo() (map[string]any, error) { - host, _ := r.settingRepo.Get(biz.SettingKeySshHost) - port, _ := r.settingRepo.Get(biz.SettingKeySshPort) - user, _ := r.settingRepo.Get(biz.SettingKeySshUser) - password, _ := r.settingRepo.Get(biz.SettingKeySshPassword) - if len(host) == 0 || len(user) == 0 || len(password) == 0 { - return nil, errors.New("SSH 配置不完整") - } - - return map[string]any{ - "host": host, - "port": cast.ToInt(port), - "user": user, - "password": password, - }, nil +func (r *sshRepo) List(page, limit uint) ([]*biz.SSH, int64, error) { + var ssh []*biz.SSH + var total int64 + err := app.Orm.Model(&biz.SSH{}).Omit("Hosts").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&ssh).Error + return ssh, total, err } -func (r *sshRepo) UpdateInfo(req *request.SSHUpdateInfo) error { - if err := r.settingRepo.Set(biz.SettingKeySshHost, req.Host); err != nil { - return err - } - if err := r.settingRepo.Set(biz.SettingKeySshPort, cast.ToString(req.Port)); err != nil { - return err - } - if err := r.settingRepo.Set(biz.SettingKeySshUser, req.User); err != nil { - return err - } - if err := r.settingRepo.Set(biz.SettingKeySshPassword, req.Password); err != nil { - return err +func (r *sshRepo) Get(id uint) (*biz.SSH, error) { + ssh := new(biz.SSH) + if err := app.Orm.Where("id = ?", id).First(ssh).Error; err != nil { + return nil, err } - return nil + return ssh, nil +} + +func (r *sshRepo) Create(req *request.SSHCreate) error { + conf := pkgssh.ClientConfig{ + AuthMethod: pkgssh.AuthMethod(req.AuthMethod), + Host: fmt.Sprintf("%s:%d", req.Host, req.Port), + User: req.User, + Password: req.Password, + Key: req.Key, + } + _, err := pkgssh.NewSSHClient(conf) + if err != nil { + return fmt.Errorf("failed to check ssh connection: %v", err) + } + + ssh := &biz.SSH{ + Name: req.Name, + Host: req.Host, + Port: req.Port, + Config: conf, + Remark: req.Remark, + } + + return app.Orm.Create(ssh).Error +} + +func (r *sshRepo) Update(req *request.SSHUpdate) error { + conf := pkgssh.ClientConfig{ + AuthMethod: pkgssh.AuthMethod(req.AuthMethod), + Host: fmt.Sprintf("%s:%d", req.Host, req.Port), + User: req.User, + Password: req.Password, + Key: req.Key, + } + _, err := pkgssh.NewSSHClient(conf) + if err != nil { + return fmt.Errorf("failed to check ssh connection: %v", err) + } + + ssh := &biz.SSH{ + ID: req.ID, + Name: req.Name, + Host: req.Host, + Port: req.Port, + Config: conf, + Remark: req.Remark, + } + + return app.Orm.Model(ssh).Updates(ssh).Error +} + +func (r *sshRepo) Delete(id uint) error { + return app.Orm.Delete(&biz.SSH{}, id).Error } diff --git a/internal/http/request/ssh.go b/internal/http/request/ssh.go index 8d891bec..e091280a 100644 --- a/internal/http/request/ssh.go +++ b/internal/http/request/ssh.go @@ -1,8 +1,24 @@ package request -type SSHUpdateInfo struct { - Host string `json:"host" form:"host" validate:"required"` - Port int `json:"port" form:"port" validate:"required,number,gte=1,lte=65535"` - User string `json:"user" form:"user" validate:"required"` - Password string `json:"password" form:"password" validate:"required"` +type SSHCreate struct { + Name string `json:"name" form:"name"` + Host string `json:"host" form:"host" validate:"required"` + Port uint `json:"port" form:"port" validate:"required,number,gte=1,lte=65535"` + AuthMethod string `json:"auth_method" form:"auth_method" validate:"required,oneof=password publickey"` + User string `json:"user" form:"user" validate:"required_if=AuthMethod password"` + Password string `json:"password" form:"password" validate:"required_if=AuthMethod password"` + Key string `json:"key" form:"key" validate:"required_if=AuthMethod publickey"` + Remark string `json:"remark" form:"remark"` +} + +type SSHUpdate struct { + ID uint `form:"id" json:"id" validate:"required,exists=sshes id"` + Name string `json:"name" form:"name"` + Host string `json:"host" form:"host" validate:"required"` + Port uint `json:"port" form:"port" validate:"required,number,gte=1,lte=65535"` + AuthMethod string `json:"auth_method" form:"auth_method" validate:"required,oneof=password publickey"` + User string `json:"user" form:"user" validate:"required_if=AuthMethod password"` + Password string `json:"password" form:"password" validate:"required_if=AuthMethod password"` + Key string `json:"key" form:"key" validate:"required_if=AuthMethod publickey"` + Remark string `json:"remark" form:"remark"` } diff --git a/internal/migration/v1.go b/internal/migration/v1.go index 75fbb7e5..8a5d5ad7 100644 --- a/internal/migration/v1.go +++ b/internal/migration/v1.go @@ -42,4 +42,17 @@ func init() { ) }, }) + Migrations = append(Migrations, &gormigrate.Migration{ + ID: "20241022-ssh", + Migrate: func(tx *gorm.DB) error { + return tx.AutoMigrate( + &biz.SSH{}, + ) + }, + Rollback: func(tx *gorm.DB) error { + return tx.Migrator().DropTable( + &biz.SSH{}, + ) + }, + }) } diff --git a/internal/route/http.go b/internal/route/http.go index d67a2ae9..290e9baa 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -158,8 +158,11 @@ func Http(r chi.Router) { r.Route("/ssh", func(r chi.Router) { r.Use(middleware.MustLogin) ssh := service.NewSSHService() - r.Get("/info", ssh.GetInfo) - r.Post("/info", ssh.UpdateInfo) + r.Get("/", ssh.List) + r.Post("/", ssh.Create) + r.Put("/{id}", ssh.Update) + r.Get("/{id}", ssh.Get) + r.Delete("/{id}", ssh.Delete) }) r.Route("/container", func(r chi.Router) { diff --git a/internal/service/ssh.go b/internal/service/ssh.go index 0053383d..e72e070d 100644 --- a/internal/service/ssh.go +++ b/internal/service/ssh.go @@ -1,6 +1,7 @@ package service import ( + "github.com/go-rat/chix" "net/http" "github.com/TheTNB/panel/internal/biz" @@ -18,25 +19,82 @@ func NewSSHService() *SSHService { } } -func (s *SSHService) GetInfo(w http.ResponseWriter, r *http.Request) { - info, err := s.sshRepo.GetInfo() - if err != nil { - Error(w, http.StatusInternalServerError, "%v", err) - return - } - - Success(w, info) -} - -func (s *SSHService) UpdateInfo(w http.ResponseWriter, r *http.Request) { - req, err := Bind[request.SSHUpdateInfo](r) +func (s *SSHService) List(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.Paginate](r) if err != nil { Error(w, http.StatusUnprocessableEntity, "%v", err) return } - if err = s.sshRepo.UpdateInfo(req); err != nil { + cron, total, err := s.sshRepo.List(req.Page, req.Limit) + if err != nil { Error(w, http.StatusInternalServerError, "%v", err) return } + + Success(w, chix.M{ + "total": total, + "items": cron, + }) +} + +func (s *SSHService) Create(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.SSHCreate](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + if err = s.sshRepo.Create(req); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) +} + +func (s *SSHService) Update(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.SSHUpdate](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + if err = s.sshRepo.Update(req); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) +} + +func (s *SSHService) Get(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ID](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + cron, err := s.sshRepo.Get(req.ID) + if err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, cron) +} + +func (s *SSHService) Delete(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ID](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + if err = s.sshRepo.Delete(req.ID); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) } diff --git a/internal/service/ws.go b/internal/service/ws.go index d7723eb9..da707bec 100644 --- a/internal/service/ws.go +++ b/internal/service/ws.go @@ -3,17 +3,16 @@ package service import ( "bufio" "context" + "github.com/TheTNB/panel/internal/http/request" "net/http" "sync" - "github.com/gorilla/websocket" - "github.com/spf13/cast" - "github.com/TheTNB/panel/internal/app" "github.com/TheTNB/panel/internal/biz" "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/pkg/shell" "github.com/TheTNB/panel/pkg/ssh" + "github.com/gorilla/websocket" ) type WsService struct { @@ -27,11 +26,17 @@ func NewWsService() *WsService { } func (s *WsService) Session(w http.ResponseWriter, r *http.Request) { - info, err := s.sshRepo.GetInfo() + req, err := Bind[request.ID](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + info, err := s.sshRepo.Get(req.ID) if err != nil { Error(w, http.StatusInternalServerError, "%v", err) return } + ws, err := s.upgrade(w, r) if err != nil { ErrorSystem(w) @@ -39,12 +44,7 @@ func (s *WsService) Session(w http.ResponseWriter, r *http.Request) { } defer ws.Close() - config := ssh.ClientConfigPassword( - cast.ToString(info["host"])+":"+cast.ToString(info["port"]), - cast.ToString(info["user"]), - cast.ToString(info["password"]), - ) - client, err := ssh.NewSSHClient(config) + client, err := ssh.NewSSHClient(info.Config) if err != nil { _ = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error())) return diff --git a/pkg/ssh/ssh.go b/pkg/ssh/ssh.go index 9282f740..278d2e31 100644 --- a/pkg/ssh/ssh.go +++ b/pkg/ssh/ssh.go @@ -6,43 +6,47 @@ import ( "golang.org/x/crypto/ssh" ) -type AuthMethod int8 +type AuthMethod string const ( - PASSWORD AuthMethod = iota + 1 - PUBLICKEY + PASSWORD AuthMethod = "password" + PUBLICKEY AuthMethod = "publickey" ) type ClientConfig struct { - AuthMethod AuthMethod - HostAddr string - User string - Password string - Key string - Timeout time.Duration + AuthMethod AuthMethod `json:"auth_method"` + Host string `json:"host"` + User string `json:"user"` + Password string `json:"password"` + Key string `json:"key"` + Timeout time.Duration `json:"timeout"` } -func ClientConfigPassword(hostAddr, user, Password string) *ClientConfig { +func ClientConfigPassword(host, user, Password string) *ClientConfig { return &ClientConfig{ - Timeout: time.Second * 5, + Timeout: 10 * time.Second, AuthMethod: PASSWORD, - HostAddr: hostAddr, + Host: host, User: user, Password: Password, } } -func ClientConfigPublicKey(hostAddr, user, key string) *ClientConfig { +func ClientConfigPublicKey(host, user, key string) *ClientConfig { return &ClientConfig{ - Timeout: time.Second * 5, + Timeout: 10 * time.Second, AuthMethod: PUBLICKEY, - HostAddr: hostAddr, + Host: host, User: user, Key: key, } } -func NewSSHClient(conf *ClientConfig) (*ssh.Client, error) { +func NewSSHClient(conf ClientConfig) (*ssh.Client, error) { + if conf.Timeout == 0 { + conf.Timeout = 10 * time.Second + } + config := &ssh.ClientConfig{} config.SetDefaults() config.Timeout = conf.Timeout @@ -59,7 +63,7 @@ func NewSSHClient(conf *ClientConfig) (*ssh.Client, error) { } config.Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)} } - c, err := ssh.Dial("tcp", conf.HostAddr, config) // TODO support ipv6 + c, err := ssh.Dial("tcp", conf.Host, config) // TODO support ipv6 if err != nil { return nil, err } diff --git a/web/package.json b/web/package.json index 29435823..e0580fa4 100644 --- a/web/package.json +++ b/web/package.json @@ -20,6 +20,8 @@ "format": "prettier --write src/" }, "dependencies": { + "@fontsource-variable/noto-sans-sc": "^5.1.0", + "@fontsource/jetbrains-mono": "^5.1.1", "@guolao/vue-monaco-editor": "^1.5.4", "@vue-js-cron/naive-ui": "^2.0.5", "@vueuse/core": "^11.1.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 2ca9e20e..49e3deae 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + '@fontsource-variable/noto-sans-sc': + specifier: ^5.1.0 + version: 5.1.0 + '@fontsource/jetbrains-mono': + specifier: ^5.1.1 + version: 5.1.1 '@guolao/vue-monaco-editor': specifier: ^1.5.4 version: 1.5.4(monaco-editor@0.52.0)(vue@3.5.12(typescript@5.6.3)) @@ -667,6 +673,12 @@ packages: resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@fontsource-variable/noto-sans-sc@5.1.0': + resolution: {integrity: sha512-PnnGFWTVyoSdiZU/QvH3w8NroJkBazLWEzrrfqdGX/S6txq6iDoNMG+bolTsAQ4dXbdvfKBTyX6kw/xg5bmdcg==} + + '@fontsource/jetbrains-mono@5.1.1': + resolution: {integrity: sha512-5rwvmdQQpXev4LlBX1P+7h2dguu6iwW/9Npjde4+DEq+HgpVJI/7QY8DI1NVVFdvLtXZNP+vO2L/5BQED6FUhA==} + '@guolao/vue-monaco-editor@1.5.4': resolution: {integrity: sha512-eyBAqxJeDpV4mZYZSpNvh3xUgKCld5eEe0dBtjJhsy2+L0MB6PYFZ/FbPHNwskgp2RoIpfn1DLrIhXXE3lVbwQ==} peerDependencies: @@ -845,30 +857,35 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-glibc@2.4.1': resolution: {integrity: sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.4.1': resolution: {integrity: sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.4.1': resolution: {integrity: sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.4.1': resolution: {integrity: sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.4.1': resolution: {integrity: sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==} @@ -936,46 +953,55 @@ packages: resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.24.0': resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.24.0': resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.24.0': resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.24.0': resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.24.0': resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.24.0': resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.24.0': resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.24.0': resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} @@ -3814,6 +3840,10 @@ snapshots: '@eslint/js@8.57.1': {} + '@fontsource-variable/noto-sans-sc@5.1.0': {} + + '@fontsource/jetbrains-mono@5.1.1': {} + '@guolao/vue-monaco-editor@1.5.4(monaco-editor@0.52.0)(vue@3.5.12(typescript@5.6.3))': dependencies: '@monaco-editor/loader': 1.4.0(monaco-editor@0.52.0) diff --git a/web/src/api/panel/cron/index.ts b/web/src/api/panel/cron/index.ts index 47377a1d..a2123cd6 100644 --- a/web/src/api/panel/cron/index.ts +++ b/web/src/api/panel/cron/index.ts @@ -15,8 +15,6 @@ export default { request.put('/cron/' + id, { name, time, script }), // 删除任务 delete: (id: number): Promise> => request.delete('/cron/' + id), - // 获取任务日志 - log: (id: number): Promise> => request.get('/cron/' + id + '/log'), // 修改任务状态 status: (id: number, status: boolean): Promise> => request.post('/cron/' + id + '/status', { status }) diff --git a/web/src/api/panel/ssh/index.ts b/web/src/api/panel/ssh/index.ts index 3745f30e..03d2a16b 100644 --- a/web/src/api/panel/ssh/index.ts +++ b/web/src/api/panel/ssh/index.ts @@ -3,13 +3,15 @@ import type { AxiosResponse } from 'axios' import { request } from '@/utils' export default { - // 获取信息 - info: (): Promise> => request.get('/ssh/info'), - // 保存信息 - saveInfo: ( - host: string, - port: number, - user: string, - password: string - ): Promise> => request.post('/ssh/info', { host, port, user, password }) + // 获取主机列表 + list: (page: number, limit: number): Promise> => + request.get('/ssh', { params: { page, limit } }), + // 获取主机信息 + get: (id: number): Promise> => request.get(`/ssh/${id}`), + // 创建主机 + create: (req: any): Promise> => request.post('/ssh', req), + // 修改主机 + update: (id: number, req: any): Promise> => request.put(`/ssh/${id}`, req), + // 删除主机 + delete: (id: number): Promise> => request.delete(`/ssh/${id}`) } diff --git a/web/src/api/ws/index.ts b/web/src/api/ws/index.ts index 45f7f018..158521ce 100644 --- a/web/src/api/ws/index.ts +++ b/web/src/api/ws/index.ts @@ -1,11 +1,11 @@ const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws' -const base = `${protocol}://${window.location.host}/api/ws/` +const base = `${protocol}://${window.location.host}/api/ws` export default { // 执行命令 exec: (cmd: string): Promise => { return new Promise((resolve, reject) => { - const ws = new WebSocket(base + 'exec') + const ws = new WebSocket(`${base}/exec`) ws.onopen = () => { ws.send(cmd) resolve(ws) @@ -14,9 +14,9 @@ export default { }) }, // 连接SSH - ssh: (): Promise => { + ssh: (id: number): Promise => { return new Promise((resolve, reject) => { - const ws = new WebSocket(base + 'ssh') + const ws = new WebSocket(`${base}/ssh?id=${id}`) ws.onopen = () => resolve(ws) ws.onerror = (e) => reject(e) }) diff --git a/web/src/main.ts b/web/src/main.ts index 6c6842d9..cc9b19fa 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -14,7 +14,7 @@ import { setupNaiveDiscreteApi } from './utils' import { install as VueMonacoEditorPlugin } from '@guolao/vue-monaco-editor' import dashboard from '@/api/panel/dashboard' -import CronNaivePlugin, { CronNaive } from '@vue-js-cron/naive-ui' +import CronNaivePlugin from '@vue-js-cron/naive-ui' async function setupApp() { const app = createApp(App) @@ -27,7 +27,6 @@ async function setupApp() { } }) app.use(CronNaivePlugin) - app.component('CronNaive', CronNaive) await setupStore(app) await setupNaiveDiscreteApi() await setupPanel().then(() => { diff --git a/web/src/styles/index.scss b/web/src/styles/index.scss index 04c805fa..2fdc985e 100644 --- a/web/src/styles/index.scss +++ b/web/src/styles/index.scss @@ -1,3 +1,5 @@ +@use '@fontsource-variable/noto-sans-sc'; + html { font-size: 4px; // * 方便unocss计算:1单位 = 0.25rem = 1px } @@ -8,7 +10,7 @@ body { height: 100%; overflow: hidden; background-color: #f2f2f2; - font-family: 'Encode Sans Condensed', sans-serif; + font-family: 'Noto Sans SC Variable', sans-serif; } #app { @@ -37,7 +39,7 @@ body { overflow: auto; &::-webkit-scrollbar { - width: 8; + width: 8px; height: 8px; } } diff --git a/web/src/views/app/IndexView.vue b/web/src/views/app/IndexView.vue index 594359ae..66515243 100644 --- a/web/src/views/app/IndexView.vue +++ b/web/src/views/app/IndexView.vue @@ -244,7 +244,7 @@ onMounted(() => {