From d366cbd00d42552ba7f1d7499728654f4e7cbf29 Mon Sep 17 00:00:00 2001 From: neverteaser Date: Tue, 16 Mar 2021 14:00:03 +0800 Subject: [PATCH 01/34] =?UTF-8?q?style(be):=E4=BD=BF=E7=94=A8goimport=20?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=E5=B9=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0golangci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/golangci-lint.yml | 21 +++++++++++++++++++++ .golangci.yml | 26 ++++++++++++++++++++++++++ go.mod | 1 + main.go | 28 +++++++++++++++------------- pkg/api/account.go | 5 +++-- pkg/api/asset.go | 8 +++++--- pkg/api/command.go | 8 +++++--- pkg/api/credential.go | 8 +++++--- pkg/api/job.go | 8 +++++--- pkg/api/login-log.go | 13 +++++++++---- pkg/api/middleware.go | 10 ++++++---- pkg/api/overview.go | 3 ++- pkg/api/property.go | 4 +++- pkg/api/resource-sharer.go | 3 ++- pkg/api/routes.go | 1 + pkg/api/security.go | 10 ++++++---- pkg/api/session.go | 16 +++++++++------- pkg/api/ssh.go | 14 ++++++++------ pkg/api/tunnel.go | 23 ++++++++++++----------- pkg/api/user-group.go | 8 +++++--- pkg/api/user.go | 20 +++++++++++++------- pkg/config/config.go | 3 ++- pkg/global/global.go | 3 ++- pkg/global/store.go | 8 +++++--- pkg/handle/runner.go | 14 ++++++++------ pkg/model/asset.go | 7 ++++--- pkg/model/asset_attribute.go | 6 ++++-- pkg/model/credential.go | 2 +- pkg/model/job.go | 14 ++++++++------ pkg/model/login_log.go | 3 ++- pkg/model/property.go | 6 ++++-- pkg/model/resource-sharer.go | 21 +++++++++++++++------ pkg/model/session.go | 7 ++++--- pkg/model/user-group.go | 3 ++- pkg/model/user.go | 3 ++- pkg/term/next_terminal.go | 3 ++- pkg/term/recording.go | 3 ++- pkg/term/ssh.go | 3 ++- pkg/term/test/test_ssh.go | 9 +++++---- pkg/utils/utils.go | 12 ++++++++---- 40 files changed, 244 insertions(+), 124 deletions(-) create mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .golangci.yml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..a3a4a90 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,21 @@ +name: golangci-lint +on: + push: + tags: + - v* + branches: + - master + - be-* + - dev + pull_request: +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: v1.29 \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..fe27639 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,26 @@ +linters-settings: + goimports: + local-prefixes: uqingtech.com + +linters: + disable-all: true + enable: + - deadcode + - errcheck + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - staticcheck + - structcheck + - typecheck + - unused + - varcheck + +run: + skip-dirs: + - api/ + - module/ + skip-files: + - test_ssh.go diff --git a/go.mod b/go.mod index db596f1..dedeb70 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/labstack/echo/v4 v4.1.17 github.com/labstack/gommon v0.3.0 github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/pkg/errors v0.9.1 github.com/pkg/sftp v1.12.0 github.com/pquerna/otp v1.3.0 github.com/robfig/cron/v3 v3.0.1 diff --git a/main.go b/main.go index 939dd7a..d782322 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,20 @@ package main import ( "bytes" "fmt" + "io" + "os" + "strconv" + "strings" + "time" + + "next-terminal/pkg/api" + "next-terminal/pkg/config" + "next-terminal/pkg/constant" + "next-terminal/pkg/global" + "next-terminal/pkg/handle" + "next-terminal/pkg/model" + "next-terminal/pkg/utils" + nested "github.com/antonfisher/nested-logrus-formatter" "github.com/labstack/gommon/log" "github.com/patrickmn/go-cache" @@ -12,18 +26,6 @@ import ( "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" - "io" - "next-terminal/pkg/api" - "next-terminal/pkg/config" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/handle" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" - "os" - "strconv" - "strings" - "time" ) const Version = "v0.3.3" @@ -227,7 +229,7 @@ func Run() error { if err != nil { return err } - if jobs == nil || len(jobs) == 0 { + if jobs == nil { job := model.Job{ ID: utils.UUID(), Name: "资产状态检测", diff --git a/pkg/api/account.go b/pkg/api/account.go index 05c7b27..0059088 100644 --- a/pkg/api/account.go +++ b/pkg/api/account.go @@ -1,12 +1,13 @@ package api import ( + "strings" + "time" + "next-terminal/pkg/global" "next-terminal/pkg/model" "next-terminal/pkg/totp" "next-terminal/pkg/utils" - "strings" - "time" "github.com/labstack/echo/v4" ) diff --git a/pkg/api/asset.go b/pkg/api/asset.go index 0c8b77b..adb854e 100644 --- a/pkg/api/asset.go +++ b/pkg/api/asset.go @@ -5,12 +5,14 @@ import ( "encoding/csv" "encoding/json" "errors" - "github.com/labstack/echo/v4" + "strconv" + "strings" + "next-terminal/pkg/constant" "next-terminal/pkg/model" "next-terminal/pkg/utils" - "strconv" - "strings" + + "github.com/labstack/echo/v4" ) func AssetCreateEndpoint(c echo.Context) error { diff --git a/pkg/api/command.go b/pkg/api/command.go index 78ba421..0beb161 100644 --- a/pkg/api/command.go +++ b/pkg/api/command.go @@ -2,11 +2,13 @@ package api import ( "errors" - "github.com/labstack/echo/v4" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" "strconv" "strings" + + "next-terminal/pkg/model" + "next-terminal/pkg/utils" + + "github.com/labstack/echo/v4" ) func CommandCreateEndpoint(c echo.Context) error { diff --git a/pkg/api/credential.go b/pkg/api/credential.go index f2ee42b..de59b44 100644 --- a/pkg/api/credential.go +++ b/pkg/api/credential.go @@ -2,12 +2,14 @@ package api import ( "errors" - "github.com/labstack/echo/v4" + "strconv" + "strings" + "next-terminal/pkg/constant" "next-terminal/pkg/model" "next-terminal/pkg/utils" - "strconv" - "strings" + + "github.com/labstack/echo/v4" ) func CredentialAllEndpoint(c echo.Context) error { diff --git a/pkg/api/job.go b/pkg/api/job.go index 7a406af..23c6a25 100644 --- a/pkg/api/job.go +++ b/pkg/api/job.go @@ -1,11 +1,13 @@ package api import ( - "github.com/labstack/echo/v4" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" "strconv" "strings" + + "next-terminal/pkg/model" + "next-terminal/pkg/utils" + + "github.com/labstack/echo/v4" ) func JobCreateEndpoint(c echo.Context) error { diff --git a/pkg/api/login-log.go b/pkg/api/login-log.go index a50c39e..b94c4a3 100644 --- a/pkg/api/login-log.go +++ b/pkg/api/login-log.go @@ -1,11 +1,14 @@ package api import ( - "github.com/labstack/echo/v4" - "next-terminal/pkg/global" - "next-terminal/pkg/model" "strconv" "strings" + + "next-terminal/pkg/global" + "next-terminal/pkg/model" + + "github.com/labstack/echo/v4" + "github.com/sirupsen/logrus" ) func LoginLogPagingEndpoint(c echo.Context) error { @@ -32,7 +35,9 @@ func LoginLogDeleteEndpoint(c echo.Context) error { for i := range split { token := split[i] global.Cache.Delete(token) - model.Logout(token) + if err := model.Logout(token); err != nil { + logrus.WithError(err).Error("Cache Delete Failed") + } } if err := model.DeleteLoginLogByIdIn(split); err != nil { return err diff --git a/pkg/api/middleware.go b/pkg/api/middleware.go index 55d4635..8ce00b0 100644 --- a/pkg/api/middleware.go +++ b/pkg/api/middleware.go @@ -2,14 +2,16 @@ package api import ( "fmt" - "github.com/labstack/echo/v4" "net" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/utils" "regexp" "strings" "time" + + "next-terminal/pkg/constant" + "next-terminal/pkg/global" + "next-terminal/pkg/utils" + + "github.com/labstack/echo/v4" ) func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc { diff --git a/pkg/api/overview.go b/pkg/api/overview.go index 174a6a7..8c6d9f4 100644 --- a/pkg/api/overview.go +++ b/pkg/api/overview.go @@ -1,9 +1,10 @@ package api import ( - "github.com/labstack/echo/v4" "next-terminal/pkg/constant" "next-terminal/pkg/model" + + "github.com/labstack/echo/v4" ) type Counter struct { diff --git a/pkg/api/property.go b/pkg/api/property.go index a7bfaba..6608d88 100644 --- a/pkg/api/property.go +++ b/pkg/api/property.go @@ -3,9 +3,11 @@ package api import ( "errors" "fmt" + + "next-terminal/pkg/model" + "github.com/labstack/echo/v4" "gorm.io/gorm" - "next-terminal/pkg/model" ) func PropertyGetEndpoint(c echo.Context) error { diff --git a/pkg/api/resource-sharer.go b/pkg/api/resource-sharer.go index da4551a..0a416ec 100644 --- a/pkg/api/resource-sharer.go +++ b/pkg/api/resource-sharer.go @@ -1,8 +1,9 @@ package api import ( - "github.com/labstack/echo/v4" "next-terminal/pkg/model" + + "github.com/labstack/echo/v4" ) type RU struct { diff --git a/pkg/api/routes.go b/pkg/api/routes.go index b81396f..09a8590 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -2,6 +2,7 @@ package api import ( "net/http" + "next-terminal/pkg/constant" "next-terminal/pkg/global" "next-terminal/pkg/log" diff --git a/pkg/api/security.go b/pkg/api/security.go index 23dccfc..52fe8aa 100644 --- a/pkg/api/security.go +++ b/pkg/api/security.go @@ -1,12 +1,14 @@ package api import ( - "github.com/labstack/echo/v4" + "strconv" + "strings" + "next-terminal/pkg/global" "next-terminal/pkg/model" "next-terminal/pkg/utils" - "strconv" - "strings" + + "github.com/labstack/echo/v4" ) func SecurityCreateEndpoint(c echo.Context) error { @@ -33,7 +35,7 @@ func ReloadAccessSecurity() error { if err != nil { return err } - if rules != nil && len(rules) > 0 { + if rules != nil { var securities []*global.Security for i := 0; i < len(rules); i++ { rule := global.Security{ diff --git a/pkg/api/session.go b/pkg/api/session.go index dad4282..469ca5a 100644 --- a/pkg/api/session.go +++ b/pkg/api/session.go @@ -4,21 +4,23 @@ import ( "bytes" "errors" "fmt" - "github.com/labstack/echo/v4" - "github.com/pkg/sftp" - "github.com/sirupsen/logrus" "io" "io/ioutil" "net/http" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" "os" "path" "strconv" "strings" "sync" + + "next-terminal/pkg/constant" + "next-terminal/pkg/global" + "next-terminal/pkg/model" + "next-terminal/pkg/utils" + + "github.com/labstack/echo/v4" + "github.com/pkg/sftp" + "github.com/sirupsen/logrus" ) func SessionPagingEndpoint(c echo.Context) error { diff --git a/pkg/api/ssh.go b/pkg/api/ssh.go index f7641e1..50e112a 100644 --- a/pkg/api/ssh.go +++ b/pkg/api/ssh.go @@ -2,19 +2,21 @@ package api import ( "encoding/json" - "github.com/gorilla/websocket" - "github.com/labstack/echo/v4" - "github.com/sirupsen/logrus" "net/http" + "path" + "strconv" + "time" + "next-terminal/pkg/constant" "next-terminal/pkg/global" "next-terminal/pkg/guacd" "next-terminal/pkg/model" "next-terminal/pkg/term" "next-terminal/pkg/utils" - "path" - "strconv" - "time" + + "github.com/gorilla/websocket" + "github.com/labstack/echo/v4" + "github.com/sirupsen/logrus" ) var UpGrader = websocket.Upgrader{ diff --git a/pkg/api/tunnel.go b/pkg/api/tunnel.go index ea8788c..dd43161 100644 --- a/pkg/api/tunnel.go +++ b/pkg/api/tunnel.go @@ -2,15 +2,17 @@ package api import ( "errors" - "github.com/gorilla/websocket" - "github.com/labstack/echo/v4" - "github.com/sirupsen/logrus" + "path" + "strconv" + "next-terminal/pkg/constant" "next-terminal/pkg/global" "next-terminal/pkg/guacd" "next-terminal/pkg/model" - "path" - "strconv" + + "github.com/gorilla/websocket" + "github.com/labstack/echo/v4" + "github.com/sirupsen/logrus" ) const ( @@ -98,7 +100,6 @@ func TunEndpoint(c echo.Context) error { configuration.SetParameter(guacd.DisableBitmapCaching, propertyMap[guacd.DisableBitmapCaching]) configuration.SetParameter(guacd.DisableOffscreenCaching, propertyMap[guacd.DisableOffscreenCaching]) configuration.SetParameter(guacd.DisableGlyphCaching, propertyMap[guacd.DisableGlyphCaching]) - break case "ssh": if len(session.PrivateKey) > 0 && session.PrivateKey != "-" { configuration.SetParameter("username", session.Username) @@ -114,11 +115,9 @@ func TunEndpoint(c echo.Context) error { configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme]) configuration.SetParameter(guacd.Backspace, propertyMap[guacd.Backspace]) configuration.SetParameter(guacd.TerminalType, propertyMap[guacd.TerminalType]) - break case "vnc": configuration.SetParameter("username", session.Username) configuration.SetParameter("password", session.Password) - break case "telnet": configuration.SetParameter("username", session.Username) configuration.SetParameter("password", session.Password) @@ -128,7 +127,6 @@ func TunEndpoint(c echo.Context) error { configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme]) configuration.SetParameter(guacd.Backspace, propertyMap[guacd.Backspace]) configuration.SetParameter(guacd.TerminalType, propertyMap[guacd.TerminalType]) - break case "kubernetes": configuration.SetParameter(guacd.FontSize, propertyMap[guacd.FontSize]) @@ -136,6 +134,9 @@ func TunEndpoint(c echo.Context) error { configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme]) configuration.SetParameter(guacd.Backspace, propertyMap[guacd.Backspace]) configuration.SetParameter(guacd.TerminalType, propertyMap[guacd.TerminalType]) + default: + logrus.WithField("configuration.Protocol", configuration.Protocol).Error("UnSupport Protocol") + return Fail(c, 400, "不支持的协议") } configuration.SetParameter("hostname", session.IP) @@ -209,7 +210,7 @@ func TunEndpoint(c echo.Context) error { } go func() { - for true { + for { instruction, err := tunnel.Read() if err != nil { if connectionId == "" { @@ -230,7 +231,7 @@ func TunEndpoint(c echo.Context) error { } }() - for true { + for { _, message, err := ws.ReadMessage() if err != nil { if connectionId == "" { diff --git a/pkg/api/user-group.go b/pkg/api/user-group.go index 5d9e840..f20ba91 100644 --- a/pkg/api/user-group.go +++ b/pkg/api/user-group.go @@ -1,12 +1,14 @@ package api import ( - "github.com/labstack/echo/v4" + "strconv" + "strings" + "next-terminal/pkg/global" "next-terminal/pkg/model" "next-terminal/pkg/utils" - "strconv" - "strings" + + "github.com/labstack/echo/v4" ) type UserGroup struct { diff --git a/pkg/api/user.go b/pkg/api/user.go index 157c2cf..4e3c863 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -1,12 +1,15 @@ package api import ( - "github.com/labstack/echo/v4" + "strconv" + "strings" + "next-terminal/pkg/global" "next-terminal/pkg/model" "next-terminal/pkg/utils" - "strconv" - "strings" + + "github.com/labstack/echo/v4" + "github.com/sirupsen/logrus" ) func UserCreateEndpoint(c echo.Context) error { @@ -87,12 +90,15 @@ func UserDeleteEndpoint(c echo.Context) error { if err != nil { return err } - if loginLogs != nil && len(loginLogs) > 0 { - for j := range loginLogs { - global.Cache.Delete(loginLogs[j].ID) - model.Logout(loginLogs[j].ID) + + for j := range loginLogs { + global.Cache.Delete(loginLogs[j].ID) + if err := model.Logout(loginLogs[j].ID); err != nil { + logrus.WithError(err).WithField("id:", loginLogs[j].ID).Error("Cache Deleted Error") + return Fail(c, 500, "强制下线错误") } } + // 删除用户 model.DeleteUserById(userId) } diff --git a/pkg/config/config.go b/pkg/config/config.go index ec37f6a..4cc2029 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,9 +1,10 @@ package config import ( - "github.com/spf13/pflag" "strings" + "github.com/spf13/pflag" + "github.com/spf13/viper" ) diff --git a/pkg/global/global.go b/pkg/global/global.go index 95d9ee0..f348b42 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -1,10 +1,11 @@ package global import ( + "next-terminal/pkg/config" + "github.com/patrickmn/go-cache" "github.com/robfig/cron/v3" "gorm.io/gorm" - "next-terminal/pkg/config" ) var DB *gorm.DB diff --git a/pkg/global/store.go b/pkg/global/store.go index 6e91a7b..c2f66dc 100644 --- a/pkg/global/store.go +++ b/pkg/global/store.go @@ -1,11 +1,13 @@ package global import ( - "github.com/gorilla/websocket" - "next-terminal/pkg/guacd" - "next-terminal/pkg/term" "strconv" "sync" + + "next-terminal/pkg/guacd" + "next-terminal/pkg/term" + + "github.com/gorilla/websocket" ) type Tun struct { diff --git a/pkg/handle/runner.go b/pkg/handle/runner.go index f9ae0c9..e6f3fc7 100644 --- a/pkg/handle/runner.go +++ b/pkg/handle/runner.go @@ -1,14 +1,16 @@ package handle import ( - "github.com/sirupsen/logrus" + "os" + "strconv" + "time" + "next-terminal/pkg/constant" "next-terminal/pkg/guacd" "next-terminal/pkg/model" "next-terminal/pkg/utils" - "os" - "strconv" - "time" + + "github.com/sirupsen/logrus" ) func RunTicker() { @@ -18,7 +20,7 @@ func RunTicker() { go func() { for range unUsedSessionTicker.C { sessions, _ := model.FindSessionByStatusIn([]string{constant.NoConnect, constant.Connecting}) - if sessions != nil && len(sessions) > 0 { + if sessions != nil { now := time.Now() for i := range sessions { if now.Sub(sessions[i].ConnectedTime.Time) > time.Hour*1 { @@ -51,7 +53,7 @@ func RunTicker() { return } - if sessions != nil && len(sessions) > 0 { + if sessions != nil { var sessionIds []string for i := range sessions { sessionIds = append(sessionIds, sessions[i].ID) diff --git a/pkg/model/asset.go b/pkg/model/asset.go index a119510..7a102a1 100644 --- a/pkg/model/asset.go +++ b/pkg/model/asset.go @@ -1,10 +1,11 @@ package model import ( + "strings" + "next-terminal/pkg/constant" "next-terminal/pkg/global" "next-terminal/pkg/utils" - "strings" ) type Asset struct { @@ -94,7 +95,7 @@ func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account return nil, 0, err } - if userGroupIds != nil && len(userGroupIds) > 0 { + if userGroupIds != nil { db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) dbCounter = dbCounter.Or("resource_sharers.user_group_id in ?", userGroupIds) } @@ -209,7 +210,7 @@ func CountAssetByUserId(userId string) (total int64, err error) { return 0, err } - if userGroupIds != nil && len(userGroupIds) > 0 { + if userGroupIds != nil { db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) } err = db.Find(&Asset{}).Count(&total).Error diff --git a/pkg/model/asset_attribute.go b/pkg/model/asset_attribute.go index de65465..8818218 100644 --- a/pkg/model/asset_attribute.go +++ b/pkg/model/asset_attribute.go @@ -2,12 +2,14 @@ package model import ( "fmt" - "github.com/labstack/echo/v4" - "gorm.io/gorm" + "next-terminal/pkg/constant" "next-terminal/pkg/global" "next-terminal/pkg/guacd" "next-terminal/pkg/utils" + + "github.com/labstack/echo/v4" + "gorm.io/gorm" ) type AssetAttribute struct { diff --git a/pkg/model/credential.go b/pkg/model/credential.go index c333cd7..ce4947d 100644 --- a/pkg/model/credential.go +++ b/pkg/model/credential.go @@ -123,7 +123,7 @@ func CountCredentialByUserId(userId string) (total int64, err error) { return 0, err } - if userGroupIds != nil && len(userGroupIds) > 0 { + if userGroupIds != nil { db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) } err = db.Find(&Credential{}).Count(&total).Error diff --git a/pkg/model/job.go b/pkg/model/job.go index 7f76888..4a6ab2b 100644 --- a/pkg/model/job.go +++ b/pkg/model/job.go @@ -4,14 +4,16 @@ import ( "encoding/json" "errors" "fmt" - "github.com/robfig/cron/v3" - "github.com/sirupsen/logrus" + "strings" + "time" + "next-terminal/pkg/constant" "next-terminal/pkg/global" "next-terminal/pkg/term" "next-terminal/pkg/utils" - "strings" - "time" + + "github.com/robfig/cron/v3" + "github.com/sirupsen/logrus" ) type Job struct { @@ -197,7 +199,7 @@ func (r CheckAssetStatusJob) Run() { assets, _ = FindAssetByIds(strings.Split(r.ResourceIds, ",")) } - if assets == nil || len(assets) == 0 { + if assets == nil { return } @@ -255,7 +257,7 @@ func (r ShellJob) Run() { assets, _ = FindAssetByProtocolAndIds("ssh", strings.Split(r.ResourceIds, ",")) } - if assets == nil || len(assets) == 0 { + if assets == nil { return } diff --git a/pkg/model/login_log.go b/pkg/model/login_log.go index 44f845a..401d292 100644 --- a/pkg/model/login_log.go +++ b/pkg/model/login_log.go @@ -1,9 +1,10 @@ package model import ( - "github.com/sirupsen/logrus" "next-terminal/pkg/global" "next-terminal/pkg/utils" + + "github.com/sirupsen/logrus" ) type LoginLog struct { diff --git a/pkg/model/property.go b/pkg/model/property.go index 00febff..2b69195 100644 --- a/pkg/model/property.go +++ b/pkg/model/property.go @@ -1,12 +1,14 @@ package model import ( - "github.com/jordan-wright/email" - "github.com/sirupsen/logrus" "net/smtp" + "next-terminal/pkg/constant" "next-terminal/pkg/global" "next-terminal/pkg/guacd" + + "github.com/jordan-wright/email" + "github.com/sirupsen/logrus" ) type Property struct { diff --git a/pkg/model/resource-sharer.go b/pkg/model/resource-sharer.go index 1139b77..1049511 100644 --- a/pkg/model/resource-sharer.go +++ b/pkg/model/resource-sharer.go @@ -1,10 +1,12 @@ package model import ( - "github.com/labstack/echo/v4" - "gorm.io/gorm" "next-terminal/pkg/global" "next-terminal/pkg/utils" + + "github.com/labstack/echo/v4" + "github.com/pkg/errors" + "gorm.io/gorm" ) type ResourceSharer struct { @@ -117,15 +119,22 @@ func AddSharerResources(userGroupId, userId, resourceType string, resourceIds [] switch resourceType { case "asset": resource := Asset{} - err = tx.Where("id = ?", resourceId).First(&resource).Error + if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil { + return errors.Wrap(err, "find asset fail") + } owner = resource.Owner case "command": resource := Command{} - err = tx.Where("id = ?", resourceId).First(&resource).Error + if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil { + return errors.Wrap(err, "find command fail") + } owner = resource.Owner case "credential": resource := Credential{} - err = tx.Where("id = ?", resourceId).First(&resource).Error + if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil { + return errors.Wrap(err, "find credential fail") + + } owner = resource.Owner } @@ -166,7 +175,7 @@ func FindAssetIdsByUserId(userId string) (assetIds []string, err error) { } db := global.DB.Table("resource_sharers").Select("resource_id").Where("user_id = ?", userId) - if groupIds != nil && len(groupIds) > 0 { + if groupIds != nil { db = db.Or("user_group_id in ?", groupIds) } err = db.Find(&sharerAssetIds).Error diff --git a/pkg/model/session.go b/pkg/model/session.go index cd7b296..78f81a6 100644 --- a/pkg/model/session.go +++ b/pkg/model/session.go @@ -1,12 +1,13 @@ package model import ( - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/utils" "os" "path" "time" + + "next-terminal/pkg/constant" + "next-terminal/pkg/global" + "next-terminal/pkg/utils" ) type Session struct { diff --git a/pkg/model/user-group.go b/pkg/model/user-group.go index 8a33eab..248d36b 100644 --- a/pkg/model/user-group.go +++ b/pkg/model/user-group.go @@ -1,9 +1,10 @@ package model import ( - "gorm.io/gorm" "next-terminal/pkg/global" "next-terminal/pkg/utils" + + "gorm.io/gorm" ) type UserGroup struct { diff --git a/pkg/model/user.go b/pkg/model/user.go index 7ed9028..b497736 100644 --- a/pkg/model/user.go +++ b/pkg/model/user.go @@ -1,9 +1,10 @@ package model import ( + "reflect" + "next-terminal/pkg/global" "next-terminal/pkg/utils" - "reflect" ) type User struct { diff --git a/pkg/term/next_terminal.go b/pkg/term/next_terminal.go index e7e1212..4970698 100644 --- a/pkg/term/next_terminal.go +++ b/pkg/term/next_terminal.go @@ -1,9 +1,10 @@ package term import ( + "io" + "github.com/pkg/sftp" "golang.org/x/crypto/ssh" - "io" ) type NextTerminal struct { diff --git a/pkg/term/recording.go b/pkg/term/recording.go index cd35efd..1beb71e 100644 --- a/pkg/term/recording.go +++ b/pkg/term/recording.go @@ -2,9 +2,10 @@ package term import ( "encoding/json" - "next-terminal/pkg/utils" "os" "time" + + "next-terminal/pkg/utils" ) type Env struct { diff --git a/pkg/term/ssh.go b/pkg/term/ssh.go index f27b6d2..3ac5aa8 100644 --- a/pkg/term/ssh.go +++ b/pkg/term/ssh.go @@ -2,8 +2,9 @@ package term import ( "fmt" - "golang.org/x/crypto/ssh" "time" + + "golang.org/x/crypto/ssh" ) func NewSshClient(ip string, port int, username, password, privateKey, passphrase string) (*ssh.Client, error) { diff --git a/pkg/term/test/test_ssh.go b/pkg/term/test/test_ssh.go index fabefdd..e714efa 100644 --- a/pkg/term/test/test_ssh.go +++ b/pkg/term/test/test_ssh.go @@ -6,6 +6,7 @@ import ( "os" "time" + "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/terminal" ) @@ -29,7 +30,7 @@ func main() { client, err := ssh.Dial("tcp", "172.16.101.32:22", sshConfig) if err != nil { - fmt.Println(err) + logrus.Error(err) } defer client.Close() @@ -68,7 +69,7 @@ func (t *SSHTerminal) updateTerminalSize() { continue } - t.Session.WindowChange(currTermHeight, currTermWidth) + err = t.Session.WindowChange(currTermHeight, currTermWidth) if err != nil { fmt.Printf("Unable to send window-change reqest: %s.", err) continue @@ -86,9 +87,9 @@ func (t *SSHTerminal) interactiveSession() error { defer func() { if t.exitMsg == "" { - fmt.Fprintln(os.Stdout, "the connection was closed on the remote side on ", time.Now().Format(time.RFC822)) + logrus.Info(os.Stdout, "the connection was closed on the remote side on ", time.Now().Format(time.RFC822)) } else { - fmt.Fprintln(os.Stdout, t.exitMsg) + logrus.Info(os.Stdout, t.exitMsg) } }() diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 6dad7bb..dcb7883 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -18,6 +18,7 @@ import ( "time" "github.com/gofrs/uuid" + "github.com/sirupsen/logrus" "golang.org/x/crypto/bcrypt" ) @@ -103,10 +104,7 @@ func ImageToBase64Encode(img image.Image) (string, error) { func FileExists(path string) bool { _, err := os.Stat(path) //os.Stat获取文件信息 if err != nil { - if os.IsExist(err) { - return true - } - return false + return os.IsExist(err) } return true } @@ -209,3 +207,9 @@ func StringToInt(in string) (out int) { out, _ = strconv.Atoi(in) return } + +func Check(f func() error) { + if err := f(); err != nil { + logrus.Error("Received error:", err) + } +} From 4f5a63751c9c3f4d83368500416609921490b663 Mon Sep 17 00:00:00 2001 From: neverteaser Date: Tue, 16 Mar 2021 17:22:16 +0800 Subject: [PATCH 02/34] =?UTF-8?q?style(be):golangci-lint=20github=20action?= =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0timeout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/golangci-lint.yml | 3 ++- main.go | 2 +- pkg/api/security.go | 2 +- pkg/handle/runner.go | 4 ++-- pkg/model/asset.go | 4 ++-- pkg/model/credential.go | 2 +- pkg/model/job.go | 4 ++-- pkg/model/resource-sharer.go | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index a3a4a90..35969fb 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -18,4 +18,5 @@ jobs: uses: golangci/golangci-lint-action@v2 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: v1.29 \ No newline at end of file + version: v1.29 + args: --timeout=10m diff --git a/main.go b/main.go index d782322..ef7781e 100644 --- a/main.go +++ b/main.go @@ -229,7 +229,7 @@ func Run() error { if err != nil { return err } - if jobs == nil { + if len(jobs) == 0 { job := model.Job{ ID: utils.UUID(), Name: "资产状态检测", diff --git a/pkg/api/security.go b/pkg/api/security.go index 52fe8aa..e7e2874 100644 --- a/pkg/api/security.go +++ b/pkg/api/security.go @@ -35,7 +35,7 @@ func ReloadAccessSecurity() error { if err != nil { return err } - if rules != nil { + if len(rules) > 0 { var securities []*global.Security for i := 0; i < len(rules); i++ { rule := global.Security{ diff --git a/pkg/handle/runner.go b/pkg/handle/runner.go index e6f3fc7..0ecaa4a 100644 --- a/pkg/handle/runner.go +++ b/pkg/handle/runner.go @@ -20,7 +20,7 @@ func RunTicker() { go func() { for range unUsedSessionTicker.C { sessions, _ := model.FindSessionByStatusIn([]string{constant.NoConnect, constant.Connecting}) - if sessions != nil { + if len(sessions) > 0 { now := time.Now() for i := range sessions { if now.Sub(sessions[i].ConnectedTime.Time) > time.Hour*1 { @@ -53,7 +53,7 @@ func RunTicker() { return } - if sessions != nil { + if len(sessions) > 0 { var sessionIds []string for i := range sessions { sessionIds = append(sessionIds, sessions[i].ID) diff --git a/pkg/model/asset.go b/pkg/model/asset.go index 7a102a1..6f46eb3 100644 --- a/pkg/model/asset.go +++ b/pkg/model/asset.go @@ -95,7 +95,7 @@ func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account return nil, 0, err } - if userGroupIds != nil { + if len(userGroupIds) > 0 { db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) dbCounter = dbCounter.Or("resource_sharers.user_group_id in ?", userGroupIds) } @@ -210,7 +210,7 @@ func CountAssetByUserId(userId string) (total int64, err error) { return 0, err } - if userGroupIds != nil { + if len(userGroupIds) > 0 { db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) } err = db.Find(&Asset{}).Count(&total).Error diff --git a/pkg/model/credential.go b/pkg/model/credential.go index ce4947d..f198f09 100644 --- a/pkg/model/credential.go +++ b/pkg/model/credential.go @@ -123,7 +123,7 @@ func CountCredentialByUserId(userId string) (total int64, err error) { return 0, err } - if userGroupIds != nil { + if len(userGroupIds) > 0 { db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) } err = db.Find(&Credential{}).Count(&total).Error diff --git a/pkg/model/job.go b/pkg/model/job.go index 4a6ab2b..f7562f5 100644 --- a/pkg/model/job.go +++ b/pkg/model/job.go @@ -199,7 +199,7 @@ func (r CheckAssetStatusJob) Run() { assets, _ = FindAssetByIds(strings.Split(r.ResourceIds, ",")) } - if assets == nil { + if len(assets) == 0 { return } @@ -257,7 +257,7 @@ func (r ShellJob) Run() { assets, _ = FindAssetByProtocolAndIds("ssh", strings.Split(r.ResourceIds, ",")) } - if assets == nil { + if len(assets) == 0 { return } diff --git a/pkg/model/resource-sharer.go b/pkg/model/resource-sharer.go index 1049511..442c7c5 100644 --- a/pkg/model/resource-sharer.go +++ b/pkg/model/resource-sharer.go @@ -175,7 +175,7 @@ func FindAssetIdsByUserId(userId string) (assetIds []string, err error) { } db := global.DB.Table("resource_sharers").Select("resource_id").Where("user_id = ?", userId) - if groupIds != nil { + if len(groupIds) > 0 { db = db.Or("user_group_id in ?", groupIds) } err = db.Find(&sharerAssetIds).Error From e1cd73260a7e409ffd314e6cb359d37d8bbf3fe7 Mon Sep 17 00:00:00 2001 From: dushixiang Date: Wed, 17 Mar 2021 20:00:36 +0800 Subject: [PATCH 03/34] =?UTF-8?q?-=20=E4=BC=98=E5=8C=96=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E7=BB=93=E6=9E=84=20-=20=E4=BF=AE=E5=A4=8D=E3=80=8CTOTP=20?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E7=95=8C=E9=9D=A2=EF=BC=8C=E5=9B=9E=E8=BD=A6?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E7=A1=AE=E5=AE=9A=E3=80=8Dclose=20#101?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/App.css | 16 ++-- web/src/App.js | 96 ++++++++++++++++++-- web/src/components/Login.js | 17 ++-- web/src/components/asset/Asset.js | 25 ----- web/src/components/command/DynamicCommand.js | 22 ----- web/src/components/credential/Credential.js | 24 ----- web/src/components/dashboard/Dashboard.js | 23 ----- web/src/components/devops/Job.js | 22 ----- web/src/components/devops/LoginLog.js | 22 ----- web/src/components/devops/Security.js | 22 ----- web/src/components/session/OfflineSession.js | 22 ----- web/src/components/session/OnlineSession.js | 22 ----- web/src/components/setting/Setting.js | 23 ----- web/src/components/user/Info.js | 22 ----- web/src/components/user/LayoutHeader.js | 87 ------------------ web/src/components/user/User.js | 24 ----- web/src/components/user/UserGroup.js | 23 ----- 17 files changed, 106 insertions(+), 406 deletions(-) delete mode 100644 web/src/components/user/LayoutHeader.js diff --git a/web/src/App.css b/web/src/App.css index 9443a7f..59ae085 100644 --- a/web/src/App.css +++ b/web/src/App.css @@ -33,7 +33,7 @@ } .layout-header { - height: 48px; + height: 60px; align-items: center; padding: 0 16px 0 0; background: #fff; @@ -45,23 +45,19 @@ padding: 0 12px; cursor: pointer; transition: all .3s; - line-height: 48px; - height: 48px; + line-height: 60px; + height: 60px; } .layout-header-right-item { margin: 0 6px; display: inline; - height: 48px; -} - -.layout-header-right-item:hover { - background-color: #eeeeee; + height: 60px; } .nickname { - line-height: 48px; - height: 48px; + line-height: 60px; + height: 60px; width: 125px; text-align: left; padding: 0 5px; diff --git a/web/src/App.js b/web/src/App.js index 0868860..8a7f90b 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import 'antd/dist/antd.css'; import './App.css'; -import {Divider, Layout, Menu} from "antd"; +import {Col, Divider, Dropdown, Layout, Menu, Popconfirm, Row, Tooltip} from "antd"; import {Link, Route, Switch} from "react-router-dom"; import Dashboard from "./components/dashboard/Dashboard"; import Asset from "./components/asset/Asset"; @@ -21,15 +21,21 @@ import { DashboardOutlined, DesktopOutlined, DisconnectOutlined, + DownOutlined, + GithubOutlined, IdcardOutlined, LinkOutlined, LoginOutlined, + LogoutOutlined, + QuestionCircleOutlined, SafetyCertificateOutlined, SettingOutlined, SolutionOutlined, TeamOutlined, UserOutlined, - UserSwitchOutlined + UserSwitchOutlined, + MenuUnfoldOutlined, + MenuFoldOutlined, } from '@ant-design/icons'; import Info from "./components/user/Info"; import request from "./common/request"; @@ -37,18 +43,18 @@ import {message} from "antd/es"; import Setting from "./components/setting/Setting"; import BatchCommand from "./components/command/BatchCommand"; import {isEmpty, NT_PACKAGE} from "./utils/utils"; -import {isAdmin} from "./service/permission"; +import {getCurrentUser, isAdmin} from "./service/permission"; import UserGroup from "./components/user/UserGroup"; import LoginLog from "./components/devops/LoginLog"; import Term from "./components/access/Term"; import Job from "./components/devops/Job"; import {Header} from "antd/es/layout/layout"; -import LayoutHeader from "./components/user/LayoutHeader"; import Security from "./components/devops/Security"; const {Footer, Sider} = Layout; const {SubMenu} = Menu; +const headerHeight = 60; class App extends Component { @@ -113,8 +119,54 @@ class App extends Component { sessionStorage.setItem('openKeys', JSON.stringify(openKeys)); } + confirm = async (e) => { + let result = await request.post('/logout'); + if (result['code'] !== 1) { + message.error(result['message']); + } else { + message.success('退出登录成功,即将跳转至登录页面。'); + window.location.reload(); + } + } + render() { + const menu = ( + + + + + + 个人中心 + + + + + + + 点个Star + + + + + + + + + + 退出登录 + + + + + ); return ( @@ -126,7 +178,7 @@ class App extends Component { - +
logo { @@ -251,8 +303,38 @@ class App extends Component {
- + style={{padding: 0, height: headerHeight, zIndex: 20}}> +
+ + + {React.createElement(this.state.collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, { + className: 'trigger', + onClick: this.onCollapse, + })} + + + +
+ + + + + + +
+ + + +
+ {getCurrentUser()['nickname']}   +
+
+ +
+ +
diff --git a/web/src/components/Login.js b/web/src/components/Login.js index 49db17c..351b442 100644 --- a/web/src/components/Login.js +++ b/web/src/components/Login.js @@ -10,7 +10,8 @@ const {Title} = Typography; class LoginForm extends Component { - formRef = React.createRef() + formRef = React.createRef(); + totpInputRef = React.createRef(); state = { inLogin: false, @@ -44,6 +45,8 @@ class LoginForm extends Component { loginAccount: params, totpModalVisible: true }) + + this.totpInputRef.current.focus(); return; } if (result.code !== 1) { @@ -74,8 +77,9 @@ class LoginForm extends Component { try { let result = await request.post('/loginWithTotp', loginAccount); - if (result.code !== 1) { - throw new Error(result.message); + if (result['code'] !== 1) { + message.error(result['message']); + return; } // 跳转登录 @@ -129,12 +133,13 @@ class LoginForm extends Component { { this.formRef.current .validateFields() .then(values => { - this.formRef.current.resetFields(); this.handleOk(values); + // this.formRef.current.resetFields(); }) .catch(info => { @@ -142,10 +147,10 @@ class LoginForm extends Component { }} onCancel={this.handleCancel}> -
+ - } placeholder="请输入双因素认证APP中显示的授权码"/> + } placeholder="请输入双因素认证APP中显示的授权码"/>
diff --git a/web/src/components/asset/Asset.js b/web/src/components/asset/Asset.js index 8786549..fb1e328 100644 --- a/web/src/components/asset/Asset.js +++ b/web/src/components/asset/Asset.js @@ -51,19 +51,6 @@ const confirm = Modal.confirm; const {Search} = Input; const {Content} = Layout; const {Title, Text} = Typography; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - breadcrumbName: '资源管理', - }, - { - path: 'assets', - breadcrumbName: '资产管理', - } -]; class Asset extends Component { @@ -660,18 +647,6 @@ class Asset extends Component { return ( <> - - -
diff --git a/web/src/components/command/DynamicCommand.js b/web/src/components/command/DynamicCommand.js index 8e7f1c1..5dcfe67 100644 --- a/web/src/components/command/DynamicCommand.js +++ b/web/src/components/command/DynamicCommand.js @@ -43,16 +43,6 @@ const {Content} = Layout; const {Title, Text} = Typography; const {Search} = Input; const CheckboxGroup = Checkbox.Group; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'command', - breadcrumbName: '动态指令', - } -]; class DynamicCommand extends Component { @@ -534,18 +524,6 @@ class DynamicCommand extends Component { return ( <> - - -
diff --git a/web/src/components/credential/Credential.js b/web/src/components/credential/Credential.js index 12e837d..15de275 100644 --- a/web/src/components/credential/Credential.js +++ b/web/src/components/credential/Credential.js @@ -10,7 +10,6 @@ import { Layout, Menu, Modal, - PageHeader, Row, Select, Space, @@ -32,7 +31,6 @@ import { SyncOutlined, UndoOutlined } from '@ant-design/icons'; -import {itemRender} from "../../utils/utils"; import {hasPermission, isAdmin} from "../../service/permission"; import dayjs from "dayjs"; @@ -41,16 +39,6 @@ const confirm = Modal.confirm; const {Search} = Input; const {Title, Text} = Typography; const {Content} = Layout; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'credentials', - breadcrumbName: '授权凭证', - } -]; class Credential extends Component { @@ -483,18 +471,6 @@ class Credential extends Component { return ( <> - - -
diff --git a/web/src/components/dashboard/Dashboard.js b/web/src/components/dashboard/Dashboard.js index 22f5b1d..7cf5090 100644 --- a/web/src/components/dashboard/Dashboard.js +++ b/web/src/components/dashboard/Dashboard.js @@ -9,18 +9,6 @@ import {Area} from '@ant-design/charts'; import {isAdmin} from "../../service/permission"; - -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'dashboard', - breadcrumbName: '仪表盘', - } -]; - class Dashboard extends Component { state = { @@ -79,17 +67,6 @@ class Dashboard extends Component { return ( <> - -
diff --git a/web/src/components/devops/Job.js b/web/src/components/devops/Job.js index 3242cb8..2ff7acd 100644 --- a/web/src/components/devops/Job.js +++ b/web/src/components/devops/Job.js @@ -40,16 +40,6 @@ const confirm = Modal.confirm; const {Content} = Layout; const {Title, Text} = Typography; const {Search} = Input; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'job', - breadcrumbName: '计划任务', - } -]; class Job extends Component { @@ -441,18 +431,6 @@ class Job extends Component { return ( <> - - -
diff --git a/web/src/components/devops/LoginLog.js b/web/src/components/devops/LoginLog.js index 8c76605..6211647 100644 --- a/web/src/components/devops/LoginLog.js +++ b/web/src/components/devops/LoginLog.js @@ -27,16 +27,6 @@ const confirm = Modal.confirm; const {Content} = Layout; const {Search} = Input; const {Title, Text} = Typography; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'loginLog', - breadcrumbName: '登录日志', - } -]; class LoginLog extends Component { @@ -280,18 +270,6 @@ class LoginLog extends Component { return ( <> - - -
diff --git a/web/src/components/devops/Security.js b/web/src/components/devops/Security.js index 9f6e13d..55f8597 100644 --- a/web/src/components/devops/Security.js +++ b/web/src/components/devops/Security.js @@ -27,16 +27,6 @@ const confirm = Modal.confirm; const {Content} = Layout; const {Title, Text} = Typography; const {Search} = Input; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'security', - breadcrumbName: '访问安全', - } -]; class Security extends Component { @@ -296,18 +286,6 @@ class Security extends Component { return ( <> - - -
diff --git a/web/src/components/session/OfflineSession.js b/web/src/components/session/OfflineSession.js index a2253d4..5cc6f5e 100644 --- a/web/src/components/session/OfflineSession.js +++ b/web/src/components/session/OfflineSession.js @@ -30,16 +30,6 @@ const confirm = Modal.confirm; const {Content} = Layout; const {Search} = Input; const {Title, Text} = Typography; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'offlineSession', - breadcrumbName: '离线会话', - } -]; class OfflineSession extends Component { @@ -365,18 +355,6 @@ class OfflineSession extends Component { return ( <> - - -
diff --git a/web/src/components/session/OnlineSession.js b/web/src/components/session/OnlineSession.js index 32a20f5..e7f04f8 100644 --- a/web/src/components/session/OnlineSession.js +++ b/web/src/components/session/OnlineSession.js @@ -31,16 +31,6 @@ const confirm = Modal.confirm; const {Content} = Layout; const {Search} = Input; const {Title, Text} = Typography; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'onlineSession', - breadcrumbName: '在线会话', - } -]; class OnlineSession extends Component { @@ -335,18 +325,6 @@ class OnlineSession extends Component { return ( <> - - -
diff --git a/web/src/components/setting/Setting.js b/web/src/components/setting/Setting.js index ad55f8c..3b056b0 100644 --- a/web/src/components/setting/Setting.js +++ b/web/src/components/setting/Setting.js @@ -10,17 +10,6 @@ const {Option} = Select; const {TabPane} = Tabs; const {Title} = Typography; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'setting', - breadcrumbName: '系统设置', - } -]; - const formItemLayout = { labelCol: {span: 12}, wrapperCol: {span: 12}, @@ -114,18 +103,6 @@ class Setting extends Component { render() { return ( <> - - - diff --git a/web/src/components/user/Info.js b/web/src/components/user/Info.js index 45bbb53..ec1246a 100644 --- a/web/src/components/user/Info.js +++ b/web/src/components/user/Info.js @@ -8,17 +8,6 @@ import {ExclamationCircleOutlined, ReloadOutlined} from "@ant-design/icons"; const {Content} = Layout; const {Meta} = Card; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'info', - breadcrumbName: '个人中心', - } -]; - const formItemLayout = { labelCol: {span: 3}, wrapperCol: {span: 6}, @@ -121,17 +110,6 @@ class Info extends Component { render() { return ( <> - -

修改密码

diff --git a/web/src/components/user/LayoutHeader.js b/web/src/components/user/LayoutHeader.js deleted file mode 100644 index 80cd048..0000000 --- a/web/src/components/user/LayoutHeader.js +++ /dev/null @@ -1,87 +0,0 @@ -import React, {Component} from 'react'; -import {Col, Dropdown, Menu, message, Popconfirm, Row, Tooltip} from "antd"; -import request from "../../common/request"; -import {getCurrentUser} from "../../service/permission"; -import {GithubOutlined, LogoutOutlined, QuestionCircleOutlined, SolutionOutlined} from "@ant-design/icons"; -import {Link} from "react-router-dom"; - -class LayoutHeader extends Component { - - confirm = async (e) => { - let result = await request.post('/logout'); - if (result['code'] !== 1) { - message.error(result['message']); - } else { - message.success('退出登录成功,即将跳转至登录页面。'); - window.location.reload(); - } - } - - - render() { - - const menu = ( - - - - - - 个人中心 - - - - - - - 点个Star - - - - - - - - - - 退出登录 - - - - - ); - - return ( -
- - - - -
- - - - - - -
- - - -
{getCurrentUser()['nickname']}
-
- -
- -
- ); - } -} - -export default LayoutHeader; \ No newline at end of file diff --git a/web/src/components/user/User.js b/web/src/components/user/User.js index fe3c5c6..c9ad087 100644 --- a/web/src/components/user/User.js +++ b/web/src/components/user/User.js @@ -34,7 +34,6 @@ import { SyncOutlined, UndoOutlined } from '@ant-design/icons'; -import LayoutHeader from "./LayoutHeader"; import UserShareAsset from "./UserShareAsset"; import {hasPermission} from "../../service/permission"; import dayjs from "dayjs"; @@ -44,17 +43,6 @@ const {Search} = Input; const {Title, Text} = Typography; const {Content} = Layout; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'user', - breadcrumbName: '用户', - } -]; - class User extends Component { inputRefOfNickname = React.createRef(); @@ -473,18 +461,6 @@ class User extends Component { return ( <> - - -
diff --git a/web/src/components/user/UserGroup.js b/web/src/components/user/UserGroup.js index d525f9b..9960d79 100644 --- a/web/src/components/user/UserGroup.js +++ b/web/src/components/user/UserGroup.js @@ -15,17 +15,6 @@ const {Search} = Input; const {Title, Text} = Typography; const {Content} = Layout; -const routes = [ - { - path: '', - breadcrumbName: '首页', - }, - { - path: 'user', - breadcrumbName: '用户组', - } -]; - class UserGroup extends Component { inputRefOfName = React.createRef(); @@ -334,18 +323,6 @@ class UserGroup extends Component { return ( <> - - -
From 0150361054513ac8b2e21e6ca7c6f6f6f557f1dd Mon Sep 17 00:00:00 2001 From: dushixiang Date: Thu, 18 Mar 2021 00:07:30 +0800 Subject: [PATCH 04/34] =?UTF-8?q?=F0=9F=90=B6=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E9=83=A8=E5=88=86=E7=94=A8=E6=88=B7=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 289 ++++++++++----------- pkg/model/user.go | 150 ----------- {pkg => server}/api/account.go | 34 ++- {pkg => server}/api/asset.go | 6 +- {pkg => server}/api/command.go | 4 +- {pkg => server}/api/credential.go | 6 +- {pkg => server}/api/job.go | 4 +- {pkg => server}/api/login-log.go | 4 +- {pkg => server}/api/middleware.go | 6 +- {pkg => server}/api/overview.go | 8 +- {pkg => server}/api/property.go | 2 +- {pkg => server}/api/resource-sharer.go | 2 +- {pkg => server}/api/routes.go | 17 +- {pkg => server}/api/security.go | 6 +- {pkg => server}/api/session.go | 8 +- {pkg => server}/api/ssh.go | 12 +- {pkg => server}/api/tunnel.go | 8 +- {pkg => server}/api/user-group.go | 6 +- {pkg => server}/api/user.go | 33 ++- {pkg => server}/config/config.go | 4 +- {pkg => server}/constant/const.go | 0 {pkg => server}/global/global.go | 2 +- {pkg => server}/global/store.go | 4 +- {pkg => server}/guacd/guacd.go | 0 {pkg => server}/handle/runner.go | 8 +- {pkg => server}/log/logger.go | 0 {pkg => server}/model/access_security.go | 2 +- {pkg => server}/model/asset.go | 6 +- {pkg => server}/model/asset_attribute.go | 8 +- {pkg => server}/model/command.go | 6 +- {pkg => server}/model/credential.go | 6 +- {pkg => server}/model/job.go | 8 +- {pkg => server}/model/job_log.go | 4 +- {pkg => server}/model/login_log.go | 47 ++-- {pkg => server}/model/num.go | 2 +- {pkg => server}/model/property.go | 6 +- {pkg => server}/model/resource-sharer.go | 4 +- {pkg => server}/model/session.go | 6 +- {pkg => server}/model/user-attribute.go | 2 +- {pkg => server}/model/user-group-member.go | 2 +- {pkg => server}/model/user-group.go | 39 +-- server/model/user.go | 35 +++ server/repository/user.go | 123 +++++++++ {pkg => server}/term/next_terminal.go | 0 {pkg => server}/term/next_writer.go | 0 {pkg => server}/term/recording.go | 2 +- {pkg => server}/term/ssh.go | 0 {pkg => server}/term/test/test_ssh.go | 0 {pkg => server}/totp/totp.go | 0 {pkg => server}/utils/utils.go | 0 50 files changed, 478 insertions(+), 453 deletions(-) delete mode 100644 pkg/model/user.go rename {pkg => server}/api/account.go (90%) rename {pkg => server}/api/asset.go (98%) rename {pkg => server}/api/command.go (97%) rename {pkg => server}/api/credential.go (97%) rename {pkg => server}/api/job.go (97%) rename {pkg => server}/api/login-log.go (93%) rename {pkg => server}/api/middleware.go (97%) rename {pkg => server}/api/overview.go (88%) rename {pkg => server}/api/property.go (96%) rename {pkg => server}/api/resource-sharer.go (98%) rename {pkg => server}/api/routes.go (96%) rename {pkg => server}/api/security.go (96%) rename {pkg => server}/api/session.go (99%) rename {pkg => server}/api/ssh.go (96%) rename {pkg => server}/api/tunnel.go (98%) rename {pkg => server}/api/user-group.go (96%) rename {pkg => server}/api/user.go (81%) rename {pkg => server}/config/config.go (97%) rename {pkg => server}/constant/const.go (100%) rename {pkg => server}/global/global.go (90%) rename {pkg => server}/global/store.go (95%) rename {pkg => server}/guacd/guacd.go (100%) rename {pkg => server}/handle/runner.go (98%) rename {pkg => server}/log/logger.go (100%) rename {pkg => server}/model/access_security.go (98%) rename {pkg => server}/model/asset.go (98%) rename {pkg => server}/model/asset_attribute.go (96%) rename {pkg => server}/model/command.go (96%) rename {pkg => server}/model/credential.go (97%) rename {pkg => server}/model/job.go (98%) rename {pkg => server}/model/job_log.go (91%) rename {pkg => server}/model/login_log.go (82%) rename {pkg => server}/model/num.go (91%) rename {pkg => server}/model/property.go (95%) rename {pkg => server}/model/resource-sharer.go (98%) rename {pkg => server}/model/session.go (98%) rename {pkg => server}/model/user-attribute.go (93%) rename {pkg => server}/model/user-group-member.go (92%) rename {pkg => server}/model/user-group.go (86%) create mode 100644 server/model/user.go create mode 100644 server/repository/user.go rename {pkg => server}/term/next_terminal.go (100%) rename {pkg => server}/term/next_writer.go (100%) rename {pkg => server}/term/recording.go (98%) rename {pkg => server}/term/ssh.go (100%) rename {pkg => server}/term/test/test_ssh.go (100%) rename {pkg => server}/totp/totp.go (100%) rename {pkg => server}/utils/utils.go (100%) diff --git a/main.go b/main.go index ef7781e..c7e4568 100644 --- a/main.go +++ b/main.go @@ -4,18 +4,19 @@ import ( "bytes" "fmt" "io" + "next-terminal/server/repository" "os" "strconv" "strings" "time" - "next-terminal/pkg/api" - "next-terminal/pkg/config" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/handle" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/api" + "next-terminal/server/config" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/handle" + "next-terminal/server/model" + "next-terminal/server/utils" nested "github.com/antonfisher/nested-logrus-formatter" "github.com/labstack/gommon/log" @@ -30,6 +31,11 @@ import ( const Version = "v0.3.3" +var ( + db *gorm.DB + userRepository repository.UserRepository +) + func main() { err := Run() if err != nil { @@ -64,151 +70,31 @@ func Run() error { logrus.SetOutput(io.MultiWriter(writer1, writer2, writer3)) - global.Config, err = config.SetupConfig() - if err != nil { - return err - } + global.Config = config.SetupConfig() + db = SetupDB() - var logMode logger.Interface - if global.Config.Debug { - logMode = logger.Default.LogMode(logger.Info) - } else { - logMode = logger.Default.LogMode(logger.Silent) - } - - fmt.Printf("当前数据库模式为:%v\n", global.Config.DB) - if global.Config.DB == "mysql" { - dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", - global.Config.Mysql.Username, - global.Config.Mysql.Password, - global.Config.Mysql.Hostname, - global.Config.Mysql.Port, - global.Config.Mysql.Database, - ) - global.DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ - Logger: logMode, - }) - } else { - global.DB, err = gorm.Open(sqlite.Open(global.Config.Sqlite.File), &gorm.Config{ - Logger: logMode, - }) - } - - if err != nil { - logrus.Errorf("连接数据库异常:%v", err.Error()) - return err - } + // 初始化 repository + global.DB = db + userRepository = repository.UserRepository{DB: db} if global.Config.ResetPassword != "" { - user, err := model.FindUserByUsername(global.Config.ResetPassword) - if err != nil { - return err - } - password := "next-terminal" - passwd, err := utils.Encoder.Encode([]byte(password)) - if err != nil { - return err - } - u := &model.User{ - Password: string(passwd), - } - model.UpdateUserById(u, user.ID) - logrus.Debugf("用户「%v」密码初始化为: %v", user.Username, password) - return nil + return ResetPassword() } - if err := global.DB.AutoMigrate(&model.User{}); err != nil { + if err := global.DB.AutoMigrate(&model.User{}, &model.Asset{}, &model.AssetAttribute{}, &model.Session{}, &model.Command{}, + &model.Credential{}, &model.Property{}, &model.ResourceSharer{}, &model.UserGroup{}, &model.UserGroupMember{}, + &model.LoginLog{}, &model.Num{}, &model.Job{}, &model.JobLog{}, &model.AccessSecurity{}); err != nil { return err } - users := model.FindAllUser() - if len(users) == 0 { - - initPassword := "admin" - var pass []byte - if pass, err = utils.Encoder.Encode([]byte(initPassword)); err != nil { - return err - } - - user := model.User{ - ID: utils.UUID(), - Username: "admin", - Password: string(pass), - Nickname: "超级管理员", - Type: constant.TypeAdmin, - Created: utils.NowJsonTime(), - } - if err := model.CreateNewUser(&user); err != nil { - return err - } - logrus.Infof("初始用户创建成功,账号:「%v」密码:「%v」", user.Username, initPassword) - } else { - for i := range users { - // 修正默认用户类型为管理员 - if users[i].Type == "" { - user := model.User{ - Type: constant.TypeAdmin, - } - model.UpdateUserById(&user, users[i].ID) - logrus.Infof("自动修正用户「%v」ID「%v」类型为管理员", users[i].Nickname, users[i].ID) - } - } + if err := InitDBData(); err != nil { + return err } - if err := global.DB.AutoMigrate(&model.Asset{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.AssetAttribute{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.Session{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.Command{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.Credential{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.Property{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.ResourceSharer{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.UserGroup{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.UserGroupMember{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.LoginLog{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.Num{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.Job{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.JobLog{}); err != nil { - return err - } - if err := global.DB.AutoMigrate(&model.AccessSecurity{}); err != nil { - return err - } if err := api.ReloadAccessSecurity(); err != nil { return err } - if len(model.FindAllTemp()) == 0 { - for i := 0; i <= 30; i++ { - if err := model.CreateNewTemp(&model.Num{I: strconv.Itoa(i)}); err != nil { - return err - } - } - } - // 配置缓存器 global.Cache = cache.New(5*time.Minute, 10*time.Minute) global.Cache.OnEvicted(func(key string, value interface{}) { @@ -225,6 +111,68 @@ func Run() error { global.Cron = cron.New(cron.WithSeconds()) //精确到秒 global.Cron.Start() + e := api.SetupRoutes(userRepository) + if err := handle.InitProperties(); err != nil { + return err + } + // 启动定时任务 + go handle.RunTicker() + go handle.RunDataFix() + + if global.Config.Server.Cert != "" && global.Config.Server.Key != "" { + return e.StartTLS(global.Config.Server.Addr, global.Config.Server.Cert, global.Config.Server.Key) + } else { + return e.Start(global.Config.Server.Addr) + } + +} + +func InitDBData() (err error) { + users := userRepository.FindAll() + + if len(users) == 0 { + initPassword := "admin" + var pass []byte + if pass, err = utils.Encoder.Encode([]byte(initPassword)); err != nil { + return err + } + + user := model.User{ + ID: utils.UUID(), + Username: "admin", + Password: string(pass), + Nickname: "超级管理员", + Type: constant.TypeAdmin, + Created: utils.NowJsonTime(), + } + if err := userRepository.Create(&user); err != nil { + return err + } + logrus.Infof("初始用户创建成功,账号:「%v」密码:「%v」", user.Username, initPassword) + } else { + for i := range users { + // 修正默认用户类型为管理员 + if users[i].Type == "" { + user := model.User{ + Type: constant.TypeAdmin, + ID: users[i].ID, + } + if err := userRepository.Update(&user); err != nil { + return err + } + logrus.Infof("自动修正用户「%v」ID「%v」类型为管理员", users[i].Nickname, users[i].ID) + } + } + } + + if len(model.FindAllTemp()) == 0 { + for i := 0; i <= 30; i++ { + if err := model.CreateNewTemp(&model.Num{I: strconv.Itoa(i)}); err != nil { + return err + } + } + } + jobs, err := model.FindJobByFunc(constant.FuncCheckAssetStatusJob) if err != nil { return err @@ -264,7 +212,7 @@ func Run() error { for i := range loginLogs { loginLog := loginLogs[i] token := loginLog.ID - user, err := model.FindUserById(loginLog.UserId) + user, err := userRepository.FindById(loginLog.UserId) if err != nil { logrus.Debugf("用户「%v」获取失败,忽略", loginLog.UserId) continue @@ -288,7 +236,7 @@ func Run() error { } // 修正用户登录状态 - onlineUsers, err := model.FindOnlineUsers() + onlineUsers, err := userRepository.FindOnlineUsers() if err != nil { return err } @@ -298,24 +246,67 @@ func Run() error { return err } if len(logs) == 0 { - if err := model.UpdateUserOnline(false, onlineUsers[i].ID); err != nil { + if err := userRepository.UpdateOnline(onlineUsers[i].ID, false); err != nil { return err } } } - e := api.SetupRoutes() - if err := handle.InitProperties(); err != nil { + return nil +} + +func ResetPassword() error { + user, err := userRepository.FindByUsername(global.Config.ResetPassword) + if err != nil { return err } - // 启动定时任务 - go handle.RunTicker() - go handle.RunDataFix() + password := "next-terminal" + passwd, err := utils.Encoder.Encode([]byte(password)) + if err != nil { + return err + } + u := &model.User{ + Password: string(passwd), + ID: user.ID, + } + if err := userRepository.Update(u); err != nil { + return err + } + logrus.Debugf("用户「%v」密码初始化为: %v", user.Username, password) + return nil +} - if global.Config.Server.Cert != "" && global.Config.Server.Key != "" { - return e.StartTLS(global.Config.Server.Addr, global.Config.Server.Cert, global.Config.Server.Key) +func SetupDB() *gorm.DB { + + var logMode logger.Interface + if global.Config.Debug { + logMode = logger.Default.LogMode(logger.Info) } else { - return e.Start(global.Config.Server.Addr) + logMode = logger.Default.LogMode(logger.Silent) } + fmt.Printf("当前数据库模式为:%v\n", global.Config.DB) + var err error + var db *gorm.DB + if global.Config.DB == "mysql" { + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", + global.Config.Mysql.Username, + global.Config.Mysql.Password, + global.Config.Mysql.Hostname, + global.Config.Mysql.Port, + global.Config.Mysql.Database, + ) + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ + Logger: logMode, + }) + } else { + db, err = gorm.Open(sqlite.Open(global.Config.Sqlite.File), &gorm.Config{ + Logger: logMode, + }) + } + + if err != nil { + logrus.WithError(err).Panic("连接数据库异常") + } + return db } diff --git a/pkg/model/user.go b/pkg/model/user.go deleted file mode 100644 index b497736..0000000 --- a/pkg/model/user.go +++ /dev/null @@ -1,150 +0,0 @@ -package model - -import ( - "reflect" - - "next-terminal/pkg/global" - "next-terminal/pkg/utils" -) - -type User struct { - ID string `gorm:"primary_key" json:"id"` - Username string `gorm:"index" json:"username"` - Password string `json:"password"` - Nickname string `json:"nickname"` - TOTPSecret string `json:"-"` - Online bool `json:"online"` - Enabled bool `json:"enabled"` - Created utils.JsonTime `json:"created"` - Type string `json:"type"` - Mail string `json:"mail"` -} - -type UserVo struct { - ID string `json:"id"` - Username string `json:"username"` - Nickname string `json:"nickname"` - TOTPSecret string `json:"totpSecret"` - Mail string `json:"mail"` - Online bool `json:"online"` - Enabled bool `json:"enabled"` - Created utils.JsonTime `json:"created"` - Type string `json:"type"` - SharerAssetCount int64 `json:"sharerAssetCount"` -} - -func (r *User) TableName() string { - return "users" -} - -func (r *User) IsEmpty() bool { - return reflect.DeepEqual(r, User{}) -} - -func FindAllUser() (o []User) { - if global.DB.Find(&o).Error != nil { - return nil - } - return -} - -func FindPageUser(pageIndex, pageSize int, username, nickname, mail, order, field string) (o []UserVo, total int64, err error) { - db := global.DB.Table("users").Select("users.id,users.username,users.nickname,users.mail,users.online,users.enabled,users.created,users.type, count(resource_sharers.user_id) as sharer_asset_count, users.totp_secret").Joins("left join resource_sharers on users.id = resource_sharers.user_id and resource_sharers.resource_type = 'asset'").Group("users.id") - dbCounter := global.DB.Table("users") - if len(username) > 0 { - db = db.Where("users.username like ?", "%"+username+"%") - dbCounter = dbCounter.Where("username like ?", "%"+username+"%") - } - - if len(nickname) > 0 { - db = db.Where("users.nickname like ?", "%"+nickname+"%") - dbCounter = dbCounter.Where("nickname like ?", "%"+nickname+"%") - } - - if len(mail) > 0 { - db = db.Where("users.mail like ?", "%"+mail+"%") - dbCounter = dbCounter.Where("mail like ?", "%"+mail+"%") - } - - err = dbCounter.Count(&total).Error - if err != nil { - return nil, 0, err - } - - if order == "ascend" { - order = "asc" - } else { - order = "desc" - } - - if field == "username" { - field = "username" - } else if field == "nickname" { - field = "nickname" - } else { - field = "created" - } - - err = db.Order("users." + field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error - if o == nil { - o = make([]UserVo, 0) - } - - for i := 0; i < len(o); i++ { - if o[i].TOTPSecret == "" || o[i].TOTPSecret == "-" { - o[i].TOTPSecret = "0" - } else { - o[i].TOTPSecret = "1" - } - } - return -} - -func CreateNewUser(o *User) (err error) { - err = global.DB.Create(o).Error - return -} - -func FindUserById(id string) (o User, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error - return -} - -func FindUserByIdIn(ids []string) (o []User, err error) { - err = global.DB.Where("id in ?", ids).First(&o).Error - return -} - -func FindUserByUsername(username string) (o User, err error) { - err = global.DB.Where("username = ?", username).First(&o).Error - return -} - -func UpdateUserById(o *User, id string) { - o.ID = id - global.DB.Updates(o) -} - -func UpdateUserOnline(online bool, id string) (err error) { - sql := "update users set online = ? where id = ?" - err = global.DB.Exec(sql, online, id).Error - return -} - -func FindOnlineUsers() (o []User, err error) { - err = global.DB.Where("online = ?", true).Find(&o).Error - return -} - -func DeleteUserById(id string) { - global.DB.Where("id = ?", id).Delete(&User{}) - // 删除用户组中的用户关系 - global.DB.Where("user_id = ?", id).Delete(&UserGroupMember{}) - // 删除用户分享到的资产 - global.DB.Where("user_id = ?", id).Delete(&ResourceSharer{}) -} - -func CountOnlineUser() (total int64, err error) { - err = global.DB.Where("online = ?", true).Find(&User{}).Count(&total).Error - return -} diff --git a/pkg/api/account.go b/server/api/account.go similarity index 90% rename from pkg/api/account.go rename to server/api/account.go index 0059088..3836d9c 100644 --- a/pkg/api/account.go +++ b/server/api/account.go @@ -4,10 +4,10 @@ import ( "strings" "time" - "next-terminal/pkg/global" - "next-terminal/pkg/model" - "next-terminal/pkg/totp" - "next-terminal/pkg/utils" + "next-terminal/server/global" + "next-terminal/server/model" + "next-terminal/server/totp" + "next-terminal/server/utils" "github.com/labstack/echo/v4" ) @@ -46,7 +46,7 @@ func LoginEndpoint(c echo.Context) error { return err } - user, err := model.FindUserByUsername(loginAccount.Username) + user, err := userRepository.FindByUsername(loginAccount.Username) // 存储登录失败次数信息 loginFailCountKey := loginAccount.Username @@ -116,8 +116,9 @@ func LoginSuccess(c echo.Context, loginAccount LoginAccount, user model.User) (t } // 修改登录状态 - model.UpdateUserById(&model.User{Online: true}, user.ID) - return token, nil + err = userRepository.Update(&model.User{Online: true, ID: user.ID}) + + return token, err } func BuildCacheKeyByToken(token string) string { @@ -147,7 +148,7 @@ func loginWithTotpEndpoint(c echo.Context) error { return Fail(c, -1, "登录失败次数过多,请稍后再试") } - user, err := model.FindUserByUsername(loginAccount.Username) + user, err := userRepository.FindByUsername(loginAccount.Username) if err != nil { count++ global.Cache.Set(loginFailCountKey, count, time.Minute*time.Duration(5)) @@ -202,9 +203,12 @@ func ConfirmTOTPEndpoint(c echo.Context) error { u := &model.User{ TOTPSecret: confirmTOTP.Secret, + ID: account.ID, } - model.UpdateUserById(u, account.ID) + if err := userRepository.Update(u); err != nil { + return err + } return Success(c, nil) } @@ -240,8 +244,11 @@ func ResetTOTPEndpoint(c echo.Context) error { account, _ := GetCurrentAccount(c) u := &model.User{ TOTPSecret: "-", + ID: account.ID, + } + if err := userRepository.Update(u); err != nil { + return err } - model.UpdateUserById(u, account.ID) return Success(c, "") } @@ -266,9 +273,12 @@ func ChangePasswordEndpoint(c echo.Context) error { } u := &model.User{ Password: string(passwd), + ID: account.ID, } - model.UpdateUserById(u, account.ID) + if err := userRepository.Update(u); err != nil { + return err + } return LogoutEndpoint(c) } @@ -284,7 +294,7 @@ type AccountInfo struct { func InfoEndpoint(c echo.Context) error { account, _ := GetCurrentAccount(c) - user, err := model.FindUserById(account.ID) + user, err := userRepository.FindById(account.ID) if err != nil { return err } diff --git a/pkg/api/asset.go b/server/api/asset.go similarity index 98% rename from pkg/api/asset.go rename to server/api/asset.go index adb854e..f160cfe 100644 --- a/pkg/api/asset.go +++ b/server/api/asset.go @@ -8,9 +8,9 @@ import ( "strconv" "strings" - "next-terminal/pkg/constant" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/model" + "next-terminal/server/utils" "github.com/labstack/echo/v4" ) diff --git a/pkg/api/command.go b/server/api/command.go similarity index 97% rename from pkg/api/command.go rename to server/api/command.go index 0beb161..72b841c 100644 --- a/pkg/api/command.go +++ b/server/api/command.go @@ -5,8 +5,8 @@ import ( "strconv" "strings" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/model" + "next-terminal/server/utils" "github.com/labstack/echo/v4" ) diff --git a/pkg/api/credential.go b/server/api/credential.go similarity index 97% rename from pkg/api/credential.go rename to server/api/credential.go index de59b44..0b29854 100644 --- a/pkg/api/credential.go +++ b/server/api/credential.go @@ -5,9 +5,9 @@ import ( "strconv" "strings" - "next-terminal/pkg/constant" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/model" + "next-terminal/server/utils" "github.com/labstack/echo/v4" ) diff --git a/pkg/api/job.go b/server/api/job.go similarity index 97% rename from pkg/api/job.go rename to server/api/job.go index 23c6a25..4d72872 100644 --- a/pkg/api/job.go +++ b/server/api/job.go @@ -4,8 +4,8 @@ import ( "strconv" "strings" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/model" + "next-terminal/server/utils" "github.com/labstack/echo/v4" ) diff --git a/pkg/api/login-log.go b/server/api/login-log.go similarity index 93% rename from pkg/api/login-log.go rename to server/api/login-log.go index b94c4a3..4c278ff 100644 --- a/pkg/api/login-log.go +++ b/server/api/login-log.go @@ -4,8 +4,8 @@ import ( "strconv" "strings" - "next-terminal/pkg/global" - "next-terminal/pkg/model" + "next-terminal/server/global" + "next-terminal/server/model" "github.com/labstack/echo/v4" "github.com/sirupsen/logrus" diff --git a/pkg/api/middleware.go b/server/api/middleware.go similarity index 97% rename from pkg/api/middleware.go rename to server/api/middleware.go index 8ce00b0..f9a55f8 100644 --- a/pkg/api/middleware.go +++ b/server/api/middleware.go @@ -7,9 +7,9 @@ import ( "strings" "time" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/utils" "github.com/labstack/echo/v4" ) diff --git a/pkg/api/overview.go b/server/api/overview.go similarity index 88% rename from pkg/api/overview.go rename to server/api/overview.go index 8c6d9f4..1fb31bc 100644 --- a/pkg/api/overview.go +++ b/server/api/overview.go @@ -1,8 +1,8 @@ package api import ( - "next-terminal/pkg/constant" - "next-terminal/pkg/model" + "next-terminal/server/constant" + "next-terminal/server/model" "github.com/labstack/echo/v4" ) @@ -24,12 +24,12 @@ func OverviewCounterEndPoint(c echo.Context) error { asset int64 ) if constant.TypeUser == account.Type { - countUser, _ = model.CountOnlineUser() + countUser, _ = userRepository.CountOnlineUser() countOnlineSession, _ = model.CountOnlineSession() credential, _ = model.CountCredentialByUserId(account.ID) asset, _ = model.CountAssetByUserId(account.ID) } else { - countUser, _ = model.CountOnlineUser() + countUser, _ = userRepository.CountOnlineUser() countOnlineSession, _ = model.CountOnlineSession() credential, _ = model.CountCredential() asset, _ = model.CountAsset() diff --git a/pkg/api/property.go b/server/api/property.go similarity index 96% rename from pkg/api/property.go rename to server/api/property.go index 6608d88..3852249 100644 --- a/pkg/api/property.go +++ b/server/api/property.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "next-terminal/pkg/model" + "next-terminal/server/model" "github.com/labstack/echo/v4" "gorm.io/gorm" diff --git a/pkg/api/resource-sharer.go b/server/api/resource-sharer.go similarity index 98% rename from pkg/api/resource-sharer.go rename to server/api/resource-sharer.go index 0a416ec..6f9d9d4 100644 --- a/pkg/api/resource-sharer.go +++ b/server/api/resource-sharer.go @@ -1,7 +1,7 @@ package api import ( - "next-terminal/pkg/model" + "next-terminal/server/model" "github.com/labstack/echo/v4" ) diff --git a/pkg/api/routes.go b/server/api/routes.go similarity index 96% rename from pkg/api/routes.go rename to server/api/routes.go index 09a8590..5249ffa 100644 --- a/pkg/api/routes.go +++ b/server/api/routes.go @@ -2,11 +2,11 @@ package api import ( "net/http" - - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/log" - "next-terminal/pkg/model" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/log" + "next-terminal/server/model" + "next-terminal/server/repository" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" @@ -14,7 +14,12 @@ import ( const Token = "X-Auth-Token" -func SetupRoutes() *echo.Echo { +var ( + userRepository repository.UserRepository +) + +func SetupRoutes(ur repository.UserRepository) *echo.Echo { + userRepository = ur e := echo.New() e.HideBanner = true diff --git a/pkg/api/security.go b/server/api/security.go similarity index 96% rename from pkg/api/security.go rename to server/api/security.go index e7e2874..fd80b29 100644 --- a/pkg/api/security.go +++ b/server/api/security.go @@ -4,9 +4,9 @@ import ( "strconv" "strings" - "next-terminal/pkg/global" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/global" + "next-terminal/server/model" + "next-terminal/server/utils" "github.com/labstack/echo/v4" ) diff --git a/pkg/api/session.go b/server/api/session.go similarity index 99% rename from pkg/api/session.go rename to server/api/session.go index 469ca5a..b295873 100644 --- a/pkg/api/session.go +++ b/server/api/session.go @@ -13,10 +13,10 @@ import ( "strings" "sync" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/model" + "next-terminal/server/utils" "github.com/labstack/echo/v4" "github.com/pkg/sftp" diff --git a/pkg/api/ssh.go b/server/api/ssh.go similarity index 96% rename from pkg/api/ssh.go rename to server/api/ssh.go index 50e112a..1285a89 100644 --- a/pkg/api/ssh.go +++ b/server/api/ssh.go @@ -7,12 +7,12 @@ import ( "strconv" "time" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/guacd" - "next-terminal/pkg/model" - "next-terminal/pkg/term" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/guacd" + "next-terminal/server/model" + "next-terminal/server/term" + "next-terminal/server/utils" "github.com/gorilla/websocket" "github.com/labstack/echo/v4" diff --git a/pkg/api/tunnel.go b/server/api/tunnel.go similarity index 98% rename from pkg/api/tunnel.go rename to server/api/tunnel.go index dd43161..975f1e5 100644 --- a/pkg/api/tunnel.go +++ b/server/api/tunnel.go @@ -5,10 +5,10 @@ import ( "path" "strconv" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/guacd" - "next-terminal/pkg/model" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/guacd" + "next-terminal/server/model" "github.com/gorilla/websocket" "github.com/labstack/echo/v4" diff --git a/pkg/api/user-group.go b/server/api/user-group.go similarity index 96% rename from pkg/api/user-group.go rename to server/api/user-group.go index f20ba91..a20d13d 100644 --- a/pkg/api/user-group.go +++ b/server/api/user-group.go @@ -4,9 +4,9 @@ import ( "strconv" "strings" - "next-terminal/pkg/global" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/global" + "next-terminal/server/model" + "next-terminal/server/utils" "github.com/labstack/echo/v4" ) diff --git a/pkg/api/user.go b/server/api/user.go similarity index 81% rename from pkg/api/user.go rename to server/api/user.go index 4e3c863..a510f83 100644 --- a/pkg/api/user.go +++ b/server/api/user.go @@ -4,9 +4,9 @@ import ( "strconv" "strings" - "next-terminal/pkg/global" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/global" + "next-terminal/server/model" + "next-terminal/server/utils" "github.com/labstack/echo/v4" "github.com/sirupsen/logrus" @@ -29,7 +29,7 @@ func UserCreateEndpoint(c echo.Context) error { item.ID = utils.UUID() item.Created = utils.NowJsonTime() - if err := model.CreateNewUser(&item); err != nil { + if err := userRepository.Create(&item); err != nil { return err } @@ -49,7 +49,7 @@ func UserPagingEndpoint(c echo.Context) error { order := c.QueryParam("order") field := c.QueryParam("field") - items, total, err := model.FindPageUser(pageIndex, pageSize, username, nickname, mail, order, field) + items, total, err := userRepository.Find(pageIndex, pageSize, username, nickname, mail, order, field) if err != nil { return err } @@ -67,8 +67,11 @@ func UserUpdateEndpoint(c echo.Context) error { if err := c.Bind(&item); err != nil { return err } + item.ID = id - model.UpdateUserById(&item, id) + if err := userRepository.Update(&item); err != nil { + return err + } return Success(c, nil) } @@ -100,7 +103,9 @@ func UserDeleteEndpoint(c echo.Context) error { } // 删除用户 - model.DeleteUserById(userId) + if err := userRepository.DeleteById(userId); err != nil { + return err + } } return Success(c, nil) @@ -109,7 +114,7 @@ func UserDeleteEndpoint(c echo.Context) error { func UserGetEndpoint(c echo.Context) error { id := c.Param("id") - item, err := model.FindUserById(id) + item, err := userRepository.FindById(id) if err != nil { return err } @@ -121,7 +126,7 @@ func UserChangePasswordEndpoint(c echo.Context) error { id := c.Param("id") password := c.QueryParam("password") - user, err := model.FindUserById(id) + user, err := userRepository.FindById(id) if err != nil { return err } @@ -132,8 +137,11 @@ func UserChangePasswordEndpoint(c echo.Context) error { } u := &model.User{ Password: string(passwd), + ID: id, + } + if err := userRepository.Update(u); err != nil { + return err } - model.UpdateUserById(u, id) if user.Mail != "" { go model.SendMail(user.Mail, "[Next Terminal] 密码修改通知", "你好,"+user.Nickname+"。管理员已将你的密码修改为:"+password) @@ -146,7 +154,10 @@ func UserResetTotpEndpoint(c echo.Context) error { id := c.Param("id") u := &model.User{ TOTPSecret: "-", + ID: id, + } + if err := userRepository.Update(u); err != nil { + return err } - model.UpdateUserById(u, id) return Success(c, "") } diff --git a/pkg/config/config.go b/server/config/config.go similarity index 97% rename from pkg/config/config.go rename to server/config/config.go index 4cc2029..3640071 100644 --- a/pkg/config/config.go +++ b/server/config/config.go @@ -36,7 +36,7 @@ type Server struct { Key string } -func SetupConfig() (*Config, error) { +func SetupConfig() *Config { viper.SetConfigName("config") viper.SetConfigType("yml") @@ -85,5 +85,5 @@ func SetupConfig() (*Config, error) { Demo: viper.GetBool("demo"), } - return config, nil + return config } diff --git a/pkg/constant/const.go b/server/constant/const.go similarity index 100% rename from pkg/constant/const.go rename to server/constant/const.go diff --git a/pkg/global/global.go b/server/global/global.go similarity index 90% rename from pkg/global/global.go rename to server/global/global.go index f348b42..e4ea8bb 100644 --- a/pkg/global/global.go +++ b/server/global/global.go @@ -1,7 +1,7 @@ package global import ( - "next-terminal/pkg/config" + "next-terminal/server/config" "github.com/patrickmn/go-cache" "github.com/robfig/cron/v3" diff --git a/pkg/global/store.go b/server/global/store.go similarity index 95% rename from pkg/global/store.go rename to server/global/store.go index c2f66dc..faca1cc 100644 --- a/pkg/global/store.go +++ b/server/global/store.go @@ -4,8 +4,8 @@ import ( "strconv" "sync" - "next-terminal/pkg/guacd" - "next-terminal/pkg/term" + "next-terminal/server/guacd" + "next-terminal/server/term" "github.com/gorilla/websocket" ) diff --git a/pkg/guacd/guacd.go b/server/guacd/guacd.go similarity index 100% rename from pkg/guacd/guacd.go rename to server/guacd/guacd.go diff --git a/pkg/handle/runner.go b/server/handle/runner.go similarity index 98% rename from pkg/handle/runner.go rename to server/handle/runner.go index 0ecaa4a..1afcb76 100644 --- a/pkg/handle/runner.go +++ b/server/handle/runner.go @@ -5,10 +5,10 @@ import ( "strconv" "time" - "next-terminal/pkg/constant" - "next-terminal/pkg/guacd" - "next-terminal/pkg/model" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/guacd" + "next-terminal/server/model" + "next-terminal/server/utils" "github.com/sirupsen/logrus" ) diff --git a/pkg/log/logger.go b/server/log/logger.go similarity index 100% rename from pkg/log/logger.go rename to server/log/logger.go diff --git a/pkg/model/access_security.go b/server/model/access_security.go similarity index 98% rename from pkg/model/access_security.go rename to server/model/access_security.go index 1453bc6..db351cb 100644 --- a/pkg/model/access_security.go +++ b/server/model/access_security.go @@ -1,7 +1,7 @@ package model import ( - "next-terminal/pkg/global" + "next-terminal/server/global" ) type AccessSecurity struct { diff --git a/pkg/model/asset.go b/server/model/asset.go similarity index 98% rename from pkg/model/asset.go rename to server/model/asset.go index 6f46eb3..6e37ca1 100644 --- a/pkg/model/asset.go +++ b/server/model/asset.go @@ -3,9 +3,9 @@ package model import ( "strings" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/utils" ) type Asset struct { diff --git a/pkg/model/asset_attribute.go b/server/model/asset_attribute.go similarity index 96% rename from pkg/model/asset_attribute.go rename to server/model/asset_attribute.go index 8818218..21db88c 100644 --- a/pkg/model/asset_attribute.go +++ b/server/model/asset_attribute.go @@ -3,10 +3,10 @@ package model import ( "fmt" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/guacd" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/guacd" + "next-terminal/server/utils" "github.com/labstack/echo/v4" "gorm.io/gorm" diff --git a/pkg/model/command.go b/server/model/command.go similarity index 96% rename from pkg/model/command.go rename to server/model/command.go index be2390b..22d82f6 100644 --- a/pkg/model/command.go +++ b/server/model/command.go @@ -1,9 +1,9 @@ package model import ( - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/utils" ) type Command struct { diff --git a/pkg/model/credential.go b/server/model/credential.go similarity index 97% rename from pkg/model/credential.go rename to server/model/credential.go index f198f09..4a5570a 100644 --- a/pkg/model/credential.go +++ b/server/model/credential.go @@ -1,9 +1,9 @@ package model import ( - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/utils" ) type Credential struct { diff --git a/pkg/model/job.go b/server/model/job.go similarity index 98% rename from pkg/model/job.go rename to server/model/job.go index f7562f5..03d21c6 100644 --- a/pkg/model/job.go +++ b/server/model/job.go @@ -7,10 +7,10 @@ import ( "strings" "time" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/term" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/term" + "next-terminal/server/utils" "github.com/robfig/cron/v3" "github.com/sirupsen/logrus" diff --git a/pkg/model/job_log.go b/server/model/job_log.go similarity index 91% rename from pkg/model/job_log.go rename to server/model/job_log.go index fb7f923..9946cc1 100644 --- a/pkg/model/job_log.go +++ b/server/model/job_log.go @@ -1,8 +1,8 @@ package model import ( - "next-terminal/pkg/global" - "next-terminal/pkg/utils" + "next-terminal/server/global" + "next-terminal/server/utils" ) type JobLog struct { diff --git a/pkg/model/login_log.go b/server/model/login_log.go similarity index 82% rename from pkg/model/login_log.go rename to server/model/login_log.go index 401d292..8aaf95f 100644 --- a/pkg/model/login_log.go +++ b/server/model/login_log.go @@ -1,10 +1,8 @@ package model import ( - "next-terminal/pkg/global" - "next-terminal/pkg/utils" - - "github.com/sirupsen/logrus" + "next-terminal/server/global" + "next-terminal/server/utils" ) type LoginLog struct { @@ -83,25 +81,26 @@ func FindLoginLogById(id string) (o LoginLog, err error) { } func Logout(token string) (err error) { - - loginLog, err := FindLoginLogById(token) - if err != nil { - logrus.Warnf("登录日志「%v」获取失败", token) - return - } - - err = global.DB.Updates(&LoginLog{LogoutTime: utils.NowJsonTime(), ID: token}).Error - if err != nil { - return err - } - - loginLogs, err := FindAliveLoginLogsByUserId(loginLog.UserId) - if err != nil { - return - } - - if len(loginLogs) == 0 { - err = UpdateUserOnline(false, loginLog.UserId) - } + // + //loginLog, err := FindLoginLogById(token) + //if err != nil { + // logrus.Warnf("登录日志「%v」获取失败", token) + // return + //} + // + //err = global.DB.Updates(&LoginLog{LogoutTime: utils.NowJsonTime(), ID: token}).Error + //if err != nil { + // return err + //} + // + //loginLogs, err := FindAliveLoginLogsByUserId(loginLog.UserId) + //if err != nil { + // return + //} + // + //if len(loginLogs) == 0 { + // // TODO + // err = UpdateUserOnline(false, loginLog.UserId) + //} return } diff --git a/pkg/model/num.go b/server/model/num.go similarity index 91% rename from pkg/model/num.go rename to server/model/num.go index 653c246..78481fb 100644 --- a/pkg/model/num.go +++ b/server/model/num.go @@ -1,7 +1,7 @@ package model import ( - "next-terminal/pkg/global" + "next-terminal/server/global" ) type Num struct { diff --git a/pkg/model/property.go b/server/model/property.go similarity index 95% rename from pkg/model/property.go rename to server/model/property.go index 2b69195..0bd7d93 100644 --- a/pkg/model/property.go +++ b/server/model/property.go @@ -3,9 +3,9 @@ package model import ( "net/smtp" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/guacd" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/guacd" "github.com/jordan-wright/email" "github.com/sirupsen/logrus" diff --git a/pkg/model/resource-sharer.go b/server/model/resource-sharer.go similarity index 98% rename from pkg/model/resource-sharer.go rename to server/model/resource-sharer.go index 442c7c5..43dcea7 100644 --- a/pkg/model/resource-sharer.go +++ b/server/model/resource-sharer.go @@ -1,8 +1,8 @@ package model import ( - "next-terminal/pkg/global" - "next-terminal/pkg/utils" + "next-terminal/server/global" + "next-terminal/server/utils" "github.com/labstack/echo/v4" "github.com/pkg/errors" diff --git a/pkg/model/session.go b/server/model/session.go similarity index 98% rename from pkg/model/session.go rename to server/model/session.go index 78f81a6..55c8c33 100644 --- a/pkg/model/session.go +++ b/server/model/session.go @@ -5,9 +5,9 @@ import ( "path" "time" - "next-terminal/pkg/constant" - "next-terminal/pkg/global" - "next-terminal/pkg/utils" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/utils" ) type Session struct { diff --git a/pkg/model/user-attribute.go b/server/model/user-attribute.go similarity index 93% rename from pkg/model/user-attribute.go rename to server/model/user-attribute.go index c97a8f4..3fa870e 100644 --- a/pkg/model/user-attribute.go +++ b/server/model/user-attribute.go @@ -1,6 +1,6 @@ package model -import "next-terminal/pkg/global" +import "next-terminal/server/global" type UserAttribute struct { Id string `gorm:"index" json:"id"` diff --git a/pkg/model/user-group-member.go b/server/model/user-group-member.go similarity index 92% rename from pkg/model/user-group-member.go rename to server/model/user-group-member.go index 88ea276..5cfadf6 100644 --- a/pkg/model/user-group-member.go +++ b/server/model/user-group-member.go @@ -1,6 +1,6 @@ package model -import "next-terminal/pkg/global" +import "next-terminal/server/global" type UserGroupMember struct { ID string `gorm:"primary_key" json:"name"` diff --git a/pkg/model/user-group.go b/server/model/user-group.go similarity index 86% rename from pkg/model/user-group.go rename to server/model/user-group.go index 248d36b..8f2797e 100644 --- a/pkg/model/user-group.go +++ b/server/model/user-group.go @@ -1,8 +1,8 @@ package model import ( - "next-terminal/pkg/global" - "next-terminal/pkg/utils" + "next-terminal/server/global" + "next-terminal/server/utils" "gorm.io/gorm" ) @@ -75,23 +75,24 @@ func CreateNewUserGroup(o *UserGroup, members []string) (err error) { } func AddUserGroupMembers(tx *gorm.DB, userIds []string, userGroupId string) error { - for i := range userIds { - userId := userIds[i] - _, err := FindUserById(userId) - if err != nil { - return err - } - - userGroupMember := UserGroupMember{ - ID: utils.Sign([]string{userGroupId, userId}), - UserId: userId, - UserGroupId: userGroupId, - } - err = tx.Create(&userGroupMember).Error - if err != nil { - return err - } - } + //for i := range userIds { + // userId := userIds[i] + // // TODO + // _, err := FindUserById(userId) + // if err != nil { + // return err + // } + // + // userGroupMember := UserGroupMember{ + // ID: utils.Sign([]string{userGroupId, userId}), + // UserId: userId, + // UserGroupId: userGroupId, + // } + // err = tx.Create(&userGroupMember).Error + // if err != nil { + // return err + // } + //} return nil } diff --git a/server/model/user.go b/server/model/user.go new file mode 100644 index 0000000..f908e7c --- /dev/null +++ b/server/model/user.go @@ -0,0 +1,35 @@ +package model + +import ( + "next-terminal/server/utils" +) + +type User struct { + ID string `gorm:"primary_key" json:"id"` + Username string `gorm:"index" json:"username"` + Password string `json:"password"` + Nickname string `json:"nickname"` + TOTPSecret string `json:"-"` + Online bool `json:"online"` + Enabled bool `json:"enabled"` + Created utils.JsonTime `json:"created"` + Type string `json:"type"` + Mail string `json:"mail"` +} + +type UserVo struct { + ID string `json:"id"` + Username string `json:"username"` + Nickname string `json:"nickname"` + TOTPSecret string `json:"totpSecret"` + Mail string `json:"mail"` + Online bool `json:"online"` + Enabled bool `json:"enabled"` + Created utils.JsonTime `json:"created"` + Type string `json:"type"` + SharerAssetCount int64 `json:"sharerAssetCount"` +} + +func (r *User) TableName() string { + return "users" +} diff --git a/server/repository/user.go b/server/repository/user.go new file mode 100644 index 0000000..b133875 --- /dev/null +++ b/server/repository/user.go @@ -0,0 +1,123 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/model" +) + +type UserRepository struct { + DB *gorm.DB +} + +func (r UserRepository) FindAll() (o []model.User) { + if r.DB.Find(&o).Error != nil { + return nil + } + return +} + +func (r UserRepository) Find(pageIndex, pageSize int, username, nickname, mail, order, field string) (o []model.UserVo, total int64, err error) { + db := r.DB.Table("users").Select("users.id,users.username,users.nickname,users.mail,users.online,users.enabled,users.created,users.type, count(resource_sharers.user_id) as sharer_asset_count, users.totp_secret").Joins("left join resource_sharers on users.id = resource_sharers.user_id and resource_sharers.resource_type = 'asset'").Group("users.id") + dbCounter := r.DB.Table("users") + if len(username) > 0 { + db = db.Where("users.username like ?", "%"+username+"%") + dbCounter = dbCounter.Where("username like ?", "%"+username+"%") + } + + if len(nickname) > 0 { + db = db.Where("users.nickname like ?", "%"+nickname+"%") + dbCounter = dbCounter.Where("nickname like ?", "%"+nickname+"%") + } + + if len(mail) > 0 { + db = db.Where("users.mail like ?", "%"+mail+"%") + dbCounter = dbCounter.Where("mail like ?", "%"+mail+"%") + } + + err = dbCounter.Count(&total).Error + if err != nil { + return nil, 0, err + } + + if order == "ascend" { + order = "asc" + } else { + order = "desc" + } + + if field == "username" { + field = "username" + } else if field == "nickname" { + field = "nickname" + } else { + field = "created" + } + + err = db.Order("users." + field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error + if o == nil { + o = make([]model.UserVo, 0) + } + + for i := 0; i < len(o); i++ { + if o[i].TOTPSecret == "" || o[i].TOTPSecret == "-" { + o[i].TOTPSecret = "0" + } else { + o[i].TOTPSecret = "1" + } + } + return +} + +func (r UserRepository) FindById(id string) (o model.User, err error) { + err = r.DB.Where("id = ?", id).First(&o).Error + return +} + +func (r UserRepository) FindByUsername(username string) (o model.User, err error) { + err = r.DB.Where("username = ?", username).First(&o).Error + return +} + +func (r UserRepository) FindOnlineUsers() (o []model.User, err error) { + err = r.DB.Where("online = ?", true).Find(&o).Error + return +} + +func (r UserRepository) Create(o *model.User) error { + return r.DB.Create(o).Error +} + +func (r UserRepository) Update(o *model.User) error { + return r.DB.Updates(o).Error +} + +func (r UserRepository) UpdateOnline(id string, online bool) error { + sql := "update users set online = ? where id = ?" + return r.DB.Exec(sql, online, id).Error +} + +func (r UserRepository) DeleteById(id string) error { + return r.DB.Transaction(func(tx *gorm.DB) (err error) { + // 删除用户 + err = tx.Where("id = ?", id).Delete(&model.User{}).Error + if err != nil { + return err + } + // 删除用户组中的用户关系 + err = tx.Where("user_id = ?", id).Delete(&model.UserGroupMember{}).Error + if err != nil { + return err + } + // 删除用户分享到的资产 + err = tx.Where("user_id = ?", id).Delete(&model.ResourceSharer{}).Error + if err != nil { + return err + } + return nil + }) +} + +func (r UserRepository) CountOnlineUser() (total int64, err error) { + err = r.DB.Where("online = ?", true).Find(&model.User{}).Count(&total).Error + return +} diff --git a/pkg/term/next_terminal.go b/server/term/next_terminal.go similarity index 100% rename from pkg/term/next_terminal.go rename to server/term/next_terminal.go diff --git a/pkg/term/next_writer.go b/server/term/next_writer.go similarity index 100% rename from pkg/term/next_writer.go rename to server/term/next_writer.go diff --git a/pkg/term/recording.go b/server/term/recording.go similarity index 98% rename from pkg/term/recording.go rename to server/term/recording.go index 1beb71e..020269f 100644 --- a/pkg/term/recording.go +++ b/server/term/recording.go @@ -5,7 +5,7 @@ import ( "os" "time" - "next-terminal/pkg/utils" + "next-terminal/server/utils" ) type Env struct { diff --git a/pkg/term/ssh.go b/server/term/ssh.go similarity index 100% rename from pkg/term/ssh.go rename to server/term/ssh.go diff --git a/pkg/term/test/test_ssh.go b/server/term/test/test_ssh.go similarity index 100% rename from pkg/term/test/test_ssh.go rename to server/term/test/test_ssh.go diff --git a/pkg/totp/totp.go b/server/totp/totp.go similarity index 100% rename from pkg/totp/totp.go rename to server/totp/totp.go diff --git a/pkg/utils/utils.go b/server/utils/utils.go similarity index 100% rename from pkg/utils/utils.go rename to server/utils/utils.go From 25b8381a4f41a2cdef7e35faec700d814e670ce1 Mon Sep 17 00:00:00 2001 From: dushixiang Date: Thu, 18 Mar 2021 23:36:25 +0800 Subject: [PATCH 05/34] =?UTF-8?q?=E5=AE=8C=E6=88=90=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E6=93=8D=E4=BD=9C=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 256 +------------ server/api/account.go | 4 +- server/api/api.go | 75 ++++ server/api/asset.go | 46 +-- server/api/login-log.go | 10 +- server/api/routes.go | 181 ++++++---- server/api/security.go | 12 +- server/api/ticker.go | 62 ++++ server/config/db.go | 53 +++ server/constant/const.go | 8 + server/global/global.go | 3 - server/model/access_security.go | 70 ---- server/model/asset.go | 200 +---------- server/model/asset_attribute.go | 119 ------- server/model/command.go | 68 ---- server/model/credential.go | 94 ----- server/model/job.go | 336 +----------------- server/model/job_log.go | 30 -- server/model/login_log.go | 76 ---- server/model/num.go | 16 - server/model/property.go | 81 ----- server/model/resource-sharer.go | 185 ---------- server/model/session.go | 154 -------- server/model/user-attribute.go | 23 -- server/model/user-group-member.go | 18 - server/model/user-group.go | 127 +------ server/repository/access_security.go | 81 +++++ server/repository/asset.go | 302 ++++++++++++++++ server/repository/command.go | 82 +++++ server/repository/credential.go | 108 ++++++ server/repository/definitions.go | 17 + server/repository/job.go | 108 ++++++ server/repository/job_log.go | 28 ++ server/repository/login_log.go | 69 ++++ server/repository/num.go | 26 ++ server/repository/property.go | 90 +++++ server/repository/resource_sharer.go | 193 ++++++++++ server/repository/session.go | 167 +++++++++ server/repository/user.go | 5 + server/repository/user_group.go | 140 ++++++++ server/service/job.go | 324 +++++++++++++++++ .../{handle/runner.go => service/property.go} | 125 ++----- server/service/session.go | 32 ++ server/service/user.go | 104 ++++++ 44 files changed, 2292 insertions(+), 2016 deletions(-) create mode 100644 server/api/api.go create mode 100644 server/api/ticker.go create mode 100644 server/config/db.go delete mode 100644 server/model/asset_attribute.go delete mode 100644 server/model/job_log.go delete mode 100644 server/model/user-attribute.go delete mode 100644 server/model/user-group-member.go create mode 100644 server/repository/access_security.go create mode 100644 server/repository/asset.go create mode 100644 server/repository/command.go create mode 100644 server/repository/credential.go create mode 100644 server/repository/definitions.go create mode 100644 server/repository/job.go create mode 100644 server/repository/job_log.go create mode 100644 server/repository/login_log.go create mode 100644 server/repository/num.go create mode 100644 server/repository/property.go create mode 100644 server/repository/resource_sharer.go create mode 100644 server/repository/session.go create mode 100644 server/repository/user_group.go create mode 100644 server/service/job.go rename server/{handle/runner.go => service/property.go} (53%) create mode 100644 server/service/session.go create mode 100644 server/service/user.go diff --git a/main.go b/main.go index c7e4568..6231d76 100644 --- a/main.go +++ b/main.go @@ -3,37 +3,22 @@ package main import ( "bytes" "fmt" - "io" - "next-terminal/server/repository" - "os" - "strconv" - "strings" - "time" - - "next-terminal/server/api" - "next-terminal/server/config" - "next-terminal/server/constant" - "next-terminal/server/global" - "next-terminal/server/handle" - "next-terminal/server/model" - "next-terminal/server/utils" - nested "github.com/antonfisher/nested-logrus-formatter" "github.com/labstack/gommon/log" - "github.com/patrickmn/go-cache" "github.com/robfig/cron/v3" "github.com/sirupsen/logrus" - "gorm.io/driver/mysql" - "gorm.io/driver/sqlite" "gorm.io/gorm" - "gorm.io/gorm/logger" + "io" + "next-terminal/server/api" + "next-terminal/server/config" + "next-terminal/server/global" + "os" ) const Version = "v0.3.3" var ( - db *gorm.DB - userRepository repository.UserRepository + db *gorm.DB ) func main() { @@ -71,52 +56,25 @@ func Run() error { logrus.SetOutput(io.MultiWriter(writer1, writer2, writer3)) global.Config = config.SetupConfig() - db = SetupDB() + db = config.SetupDB() - // 初始化 repository - global.DB = db - userRepository = repository.UserRepository{DB: db} - - if global.Config.ResetPassword != "" { - return ResetPassword() - } - - if err := global.DB.AutoMigrate(&model.User{}, &model.Asset{}, &model.AssetAttribute{}, &model.Session{}, &model.Command{}, - &model.Credential{}, &model.Property{}, &model.ResourceSharer{}, &model.UserGroup{}, &model.UserGroupMember{}, - &model.LoginLog{}, &model.Num{}, &model.Job{}, &model.JobLog{}, &model.AccessSecurity{}); err != nil { - return err - } - - if err := InitDBData(); err != nil { - return err - } + //if global.Config.ResetPassword != "" { + // return ResetPassword() + //} if err := api.ReloadAccessSecurity(); err != nil { return err } - // 配置缓存器 - global.Cache = cache.New(5*time.Minute, 10*time.Minute) - global.Cache.OnEvicted(func(key string, value interface{}) { - if strings.HasPrefix(key, api.Token) { - token := api.GetTokenFormCacheKey(key) - logrus.Debugf("用户Token「%v」过期", token) - err := model.Logout(token) - if err != nil { - logrus.Errorf("退出登录失败 %v", err) - } - } - }) global.Store = global.NewStore() global.Cron = cron.New(cron.WithSeconds()) //精确到秒 global.Cron.Start() - e := api.SetupRoutes(userRepository) - if err := handle.InitProperties(); err != nil { - return err - } - // 启动定时任务 - go handle.RunTicker() + e := api.SetupRoutes(db) + global.Cache = api.SetupCache() + + api.SetupTicker() + go handle.RunDataFix() if global.Config.Server.Cert != "" && global.Config.Server.Key != "" { @@ -126,187 +84,3 @@ func Run() error { } } - -func InitDBData() (err error) { - users := userRepository.FindAll() - - if len(users) == 0 { - initPassword := "admin" - var pass []byte - if pass, err = utils.Encoder.Encode([]byte(initPassword)); err != nil { - return err - } - - user := model.User{ - ID: utils.UUID(), - Username: "admin", - Password: string(pass), - Nickname: "超级管理员", - Type: constant.TypeAdmin, - Created: utils.NowJsonTime(), - } - if err := userRepository.Create(&user); err != nil { - return err - } - logrus.Infof("初始用户创建成功,账号:「%v」密码:「%v」", user.Username, initPassword) - } else { - for i := range users { - // 修正默认用户类型为管理员 - if users[i].Type == "" { - user := model.User{ - Type: constant.TypeAdmin, - ID: users[i].ID, - } - if err := userRepository.Update(&user); err != nil { - return err - } - logrus.Infof("自动修正用户「%v」ID「%v」类型为管理员", users[i].Nickname, users[i].ID) - } - } - } - - if len(model.FindAllTemp()) == 0 { - for i := 0; i <= 30; i++ { - if err := model.CreateNewTemp(&model.Num{I: strconv.Itoa(i)}); err != nil { - return err - } - } - } - - jobs, err := model.FindJobByFunc(constant.FuncCheckAssetStatusJob) - if err != nil { - return err - } - if len(jobs) == 0 { - job := model.Job{ - ID: utils.UUID(), - Name: "资产状态检测", - Func: constant.FuncCheckAssetStatusJob, - Cron: "0 0 0/1 * * ?", - Mode: constant.JobModeAll, - Status: constant.JobStatusRunning, - Created: utils.NowJsonTime(), - Updated: utils.NowJsonTime(), - } - if err := model.CreateNewJob(&job); err != nil { - return err - } - logrus.Debugf("创建计划任务「%v」cron「%v」", job.Name, job.Cron) - } else { - for i := range jobs { - if jobs[i].Status == constant.JobStatusRunning { - err := model.ChangeJobStatusById(jobs[i].ID, constant.JobStatusRunning) - if err != nil { - return err - } - logrus.Debugf("启动计划任务「%v」cron「%v」", jobs[i].Name, jobs[i].Cron) - } - } - } - - loginLogs, err := model.FindAliveLoginLogs() - if err != nil { - return err - } - - for i := range loginLogs { - loginLog := loginLogs[i] - token := loginLog.ID - user, err := userRepository.FindById(loginLog.UserId) - if err != nil { - logrus.Debugf("用户「%v」获取失败,忽略", loginLog.UserId) - continue - } - - authorization := api.Authorization{ - Token: token, - Remember: loginLog.Remember, - User: user, - } - - cacheKey := api.BuildCacheKeyByToken(token) - - if authorization.Remember { - // 记住登录有效期两周 - global.Cache.Set(cacheKey, authorization, api.RememberEffectiveTime) - } else { - global.Cache.Set(cacheKey, authorization, api.NotRememberEffectiveTime) - } - logrus.Debugf("重新加载用户「%v」授权Token「%v」到缓存", user.Nickname, token) - } - - // 修正用户登录状态 - onlineUsers, err := userRepository.FindOnlineUsers() - if err != nil { - return err - } - for i := range onlineUsers { - logs, err := model.FindAliveLoginLogsByUserId(onlineUsers[i].ID) - if err != nil { - return err - } - if len(logs) == 0 { - if err := userRepository.UpdateOnline(onlineUsers[i].ID, false); err != nil { - return err - } - } - } - - return nil -} - -func ResetPassword() error { - user, err := userRepository.FindByUsername(global.Config.ResetPassword) - if err != nil { - return err - } - password := "next-terminal" - passwd, err := utils.Encoder.Encode([]byte(password)) - if err != nil { - return err - } - u := &model.User{ - Password: string(passwd), - ID: user.ID, - } - if err := userRepository.Update(u); err != nil { - return err - } - logrus.Debugf("用户「%v」密码初始化为: %v", user.Username, password) - return nil -} - -func SetupDB() *gorm.DB { - - var logMode logger.Interface - if global.Config.Debug { - logMode = logger.Default.LogMode(logger.Info) - } else { - logMode = logger.Default.LogMode(logger.Silent) - } - - fmt.Printf("当前数据库模式为:%v\n", global.Config.DB) - var err error - var db *gorm.DB - if global.Config.DB == "mysql" { - dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", - global.Config.Mysql.Username, - global.Config.Mysql.Password, - global.Config.Mysql.Hostname, - global.Config.Mysql.Port, - global.Config.Mysql.Database, - ) - db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ - Logger: logMode, - }) - } else { - db, err = gorm.Open(sqlite.Open(global.Config.Sqlite.File), &gorm.Config{ - Logger: logMode, - }) - } - - if err != nil { - logrus.WithError(err).Panic("连接数据库异常") - } - return db -} diff --git a/server/api/account.go b/server/api/account.go index 3836d9c..237faa6 100644 --- a/server/api/account.go +++ b/server/api/account.go @@ -111,7 +111,7 @@ func LoginSuccess(c echo.Context, loginAccount LoginAccount, user model.User) (t Remember: authorization.Remember, } - if model.CreateNewLoginLog(&loginLog) != nil { + if loginLogRepository.Create(&loginLog) != nil { return "", err } @@ -179,7 +179,7 @@ func LogoutEndpoint(c echo.Context) error { token := GetToken(c) cacheKey := BuildCacheKeyByToken(token) global.Cache.Delete(cacheKey) - err := model.Logout(token) + err := userService.Logout(token) if err != nil { return err } diff --git a/server/api/api.go b/server/api/api.go new file mode 100644 index 0000000..0c4db84 --- /dev/null +++ b/server/api/api.go @@ -0,0 +1,75 @@ +package api + +import ( + "github.com/labstack/echo/v4" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/model" +) + +type H map[string]interface{} + +func Fail(c echo.Context, code int, message string) error { + return c.JSON(200, H{ + "code": code, + "message": message, + }) +} + +func FailWithData(c echo.Context, code int, message string, data interface{}) error { + return c.JSON(200, H{ + "code": code, + "message": message, + "data": data, + }) +} + +func Success(c echo.Context, data interface{}) error { + return c.JSON(200, H{ + "code": 1, + "message": "success", + "data": data, + }) +} + +func NotFound(c echo.Context, message string) error { + return c.JSON(200, H{ + "code": -1, + "message": message, + }) +} + +func GetToken(c echo.Context) string { + token := c.Request().Header.Get(Token) + if len(token) > 0 { + return token + } + return c.QueryParam(Token) +} + +func GetCurrentAccount(c echo.Context) (model.User, bool) { + token := GetToken(c) + cacheKey := BuildCacheKeyByToken(token) + get, b := global.Cache.Get(cacheKey) + if b { + return get.(Authorization).User, true + } + return model.User{}, false +} + +func HasPermission(c echo.Context, owner string) bool { + // 检测是否登录 + account, found := GetCurrentAccount(c) + if !found { + return false + } + // 检测是否为管理人员 + if constant.TypeAdmin == account.Type { + return true + } + // 检测是否为所有者 + if owner == account.ID { + return true + } + return false +} diff --git a/server/api/asset.go b/server/api/asset.go index f160cfe..56ff2d2 100644 --- a/server/api/asset.go +++ b/server/api/asset.go @@ -32,18 +32,18 @@ func AssetCreateEndpoint(c echo.Context) error { item.ID = utils.UUID() item.Created = utils.NowJsonTime() - if err := model.CreateNewAsset(&item); err != nil { + if err := assetRepository.Create(&item); err != nil { return err } - if err := model.UpdateAssetAttributes(item.ID, item.Protocol, m); err != nil { + if err := assetRepository.UpdateAttributes(item.ID, item.Protocol, m); err != nil { return err } // 创建后自动检测资产是否存活 go func() { active := utils.Tcping(item.IP, item.Port) - model.UpdateAssetActiveById(active, item.ID) + assetRepository.UpdateActiveById(active, item.ID) }() return Success(c, item) @@ -98,7 +98,7 @@ func AssetImportEndpoint(c echo.Context) error { Owner: account.ID, } - err := model.CreateNewAsset(&asset) + err := assetRepository.Create(&asset) if err != nil { errorCount++ m[strconv.Itoa(i)] = err.Error() @@ -107,7 +107,7 @@ func AssetImportEndpoint(c echo.Context) error { // 创建后自动检测资产是否存活 go func() { active := utils.Tcping(asset.IP, asset.Port) - model.UpdateAssetActiveById(active, asset.ID) + assetRepository.UpdateActiveById(active, asset.ID) }() } } @@ -135,7 +135,7 @@ func AssetPagingEndpoint(c echo.Context) error { field := c.QueryParam("field") account, _ := GetCurrentAccount(c) - items, total, err := model.FindPageAsset(pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field) + items, total, err := assetRepository.Find(pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field) if err != nil { return err } @@ -149,7 +149,7 @@ func AssetPagingEndpoint(c echo.Context) error { func AssetAllEndpoint(c echo.Context) error { protocol := c.QueryParam("protocol") account, _ := GetCurrentAccount(c) - items, _ := model.FindAssetByConditions(protocol, account) + items, _ := assetRepository.FindByProtocolAndUser(protocol, account) return Success(c, items) } @@ -199,8 +199,10 @@ func AssetUpdateEndpoint(c echo.Context) error { item.Description = "-" } - model.UpdateAssetById(&item, id) - if err := model.UpdateAssetAttributes(id, item.Protocol, m); err != nil { + if err := assetRepository.UpdateById(&item, id); err != nil { + return err + } + if err := assetRepository.UpdateAttributes(id, item.Protocol, m); err != nil { return err } @@ -214,7 +216,7 @@ func AssetGetAttributeEndpoint(c echo.Context) error { return err } - attributeMap, err := model.FindAssetAttrMapByAssetId(assetId) + attributeMap, err := assetRepository.FindAssetAttrMapByAssetId(assetId) if err != nil { return err } @@ -229,7 +231,7 @@ func AssetUpdateAttributeEndpoint(c echo.Context) error { assetId := c.Param("id") protocol := c.QueryParam("protocol") - err := model.UpdateAssetAttributes(assetId, protocol, m) + err := assetRepository.UpdateAttributes(assetId, protocol, m) if err != nil { return err } @@ -243,11 +245,11 @@ func AssetDeleteEndpoint(c echo.Context) error { if err := PreCheckAssetPermission(c, split[i]); err != nil { return err } - if err := model.DeleteAssetById(split[i]); err != nil { + if err := assetRepository.DeleteById(split[i]); err != nil { return err } // 删除资产与用户的关系 - if err := model.DeleteResourceSharerByResourceId(split[i]); err != nil { + if err := resourceSharerRepository.DeleteResourceSharerByResourceId(split[i]); err != nil { return err } } @@ -262,10 +264,10 @@ func AssetGetEndpoint(c echo.Context) (err error) { } var item model.Asset - if item, err = model.FindAssetById(id); err != nil { + if item, err = assetRepository.FindById(id); err != nil { return err } - attributeMap, err := model.FindAssetAttrMapByAssetId(id) + attributeMap, err := assetRepository.FindAssetAttrMapByAssetId(id) if err != nil { return err } @@ -281,19 +283,21 @@ func AssetTcpingEndpoint(c echo.Context) (err error) { id := c.Param("id") var item model.Asset - if item, err = model.FindAssetById(id); err != nil { + if item, err = assetRepository.FindById(id); err != nil { return err } active := utils.Tcping(item.IP, item.Port) - model.UpdateAssetActiveById(active, item.ID) + if err := assetRepository.UpdateActiveById(active, item.ID); err != nil { + return err + } return Success(c, active) } func AssetTagsEndpoint(c echo.Context) (err error) { var items []string - if items, err = model.FindAssetTags(); err != nil { + if items, err = assetRepository.FindTags(); err != nil { return err } return Success(c, items) @@ -307,12 +311,14 @@ func AssetChangeOwnerEndpoint(c echo.Context) (err error) { } owner := c.QueryParam("owner") - model.UpdateAssetById(&model.Asset{Owner: owner}, id) + if err := assetRepository.UpdateById(&model.Asset{Owner: owner}, id); err != nil { + return err + } return Success(c, "") } func PreCheckAssetPermission(c echo.Context, id string) error { - item, err := model.FindAssetById(id) + item, err := assetRepository.FindById(id) if err != nil { return err } diff --git a/server/api/login-log.go b/server/api/login-log.go index 4c278ff..ec3e6df 100644 --- a/server/api/login-log.go +++ b/server/api/login-log.go @@ -4,11 +4,9 @@ import ( "strconv" "strings" - "next-terminal/server/global" - "next-terminal/server/model" - "github.com/labstack/echo/v4" "github.com/sirupsen/logrus" + "next-terminal/server/global" ) func LoginLogPagingEndpoint(c echo.Context) error { @@ -17,7 +15,7 @@ func LoginLogPagingEndpoint(c echo.Context) error { userId := c.QueryParam("userId") clientIp := c.QueryParam("clientIp") - items, total, err := model.FindPageLoginLog(pageIndex, pageSize, userId, clientIp) + items, total, err := loginLogRepository.Find(pageIndex, pageSize, userId, clientIp) if err != nil { return err @@ -35,11 +33,11 @@ func LoginLogDeleteEndpoint(c echo.Context) error { for i := range split { token := split[i] global.Cache.Delete(token) - if err := model.Logout(token); err != nil { + if err := userService.Logout(token); err != nil { logrus.WithError(err).Error("Cache Delete Failed") } } - if err := model.DeleteLoginLogByIdIn(split); err != nil { + if err := loginLogRepository.DeleteByIdIn(split); err != nil { return err } diff --git a/server/api/routes.go b/server/api/routes.go index 5249ffa..e907648 100644 --- a/server/api/routes.go +++ b/server/api/routes.go @@ -1,12 +1,19 @@ package api import ( + "github.com/patrickmn/go-cache" + "github.com/sirupsen/logrus" + "gorm.io/gorm" "net/http" - "next-terminal/server/constant" "next-terminal/server/global" "next-terminal/server/log" "next-terminal/server/model" "next-terminal/server/repository" + "next-terminal/server/service" + "next-terminal/server/utils" + "strconv" + "strings" + "time" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" @@ -15,11 +22,32 @@ import ( const Token = "X-Auth-Token" var ( - userRepository repository.UserRepository + userRepository *repository.UserRepository + userGroupRepository *repository.UserGroupRepository + resourceSharerRepository *repository.ResourceSharerRepository + assetRepository *repository.AssetRepository + credentialRepository *repository.CredentialRepository + propertyRepository *repository.PropertyRepository + commandRepository *repository.CommandRepository + sessionRepository *repository.SessionRepository + numRepository *repository.NumRepository + accessSecurityRepository *repository.AccessSecurityRepository + jobRepository *repository.JobRepository + jobLogRepository *repository.JobLogRepository + loginLogRepository *repository.LoginLogRepository + + jobService *service.JobService + propertyService *service.PropertyService + userService *service.UserService + sessionService *service.SessionService ) -func SetupRoutes(ur repository.UserRepository) *echo.Echo { - userRepository = ur +func SetupRoutes(db *gorm.DB) *echo.Echo { + + InitRepository(db) + InitService() + + InitDBData() e := echo.New() e.HideBanner = true @@ -178,69 +206,88 @@ func SetupRoutes(ur repository.UserRepository) *echo.Echo { return e } -type H map[string]interface{} +func InitRepository(db *gorm.DB) { + userRepository = repository.NewUserRepository(db) + userGroupRepository = repository.NewUserGroupRepository(db) + resourceSharerRepository = repository.NewResourceSharerRepository(db) + assetRepository = repository.NewAssetRepository(db) + credentialRepository = repository.NewCredentialRepository(db) + propertyRepository = repository.NewPropertyRepository(db) + commandRepository = repository.NewCommandRepository(db) + sessionRepository = repository.NewSessionRepository(db) + numRepository = repository.NewNumRepository(db) + accessSecurityRepository = repository.NewAccessSecurityRepository(db) + jobRepository = repository.NewJobRepository(db) + jobLogRepository = repository.NewJobLogRepository(db) + loginLogRepository = repository.NewLoginLogRepository(db) +} -func Fail(c echo.Context, code int, message string) error { - return c.JSON(200, H{ - "code": code, - "message": message, +func InitService() { + jobService = service.NewJobService(jobRepository, jobLogRepository, assetRepository, credentialRepository) + propertyService = service.NewPropertyService(propertyRepository) + userService = service.NewUserService(userRepository) + sessionService = service.NewSessionService(sessionRepository) +} + +func InitDBData() (err error) { + if err := propertyService.InitProperties(); err != nil { + return err + } + if err := userService.InitUser(); err != nil { + return err + } + if err := userService.FixedOnlineState(); err != nil { + return err + } + if err := jobService.InitJob(); err != nil { + return err + } + + sessionService.Fix() + nums, _ := numRepository.FindAll() + if nums == nil || len(nums) == 0 { + for i := 0; i <= 30; i++ { + if err := numRepository.Create(&model.Num{I: strconv.Itoa(i)}); err != nil { + return err + } + } + } + return nil +} + +func ResetPassword() error { + user, err := userRepository.FindByUsername(global.Config.ResetPassword) + if err != nil { + return err + } + password := "next-terminal" + passwd, err := utils.Encoder.Encode([]byte(password)) + if err != nil { + return err + } + u := &model.User{ + Password: string(passwd), + ID: user.ID, + } + if err := userRepository.Update(u); err != nil { + return err + } + logrus.Debugf("用户「%v」密码初始化为: %v", user.Username, password) + return nil +} + +func SetupCache() *cache.Cache { + // 配置缓存器 + mCache := cache.New(5*time.Minute, 10*time.Minute) + mCache.OnEvicted(func(key string, value interface{}) { + if strings.HasPrefix(key, Token) { + token := GetTokenFormCacheKey(key) + logrus.Debugf("用户Token「%v」过期", token) + err := userService.Logout(token) + if err != nil { + logrus.Errorf("退出登录失败 %v", err) + } + } }) -} - -func FailWithData(c echo.Context, code int, message string, data interface{}) error { - return c.JSON(200, H{ - "code": code, - "message": message, - "data": data, - }) -} - -func Success(c echo.Context, data interface{}) error { - return c.JSON(200, H{ - "code": 1, - "message": "success", - "data": data, - }) -} - -func NotFound(c echo.Context, message string) error { - return c.JSON(200, H{ - "code": -1, - "message": message, - }) -} - -func GetToken(c echo.Context) string { - token := c.Request().Header.Get(Token) - if len(token) > 0 { - return token - } - return c.QueryParam(Token) -} - -func GetCurrentAccount(c echo.Context) (model.User, bool) { - token := GetToken(c) - cacheKey := BuildCacheKeyByToken(token) - get, b := global.Cache.Get(cacheKey) - if b { - return get.(Authorization).User, true - } - return model.User{}, false -} - -func HasPermission(c echo.Context, owner string) bool { - // 检测是否登录 - account, found := GetCurrentAccount(c) - if !found { - return false - } - // 检测是否为管理人员 - if constant.TypeAdmin == account.Type { - return true - } - // 检测是否为所有者 - if owner == account.ID { - return true - } - return false + return mCache } diff --git a/server/api/security.go b/server/api/security.go index fd80b29..a879713 100644 --- a/server/api/security.go +++ b/server/api/security.go @@ -20,7 +20,7 @@ func SecurityCreateEndpoint(c echo.Context) error { item.ID = utils.UUID() item.Source = "管理员添加" - if err := model.CreateNewSecurity(&item); err != nil { + if err := accessSecurityRepository.Create(&item); err != nil { return err } // 更新内存中的安全规则 @@ -31,7 +31,7 @@ func SecurityCreateEndpoint(c echo.Context) error { } func ReloadAccessSecurity() error { - rules, err := model.FindAllAccessSecurities() + rules, err := accessSecurityRepository.FindAllAccessSecurities() if err != nil { return err } @@ -58,7 +58,7 @@ func SecurityPagingEndpoint(c echo.Context) error { order := c.QueryParam("order") field := c.QueryParam("field") - items, total, err := model.FindPageSecurity(pageIndex, pageSize, ip, rule, order, field) + items, total, err := accessSecurityRepository.Find(pageIndex, pageSize, ip, rule, order, field) if err != nil { return err } @@ -77,7 +77,7 @@ func SecurityUpdateEndpoint(c echo.Context) error { return err } - if err := model.UpdateSecurityById(&item, id); err != nil { + if err := accessSecurityRepository.UpdateById(&item, id); err != nil { return err } // 更新内存中的安全规则 @@ -93,7 +93,7 @@ func SecurityDeleteEndpoint(c echo.Context) error { split := strings.Split(ids, ",") for i := range split { jobId := split[i] - if err := model.DeleteSecurityById(jobId); err != nil { + if err := accessSecurityRepository.DeleteById(jobId); err != nil { return err } } @@ -107,7 +107,7 @@ func SecurityDeleteEndpoint(c echo.Context) error { func SecurityGetEndpoint(c echo.Context) error { id := c.Param("id") - item, err := model.FindSecurityById(id) + item, err := accessSecurityRepository.FindById(id) if err != nil { return err } diff --git a/server/api/ticker.go b/server/api/ticker.go new file mode 100644 index 0000000..fa9d2f8 --- /dev/null +++ b/server/api/ticker.go @@ -0,0 +1,62 @@ +package api + +import ( + "github.com/sirupsen/logrus" + "next-terminal/server/constant" + "strconv" + "time" +) + +func SetupTicker() { + + // 每隔一小时删除一次未使用的会话信息 + unUsedSessionTicker := time.NewTicker(time.Minute * 60) + go func() { + for range unUsedSessionTicker.C { + sessions, _ := sessionRepository.FindByStatusIn([]string{constant.NoConnect, constant.Connecting}) + if len(sessions) > 0 { + now := time.Now() + for i := range sessions { + if now.Sub(sessions[i].ConnectedTime.Time) > time.Hour*1 { + _ = sessionRepository.DeleteById(sessions[i].ID) + s := sessions[i].Username + "@" + sessions[i].IP + ":" + strconv.Itoa(sessions[i].Port) + logrus.Infof("会话「%v」ID「%v」超过1小时未打开,已删除。", s, sessions[i].ID) + } + } + } + } + }() + + // 每日凌晨删除超过时长限制的会话 + timeoutSessionTicker := time.NewTicker(time.Hour * 24) + go func() { + for range timeoutSessionTicker.C { + property, err := propertyRepository.FindByName("session-saved-limit") + if err != nil { + return + } + if property.Value == "" || property.Value == "-" { + return + } + limit, err := strconv.Atoi(property.Value) + if err != nil { + return + } + sessions, err := sessionRepository.FindOutTimeSessions(limit) + if err != nil { + return + } + + if len(sessions) > 0 { + var sessionIds []string + for i := range sessions { + sessionIds = append(sessionIds, sessions[i].ID) + } + err := sessionRepository.DeleteByIds(sessionIds) + if err != nil { + logrus.Errorf("删除离线会话失败 %v", err) + } + } + } + }() +} diff --git a/server/config/db.go b/server/config/db.go new file mode 100644 index 0000000..3ef9428 --- /dev/null +++ b/server/config/db.go @@ -0,0 +1,53 @@ +package config + +import ( + "fmt" + "github.com/sirupsen/logrus" + "gorm.io/driver/mysql" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "next-terminal/server/global" + "next-terminal/server/model" +) + +func SetupDB() *gorm.DB { + + var logMode logger.Interface + if global.Config.Debug { + logMode = logger.Default.LogMode(logger.Info) + } else { + logMode = logger.Default.LogMode(logger.Silent) + } + + fmt.Printf("当前数据库模式为:%v\n", global.Config.DB) + var err error + var db *gorm.DB + if global.Config.DB == "mysql" { + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", + global.Config.Mysql.Username, + global.Config.Mysql.Password, + global.Config.Mysql.Hostname, + global.Config.Mysql.Port, + global.Config.Mysql.Database, + ) + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ + Logger: logMode, + }) + } else { + db, err = gorm.Open(sqlite.Open(global.Config.Sqlite.File), &gorm.Config{ + Logger: logMode, + }) + } + + if err != nil { + logrus.WithError(err).Panic("连接数据库异常") + } + + if err := db.AutoMigrate(&model.User{}, &model.Asset{}, &model.AssetAttribute{}, &model.Session{}, &model.Command{}, + &model.Credential{}, &model.Property{}, &model.ResourceSharer{}, &model.UserGroup{}, &model.UserGroupMember{}, + &model.LoginLog{}, &model.Num{}, &model.Job{}, &model.JobLog{}, &model.AccessSecurity{}); err != nil { + logrus.WithError(err).Panic("初始化数据库表结构异常") + } + return db +} diff --git a/server/constant/const.go b/server/constant/const.go index cdba074..0865990 100644 --- a/server/constant/const.go +++ b/server/constant/const.go @@ -1,5 +1,7 @@ package constant +import "next-terminal/server/guacd" + const ( AccessRuleAllow = "allow" // 允许访问 AccessRuleReject = "reject" // 拒绝访问 @@ -31,3 +33,9 @@ const ( TypeUser = "user" // 普通用户 TypeAdmin = "admin" // 管理员 ) + +var SSHParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, SshMode} +var RDPParameterNames = []string{guacd.Domain, guacd.RemoteApp, guacd.RemoteAppDir, guacd.RemoteAppArgs} +var VNCParameterNames = []string{guacd.ColorDepth, guacd.Cursor, guacd.SwapRedBlue, guacd.DestHost, guacd.DestPort} +var TelnetParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.UsernameRegex, guacd.PasswordRegex, guacd.LoginSuccessRegex, guacd.LoginFailureRegex} +var KubernetesParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.Namespace, guacd.Pod, guacd.Container, guacd.UesSSL, guacd.ClientCert, guacd.ClientKey, guacd.CaCert, guacd.IgnoreCert} diff --git a/server/global/global.go b/server/global/global.go index e4ea8bb..1903e6f 100644 --- a/server/global/global.go +++ b/server/global/global.go @@ -5,11 +5,8 @@ import ( "github.com/patrickmn/go-cache" "github.com/robfig/cron/v3" - "gorm.io/gorm" ) -var DB *gorm.DB - var Cache *cache.Cache var Config *config.Config diff --git a/server/model/access_security.go b/server/model/access_security.go index db351cb..c111d12 100644 --- a/server/model/access_security.go +++ b/server/model/access_security.go @@ -1,9 +1,5 @@ package model -import ( - "next-terminal/server/global" -) - type AccessSecurity struct { ID string `json:"id"` Rule string `json:"rule"` @@ -15,69 +11,3 @@ type AccessSecurity struct { func (r *AccessSecurity) TableName() string { return "access_securities" } - -func FindAllAccessSecurities() (o []AccessSecurity, err error) { - db := global.DB - err = db.Order("priority asc").Find(&o).Error - return -} - -func FindPageSecurity(pageIndex, pageSize int, ip, rule, order, field string) (o []AccessSecurity, total int64, err error) { - t := AccessSecurity{} - db := global.DB.Table(t.TableName()) - dbCounter := global.DB.Table(t.TableName()) - - if len(ip) > 0 { - db = db.Where("ip like ?", "%"+ip+"%") - dbCounter = dbCounter.Where("ip like ?", "%"+ip+"%") - } - - if len(rule) > 0 { - db = db.Where("rule = ?", rule) - dbCounter = dbCounter.Where("rule = ?", rule) - } - - err = dbCounter.Count(&total).Error - if err != nil { - return nil, 0, err - } - - if order == "descend" { - order = "desc" - } else { - order = "asc" - } - - if field == "ip" { - field = "ip" - } else if field == "rule" { - field = "rule" - } else { - field = "priority" - } - - err = db.Order(field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error - if o == nil { - o = make([]AccessSecurity, 0) - } - return -} - -func CreateNewSecurity(o *AccessSecurity) error { - return global.DB.Create(o).Error -} - -func UpdateSecurityById(o *AccessSecurity, id string) error { - o.ID = id - return global.DB.Updates(o).Error -} - -func DeleteSecurityById(id string) error { - - return global.DB.Where("id = ?", id).Delete(AccessSecurity{}).Error -} - -func FindSecurityById(id string) (o *AccessSecurity, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error - return -} diff --git a/server/model/asset.go b/server/model/asset.go index 6e37ca1..8b8a7b3 100644 --- a/server/model/asset.go +++ b/server/model/asset.go @@ -1,10 +1,6 @@ package model import ( - "strings" - - "next-terminal/server/constant" - "next-terminal/server/global" "next-terminal/server/utils" ) @@ -45,195 +41,13 @@ func (r *Asset) TableName() string { return "assets" } -func FindAllAsset() (o []Asset, err error) { - err = global.DB.Find(&o).Error - return +type AssetAttribute struct { + Id string `gorm:"index" json:"id"` + AssetId string `gorm:"index" json:"assetId"` + Name string `gorm:"index" json:"name"` + Value string `json:"value"` } -func FindAssetByIds(assetIds []string) (o []Asset, err error) { - err = global.DB.Where("id in ?", assetIds).Find(&o).Error - return -} - -func FindAssetByProtocol(protocol string) (o []Asset, err error) { - err = global.DB.Where("protocol = ?", protocol).Find(&o).Error - return -} - -func FindAssetByProtocolAndIds(protocol string, assetIds []string) (o []Asset, err error) { - err = global.DB.Where("protocol = ? and id in ?", protocol, assetIds).Find(&o).Error - return -} - -func FindAssetByConditions(protocol string, account User) (o []Asset, err error) { - db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id") - - if constant.TypeUser == account.Type { - owner := account.ID - db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner) - } - - if len(protocol) > 0 { - db = db.Where("assets.protocol = ?", protocol) - } - err = db.Find(&o).Error - return -} - -func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account User, owner, sharer, userGroupId, ip, order, field string) (o []AssetVo, total int64, err error) { - db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created,assets.tags, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id") - dbCounter := global.DB.Table("assets").Select("DISTINCT assets.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id") - - if constant.TypeUser == account.Type { - owner := account.ID - db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner) - dbCounter = dbCounter.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner) - - // 查询用户所在用户组列表 - userGroupIds, err := FindUserGroupIdsByUserId(account.ID) - if err != nil { - return nil, 0, err - } - - if len(userGroupIds) > 0 { - db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) - dbCounter = dbCounter.Or("resource_sharers.user_group_id in ?", userGroupIds) - } - } else { - if len(owner) > 0 { - db = db.Where("assets.owner = ?", owner) - dbCounter = dbCounter.Where("assets.owner = ?", owner) - } - if len(sharer) > 0 { - db = db.Where("resource_sharers.user_id = ?", sharer) - dbCounter = dbCounter.Where("resource_sharers.user_id = ?", sharer) - } - - if len(userGroupId) > 0 { - db = db.Where("resource_sharers.user_group_id = ?", userGroupId) - dbCounter = dbCounter.Where("resource_sharers.user_group_id = ?", userGroupId) - } - } - - if len(name) > 0 { - db = db.Where("assets.name like ?", "%"+name+"%") - dbCounter = dbCounter.Where("assets.name like ?", "%"+name+"%") - } - - if len(ip) > 0 { - db = db.Where("assets.ip like ?", "%"+ip+"%") - dbCounter = dbCounter.Where("assets.ip like ?", "%"+ip+"%") - } - - if len(protocol) > 0 { - db = db.Where("assets.protocol = ?", protocol) - dbCounter = dbCounter.Where("assets.protocol = ?", protocol) - } - - if len(tags) > 0 { - tagArr := strings.Split(tags, ",") - for i := range tagArr { - if global.Config.DB == "sqlite" { - db = db.Where("(',' || assets.tags || ',') LIKE ?", "%,"+tagArr[i]+",%") - dbCounter = dbCounter.Where("(',' || assets.tags || ',') LIKE ?", "%,"+tagArr[i]+",%") - } else { - db = db.Where("find_in_set(?, assets.tags)", tagArr[i]) - dbCounter = dbCounter.Where("find_in_set(?, assets.tags)", tagArr[i]) - } - } - } - - err = dbCounter.Count(&total).Error - if err != nil { - return nil, 0, err - } - - if order == "ascend" { - order = "asc" - } else { - order = "desc" - } - - if field == "name" { - field = "name" - } else { - field = "created" - } - - err = db.Order("assets." + field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error - - if o == nil { - o = make([]AssetVo, 0) - } - return -} - -func CreateNewAsset(o *Asset) (err error) { - if err = global.DB.Create(o).Error; err != nil { - return err - } - return nil -} - -func FindAssetById(id string) (o Asset, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error - return -} - -func UpdateAssetById(o *Asset, id string) { - o.ID = id - global.DB.Updates(o) -} - -func UpdateAssetActiveById(active bool, id string) { - sql := "update assets set active = ? where id = ?" - global.DB.Exec(sql, active, id) -} - -func DeleteAssetById(id string) error { - return global.DB.Where("id = ?", id).Delete(&Asset{}).Error -} - -func CountAsset() (total int64, err error) { - err = global.DB.Find(&Asset{}).Count(&total).Error - return -} - -func CountAssetByUserId(userId string) (total int64, err error) { - db := global.DB.Joins("left join resource_sharers on assets.id = resource_sharers.resource_id") - - db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", userId, userId) - - // 查询用户所在用户组列表 - userGroupIds, err := FindUserGroupIdsByUserId(userId) - if err != nil { - return 0, err - } - - if len(userGroupIds) > 0 { - db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) - } - err = db.Find(&Asset{}).Count(&total).Error - return -} - -func FindAssetTags() (o []string, err error) { - var assets []Asset - err = global.DB.Not("tags = ?", "").Find(&assets).Error - if err != nil { - return nil, err - } - - o = make([]string, 0) - - for i := range assets { - if len(assets[i].Tags) == 0 { - continue - } - split := strings.Split(assets[i].Tags, ",") - - o = append(o, split...) - } - - return utils.Distinct(o), nil +func (r *AssetAttribute) TableName() string { + return "asset_attributes" } diff --git a/server/model/asset_attribute.go b/server/model/asset_attribute.go deleted file mode 100644 index 21db88c..0000000 --- a/server/model/asset_attribute.go +++ /dev/null @@ -1,119 +0,0 @@ -package model - -import ( - "fmt" - - "next-terminal/server/constant" - "next-terminal/server/global" - "next-terminal/server/guacd" - "next-terminal/server/utils" - - "github.com/labstack/echo/v4" - "gorm.io/gorm" -) - -type AssetAttribute struct { - Id string `gorm:"index" json:"id"` - AssetId string `gorm:"index" json:"assetId"` - Name string `gorm:"index" json:"name"` - Value string `json:"value"` -} - -func (r *AssetAttribute) TableName() string { - return "asset_attributes" -} - -var SSHParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, constant.SshMode} -var RDPParameterNames = []string{guacd.Domain, guacd.RemoteApp, guacd.RemoteAppDir, guacd.RemoteAppArgs} -var VNCParameterNames = []string{guacd.ColorDepth, guacd.Cursor, guacd.SwapRedBlue, guacd.DestHost, guacd.DestPort} -var TelnetParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.UsernameRegex, guacd.PasswordRegex, guacd.LoginSuccessRegex, guacd.LoginFailureRegex} -var KubernetesParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.Namespace, guacd.Pod, guacd.Container, guacd.UesSSL, guacd.ClientCert, guacd.ClientKey, guacd.CaCert, guacd.IgnoreCert} - -func UpdateAssetAttributes(assetId, protocol string, m echo.Map) error { - var data []AssetAttribute - var parameterNames []string - switch protocol { - case "ssh": - parameterNames = SSHParameterNames - case "rdp": - parameterNames = RDPParameterNames - case "vnc": - parameterNames = VNCParameterNames - case "telnet": - parameterNames = TelnetParameterNames - case "kubernetes": - parameterNames = KubernetesParameterNames - - } - - for i := range parameterNames { - name := parameterNames[i] - if m[name] != nil && m[name] != "" { - data = append(data, genAttribute(assetId, name, m)) - } - } - - return global.DB.Transaction(func(tx *gorm.DB) error { - err := tx.Where("asset_id = ?", assetId).Delete(&AssetAttribute{}).Error - if err != nil { - return err - } - return tx.CreateInBatches(&data, len(data)).Error - }) -} - -func genAttribute(assetId, name string, m echo.Map) AssetAttribute { - value := fmt.Sprintf("%v", m[name]) - attribute := AssetAttribute{ - Id: utils.Sign([]string{assetId, name}), - AssetId: assetId, - Name: name, - Value: value, - } - return attribute -} - -func FindAssetAttributeByAssetId(assetId string) (o []AssetAttribute, err error) { - err = global.DB.Where("asset_id = ?", assetId).Find(&o).Error - if o == nil { - o = make([]AssetAttribute, 0) - } - return o, err -} - -func FindAssetAttrMapByAssetId(assetId string) (map[string]interface{}, error) { - asset, err := FindAssetById(assetId) - if err != nil { - return nil, err - } - attributes, err := FindAssetAttributeByAssetId(assetId) - if err != nil { - return nil, err - } - - var parameterNames []string - switch asset.Protocol { - case "ssh": - parameterNames = SSHParameterNames - case "rdp": - parameterNames = RDPParameterNames - case "vnc": - parameterNames = VNCParameterNames - case "telnet": - parameterNames = TelnetParameterNames - case "kubernetes": - parameterNames = KubernetesParameterNames - } - propertiesMap := FindAllPropertiesMap() - var attributeMap = make(map[string]interface{}) - for name := range propertiesMap { - if utils.Contains(parameterNames, name) { - attributeMap[name] = propertiesMap[name] - } - } - - for i := range attributes { - attributeMap[attributes[i].Name] = attributes[i].Value - } - return attributeMap, nil -} diff --git a/server/model/command.go b/server/model/command.go index 22d82f6..b085ea3 100644 --- a/server/model/command.go +++ b/server/model/command.go @@ -1,8 +1,6 @@ package model import ( - "next-terminal/server/constant" - "next-terminal/server/global" "next-terminal/server/utils" ) @@ -27,69 +25,3 @@ type CommandVo struct { func (r *Command) TableName() string { return "commands" } - -func FindPageCommand(pageIndex, pageSize int, name, content, order, field string, account User) (o []CommandVo, total int64, err error) { - - db := global.DB.Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id") - dbCounter := global.DB.Table("commands").Select("DISTINCT commands.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id") - - if constant.TypeUser == account.Type { - owner := account.ID - db = db.Where("commands.owner = ? or resource_sharers.user_id = ?", owner, owner) - dbCounter = dbCounter.Where("commands.owner = ? or resource_sharers.user_id = ?", owner, owner) - } - - if len(name) > 0 { - db = db.Where("commands.name like ?", "%"+name+"%") - dbCounter = dbCounter.Where("commands.name like ?", "%"+name+"%") - } - - if len(content) > 0 { - db = db.Where("commands.content like ?", "%"+content+"%") - dbCounter = dbCounter.Where("commands.content like ?", "%"+content+"%") - } - - err = dbCounter.Count(&total).Error - if err != nil { - return nil, 0, err - } - - if order == "ascend" { - order = "asc" - } else { - order = "desc" - } - - if field == "name" { - field = "name" - } else { - field = "created" - } - - err = db.Order("commands." + field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error - if o == nil { - o = make([]CommandVo, 0) - } - return -} - -func CreateNewCommand(o *Command) (err error) { - if err = global.DB.Create(o).Error; err != nil { - return err - } - return nil -} - -func FindCommandById(id string) (o Command, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error - return -} - -func UpdateCommandById(o *Command, id string) { - o.ID = id - global.DB.Updates(o) -} - -func DeleteCommandById(id string) error { - return global.DB.Where("id = ?", id).Delete(&Command{}).Error -} diff --git a/server/model/credential.go b/server/model/credential.go index 4a5570a..ddebdb0 100644 --- a/server/model/credential.go +++ b/server/model/credential.go @@ -1,8 +1,6 @@ package model import ( - "next-terminal/server/constant" - "next-terminal/server/global" "next-terminal/server/utils" ) @@ -37,95 +35,3 @@ type CredentialSimpleVo struct { ID string `json:"id"` Name string `json:"name"` } - -func FindAllCredential(account User) (o []CredentialSimpleVo, err error) { - db := global.DB.Table("credentials").Select("DISTINCT credentials.id,credentials.name").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id") - if account.Type == constant.TypeUser { - db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", account.ID, account.ID) - } - err = db.Find(&o).Error - return -} - -func FindPageCredential(pageIndex, pageSize int, name, order, field string, account User) (o []CredentialVo, total int64, err error) { - db := global.DB.Table("credentials").Select("credentials.id,credentials.name,credentials.type,credentials.username,credentials.owner,credentials.created,users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on credentials.owner = users.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id").Group("credentials.id") - dbCounter := global.DB.Table("credentials").Select("DISTINCT credentials.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id").Group("credentials.id") - - if constant.TypeUser == account.Type { - owner := account.ID - db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", owner, owner) - dbCounter = dbCounter.Where("credentials.owner = ? or resource_sharers.user_id = ?", owner, owner) - } - - if len(name) > 0 { - db = db.Where("credentials.name like ?", "%"+name+"%") - dbCounter = dbCounter.Where("credentials.name like ?", "%"+name+"%") - } - - err = dbCounter.Count(&total).Error - if err != nil { - return nil, 0, err - } - - if order == "ascend" { - order = "asc" - } else { - order = "desc" - } - - if field == "name" { - field = "name" - } else { - field = "created" - } - - err = db.Order("credentials." + field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error - if o == nil { - o = make([]CredentialVo, 0) - } - return -} - -func CreateNewCredential(o *Credential) (err error) { - if err = global.DB.Create(o).Error; err != nil { - return err - } - return nil -} - -func FindCredentialById(id string) (o Credential, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error - return -} - -func UpdateCredentialById(o *Credential, id string) { - o.ID = id - global.DB.Updates(o) -} - -func DeleteCredentialById(id string) error { - return global.DB.Where("id = ?", id).Delete(&Credential{}).Error -} - -func CountCredential() (total int64, err error) { - err = global.DB.Find(&Credential{}).Count(&total).Error - return -} - -func CountCredentialByUserId(userId string) (total int64, err error) { - db := global.DB.Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id") - - db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", userId, userId) - - // 查询用户所在用户组列表 - userGroupIds, err := FindUserGroupIdsByUserId(userId) - if err != nil { - return 0, err - } - - if len(userGroupIds) > 0 { - db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) - } - err = db.Find(&Credential{}).Count(&total).Error - return -} diff --git a/server/model/job.go b/server/model/job.go index 03d21c6..35a337f 100644 --- a/server/model/job.go +++ b/server/model/job.go @@ -1,19 +1,7 @@ package model import ( - "encoding/json" - "errors" - "fmt" - "strings" - "time" - - "next-terminal/server/constant" - "next-terminal/server/global" - "next-terminal/server/term" "next-terminal/server/utils" - - "github.com/robfig/cron/v3" - "github.com/sirupsen/logrus" ) type Job struct { @@ -34,323 +22,13 @@ func (r *Job) TableName() string { return "jobs" } -func FindPageJob(pageIndex, pageSize int, name, status, order, field string) (o []Job, total int64, err error) { - job := Job{} - db := global.DB.Table(job.TableName()) - dbCounter := global.DB.Table(job.TableName()) - - if len(name) > 0 { - db = db.Where("name like ?", "%"+name+"%") - dbCounter = dbCounter.Where("name like ?", "%"+name+"%") - } - - if len(status) > 0 { - db = db.Where("status = ?", status) - dbCounter = dbCounter.Where("status = ?", status) - } - - err = dbCounter.Count(&total).Error - if err != nil { - return nil, 0, err - } - - if order == "ascend" { - order = "asc" - } else { - order = "desc" - } - - if field == "name" { - field = "name" - } else if field == "created" { - field = "created" - } else { - field = "updated" - } - - err = db.Order(field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error - if o == nil { - o = make([]Job, 0) - } - return +type JobLog struct { + ID string `json:"id"` + Timestamp utils.JsonTime `json:"timestamp"` + JobId string `json:"jobId"` + Message string `json:"message"` } -func FindJobByFunc(function string) (o []Job, err error) { - db := global.DB - err = db.Where("func = ?", function).Find(&o).Error - return -} - -func CreateNewJob(o *Job) (err error) { - - if o.Status == constant.JobStatusRunning { - j, err := getJob(o) - if err != nil { - return err - } - jobId, err := global.Cron.AddJob(o.Cron, j) - if err != nil { - return err - } - o.CronJobId = int(jobId) - } - - return global.DB.Create(o).Error -} - -func UpdateJobById(o *Job, id string) (err error) { - if o.Status == constant.JobStatusRunning { - return errors.New("请先停止定时任务后再修改") - } - - o.ID = id - return global.DB.Updates(o).Error -} - -func UpdateJonUpdatedById(id string) (err error) { - err = global.DB.Updates(Job{ID: id, Updated: utils.NowJsonTime()}).Error - return -} - -func ChangeJobStatusById(id, status string) (err error) { - var job Job - err = global.DB.Where("id = ?", id).First(&job).Error - if err != nil { - return err - } - if status == constant.JobStatusRunning { - j, err := getJob(&job) - if err != nil { - return err - } - entryID, err := global.Cron.AddJob(job.Cron, j) - if err != nil { - return err - } - logrus.Debugf("开启计划任务「%v」,运行中计划任务数量「%v」", job.Name, len(global.Cron.Entries())) - - return global.DB.Updates(Job{ID: id, Status: constant.JobStatusRunning, CronJobId: int(entryID)}).Error - } else { - global.Cron.Remove(cron.EntryID(job.CronJobId)) - logrus.Debugf("关闭计划任务「%v」,运行中计划任务数量「%v」", job.Name, len(global.Cron.Entries())) - return global.DB.Updates(Job{ID: id, Status: constant.JobStatusNotRunning}).Error - } -} - -func ExecJobById(id string) (err error) { - job, err := FindJobById(id) - if err != nil { - return err - } - j, err := getJob(&job) - if err != nil { - return err - } - j.Run() - return nil -} - -func FindJobById(id string) (o Job, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error - return -} - -func DeleteJobById(id string) error { - job, err := FindJobById(id) - if err != nil { - return err - } - if job.Status == constant.JobStatusRunning { - if err := ChangeJobStatusById(id, constant.JobStatusNotRunning); err != nil { - return err - } - } - return global.DB.Where("id = ?", id).Delete(Job{}).Error -} - -func getJob(j *Job) (job cron.Job, err error) { - switch j.Func { - case constant.FuncCheckAssetStatusJob: - job = CheckAssetStatusJob{ID: j.ID, Mode: j.Mode, ResourceIds: j.ResourceIds, Metadata: j.Metadata} - case constant.FuncShellJob: - job = ShellJob{ID: j.ID, Mode: j.Mode, ResourceIds: j.ResourceIds, Metadata: j.Metadata} - default: - return nil, errors.New("未识别的任务") - } - return job, err -} - -type CheckAssetStatusJob struct { - ID string - Mode string - ResourceIds string - Metadata string -} - -func (r CheckAssetStatusJob) Run() { - if r.ID == "" { - return - } - - var assets []Asset - if r.Mode == constant.JobModeAll { - assets, _ = FindAllAsset() - } else { - assets, _ = FindAssetByIds(strings.Split(r.ResourceIds, ",")) - } - - if len(assets) == 0 { - return - } - - msgChan := make(chan string) - for i := range assets { - asset := assets[i] - go func() { - t1 := time.Now() - active := utils.Tcping(asset.IP, asset.Port) - elapsed := time.Since(t1) - msg := fmt.Sprintf("资产「%v」存活状态检测完成,存活「%v」,耗时「%v」", asset.Name, active, elapsed) - - UpdateAssetActiveById(active, asset.ID) - logrus.Infof(msg) - msgChan <- msg - }() - } - - var message = "" - for i := 0; i < len(assets); i++ { - message += <-msgChan + "\n" - } - - _ = UpdateJonUpdatedById(r.ID) - jobLog := JobLog{ - ID: utils.UUID(), - JobId: r.ID, - Timestamp: utils.NowJsonTime(), - Message: message, - } - - _ = CreateNewJobLog(&jobLog) -} - -type ShellJob struct { - ID string - Mode string - ResourceIds string - Metadata string -} - -type MetadataShell struct { - Shell string -} - -func (r ShellJob) Run() { - if r.ID == "" { - return - } - - var assets []Asset - if r.Mode == constant.JobModeAll { - assets, _ = FindAssetByProtocol("ssh") - } else { - assets, _ = FindAssetByProtocolAndIds("ssh", strings.Split(r.ResourceIds, ",")) - } - - if len(assets) == 0 { - return - } - - var metadataShell MetadataShell - err := json.Unmarshal([]byte(r.Metadata), &metadataShell) - if err != nil { - logrus.Errorf("JSON数据解析失败 %v", err) - return - } - - msgChan := make(chan string) - for i := range assets { - asset, err := FindAssetById(assets[i].ID) - if err != nil { - msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询数据异常「%v」", assets[i].Name, err.Error()) - return - } - - var ( - username = asset.Username - password = asset.Password - privateKey = asset.PrivateKey - passphrase = asset.Passphrase - ip = asset.IP - port = asset.Port - ) - - if asset.AccountType == "credential" { - credential, err := FindCredentialById(asset.CredentialId) - if err != nil { - msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询授权凭证数据异常「%v」", assets[i].Name, err.Error()) - return - } - - if credential.Type == constant.Custom { - username = credential.Username - password = credential.Password - } else { - username = credential.Username - privateKey = credential.PrivateKey - passphrase = credential.Passphrase - } - } - - go func() { - - t1 := time.Now() - result, err := ExecCommandBySSH(metadataShell.Shell, ip, port, username, password, privateKey, passphrase) - elapsed := time.Since(t1) - var msg string - if err != nil { - msg = fmt.Sprintf("资产「%v」Shell执行失败,返回值「%v」,耗时「%v」", asset.Name, err.Error(), elapsed) - logrus.Infof(msg) - } else { - msg = fmt.Sprintf("资产「%v」Shell执行成功,返回值「%v」,耗时「%v」", asset.Name, result, elapsed) - logrus.Infof(msg) - } - - msgChan <- msg - }() - } - - var message = "" - for i := 0; i < len(assets); i++ { - message += <-msgChan + "\n" - } - - _ = UpdateJonUpdatedById(r.ID) - jobLog := JobLog{ - ID: utils.UUID(), - JobId: r.ID, - Timestamp: utils.NowJsonTime(), - Message: message, - } - - _ = CreateNewJobLog(&jobLog) -} - -func ExecCommandBySSH(cmd, ip string, port int, username, password, privateKey, passphrase string) (result string, err error) { - sshClient, err := term.NewSshClient(ip, port, username, password, privateKey, passphrase) - if err != nil { - return "", err - } - - session, err := sshClient.NewSession() - if err != nil { - return "", err - } - defer session.Close() - //执行远程命令 - combo, err := session.CombinedOutput(cmd) - if err != nil { - return "", err - } - return string(combo), nil +func (r *JobLog) TableName() string { + return "job_logs" } diff --git a/server/model/job_log.go b/server/model/job_log.go deleted file mode 100644 index 9946cc1..0000000 --- a/server/model/job_log.go +++ /dev/null @@ -1,30 +0,0 @@ -package model - -import ( - "next-terminal/server/global" - "next-terminal/server/utils" -) - -type JobLog struct { - ID string `json:"id"` - Timestamp utils.JsonTime `json:"timestamp"` - JobId string `json:"jobId"` - Message string `json:"message"` -} - -func (r *JobLog) TableName() string { - return "job_logs" -} - -func CreateNewJobLog(o *JobLog) error { - return global.DB.Create(o).Error -} - -func FindJobLogs(jobId string) (o []JobLog, err error) { - err = global.DB.Where("job_id = ?", jobId).Order("timestamp asc").Find(&o).Error - return -} - -func DeleteJobLogByJobId(jobId string) error { - return global.DB.Where("job_id = ?", jobId).Delete(JobLog{}).Error -} diff --git a/server/model/login_log.go b/server/model/login_log.go index 8aaf95f..fcf7856 100644 --- a/server/model/login_log.go +++ b/server/model/login_log.go @@ -1,7 +1,6 @@ package model import ( - "next-terminal/server/global" "next-terminal/server/utils" ) @@ -29,78 +28,3 @@ type LoginLogVo struct { func (r *LoginLog) TableName() string { return "login_logs" } - -func FindPageLoginLog(pageIndex, pageSize int, userId, clientIp string) (o []LoginLogVo, total int64, err error) { - - db := global.DB.Table("login_logs").Select("login_logs.id,login_logs.user_id,login_logs.client_ip,login_logs.client_user_agent,login_logs.login_time, login_logs.logout_time, users.nickname as user_name").Joins("left join users on login_logs.user_id = users.id") - dbCounter := global.DB.Table("login_logs").Select("DISTINCT login_logs.id") - - if userId != "" { - db = db.Where("login_logs.user_id = ?", userId) - dbCounter = dbCounter.Where("login_logs.user_id = ?", userId) - } - - if clientIp != "" { - db = db.Where("login_logs.client_ip like ?", "%"+clientIp+"%") - dbCounter = dbCounter.Where("login_logs.client_ip like ?", "%"+clientIp+"%") - } - - err = dbCounter.Count(&total).Error - if err != nil { - return nil, 0, err - } - - err = db.Order("login_logs.login_time desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error - if o == nil { - o = make([]LoginLogVo, 0) - } - return -} - -func FindAliveLoginLogs() (o []LoginLog, err error) { - err = global.DB.Where("logout_time is null").Find(&o).Error - return -} - -func FindAliveLoginLogsByUserId(userId string) (o []LoginLog, err error) { - err = global.DB.Where("logout_time is null and user_id = ?", userId).Find(&o).Error - return -} - -func CreateNewLoginLog(o *LoginLog) (err error) { - return global.DB.Create(o).Error -} - -func DeleteLoginLogByIdIn(ids []string) (err error) { - return global.DB.Where("id in ?", ids).Delete(&LoginLog{}).Error -} - -func FindLoginLogById(id string) (o LoginLog, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error - return -} - -func Logout(token string) (err error) { - // - //loginLog, err := FindLoginLogById(token) - //if err != nil { - // logrus.Warnf("登录日志「%v」获取失败", token) - // return - //} - // - //err = global.DB.Updates(&LoginLog{LogoutTime: utils.NowJsonTime(), ID: token}).Error - //if err != nil { - // return err - //} - // - //loginLogs, err := FindAliveLoginLogsByUserId(loginLog.UserId) - //if err != nil { - // return - //} - // - //if len(loginLogs) == 0 { - // // TODO - // err = UpdateUserOnline(false, loginLog.UserId) - //} - return -} diff --git a/server/model/num.go b/server/model/num.go index 78481fb..fa7bd58 100644 --- a/server/model/num.go +++ b/server/model/num.go @@ -1,9 +1,5 @@ package model -import ( - "next-terminal/server/global" -) - type Num struct { I string `gorm:"primary_key" json:"i"` } @@ -11,15 +7,3 @@ type Num struct { func (r *Num) TableName() string { return "nums" } - -func FindAllTemp() (o []Num) { - if global.DB.Find(&o).Error != nil { - return nil - } - return -} - -func CreateNewTemp(o *Num) (err error) { - err = global.DB.Create(o).Error - return -} diff --git a/server/model/property.go b/server/model/property.go index 0bd7d93..7f2aae8 100644 --- a/server/model/property.go +++ b/server/model/property.go @@ -1,16 +1,5 @@ package model -import ( - "net/smtp" - - "next-terminal/server/constant" - "next-terminal/server/global" - "next-terminal/server/guacd" - - "github.com/jordan-wright/email" - "github.com/sirupsen/logrus" -) - type Property struct { Name string `gorm:"primary_key" json:"name"` Value string `json:"value"` @@ -19,73 +8,3 @@ type Property struct { func (r *Property) TableName() string { return "properties" } - -func FindAllProperties() (o []Property) { - if global.DB.Find(&o).Error != nil { - return nil - } - return -} - -func CreateNewProperty(o *Property) (err error) { - err = global.DB.Create(o).Error - return -} - -func UpdatePropertyByName(o *Property, name string) { - o.Name = name - global.DB.Updates(o) -} - -func FindPropertyByName(name string) (o Property, err error) { - err = global.DB.Where("name = ?", name).First(&o).Error - return -} - -func FindAllPropertiesMap() map[string]string { - properties := FindAllProperties() - propertyMap := make(map[string]string) - for i := range properties { - propertyMap[properties[i].Name] = properties[i].Value - } - return propertyMap -} - -func GetDrivePath() (string, error) { - property, err := FindPropertyByName(guacd.DrivePath) - if err != nil { - return "", err - } - return property.Value, nil -} - -func GetRecordingPath() (string, error) { - property, err := FindPropertyByName(guacd.RecordingPath) - if err != nil { - return "", err - } - return property.Value, nil -} - -func SendMail(to, subject, text string) { - propertiesMap := FindAllPropertiesMap() - host := propertiesMap[constant.MailHost] - port := propertiesMap[constant.MailPort] - username := propertiesMap[constant.MailUsername] - password := propertiesMap[constant.MailPassword] - - if host == "" || port == "" || username == "" || password == "" { - logrus.Debugf("邮箱信息不完整,跳过发送邮件。") - return - } - - e := email.NewEmail() - e.From = "Next Terminal <" + username + ">" - e.To = []string{to} - e.Subject = subject - e.Text = []byte(text) - err := e.Send(host+":"+port, smtp.PlainAuth("", username, password, host)) - if err != nil { - logrus.Errorf("邮件发送失败: %v", err.Error()) - } -} diff --git a/server/model/resource-sharer.go b/server/model/resource-sharer.go index 43dcea7..8a9591b 100644 --- a/server/model/resource-sharer.go +++ b/server/model/resource-sharer.go @@ -1,14 +1,5 @@ package model -import ( - "next-terminal/server/global" - "next-terminal/server/utils" - - "github.com/labstack/echo/v4" - "github.com/pkg/errors" - "gorm.io/gorm" -) - type ResourceSharer struct { ID string `gorm:"primary_key" json:"id"` ResourceId string `gorm:"index" json:"resourceId"` @@ -20,179 +11,3 @@ type ResourceSharer struct { func (r *ResourceSharer) TableName() string { return "resource_sharers" } - -func FindUserIdsByResourceId(resourceId string) (r []string, err error) { - db := global.DB - err = db.Table("resource_sharers").Select("user_id").Where("resource_id = ?", resourceId).Find(&r).Error - if r == nil { - r = make([]string, 0) - } - return -} - -func OverwriteUserIdsByResourceId(resourceId, resourceType string, userIds []string) (err error) { - db := global.DB.Begin() - - var owner string - // 检查资产是否存在 - switch resourceType { - case "asset": - resource := Asset{} - err = db.Where("id = ?", resourceId).First(&resource).Error - owner = resource.Owner - case "command": - resource := Command{} - err = db.Where("id = ?", resourceId).First(&resource).Error - owner = resource.Owner - case "credential": - resource := Credential{} - err = db.Where("id = ?", resourceId).First(&resource).Error - owner = resource.Owner - } - - if err == gorm.ErrRecordNotFound { - return echo.NewHTTPError(404, "资源「"+resourceId+"」不存在") - } - - for i := range userIds { - if owner == userIds[i] { - return echo.NewHTTPError(400, "参数错误") - } - } - - db.Where("resource_id = ?", resourceId).Delete(&ResourceSharer{}) - - for i := range userIds { - userId := userIds[i] - if len(userId) == 0 { - continue - } - id := utils.Sign([]string{resourceId, resourceType, userId}) - resource := &ResourceSharer{ - ID: id, - ResourceId: resourceId, - ResourceType: resourceType, - UserId: userId, - } - err = db.Create(resource).Error - if err != nil { - return err - } - } - db.Commit() - return nil -} - -func DeleteByUserIdAndResourceTypeAndResourceIdIn(userGroupId, userId, resourceType string, resourceIds []string) error { - db := global.DB - if userGroupId != "" { - db = db.Where("user_group_id = ?", userGroupId) - } - - if userId != "" { - db = db.Where("user_id = ?", userId) - } - - if resourceType != "" { - db = db.Where("resource_type = ?", resourceType) - } - - if resourceIds != nil { - db = db.Where("resource_id in ?", resourceIds) - } - - return db.Delete(&ResourceSharer{}).Error -} - -func DeleteResourceSharerByResourceId(resourceId string) error { - return global.DB.Where("resource_id = ?", resourceId).Delete(&ResourceSharer{}).Error -} - -func AddSharerResources(userGroupId, userId, resourceType string, resourceIds []string) error { - return global.DB.Transaction(func(tx *gorm.DB) (err error) { - - for i := range resourceIds { - resourceId := resourceIds[i] - - var owner string - // 检查资产是否存在 - switch resourceType { - case "asset": - resource := Asset{} - if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil { - return errors.Wrap(err, "find asset fail") - } - owner = resource.Owner - case "command": - resource := Command{} - if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil { - return errors.Wrap(err, "find command fail") - } - owner = resource.Owner - case "credential": - resource := Credential{} - if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil { - return errors.Wrap(err, "find credential fail") - - } - owner = resource.Owner - } - - if owner == userId { - return echo.NewHTTPError(400, "参数错误") - } - - id := utils.Sign([]string{resourceId, resourceType, userId, userGroupId}) - resource := &ResourceSharer{ - ID: id, - ResourceId: resourceId, - ResourceType: resourceType, - UserId: userId, - UserGroupId: userGroupId, - } - err = tx.Create(resource).Error - if err != nil { - return err - } - } - return nil - }) -} - -func FindAssetIdsByUserId(userId string) (assetIds []string, err error) { - // 查询当前用户创建的资产 - var ownerAssetIds, sharerAssetIds []string - asset := Asset{} - err = global.DB.Table(asset.TableName()).Select("id").Where("owner = ?", userId).Find(&ownerAssetIds).Error - if err != nil { - return nil, err - } - - // 查询其他用户授权给该用户的资产 - groupIds, err := FindUserGroupIdsByUserId(userId) - if err != nil { - return nil, err - } - - db := global.DB.Table("resource_sharers").Select("resource_id").Where("user_id = ?", userId) - if len(groupIds) > 0 { - db = db.Or("user_group_id in ?", groupIds) - } - err = db.Find(&sharerAssetIds).Error - if err != nil { - return nil, err - } - - // 合并查询到的资产ID - assetIds = make([]string, 0) - - if ownerAssetIds != nil { - assetIds = append(assetIds, ownerAssetIds...) - } - - if sharerAssetIds != nil { - assetIds = append(assetIds, sharerAssetIds...) - } - - return -} diff --git a/server/model/session.go b/server/model/session.go index 55c8c33..dd3a639 100644 --- a/server/model/session.go +++ b/server/model/session.go @@ -1,12 +1,6 @@ package model import ( - "os" - "path" - "time" - - "next-terminal/server/constant" - "next-terminal/server/global" "next-terminal/server/utils" ) @@ -60,151 +54,3 @@ type SessionVo struct { Message string `json:"message"` Mode string `json:"mode"` } - -func FindPageSession(pageIndex, pageSize int, status, userId, clientIp, assetId, protocol string) (results []SessionVo, total int64, err error) { - - db := global.DB - var params []interface{} - - params = append(params, status) - - itemSql := "SELECT s.id,s.mode, s.protocol,s.recording, s.connection_id, s.asset_id, s.creator, s.client_ip, s.width, s.height, s.ip, s.port, s.username, s.status, s.connected_time, s.disconnected_time,s.code, s.message, a.name AS asset_name, u.nickname AS creator_name FROM sessions s LEFT JOIN assets a ON s.asset_id = a.id LEFT JOIN users u ON s.creator = u.id WHERE s.STATUS = ? " - countSql := "select count(*) from sessions as s where s.status = ? " - - if len(userId) > 0 { - itemSql += " and s.creator = ?" - countSql += " and s.creator = ?" - params = append(params, userId) - } - - if len(clientIp) > 0 { - itemSql += " and s.client_ip like ?" - countSql += " and s.client_ip like ?" - params = append(params, "%"+clientIp+"%") - } - - if len(assetId) > 0 { - itemSql += " and s.asset_id = ?" - countSql += " and s.asset_id = ?" - params = append(params, assetId) - } - - if len(protocol) > 0 { - itemSql += " and s.protocol = ?" - countSql += " and s.protocol = ?" - params = append(params, protocol) - } - - params = append(params, (pageIndex-1)*pageSize, pageSize) - itemSql += " order by s.connected_time desc LIMIT ?, ?" - - db.Raw(countSql, params...).Scan(&total) - - err = db.Raw(itemSql, params...).Scan(&results).Error - - if results == nil { - results = make([]SessionVo, 0) - } - return -} - -func FindSessionByStatus(status string) (o []Session, err error) { - err = global.DB.Where("status = ?", status).Find(&o).Error - return -} - -func FindSessionByStatusIn(statuses []string) (o []Session, err error) { - err = global.DB.Where("status in ?", statuses).Find(&o).Error - return -} - -func FindOutTimeSessions(dayLimit int) (o []Session, err error) { - limitTime := time.Now().Add(time.Duration(-dayLimit*24) * time.Hour) - err = global.DB.Where("status = ? and connected_time < ?", constant.Disconnected, limitTime).Find(&o).Error - return -} - -func CreateNewSession(o *Session) (err error) { - err = global.DB.Create(o).Error - return -} - -func FindSessionById(id string) (o Session, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error - return -} - -func FindSessionByConnectionId(connectionId string) (o Session, err error) { - err = global.DB.Where("connection_id = ?", connectionId).First(&o).Error - return -} - -func UpdateSessionById(o *Session, id string) error { - o.ID = id - return global.DB.Updates(o).Error -} - -func UpdateSessionWindowSizeById(width, height int, id string) error { - session := Session{} - session.Width = width - session.Height = height - - return UpdateSessionById(&session, id) -} - -func DeleteSessionById(id string) error { - return global.DB.Where("id = ?", id).Delete(&Session{}).Error -} - -func DeleteSessionByIds(sessionIds []string) error { - drivePath, err := GetRecordingPath() - if err != nil { - return err - } - for i := range sessionIds { - if err := os.RemoveAll(path.Join(drivePath, sessionIds[i])); err != nil { - return err - } - if err := DeleteSessionById(sessionIds[i]); err != nil { - return err - } - } - return nil -} - -func DeleteSessionByStatus(status string) { - global.DB.Where("status = ?", status).Delete(&Session{}) -} - -func CountOnlineSession() (total int64, err error) { - err = global.DB.Where("status = ?", constant.Connected).Find(&Session{}).Count(&total).Error - return -} - -type D struct { - Day string `json:"day"` - Count int `json:"count"` - Protocol string `json:"protocol"` -} - -func CountSessionByDay(day int) (results []D, err error) { - - today := time.Now().Format("20060102") - sql := "select t1.`day`, count(t2.id) as count\nfrom (\n SELECT @date := DATE_ADD(@date, INTERVAL - 1 DAY) day\n FROM (SELECT @date := DATE_ADD('" + today + "', INTERVAL + 1 DAY) FROM nums) as t0\n LIMIT ?\n )\n as t1\n left join\n (\n select DATE(s.connected_time) as day, s.id\n from sessions as s\n WHERE protocol = ? and DATE(connected_time) <= '" + today + "'\n AND DATE(connected_time) > DATE_SUB('" + today + "', INTERVAL ? DAY)\n ) as t2 on t1.day = t2.day\ngroup by t1.day" - - protocols := []string{"rdp", "ssh", "vnc", "telnet"} - - for i := range protocols { - var result []D - err = global.DB.Raw(sql, day, protocols[i], day).Scan(&result).Error - if err != nil { - return nil, err - } - for j := range result { - result[j].Protocol = protocols[i] - } - results = append(results, result...) - } - - return -} diff --git a/server/model/user-attribute.go b/server/model/user-attribute.go deleted file mode 100644 index 3fa870e..0000000 --- a/server/model/user-attribute.go +++ /dev/null @@ -1,23 +0,0 @@ -package model - -import "next-terminal/server/global" - -type UserAttribute struct { - Id string `gorm:"index" json:"id"` - UserId string `gorm:"index" json:"userId"` - Name string `gorm:"index" json:"name"` - Value string `json:"value"` -} - -func (r *UserAttribute) TableName() string { - return "user_attributes" -} - -func CreateUserAttribute(o *UserAttribute) error { - return global.DB.Create(o).Error -} - -func FindUserAttributeByUserId(userId string) (o []UserAttribute, err error) { - err = global.DB.Where("user_id = ?", userId).Find(&o).Error - return o, err -} diff --git a/server/model/user-group-member.go b/server/model/user-group-member.go deleted file mode 100644 index 5cfadf6..0000000 --- a/server/model/user-group-member.go +++ /dev/null @@ -1,18 +0,0 @@ -package model - -import "next-terminal/server/global" - -type UserGroupMember struct { - ID string `gorm:"primary_key" json:"name"` - UserId string `gorm:"index" json:"userId"` - UserGroupId string `gorm:"index" json:"userGroupId"` -} - -func (r *UserGroupMember) TableName() string { - return "user_group_members" -} - -func FindUserGroupMembersByUserGroupId(id string) (o []string, err error) { - err = global.DB.Table("user_group_members").Select("user_id").Where("user_group_id = ?", id).Find(&o).Error - return -} diff --git a/server/model/user-group.go b/server/model/user-group.go index 8f2797e..cdf2592 100644 --- a/server/model/user-group.go +++ b/server/model/user-group.go @@ -1,137 +1,26 @@ package model import ( - "next-terminal/server/global" "next-terminal/server/utils" - - "gorm.io/gorm" ) type UserGroup struct { - ID string `gorm:"primary_key" json:"id"` - Name string `json:"name"` - Created utils.JsonTime `json:"created"` -} - -type UserGroupVo struct { - ID string `json:"id"` + ID string `gorm:"primary_key" json:"id"` Name string `json:"name"` Created utils.JsonTime `json:"created"` - AssetCount int64 `json:"assetCount"` + AssetCount int64 `gorm:"-" json:"assetCount"` } func (r *UserGroup) TableName() string { return "user_groups" } -func FindPageUserGroup(pageIndex, pageSize int, name, order, field string) (o []UserGroupVo, total int64, err error) { - db := global.DB.Table("user_groups").Select("user_groups.id, user_groups.name, user_groups.created, count(resource_sharers.user_group_id) as asset_count").Joins("left join resource_sharers on user_groups.id = resource_sharers.user_group_id and resource_sharers.resource_type = 'asset'").Group("user_groups.id") - dbCounter := global.DB.Table("user_groups") - if len(name) > 0 { - db = db.Where("user_groups.name like ?", "%"+name+"%") - dbCounter = dbCounter.Where("name like ?", "%"+name+"%") - } - - err = dbCounter.Count(&total).Error - if err != nil { - return nil, 0, err - } - - if order == "ascend" { - order = "asc" - } else { - order = "desc" - } - - if field == "name" { - field = "name" - } else { - field = "created" - } - - err = db.Order("user_groups." + field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error - if o == nil { - o = make([]UserGroupVo, 0) - } - return +type UserGroupMember struct { + ID string `gorm:"primary_key" json:"name"` + UserId string `gorm:"index" json:"userId"` + UserGroupId string `gorm:"index" json:"userGroupId"` } -func CreateNewUserGroup(o *UserGroup, members []string) (err error) { - return global.DB.Transaction(func(tx *gorm.DB) error { - err = tx.Create(o).Error - if err != nil { - return err - } - - if members != nil { - userGroupId := o.ID - err = AddUserGroupMembers(tx, members, userGroupId) - if err != nil { - return err - } - } - return err - }) -} - -func AddUserGroupMembers(tx *gorm.DB, userIds []string, userGroupId string) error { - //for i := range userIds { - // userId := userIds[i] - // // TODO - // _, err := FindUserById(userId) - // if err != nil { - // return err - // } - // - // userGroupMember := UserGroupMember{ - // ID: utils.Sign([]string{userGroupId, userId}), - // UserId: userId, - // UserGroupId: userGroupId, - // } - // err = tx.Create(&userGroupMember).Error - // if err != nil { - // return err - // } - //} - return nil -} - -func FindUserGroupById(id string) (o UserGroup, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error - return -} - -func FindUserGroupIdsByUserId(userId string) (o []string, err error) { - // 先查询用户所在的用户 - err = global.DB.Table("user_group_members").Select("user_group_id").Where("user_id = ?", userId).Find(&o).Error - return -} - -func UpdateUserGroupById(o *UserGroup, members []string, id string) error { - return global.DB.Transaction(func(tx *gorm.DB) error { - o.ID = id - err := tx.Updates(o).Error - if err != nil { - return err - } - - err = tx.Where("user_group_id = ?", id).Delete(&UserGroupMember{}).Error - if err != nil { - return err - } - if members != nil { - userGroupId := o.ID - err = AddUserGroupMembers(tx, members, userGroupId) - if err != nil { - return err - } - } - return err - }) - -} - -func DeleteUserGroupById(id string) { - global.DB.Where("id = ?", id).Delete(&UserGroup{}) - global.DB.Where("user_group_id = ?", id).Delete(&UserGroupMember{}) +func (r *UserGroupMember) TableName() string { + return "user_group_members" } diff --git a/server/repository/access_security.go b/server/repository/access_security.go new file mode 100644 index 0000000..469675f --- /dev/null +++ b/server/repository/access_security.go @@ -0,0 +1,81 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/global" + "next-terminal/server/model" +) + +type AccessSecurityRepository struct { + DB *gorm.DB +} + +func NewAccessSecurityRepository(db *gorm.DB) *AccessSecurityRepository { + accessSecurityRepository = &AccessSecurityRepository{DB: db} + return accessSecurityRepository +} + +func (r AccessSecurityRepository) FindAllAccessSecurities() (o []model.AccessSecurity, err error) { + db := r.DB + err = db.Order("priority asc").Find(&o).Error + return +} + +func (r AccessSecurityRepository) Find(pageIndex, pageSize int, ip, rule, order, field string) (o []model.AccessSecurity, total int64, err error) { + t := model.AccessSecurity{} + db := r.DB.Table(t.TableName()) + dbCounter := r.DB.Table(t.TableName()) + + if len(ip) > 0 { + db = db.Where("ip like ?", "%"+ip+"%") + dbCounter = dbCounter.Where("ip like ?", "%"+ip+"%") + } + + if len(rule) > 0 { + db = db.Where("rule = ?", rule) + dbCounter = dbCounter.Where("rule = ?", rule) + } + + err = dbCounter.Count(&total).Error + if err != nil { + return nil, 0, err + } + + if order == "descend" { + order = "desc" + } else { + order = "asc" + } + + if field == "ip" { + field = "ip" + } else if field == "rule" { + field = "rule" + } else { + field = "priority" + } + + err = db.Order(field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error + if o == nil { + o = make([]model.AccessSecurity, 0) + } + return +} + +func (r AccessSecurityRepository) Create(o *model.AccessSecurity) error { + return global.DB.Create(o).Error +} + +func (r AccessSecurityRepository) UpdateById(o *model.AccessSecurity, id string) error { + o.ID = id + return global.DB.Updates(o).Error +} + +func (r AccessSecurityRepository) DeleteById(id string) error { + return global.DB.Where("id = ?", id).Delete(model.AccessSecurity{}).Error +} + +func (r AccessSecurityRepository) FindById(id string) (o *model.AccessSecurity, err error) { + err = global.DB.Where("id = ?", id).First(&o).Error + return +} diff --git a/server/repository/asset.go b/server/repository/asset.go new file mode 100644 index 0000000..e0b383c --- /dev/null +++ b/server/repository/asset.go @@ -0,0 +1,302 @@ +package repository + +import ( + "fmt" + "github.com/labstack/echo/v4" + "gorm.io/gorm" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/model" + "next-terminal/server/utils" + "strings" +) + +type AssetRepository struct { + DB *gorm.DB +} + +func NewAssetRepository(db *gorm.DB) *AssetRepository { + assetRepository = &AssetRepository{DB: db} + return assetRepository +} + +func (r AssetRepository) FindAll() (o []model.Asset, err error) { + err = r.DB.Find(&o).Error + return +} + +func (r AssetRepository) FindByIds(assetIds []string) (o []model.Asset, err error) { + err = r.DB.Where("id in ?", assetIds).Find(&o).Error + return +} + +func (r AssetRepository) FindByProtocol(protocol string) (o []model.Asset, err error) { + err = r.DB.Where("protocol = ?", protocol).Find(&o).Error + return +} + +func (r AssetRepository) FindByProtocolAndIds(protocol string, assetIds []string) (o []model.Asset, err error) { + err = r.DB.Where("protocol = ? and id in ?", protocol, assetIds).Find(&o).Error + return +} + +func (r AssetRepository) FindByProtocolAndUser(protocol string, account model.User) (o []model.Asset, err error) { + db := r.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id") + + if constant.TypeUser == account.Type { + owner := account.ID + db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner) + } + + if len(protocol) > 0 { + db = db.Where("assets.protocol = ?", protocol) + } + err = db.Find(&o).Error + return +} + +func (r AssetRepository) Find(pageIndex, pageSize int, name, protocol, tags string, account model.User, owner, sharer, userGroupId, ip, order, field string) (o []model.AssetVo, total int64, err error) { + db := r.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created,assets.tags, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id") + dbCounter := r.DB.Table("assets").Select("DISTINCT assets.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id") + + if constant.TypeUser == account.Type { + owner := account.ID + db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner) + dbCounter = dbCounter.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner) + + // 查询用户所在用户组列表 + userGroupIds, err := userGroupRepository.FindUserGroupIdsByUserId(account.ID) + if err != nil { + return nil, 0, err + } + + if len(userGroupIds) > 0 { + db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) + dbCounter = dbCounter.Or("resource_sharers.user_group_id in ?", userGroupIds) + } + } else { + if len(owner) > 0 { + db = db.Where("assets.owner = ?", owner) + dbCounter = dbCounter.Where("assets.owner = ?", owner) + } + if len(sharer) > 0 { + db = db.Where("resource_sharers.user_id = ?", sharer) + dbCounter = dbCounter.Where("resource_sharers.user_id = ?", sharer) + } + + if len(userGroupId) > 0 { + db = db.Where("resource_sharers.user_group_id = ?", userGroupId) + dbCounter = dbCounter.Where("resource_sharers.user_group_id = ?", userGroupId) + } + } + + if len(name) > 0 { + db = db.Where("assets.name like ?", "%"+name+"%") + dbCounter = dbCounter.Where("assets.name like ?", "%"+name+"%") + } + + if len(ip) > 0 { + db = db.Where("assets.ip like ?", "%"+ip+"%") + dbCounter = dbCounter.Where("assets.ip like ?", "%"+ip+"%") + } + + if len(protocol) > 0 { + db = db.Where("assets.protocol = ?", protocol) + dbCounter = dbCounter.Where("assets.protocol = ?", protocol) + } + + if len(tags) > 0 { + tagArr := strings.Split(tags, ",") + for i := range tagArr { + if global.Config.DB == "sqlite" { + db = db.Where("(',' || assets.tags || ',') LIKE ?", "%,"+tagArr[i]+",%") + dbCounter = dbCounter.Where("(',' || assets.tags || ',') LIKE ?", "%,"+tagArr[i]+",%") + } else { + db = db.Where("find_in_set(?, assets.tags)", tagArr[i]) + dbCounter = dbCounter.Where("find_in_set(?, assets.tags)", tagArr[i]) + } + } + } + + err = dbCounter.Count(&total).Error + if err != nil { + return nil, 0, err + } + + if order == "ascend" { + order = "asc" + } else { + order = "desc" + } + + if field == "name" { + field = "name" + } else { + field = "created" + } + + err = db.Order("assets." + field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error + + if o == nil { + o = make([]model.AssetVo, 0) + } + return +} + +func (r AssetRepository) Create(o *model.Asset) (err error) { + if err = r.DB.Create(o).Error; err != nil { + return err + } + return nil +} + +func (r AssetRepository) FindById(id string) (o model.Asset, err error) { + err = r.DB.Where("id = ?", id).First(&o).Error + return +} + +func (r AssetRepository) UpdateById(o *model.Asset, id string) error { + o.ID = id + return r.DB.Updates(o).Error +} + +func (r AssetRepository) UpdateActiveById(active bool, id string) error { + sql := "update assets set active = ? where id = ?" + return r.DB.Exec(sql, active, id).Error +} + +func (r AssetRepository) DeleteById(id string) error { + return r.DB.Where("id = ?", id).Delete(&model.Asset{}).Error +} + +func (r AssetRepository) CountAsset() (total int64, err error) { + err = r.DB.Find(&model.Asset{}).Count(&total).Error + return +} + +func (r AssetRepository) CountByUserId(userId string) (total int64, err error) { + db := r.DB.Joins("left join resource_sharers on assets.id = resource_sharers.resource_id") + + db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", userId, userId) + + // 查询用户所在用户组列表 + userGroupIds, err := userGroupRepository.FindUserGroupIdsByUserId(userId) + if err != nil { + return 0, err + } + + if len(userGroupIds) > 0 { + db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) + } + err = db.Find(&model.Asset{}).Count(&total).Error + return +} + +func (r AssetRepository) FindTags() (o []string, err error) { + var assets []model.Asset + err = r.DB.Not("tags = ?", "").Find(&assets).Error + if err != nil { + return nil, err + } + + o = make([]string, 0) + + for i := range assets { + if len(assets[i].Tags) == 0 { + continue + } + split := strings.Split(assets[i].Tags, ",") + + o = append(o, split...) + } + + return utils.Distinct(o), nil +} + +func (r AssetRepository) UpdateAttributes(assetId, protocol string, m echo.Map) error { + var data []model.AssetAttribute + var parameterNames []string + switch protocol { + case "ssh": + parameterNames = constant.SSHParameterNames + case "rdp": + parameterNames = constant.RDPParameterNames + case "vnc": + parameterNames = constant.VNCParameterNames + case "telnet": + parameterNames = constant.TelnetParameterNames + case "kubernetes": + parameterNames = constant.KubernetesParameterNames + } + + for i := range parameterNames { + name := parameterNames[i] + if m[name] != nil && m[name] != "" { + data = append(data, genAttribute(assetId, name, m)) + } + } + + return r.DB.Transaction(func(tx *gorm.DB) error { + err := tx.Where("asset_id = ?", assetId).Delete(&model.AssetAttribute{}).Error + if err != nil { + return err + } + return tx.CreateInBatches(&data, len(data)).Error + }) +} + +func genAttribute(assetId, name string, m echo.Map) model.AssetAttribute { + value := fmt.Sprintf("%v", m[name]) + attribute := model.AssetAttribute{ + Id: utils.Sign([]string{assetId, name}), + AssetId: assetId, + Name: name, + Value: value, + } + return attribute +} + +func (r AssetRepository) FindAttrById(assetId string) (o []model.AssetAttribute, err error) { + err = r.DB.Where("asset_id = ?", assetId).Find(&o).Error + if o == nil { + o = make([]model.AssetAttribute, 0) + } + return o, err +} + +func (r AssetRepository) FindAssetAttrMapByAssetId(assetId string) (map[string]interface{}, error) { + asset, err := r.FindById(assetId) + if err != nil { + return nil, err + } + attributes, err := r.FindAttrById(assetId) + if err != nil { + return nil, err + } + + var parameterNames []string + switch asset.Protocol { + case "ssh": + parameterNames = constant.SSHParameterNames + case "rdp": + parameterNames = constant.RDPParameterNames + case "vnc": + parameterNames = constant.VNCParameterNames + case "telnet": + parameterNames = constant.TelnetParameterNames + case "kubernetes": + parameterNames = constant.KubernetesParameterNames + } + propertiesMap := propertyRepository.FindAllMap() + var attributeMap = make(map[string]interface{}) + for name := range propertiesMap { + if utils.Contains(parameterNames, name) { + attributeMap[name] = propertiesMap[name] + } + } + + for i := range attributes { + attributeMap[attributes[i].Name] = attributes[i].Value + } + return attributeMap, nil +} diff --git a/server/repository/command.go b/server/repository/command.go new file mode 100644 index 0000000..a47112c --- /dev/null +++ b/server/repository/command.go @@ -0,0 +1,82 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/model" +) + +type CommandRepository struct { + DB *gorm.DB +} + +func NewCommandRepository(db *gorm.DB) *CommandRepository { + commandRepository = &CommandRepository{DB: db} + return commandRepository +} + +func (r CommandRepository) Find(pageIndex, pageSize int, name, content, order, field string, account model.User) (o []model.CommandVo, total int64, err error) { + db := r.DB.Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id") + dbCounter := r.DB.Table("commands").Select("DISTINCT commands.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id") + + if constant.TypeUser == account.Type { + owner := account.ID + db = db.Where("commands.owner = ? or resource_sharers.user_id = ?", owner, owner) + dbCounter = dbCounter.Where("commands.owner = ? or resource_sharers.user_id = ?", owner, owner) + } + + if len(name) > 0 { + db = db.Where("commands.name like ?", "%"+name+"%") + dbCounter = dbCounter.Where("commands.name like ?", "%"+name+"%") + } + + if len(content) > 0 { + db = db.Where("commands.content like ?", "%"+content+"%") + dbCounter = dbCounter.Where("commands.content like ?", "%"+content+"%") + } + + err = dbCounter.Count(&total).Error + if err != nil { + return nil, 0, err + } + + if order == "ascend" { + order = "asc" + } else { + order = "desc" + } + + if field == "name" { + field = "name" + } else { + field = "created" + } + + err = db.Order("commands." + field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error + if o == nil { + o = make([]model.CommandVo, 0) + } + return +} + +func (r CommandRepository) Create(o *model.Command) (err error) { + if err = r.DB.Create(o).Error; err != nil { + return err + } + return nil +} + +func (r CommandRepository) FindById(id string) (o model.Command, err error) { + err = global.DB.Where("id = ?", id).First(&o).Error + return +} + +func (r CommandRepository) UpdateById(o *model.Command, id string) { + o.ID = id + global.DB.Updates(o) +} + +func (r CommandRepository) DeleteById(id string) error { + return r.DB.Where("id = ?", id).Delete(&model.Command{}).Error +} diff --git a/server/repository/credential.go b/server/repository/credential.go new file mode 100644 index 0000000..25d5915 --- /dev/null +++ b/server/repository/credential.go @@ -0,0 +1,108 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/constant" + "next-terminal/server/model" +) + +type CredentialRepository struct { + DB *gorm.DB +} + +func NewCredentialRepository(db *gorm.DB) *CredentialRepository { + credentialRepository = &CredentialRepository{DB: db} + return credentialRepository +} + +func (r CredentialRepository) FindAllByUser(account model.User) (o []model.CredentialSimpleVo, err error) { + db := r.DB.Table("credentials").Select("DISTINCT credentials.id,credentials.name").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id") + if account.Type == constant.TypeUser { + db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", account.ID, account.ID) + } + err = db.Find(&o).Error + return +} + +func (r CredentialRepository) Find(pageIndex, pageSize int, name, order, field string, account model.User) (o []model.CredentialVo, total int64, err error) { + db := r.DB.Table("credentials").Select("credentials.id,credentials.name,credentials.type,credentials.username,credentials.owner,credentials.created,users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on credentials.owner = users.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id").Group("credentials.id") + dbCounter := r.DB.Table("credentials").Select("DISTINCT credentials.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id").Group("credentials.id") + + if constant.TypeUser == account.Type { + owner := account.ID + db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", owner, owner) + dbCounter = dbCounter.Where("credentials.owner = ? or resource_sharers.user_id = ?", owner, owner) + } + + if len(name) > 0 { + db = db.Where("credentials.name like ?", "%"+name+"%") + dbCounter = dbCounter.Where("credentials.name like ?", "%"+name+"%") + } + + err = dbCounter.Count(&total).Error + if err != nil { + return nil, 0, err + } + + if order == "ascend" { + order = "asc" + } else { + order = "desc" + } + + if field == "name" { + field = "name" + } else { + field = "created" + } + + err = db.Order("credentials." + field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error + if o == nil { + o = make([]model.CredentialVo, 0) + } + return +} + +func (r CredentialRepository) Create(o *model.Credential) (err error) { + if err = r.DB.Create(o).Error; err != nil { + return err + } + return nil +} + +func (r CredentialRepository) FindById(id string) (o model.Credential, err error) { + err = r.DB.Where("id = ?", id).First(&o).Error + return +} + +func (r CredentialRepository) UpdateById(o *model.Credential, id string) error { + o.ID = id + return r.DB.Updates(o).Error +} + +func (r CredentialRepository) DeleteById(id string) error { + return r.DB.Where("id = ?", id).Delete(&model.Credential{}).Error +} + +func (r CredentialRepository) Count() (total int64, err error) { + err = r.DB.Find(&model.Credential{}).Count(&total).Error + return +} + +func (r CredentialRepository) CountByUserId(userId string) (total int64, err error) { + db := r.DB.Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id") + + db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", userId, userId) + + // 查询用户所在用户组列表 + userGroupIds, err := userGroupRepository.FindUserGroupIdsByUserId(userId) + if err != nil { + return 0, err + } + + if len(userGroupIds) > 0 { + db = db.Or("resource_sharers.user_group_id in ?", userGroupIds) + } + err = db.Find(&model.Credential{}).Count(&total).Error + return +} diff --git a/server/repository/definitions.go b/server/repository/definitions.go new file mode 100644 index 0000000..56686fb --- /dev/null +++ b/server/repository/definitions.go @@ -0,0 +1,17 @@ +package repository + +var ( + userRepository *UserRepository + userGroupRepository *UserGroupRepository + resourceSharerRepository *ResourceSharerRepository + assetRepository *AssetRepository + credentialRepository *CredentialRepository + propertyRepository *PropertyRepository + commandRepository *CommandRepository + sessionRepository *SessionRepository + numRepository *NumRepository + accessSecurityRepository *AccessSecurityRepository + jobRepository *JobRepository + jobLogRepository *JobLogRepository + loginLogRepository *LoginLogRepository +) diff --git a/server/repository/job.go b/server/repository/job.go new file mode 100644 index 0000000..08086cb --- /dev/null +++ b/server/repository/job.go @@ -0,0 +1,108 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/global" + "next-terminal/server/model" + "next-terminal/server/utils" +) + +type JobRepository struct { + DB *gorm.DB +} + +func NewJobRepository(db *gorm.DB) *JobRepository { + jobRepository = &JobRepository{DB: db} + return jobRepository +} + +func (r JobRepository) Find(pageIndex, pageSize int, name, status, order, field string) (o []model.Job, total int64, err error) { + job := model.Job{} + db := r.DB.Table(job.TableName()) + dbCounter := r.DB.Table(job.TableName()) + + if len(name) > 0 { + db = db.Where("name like ?", "%"+name+"%") + dbCounter = dbCounter.Where("name like ?", "%"+name+"%") + } + + if len(status) > 0 { + db = db.Where("status = ?", status) + dbCounter = dbCounter.Where("status = ?", status) + } + + err = dbCounter.Count(&total).Error + if err != nil { + return nil, 0, err + } + + if order == "ascend" { + order = "asc" + } else { + order = "desc" + } + + if field == "name" { + field = "name" + } else if field == "created" { + field = "created" + } else { + field = "updated" + } + + err = db.Order(field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error + if o == nil { + o = make([]model.Job, 0) + } + return +} + +func (r JobRepository) FindByFunc(function string) (o []model.Job, err error) { + db := r.DB + err = db.Where("func = ?", function).Find(&o).Error + return +} + +func (r JobRepository) Create(o *model.Job) (err error) { + // + //if o.Status == constant.JobStatusRunning { + // j, err := getJob(o) + // if err != nil { + // return err + // } + // jobId, err := global.Cron.AddJob(o.Cron, j) + // if err != nil { + // return err + // } + // o.CronJobId = int(jobId) + //} + + return r.DB.Create(o).Error +} + +func (r JobRepository) UpdateById(o *model.Job) (err error) { + return r.DB.Updates(o).Error +} + +func (r JobRepository) UpdateLastUpdatedById(id string) (err error) { + err = r.DB.Updates(model.Job{ID: id, Updated: utils.NowJsonTime()}).Error + return +} + +func (r JobRepository) FindById(id string) (o model.Job, err error) { + err = r.DB.Where("id = ?", id).First(&o).Error + return +} + +func (r JobRepository) DeleteJobById(id string) error { + //job, err := r.FindById(id) + //if err != nil { + // return err + //} + //if job.Status == constant.JobStatusRunning { + // if err := r.ChangeStatusById(id, constant.JobStatusNotRunning); err != nil { + // return err + // } + //} + return global.DB.Where("id = ?", id).Delete(model.Job{}).Error +} diff --git a/server/repository/job_log.go b/server/repository/job_log.go new file mode 100644 index 0000000..10ca91d --- /dev/null +++ b/server/repository/job_log.go @@ -0,0 +1,28 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/model" +) + +type JobLogRepository struct { + DB *gorm.DB +} + +func NewJobLogRepository(db *gorm.DB) *JobLogRepository { + jobLogRepository = &JobLogRepository{DB: db} + return jobLogRepository +} + +func (r JobLogRepository) Create(o *model.JobLog) error { + return r.DB.Create(o).Error +} + +func (r JobLogRepository) FindByJobId(jobId string) (o []model.JobLog, err error) { + err = r.DB.Where("job_id = ?", jobId).Order("timestamp asc").Find(&o).Error + return +} + +func (r JobLogRepository) DeleteByJobId(jobId string) error { + return r.DB.Where("job_id = ?", jobId).Delete(model.JobLog{}).Error +} diff --git a/server/repository/login_log.go b/server/repository/login_log.go new file mode 100644 index 0000000..ebae58c --- /dev/null +++ b/server/repository/login_log.go @@ -0,0 +1,69 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/model" +) + +type LoginLogRepository struct { + DB *gorm.DB +} + +func NewLoginLogRepository(db *gorm.DB) *LoginLogRepository { + loginLogRepository = &LoginLogRepository{DB: db} + return loginLogRepository +} + +func (r LoginLogRepository) Find(pageIndex, pageSize int, userId, clientIp string) (o []model.LoginLogVo, total int64, err error) { + + db := r.DB.Table("login_logs").Select("login_logs.id,login_logs.user_id,login_logs.client_ip,login_logs.client_user_agent,login_logs.login_time, login_logs.logout_time, users.nickname as user_name").Joins("left join users on login_logs.user_id = users.id") + dbCounter := r.DB.Table("login_logs").Select("DISTINCT login_logs.id") + + if userId != "" { + db = db.Where("login_logs.user_id = ?", userId) + dbCounter = dbCounter.Where("login_logs.user_id = ?", userId) + } + + if clientIp != "" { + db = db.Where("login_logs.client_ip like ?", "%"+clientIp+"%") + dbCounter = dbCounter.Where("login_logs.client_ip like ?", "%"+clientIp+"%") + } + + err = dbCounter.Count(&total).Error + if err != nil { + return nil, 0, err + } + + err = db.Order("login_logs.login_time desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error + if o == nil { + o = make([]model.LoginLogVo, 0) + } + return +} + +func (r LoginLogRepository) FindAliveLoginLogs() (o []model.LoginLog, err error) { + err = r.DB.Where("logout_time is null").Find(&o).Error + return +} + +func (r LoginLogRepository) FindAliveLoginLogsByUserId(userId string) (o []model.LoginLog, err error) { + err = r.DB.Where("logout_time is null and user_id = ?", userId).Find(&o).Error + return +} + +func (r LoginLogRepository) Create(o *model.LoginLog) (err error) { + return r.DB.Create(o).Error +} + +func (r LoginLogRepository) DeleteByIdIn(ids []string) (err error) { + return r.DB.Where("id in ?", ids).Delete(&model.LoginLog{}).Error +} + +func (r LoginLogRepository) FindById(id string) (o model.LoginLog, err error) { + err = r.DB.Where("id = ?", id).First(&o).Error + return +} + +func (r LoginLogRepository) Update(o *model.LoginLog) error { + return r.DB.Updates(o).Error +} diff --git a/server/repository/num.go b/server/repository/num.go new file mode 100644 index 0000000..7309127 --- /dev/null +++ b/server/repository/num.go @@ -0,0 +1,26 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/global" + "next-terminal/server/model" +) + +type NumRepository struct { + DB *gorm.DB +} + +func NewNumRepository(db *gorm.DB) *NumRepository { + numRepository = &NumRepository{DB: db} + return numRepository +} + +func (r NumRepository) FindAll() (o []model.Num, err error) { + err = r.DB.Find(&o).Error + return +} + +func (r NumRepository) Create(o *model.Num) (err error) { + err = global.DB.Create(o).Error + return +} diff --git a/server/repository/property.go b/server/repository/property.go new file mode 100644 index 0000000..d8320af --- /dev/null +++ b/server/repository/property.go @@ -0,0 +1,90 @@ +package repository + +import ( + "github.com/jordan-wright/email" + "github.com/sirupsen/logrus" + "gorm.io/gorm" + "net/smtp" + "next-terminal/server/constant" + "next-terminal/server/guacd" + "next-terminal/server/model" +) + +type PropertyRepository struct { + DB *gorm.DB +} + +func NewPropertyRepository(db *gorm.DB) *PropertyRepository { + propertyRepository = &PropertyRepository{DB: db} + return propertyRepository +} + +func (r PropertyRepository) FindAll() (o []model.Property) { + if r.DB.Find(&o).Error != nil { + return nil + } + return +} + +func (r PropertyRepository) Create(o *model.Property) (err error) { + err = r.DB.Create(o).Error + return +} + +func (r PropertyRepository) UpdatePropertyByName(o *model.Property, name string) error { + o.Name = name + return r.DB.Updates(o).Error +} + +func (r PropertyRepository) FindByName(name string) (o model.Property, err error) { + err = r.DB.Where("name = ?", name).First(&o).Error + return +} + +func (r PropertyRepository) FindAllMap() map[string]string { + properties := r.FindAll() + propertyMap := make(map[string]string) + for i := range properties { + propertyMap[properties[i].Name] = properties[i].Value + } + return propertyMap +} + +func (r PropertyRepository) GetDrivePath() (string, error) { + property, err := r.FindByName(guacd.DrivePath) + if err != nil { + return "", err + } + return property.Value, nil +} + +func (r PropertyRepository) GetRecordingPath() (string, error) { + property, err := r.FindByName(guacd.RecordingPath) + if err != nil { + return "", err + } + return property.Value, nil +} + +func (r PropertyRepository) SendMail(to, subject, text string) { + propertiesMap := r.FindAllMap() + host := propertiesMap[constant.MailHost] + port := propertiesMap[constant.MailPort] + username := propertiesMap[constant.MailUsername] + password := propertiesMap[constant.MailPassword] + + if host == "" || port == "" || username == "" || password == "" { + logrus.Debugf("邮箱信息不完整,跳过发送邮件。") + return + } + + e := email.NewEmail() + e.From = "Next Terminal <" + username + ">" + e.To = []string{to} + e.Subject = subject + e.Text = []byte(text) + err := e.Send(host+":"+port, smtp.PlainAuth("", username, password, host)) + if err != nil { + logrus.Errorf("邮件发送失败: %v", err.Error()) + } +} diff --git a/server/repository/resource_sharer.go b/server/repository/resource_sharer.go new file mode 100644 index 0000000..ee3e56c --- /dev/null +++ b/server/repository/resource_sharer.go @@ -0,0 +1,193 @@ +package repository + +import ( + "github.com/labstack/echo/v4" + "github.com/pkg/errors" + "gorm.io/gorm" + "next-terminal/server/model" + "next-terminal/server/utils" +) + +type ResourceSharerRepository struct { + DB *gorm.DB +} + +func NewResourceSharerRepository(db *gorm.DB) *ResourceSharerRepository { + resourceSharerRepository = &ResourceSharerRepository{DB: db} + return resourceSharerRepository +} + +func (r *ResourceSharerRepository) FindUserIdsByResourceId(resourceId string) (o []string, err error) { + err = r.DB.Table("resource_sharers").Select("user_id").Where("resource_id = ?", resourceId).Find(&o).Error + if o == nil { + o = make([]string, 0) + } + return +} + +func (r *ResourceSharerRepository) OverwriteUserIdsByResourceId(resourceId, resourceType string, userIds []string) (err error) { + db := r.DB.Begin() + + var owner string + // 检查资产是否存在 + switch resourceType { + case "asset": + resource := model.Asset{} + err = db.Where("id = ?", resourceId).First(&resource).Error + owner = resource.Owner + case "command": + resource := model.Command{} + err = db.Where("id = ?", resourceId).First(&resource).Error + owner = resource.Owner + case "credential": + resource := model.Credential{} + err = db.Where("id = ?", resourceId).First(&resource).Error + owner = resource.Owner + } + + if err == gorm.ErrRecordNotFound { + return echo.NewHTTPError(404, "资源「"+resourceId+"」不存在") + } + + for i := range userIds { + if owner == userIds[i] { + return echo.NewHTTPError(400, "参数错误") + } + } + + db.Where("resource_id = ?", resourceId).Delete(&ResourceSharerRepository{}) + + for i := range userIds { + userId := userIds[i] + if len(userId) == 0 { + continue + } + id := utils.Sign([]string{resourceId, resourceType, userId}) + resource := &model.ResourceSharer{ + ID: id, + ResourceId: resourceId, + ResourceType: resourceType, + UserId: userId, + } + err = db.Create(resource).Error + if err != nil { + return err + } + } + db.Commit() + return nil +} + +func (r *ResourceSharerRepository) DeleteByUserIdAndResourceTypeAndResourceIdIn(userGroupId, userId, resourceType string, resourceIds []string) error { + db := r.DB + if userGroupId != "" { + db = db.Where("user_group_id = ?", userGroupId) + } + + if userId != "" { + db = db.Where("user_id = ?", userId) + } + + if resourceType != "" { + db = db.Where("resource_type = ?", resourceType) + } + + if resourceIds != nil { + db = db.Where("resource_id in ?", resourceIds) + } + + return db.Delete(&ResourceSharerRepository{}).Error +} + +func (r *ResourceSharerRepository) DeleteResourceSharerByResourceId(resourceId string) error { + return r.DB.Where("resource_id = ?", resourceId).Delete(&ResourceSharerRepository{}).Error +} + +func (r *ResourceSharerRepository) AddSharerResources(userGroupId, userId, resourceType string, resourceIds []string) error { + return r.DB.Transaction(func(tx *gorm.DB) (err error) { + + for i := range resourceIds { + resourceId := resourceIds[i] + + var owner string + // 检查资产是否存在 + switch resourceType { + case "asset": + resource := model.Asset{} + if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil { + return errors.Wrap(err, "find asset fail") + } + owner = resource.Owner + case "command": + resource := model.Command{} + if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil { + return errors.Wrap(err, "find command fail") + } + owner = resource.Owner + case "credential": + resource := model.Credential{} + if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil { + return errors.Wrap(err, "find credential fail") + + } + owner = resource.Owner + } + + if owner == userId { + return echo.NewHTTPError(400, "参数错误") + } + + id := utils.Sign([]string{resourceId, resourceType, userId, userGroupId}) + resource := &model.ResourceSharer{ + ID: id, + ResourceId: resourceId, + ResourceType: resourceType, + UserId: userId, + UserGroupId: userGroupId, + } + err = tx.Create(resource).Error + if err != nil { + return err + } + } + return nil + }) +} + +func (r *ResourceSharerRepository) FindAssetIdsByUserId(userId string) (assetIds []string, err error) { + // 查询当前用户创建的资产 + var ownerAssetIds, sharerAssetIds []string + asset := model.Asset{} + err = r.DB.Table(asset.TableName()).Select("id").Where("owner = ?", userId).Find(&ownerAssetIds).Error + if err != nil { + return nil, err + } + + // 查询其他用户授权给该用户的资产 + groupIds, err := userGroupRepository.FindUserGroupIdsByUserId(userId) + if err != nil { + return nil, err + } + + db := r.DB.Table("resource_sharers").Select("resource_id").Where("user_id = ?", userId) + if len(groupIds) > 0 { + db = db.Or("user_group_id in ?", groupIds) + } + err = db.Find(&sharerAssetIds).Error + if err != nil { + return nil, err + } + + // 合并查询到的资产ID + assetIds = make([]string, 0) + + if ownerAssetIds != nil { + assetIds = append(assetIds, ownerAssetIds...) + } + + if sharerAssetIds != nil { + assetIds = append(assetIds, sharerAssetIds...) + } + + return +} diff --git a/server/repository/session.go b/server/repository/session.go new file mode 100644 index 0000000..fc3e8cf --- /dev/null +++ b/server/repository/session.go @@ -0,0 +1,167 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/constant" + "next-terminal/server/model" + "os" + "path" + "time" +) + +type SessionRepository struct { + DB *gorm.DB +} + +func NewSessionRepository(db *gorm.DB) *SessionRepository { + sessionRepository = &SessionRepository{DB: db} + return sessionRepository +} + +func (r SessionRepository) Find(pageIndex, pageSize int, status, userId, clientIp, assetId, protocol string) (results []model.SessionVo, total int64, err error) { + + db := r.DB + var params []interface{} + + params = append(params, status) + + itemSql := "SELECT s.id,s.mode, s.protocol,s.recording, s.connection_id, s.asset_id, s.creator, s.client_ip, s.width, s.height, s.ip, s.port, s.username, s.status, s.connected_time, s.disconnected_time,s.code, s.message, a.name AS asset_name, u.nickname AS creator_name FROM sessions s LEFT JOIN assets a ON s.asset_id = a.id LEFT JOIN users u ON s.creator = u.id WHERE s.STATUS = ? " + countSql := "select count(*) from sessions as s where s.status = ? " + + if len(userId) > 0 { + itemSql += " and s.creator = ?" + countSql += " and s.creator = ?" + params = append(params, userId) + } + + if len(clientIp) > 0 { + itemSql += " and s.client_ip like ?" + countSql += " and s.client_ip like ?" + params = append(params, "%"+clientIp+"%") + } + + if len(assetId) > 0 { + itemSql += " and s.asset_id = ?" + countSql += " and s.asset_id = ?" + params = append(params, assetId) + } + + if len(protocol) > 0 { + itemSql += " and s.protocol = ?" + countSql += " and s.protocol = ?" + params = append(params, protocol) + } + + params = append(params, (pageIndex-1)*pageSize, pageSize) + itemSql += " order by s.connected_time desc LIMIT ?, ?" + + db.Raw(countSql, params...).Scan(&total) + + err = db.Raw(itemSql, params...).Scan(&results).Error + + if results == nil { + results = make([]model.SessionVo, 0) + } + return +} + +func (r SessionRepository) FindByStatus(status string) (o []model.Session, err error) { + err = r.DB.Where("status = ?", status).Find(&o).Error + return +} + +func (r SessionRepository) FindByStatusIn(statuses []string) (o []model.Session, err error) { + err = r.DB.Where("status in ?", statuses).Find(&o).Error + return +} + +func (r SessionRepository) FindOutTimeSessions(dayLimit int) (o []model.Session, err error) { + limitTime := time.Now().Add(time.Duration(-dayLimit*24) * time.Hour) + err = r.DB.Where("status = ? and connected_time < ?", constant.Disconnected, limitTime).Find(&o).Error + return +} + +func (r SessionRepository) Create(o *model.Session) (err error) { + err = r.DB.Create(o).Error + return +} + +func (r SessionRepository) FindById(id string) (o model.Session, err error) { + err = r.DB.Where("id = ?", id).First(&o).Error + return +} + +func (r SessionRepository) FindByConnectionId(connectionId string) (o model.Session, err error) { + err = r.DB.Where("connection_id = ?", connectionId).First(&o).Error + return +} + +func (r SessionRepository) UpdateById(o *model.Session, id string) error { + o.ID = id + return r.DB.Updates(o).Error +} + +func (r SessionRepository) UpdateWindowSizeById(width, height int, id string) error { + session := model.Session{} + session.Width = width + session.Height = height + + return r.UpdateById(&session, id) +} + +func (r SessionRepository) DeleteById(id string) error { + return r.DB.Where("id = ?", id).Delete(&model.Session{}).Error +} + +func (r SessionRepository) DeleteByIds(sessionIds []string) error { + drivePath, err := propertyRepository.GetRecordingPath() + if err != nil { + return err + } + for i := range sessionIds { + if err := os.RemoveAll(path.Join(drivePath, sessionIds[i])); err != nil { + return err + } + if err := r.DeleteById(sessionIds[i]); err != nil { + return err + } + } + return nil +} + +func (r SessionRepository) DeleteByStatus(status string) error { + return r.DB.Where("status = ?", status).Delete(&model.Session{}).Error +} + +func (r SessionRepository) CountOnlineSession() (total int64, err error) { + err = r.DB.Where("status = ?", constant.Connected).Find(&model.Session{}).Count(&total).Error + return +} + +type D struct { + Day string `json:"day"` + Count int `json:"count"` + Protocol string `json:"protocol"` +} + +func (r SessionRepository) CountSessionByDay(day int) (results []D, err error) { + + today := time.Now().Format("20060102") + sql := "select t1.`day`, count(t2.id) as count\nfrom (\n SELECT @date := DATE_ADD(@date, INTERVAL - 1 DAY) day\n FROM (SELECT @date := DATE_ADD('" + today + "', INTERVAL + 1 DAY) FROM nums) as t0\n LIMIT ?\n )\n as t1\n left join\n (\n select DATE(s.connected_time) as day, s.id\n from sessions as s\n WHERE protocol = ? and DATE(connected_time) <= '" + today + "'\n AND DATE(connected_time) > DATE_SUB('" + today + "', INTERVAL ? DAY)\n ) as t2 on t1.day = t2.day\ngroup by t1.day" + + protocols := []string{"rdp", "ssh", "vnc", "telnet"} + + for i := range protocols { + var result []D + err = r.DB.Raw(sql, day, protocols[i], day).Scan(&result).Error + if err != nil { + return nil, err + } + for j := range result { + result[j].Protocol = protocols[i] + } + results = append(results, result...) + } + + return +} diff --git a/server/repository/user.go b/server/repository/user.go index b133875..7391644 100644 --- a/server/repository/user.go +++ b/server/repository/user.go @@ -9,6 +9,11 @@ type UserRepository struct { DB *gorm.DB } +func NewUserRepository(db *gorm.DB) *UserRepository { + userRepository = &UserRepository{DB: db} + return userRepository +} + func (r UserRepository) FindAll() (o []model.User) { if r.DB.Find(&o).Error != nil { return nil diff --git a/server/repository/user_group.go b/server/repository/user_group.go new file mode 100644 index 0000000..64323c1 --- /dev/null +++ b/server/repository/user_group.go @@ -0,0 +1,140 @@ +package repository + +import ( + "gorm.io/gorm" + "next-terminal/server/global" + "next-terminal/server/model" + "next-terminal/server/utils" +) + +type UserGroupRepository struct { + DB *gorm.DB +} + +func NewUserGroupRepository(db *gorm.DB) *UserGroupRepository { + userGroupRepository = &UserGroupRepository{DB: db} + return userGroupRepository +} + +func (r UserGroupRepository) FindAll() (o []model.UserGroup) { + if r.DB.Find(&o).Error != nil { + return nil + } + return +} + +func (r UserGroupRepository) Find(pageIndex, pageSize int, name, order, field string) (o []model.UserGroup, total int64, err error) { + db := r.DB.Table("user_groups").Select("user_groups.id, user_groups.name, user_groups.created, count(resource_sharers.user_group_id) as asset_count").Joins("left join resource_sharers on user_groups.id = resource_sharers.user_group_id and resource_sharers.resource_type = 'asset'").Group("user_groups.id") + dbCounter := r.DB.Table("user_groups") + if len(name) > 0 { + db = db.Where("user_groups.name like ?", "%"+name+"%") + dbCounter = dbCounter.Where("name like ?", "%"+name+"%") + } + + err = dbCounter.Count(&total).Error + if err != nil { + return nil, 0, err + } + + if order == "ascend" { + order = "asc" + } else { + order = "desc" + } + + if field == "name" { + field = "name" + } else { + field = "created" + } + + err = db.Order("user_groups." + field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error + if o == nil { + o = make([]model.UserGroup, 0) + } + return +} + +func (r UserGroupRepository) FindById(id string) (o model.UserGroup, err error) { + err = global.DB.Where("id = ?", id).First(&o).Error + return +} + +func (r UserGroupRepository) FindUserGroupIdsByUserId(userId string) (o []string, err error) { + // 先查询用户所在的用户 + err = r.DB.Table("user_group_members").Select("user_group_id").Where("user_id = ?", userId).Find(&o).Error + return +} + +func (r UserGroupRepository) FindUserGroupMembersByUserGroupId(userGroupId string) (o []string, err error) { + err = r.DB.Table("user_group_members").Select("user_id").Where("user_group_id = ?", userGroupId).Find(&o).Error + return +} + +func (r UserGroupRepository) Create(o *model.UserGroup, members []string) (err error) { + return r.DB.Transaction(func(tx *gorm.DB) error { + err = tx.Create(o).Error + if err != nil { + return err + } + + if members != nil { + userGroupId := o.ID + err = AddUserGroupMembers(tx, members, userGroupId) + if err != nil { + return err + } + } + return err + }) +} + +func (r UserGroupRepository) Update(o *model.UserGroup, members []string, id string) error { + return r.DB.Transaction(func(tx *gorm.DB) error { + o.ID = id + err := tx.Updates(o).Error + if err != nil { + return err + } + + err = tx.Where("user_group_id = ?", id).Delete(&model.UserGroupMember{}).Error + if err != nil { + return err + } + if members != nil { + userGroupId := o.ID + err = AddUserGroupMembers(tx, members, userGroupId) + if err != nil { + return err + } + } + return err + }) +} + +func (r UserGroupRepository) DeleteById(id string) { + r.DB.Where("id = ?", id).Delete(&model.UserGroup{}) + r.DB.Where("user_group_id = ?", id).Delete(&model.UserGroupMember{}) +} + +func AddUserGroupMembers(tx *gorm.DB, userIds []string, userGroupId string) error { + userRepository := NewUserRepository(tx) + for i := range userIds { + userId := userIds[i] + _, err := userRepository.FindById(userId) + if err != nil { + return err + } + + userGroupMember := model.UserGroupMember{ + ID: utils.Sign([]string{userGroupId, userId}), + UserId: userId, + UserGroupId: userGroupId, + } + err = tx.Create(&userGroupMember).Error + if err != nil { + return err + } + } + return nil +} diff --git a/server/service/job.go b/server/service/job.go new file mode 100644 index 0000000..731f5a9 --- /dev/null +++ b/server/service/job.go @@ -0,0 +1,324 @@ +package service + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/robfig/cron/v3" + "github.com/sirupsen/logrus" + "next-terminal/server/api" + "next-terminal/server/constant" + "next-terminal/server/global" + "next-terminal/server/model" + "next-terminal/server/repository" + "next-terminal/server/term" + "next-terminal/server/utils" + "strings" + "time" +) + +type JobService struct { + jobRepository *repository.JobRepository + jobLogRepository *repository.JobLogRepository + assetRepository *repository.AssetRepository + credentialRepository *repository.CredentialRepository +} + +func NewJobService(jobRepository *repository.JobRepository, jobLogRepository *repository.JobLogRepository, assetRepository *repository.AssetRepository, credentialRepository *repository.CredentialRepository) *JobService { + return &JobService{jobRepository: jobRepository, jobLogRepository: jobLogRepository, assetRepository: assetRepository, credentialRepository: credentialRepository} +} + +func (r JobService) ChangeJobStatusById(id, status string) error { + job, err := r.jobRepository.FindById(id) + if err != nil { + return err + } + if status == constant.JobStatusRunning { + j, err := getJob(&job, &r) + if err != nil { + return err + } + entryID, err := global.Cron.AddJob(job.Cron, j) + if err != nil { + return err + } + logrus.Debugf("开启计划任务「%v」,运行中计划任务数量「%v」", job.Name, len(global.Cron.Entries())) + + jobForUpdate := model.Job{ID: id, Status: constant.JobStatusRunning, CronJobId: int(entryID)} + + return r.jobRepository.UpdateById(&jobForUpdate) + } else { + global.Cron.Remove(cron.EntryID(job.CronJobId)) + logrus.Debugf("关闭计划任务「%v」,运行中计划任务数量「%v」", job.Name, len(global.Cron.Entries())) + jobForUpdate := model.Job{ID: id, Status: constant.JobStatusNotRunning} + return r.jobRepository.UpdateById(&jobForUpdate) + } +} + +func getJob(j *model.Job, jobService *JobService) (job cron.Job, err error) { + switch j.Func { + case constant.FuncCheckAssetStatusJob: + job = CheckAssetStatusJob{ID: j.ID, Mode: j.Mode, ResourceIds: j.ResourceIds, Metadata: j.Metadata, jobService: jobService} + case constant.FuncShellJob: + job = ShellJob{ID: j.ID, Mode: j.Mode, ResourceIds: j.ResourceIds, Metadata: j.Metadata, jobService: jobService} + default: + return nil, errors.New("未识别的任务") + } + return job, err +} + +type CheckAssetStatusJob struct { + ID string + Mode string + ResourceIds string + Metadata string + jobService *JobService +} + +func (r CheckAssetStatusJob) Run() { + if r.ID == "" { + return + } + + var assets []model.Asset + if r.Mode == constant.JobModeAll { + assets, _ = r.jobService.assetRepository.FindAll() + } else { + assets, _ = r.jobService.assetRepository.FindByIds(strings.Split(r.ResourceIds, ",")) + } + + if len(assets) == 0 { + return + } + + msgChan := make(chan string) + for i := range assets { + asset := assets[i] + go func() { + t1 := time.Now() + active := utils.Tcping(asset.IP, asset.Port) + elapsed := time.Since(t1) + msg := fmt.Sprintf("资产「%v」存活状态检测完成,存活「%v」,耗时「%v」", asset.Name, active, elapsed) + + _ = r.jobService.assetRepository.UpdateActiveById(active, asset.ID) + logrus.Infof(msg) + msgChan <- msg + }() + } + + var message = "" + for i := 0; i < len(assets); i++ { + message += <-msgChan + "\n" + } + + _ = r.jobService.jobRepository.UpdateLastUpdatedById(r.ID) + jobLog := model.JobLog{ + ID: utils.UUID(), + JobId: r.ID, + Timestamp: utils.NowJsonTime(), + Message: message, + } + + _ = r.jobService.jobLogRepository.Create(&jobLog) +} + +type ShellJob struct { + ID string + Mode string + ResourceIds string + Metadata string + jobService *JobService +} + +type MetadataShell struct { + Shell string +} + +func (r ShellJob) Run() { + if r.ID == "" { + return + } + + var assets []model.Asset + if r.Mode == constant.JobModeAll { + assets, _ = r.jobService.assetRepository.FindByProtocol("ssh") + } else { + assets, _ = r.jobService.assetRepository.FindByProtocolAndIds("ssh", strings.Split(r.ResourceIds, ",")) + } + + if len(assets) == 0 { + return + } + + var metadataShell MetadataShell + err := json.Unmarshal([]byte(r.Metadata), &metadataShell) + if err != nil { + logrus.Errorf("JSON数据解析失败 %v", err) + return + } + + msgChan := make(chan string) + for i := range assets { + asset, err := r.jobService.assetRepository.FindById(assets[i].ID) + if err != nil { + msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询数据异常「%v」", assets[i].Name, err.Error()) + return + } + + var ( + username = asset.Username + password = asset.Password + privateKey = asset.PrivateKey + passphrase = asset.Passphrase + ip = asset.IP + port = asset.Port + ) + + if asset.AccountType == "credential" { + credential, err := r.jobService.credentialRepository.FindById(asset.CredentialId) + if err != nil { + msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询授权凭证数据异常「%v」", assets[i].Name, err.Error()) + return + } + + if credential.Type == constant.Custom { + username = credential.Username + password = credential.Password + } else { + username = credential.Username + privateKey = credential.PrivateKey + passphrase = credential.Passphrase + } + } + + go func() { + + t1 := time.Now() + result, err := ExecCommandBySSH(metadataShell.Shell, ip, port, username, password, privateKey, passphrase) + elapsed := time.Since(t1) + var msg string + if err != nil { + msg = fmt.Sprintf("资产「%v」Shell执行失败,返回值「%v」,耗时「%v」", asset.Name, err.Error(), elapsed) + logrus.Infof(msg) + } else { + msg = fmt.Sprintf("资产「%v」Shell执行成功,返回值「%v」,耗时「%v」", asset.Name, result, elapsed) + logrus.Infof(msg) + } + + msgChan <- msg + }() + } + + var message = "" + for i := 0; i < len(assets); i++ { + message += <-msgChan + "\n" + } + + _ = r.jobService.jobRepository.UpdateLastUpdatedById(r.ID) + jobLog := model.JobLog{ + ID: utils.UUID(), + JobId: r.ID, + Timestamp: utils.NowJsonTime(), + Message: message, + } + + _ = r.jobService.jobLogRepository.Create(&jobLog) +} + +func ExecCommandBySSH(cmd, ip string, port int, username, password, privateKey, passphrase string) (result string, err error) { + sshClient, err := term.NewSshClient(ip, port, username, password, privateKey, passphrase) + if err != nil { + return "", err + } + + session, err := sshClient.NewSession() + if err != nil { + return "", err + } + defer session.Close() + //执行远程命令 + combo, err := session.CombinedOutput(cmd) + if err != nil { + return "", err + } + return string(combo), nil +} + +func (r JobService) ExecJobById(id string) (err error) { + job, err := r.jobRepository.FindById(id) + if err != nil { + return err + } + j, err := getJob(&job, &r) + if err != nil { + return err + } + j.Run() + return nil +} + +func (r JobService) InitJob() error { + jobs, _ := r.jobRepository.FindByFunc(constant.FuncCheckAssetStatusJob) + if jobs == nil || len(jobs) == 0 { + job := model.Job{ + ID: utils.UUID(), + Name: "资产状态检测", + Func: constant.FuncCheckAssetStatusJob, + Cron: "0 0 0/1 * * ?", + Mode: constant.JobModeAll, + Status: constant.JobStatusRunning, + Created: utils.NowJsonTime(), + Updated: utils.NowJsonTime(), + } + if err := r.jobRepository.Create(&job); err != nil { + return err + } + logrus.Debugf("创建计划任务「%v」cron「%v」", job.Name, job.Cron) + } else { + for i := range jobs { + if jobs[i].Status == constant.JobStatusRunning { + err := r.ChangeJobStatusById(jobs[i].ID, constant.JobStatusRunning) + if err != nil { + return err + } + logrus.Debugf("启动计划任务「%v」cron「%v」", jobs[i].Name, jobs[i].Cron) + } + } + } + return nil +} + +// TODO 可能存在循环引用 +func (r UserService) ReloadToken() error { + loginLogs, err := r.loginLogRepository.FindAliveLoginLogs() + if err != nil { + return err + } + + for i := range loginLogs { + loginLog := loginLogs[i] + token := loginLog.ID + user, err := r.userRepository.FindById(loginLog.UserId) + if err != nil { + logrus.Debugf("用户「%v」获取失败,忽略", loginLog.UserId) + continue + } + + authorization := api.Authorization{ + Token: token, + Remember: loginLog.Remember, + User: user, + } + + cacheKey := api.BuildCacheKeyByToken(token) + + if authorization.Remember { + // 记住登录有效期两周 + global.Cache.Set(cacheKey, authorization, api.RememberEffectiveTime) + } else { + global.Cache.Set(cacheKey, authorization, api.NotRememberEffectiveTime) + } + logrus.Debugf("重新加载用户「%v」授权Token「%v」到缓存", user.Nickname, token) + } + return nil +} diff --git a/server/handle/runner.go b/server/service/property.go similarity index 53% rename from server/handle/runner.go rename to server/service/property.go index 1afcb76..62f5119 100644 --- a/server/handle/runner.go +++ b/server/service/property.go @@ -1,97 +1,30 @@ -package handle +package service import ( - "os" - "strconv" - "time" - - "next-terminal/server/constant" "next-terminal/server/guacd" "next-terminal/server/model" + "next-terminal/server/repository" "next-terminal/server/utils" - - "github.com/sirupsen/logrus" + "os" ) -func RunTicker() { - - // 每隔一小时删除一次未使用的会话信息 - unUsedSessionTicker := time.NewTicker(time.Minute * 60) - go func() { - for range unUsedSessionTicker.C { - sessions, _ := model.FindSessionByStatusIn([]string{constant.NoConnect, constant.Connecting}) - if len(sessions) > 0 { - now := time.Now() - for i := range sessions { - if now.Sub(sessions[i].ConnectedTime.Time) > time.Hour*1 { - _ = model.DeleteSessionById(sessions[i].ID) - s := sessions[i].Username + "@" + sessions[i].IP + ":" + strconv.Itoa(sessions[i].Port) - logrus.Infof("会话「%v」ID「%v」超过1小时未打开,已删除。", s, sessions[i].ID) - } - } - } - } - }() - - // 每日凌晨删除超过时长限制的会话 - timeoutSessionTicker := time.NewTicker(time.Hour * 24) - go func() { - for range timeoutSessionTicker.C { - property, err := model.FindPropertyByName("session-saved-limit") - if err != nil { - return - } - if property.Value == "" || property.Value == "-" { - return - } - limit, err := strconv.Atoi(property.Value) - if err != nil { - return - } - sessions, err := model.FindOutTimeSessions(limit) - if err != nil { - return - } - - if len(sessions) > 0 { - var sessionIds []string - for i := range sessions { - sessionIds = append(sessionIds, sessions[i].ID) - } - err := model.DeleteSessionByIds(sessionIds) - if err != nil { - logrus.Errorf("删除离线会话失败 %v", err) - } - } - } - }() +type PropertyService struct { + propertyRepository *repository.PropertyRepository } -func RunDataFix() { - sessions, _ := model.FindSessionByStatus(constant.Connected) - if sessions == nil { - return - } - - for i := range sessions { - session := model.Session{ - Status: constant.Disconnected, - DisconnectedTime: utils.NowJsonTime(), - } - - _ = model.UpdateSessionById(&session, sessions[i].ID) - } +func NewPropertyService(propertyRepository *repository.PropertyRepository) *PropertyService { + return &PropertyService{propertyRepository: propertyRepository} } -func InitProperties() error { - propertyMap := model.FindAllPropertiesMap() +func (r PropertyService) InitProperties() error { + propertyMap := r.propertyRepository.FindAllMap() if len(propertyMap[guacd.Host]) == 0 { property := model.Property{ Name: guacd.Host, Value: "127.0.0.1", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -101,7 +34,7 @@ func InitProperties() error { Name: guacd.Port, Value: "4822", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -111,7 +44,7 @@ func InitProperties() error { Name: guacd.EnableRecording, Value: "true", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -127,7 +60,7 @@ func InitProperties() error { return err } } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -137,7 +70,7 @@ func InitProperties() error { Name: guacd.CreateRecordingPath, Value: "true", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -147,7 +80,7 @@ func InitProperties() error { Name: guacd.DriveName, Value: "File-System", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -165,7 +98,7 @@ func InitProperties() error { return err } } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -175,7 +108,7 @@ func InitProperties() error { Name: guacd.FontName, Value: "menlo", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -185,7 +118,7 @@ func InitProperties() error { Name: guacd.FontSize, Value: "12", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -195,7 +128,7 @@ func InitProperties() error { Name: guacd.ColorScheme, Value: "gray-black", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -205,7 +138,7 @@ func InitProperties() error { Name: guacd.EnableDrive, Value: "true", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -215,7 +148,7 @@ func InitProperties() error { Name: guacd.EnableWallpaper, Value: "false", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -225,7 +158,7 @@ func InitProperties() error { Name: guacd.EnableTheming, Value: "false", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -235,7 +168,7 @@ func InitProperties() error { Name: guacd.EnableFontSmoothing, Value: "false", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -245,7 +178,7 @@ func InitProperties() error { Name: guacd.EnableFullWindowDrag, Value: "false", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -255,7 +188,7 @@ func InitProperties() error { Name: guacd.EnableDesktopComposition, Value: "false", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -265,7 +198,7 @@ func InitProperties() error { Name: guacd.EnableMenuAnimations, Value: "false", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -275,7 +208,7 @@ func InitProperties() error { Name: guacd.DisableBitmapCaching, Value: "false", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -285,7 +218,7 @@ func InitProperties() error { Name: guacd.DisableOffscreenCaching, Value: "false", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } @@ -295,7 +228,7 @@ func InitProperties() error { Name: guacd.DisableGlyphCaching, Value: "false", } - if err := model.CreateNewProperty(&property); err != nil { + if err := r.propertyRepository.Create(&property); err != nil { return err } } diff --git a/server/service/session.go b/server/service/session.go new file mode 100644 index 0000000..672db5a --- /dev/null +++ b/server/service/session.go @@ -0,0 +1,32 @@ +package service + +import ( + "next-terminal/server/constant" + "next-terminal/server/model" + "next-terminal/server/repository" + "next-terminal/server/utils" +) + +type SessionService struct { + sessionRepository *repository.SessionRepository +} + +func NewSessionService(sessionRepository *repository.SessionRepository) *SessionService { + return &SessionService{sessionRepository: sessionRepository} +} + +func (r SessionService) Fix() { + sessions, _ := r.sessionRepository.FindByStatus(constant.Connected) + if sessions == nil { + return + } + + for i := range sessions { + session := model.Session{ + Status: constant.Disconnected, + DisconnectedTime: utils.NowJsonTime(), + } + + _ = r.sessionRepository.UpdateById(&session, sessions[i].ID) + } +} diff --git a/server/service/user.go b/server/service/user.go new file mode 100644 index 0000000..25a4ed1 --- /dev/null +++ b/server/service/user.go @@ -0,0 +1,104 @@ +package service + +import ( + "github.com/sirupsen/logrus" + "next-terminal/server/constant" + "next-terminal/server/model" + "next-terminal/server/repository" + "next-terminal/server/utils" +) + +type UserService struct { + userRepository *repository.UserRepository + loginLogRepository *repository.LoginLogRepository +} + +func NewUserService(userRepository *repository.UserRepository) *UserService { + return &UserService{userRepository: userRepository} +} + +func (r UserService) InitUser() (err error) { + + users := r.userRepository.FindAll() + + if len(users) == 0 { + initPassword := "admin" + var pass []byte + if pass, err = utils.Encoder.Encode([]byte(initPassword)); err != nil { + return err + } + + user := model.User{ + ID: utils.UUID(), + Username: "admin", + Password: string(pass), + Nickname: "超级管理员", + Type: constant.TypeAdmin, + Created: utils.NowJsonTime(), + } + if err := r.userRepository.Create(&user); err != nil { + return err + } + logrus.Infof("初始用户创建成功,账号:「%v」密码:「%v」", user.Username, initPassword) + } else { + for i := range users { + // 修正默认用户类型为管理员 + if users[i].Type == "" { + user := model.User{ + Type: constant.TypeAdmin, + ID: users[i].ID, + } + if err := r.userRepository.Update(&user); err != nil { + return err + } + logrus.Infof("自动修正用户「%v」ID「%v」类型为管理员", users[i].Nickname, users[i].ID) + } + } + } + return nil +} + +func (r UserService) FixedOnlineState() error { + // 修正用户登录状态 + onlineUsers, err := r.userRepository.FindOnlineUsers() + if err != nil { + return err + } + for i := range onlineUsers { + logs, err := r.loginLogRepository.FindAliveLoginLogsByUserId(onlineUsers[i].ID) + if err != nil { + return err + } + if len(logs) == 0 { + if err := r.userRepository.UpdateOnline(onlineUsers[i].ID, false); err != nil { + return err + } + } + } + return nil +} + +func (r UserService) Logout(token string) (err error) { + + loginLog, err := r.loginLogRepository.FindById(token) + if err != nil { + logrus.Warnf("登录日志「%v」获取失败", token) + return + } + + loginLogForUpdate := &model.LoginLog{LogoutTime: utils.NowJsonTime(), ID: token} + err = r.loginLogRepository.Update(loginLogForUpdate) + if err != nil { + return err + } + + loginLogs, err := r.loginLogRepository.FindAliveLoginLogsByUserId(loginLog.UserId) + if err != nil { + return + } + + if len(loginLogs) == 0 { + err = r.userRepository.UpdateOnline(loginLog.UserId, false) + } + return +} From 2eb4dc39697e0b3a6ebb593063a9e7834584326a Mon Sep 17 00:00:00 2001 From: dushixiang Date: Fri, 19 Mar 2021 20:22:04 +0800 Subject: [PATCH 06/34] =?UTF-8?q?=E5=AE=8C=E6=88=90=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E6=93=8D=E4=BD=9C=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 22 ++------ server/api/command.go | 20 ++++--- server/api/credential.go | 22 +++++--- server/api/job.go | 20 +++---- server/api/overview.go | 20 +++---- server/api/property.go | 10 ++-- server/api/resource-sharer.go | 10 ++-- server/api/routes.go | 58 +++++++++++++++++++- server/api/session.go | 50 ++++++++--------- server/api/ssh.go | 8 +-- server/api/tunnel.go | 10 ++-- server/api/user-group.go | 44 +++------------ server/api/user.go | 42 ++++++++++++-- server/config/db.go | 53 ------------------ server/repository/access_security.go | 9 ++- server/repository/asset.go | 2 +- server/repository/command.go | 7 +-- server/repository/credential.go | 2 +- server/repository/job.go | 16 +----- server/repository/num.go | 3 +- server/repository/property.go | 29 +--------- server/repository/user_group.go | 14 +++-- server/service/job.go | 58 +++++++++----------- server/service/mail.go | 40 ++++++++++++++ server/service/user.go | 4 +- web/src/components/asset/Asset.js | 5 +- web/src/components/command/DynamicCommand.js | 3 +- web/src/components/dashboard/Dashboard.js | 3 +- web/src/components/devops/Job.js | 2 - web/src/components/devops/LoginLog.js | 3 +- web/src/components/devops/Security.js | 17 +----- web/src/components/session/OfflineSession.js | 3 +- web/src/components/session/OnlineSession.js | 3 +- web/src/components/setting/Setting.js | 3 +- web/src/components/user/Info.js | 3 +- web/src/components/user/User.js | 2 - web/src/components/user/UserGroup.js | 3 +- 37 files changed, 294 insertions(+), 329 deletions(-) delete mode 100644 server/config/db.go create mode 100644 server/service/mail.go diff --git a/main.go b/main.go index 6231d76..6c7255c 100644 --- a/main.go +++ b/main.go @@ -7,7 +7,6 @@ import ( "github.com/labstack/gommon/log" "github.com/robfig/cron/v3" "github.com/sirupsen/logrus" - "gorm.io/gorm" "io" "next-terminal/server/api" "next-terminal/server/config" @@ -15,11 +14,7 @@ import ( "os" ) -const Version = "v0.3.3" - -var ( - db *gorm.DB -) +const Version = "v0.3.4" func main() { err := Run() @@ -56,27 +51,20 @@ func Run() error { logrus.SetOutput(io.MultiWriter(writer1, writer2, writer3)) global.Config = config.SetupConfig() - db = config.SetupDB() - - //if global.Config.ResetPassword != "" { - // return ResetPassword() - //} - - if err := api.ReloadAccessSecurity(); err != nil { - return err - } global.Store = global.NewStore() global.Cron = cron.New(cron.WithSeconds()) //精确到秒 global.Cron.Start() + db := api.SetupDB() e := api.SetupRoutes(db) global.Cache = api.SetupCache() + if global.Config.ResetPassword != "" { + return api.ResetPassword() + } api.SetupTicker() - go handle.RunDataFix() - if global.Config.Server.Cert != "" && global.Config.Server.Key != "" { return e.StartTLS(global.Config.Server.Addr, global.Config.Server.Cert, global.Config.Server.Key) } else { diff --git a/server/api/command.go b/server/api/command.go index 72b841c..afffffc 100644 --- a/server/api/command.go +++ b/server/api/command.go @@ -22,7 +22,7 @@ func CommandCreateEndpoint(c echo.Context) error { item.ID = utils.UUID() item.Created = utils.NowJsonTime() - if err := model.CreateNewCommand(&item); err != nil { + if err := commandRepository.Create(&item); err != nil { return err } @@ -39,7 +39,7 @@ func CommandPagingEndpoint(c echo.Context) error { order := c.QueryParam("order") field := c.QueryParam("field") - items, total, err := model.FindPageCommand(pageIndex, pageSize, name, content, order, field, account) + items, total, err := commandRepository.Find(pageIndex, pageSize, name, content, order, field, account) if err != nil { return err } @@ -61,7 +61,9 @@ func CommandUpdateEndpoint(c echo.Context) error { return err } - model.UpdateCommandById(&item, id) + if err := commandRepository.UpdateById(&item, id); err != nil { + return err + } return Success(c, nil) } @@ -73,11 +75,11 @@ func CommandDeleteEndpoint(c echo.Context) error { if err := PreCheckCommandPermission(c, split[i]); err != nil { return err } - if err := model.DeleteCommandById(split[i]); err != nil { + if err := commandRepository.DeleteById(split[i]); err != nil { return err } // 删除资产与用户的关系 - if err := model.DeleteResourceSharerByResourceId(split[i]); err != nil { + if err := resourceSharerRepository.DeleteResourceSharerByResourceId(split[i]); err != nil { return err } } @@ -92,7 +94,7 @@ func CommandGetEndpoint(c echo.Context) (err error) { } var item model.Command - if item, err = model.FindCommandById(id); err != nil { + if item, err = commandRepository.FindById(id); err != nil { return err } return Success(c, item) @@ -106,12 +108,14 @@ func CommandChangeOwnerEndpoint(c echo.Context) (err error) { } owner := c.QueryParam("owner") - model.UpdateCommandById(&model.Command{Owner: owner}, id) + if err := commandRepository.UpdateById(&model.Command{Owner: owner}, id); err != nil { + return err + } return Success(c, "") } func PreCheckCommandPermission(c echo.Context, id string) error { - item, err := model.FindCommandById(id) + item, err := commandRepository.FindById(id) if err != nil { return err } diff --git a/server/api/credential.go b/server/api/credential.go index 0b29854..8fa8677 100644 --- a/server/api/credential.go +++ b/server/api/credential.go @@ -14,7 +14,7 @@ import ( func CredentialAllEndpoint(c echo.Context) error { account, _ := GetCurrentAccount(c) - items, _ := model.FindAllCredential(account) + items, _ := credentialRepository.FindByUser(account) return Success(c, items) } func CredentialCreateEndpoint(c echo.Context) error { @@ -53,7 +53,7 @@ func CredentialCreateEndpoint(c echo.Context) error { return Fail(c, -1, "类型错误") } - if err := model.CreateNewCredential(&item); err != nil { + if err := credentialRepository.Create(&item); err != nil { return err } @@ -69,7 +69,7 @@ func CredentialPagingEndpoint(c echo.Context) error { field := c.QueryParam("field") account, _ := GetCurrentAccount(c) - items, total, err := model.FindPageCredential(pageIndex, pageSize, name, order, field, account) + items, total, err := credentialRepository.Find(pageIndex, pageSize, name, order, field, account) if err != nil { return err } @@ -117,7 +117,9 @@ func CredentialUpdateEndpoint(c echo.Context) error { return Fail(c, -1, "类型错误") } - model.UpdateCredentialById(&item, id) + if err := credentialRepository.UpdateById(&item, id); err != nil { + return err + } return Success(c, nil) } @@ -129,11 +131,11 @@ func CredentialDeleteEndpoint(c echo.Context) error { if err := PreCheckCredentialPermission(c, split[i]); err != nil { return err } - if err := model.DeleteCredentialById(split[i]); err != nil { + if err := credentialRepository.DeleteById(split[i]); err != nil { return err } // 删除资产与用户的关系 - if err := model.DeleteResourceSharerByResourceId(split[i]); err != nil { + if err := resourceSharerRepository.DeleteResourceSharerByResourceId(split[i]); err != nil { return err } } @@ -147,7 +149,7 @@ func CredentialGetEndpoint(c echo.Context) error { return err } - item, err := model.FindCredentialById(id) + item, err := credentialRepository.FindById(id) if err != nil { return err } @@ -167,12 +169,14 @@ func CredentialChangeOwnerEndpoint(c echo.Context) error { } owner := c.QueryParam("owner") - model.UpdateCredentialById(&model.Credential{Owner: owner}, id) + if err := credentialRepository.UpdateById(&model.Credential{Owner: owner}, id); err != nil { + return err + } return Success(c, "") } func PreCheckCredentialPermission(c echo.Context, id string) error { - item, err := model.FindCredentialById(id) + item, err := credentialRepository.FindById(id) if err != nil { return err } diff --git a/server/api/job.go b/server/api/job.go index 4d72872..6f908f6 100644 --- a/server/api/job.go +++ b/server/api/job.go @@ -19,7 +19,7 @@ func JobCreateEndpoint(c echo.Context) error { item.ID = utils.UUID() item.Created = utils.NowJsonTime() - if err := model.CreateNewJob(&item); err != nil { + if err := jobService.Create(&item); err != nil { return err } return Success(c, "") @@ -34,7 +34,7 @@ func JobPagingEndpoint(c echo.Context) error { order := c.QueryParam("order") field := c.QueryParam("field") - items, total, err := model.FindPageJob(pageIndex, pageSize, name, status, order, field) + items, total, err := jobRepository.Find(pageIndex, pageSize, name, status, order, field) if err != nil { return err } @@ -52,8 +52,8 @@ func JobUpdateEndpoint(c echo.Context) error { if err := c.Bind(&item); err != nil { return err } - - if err := model.UpdateJobById(&item, id); err != nil { + item.ID = id + if err := jobRepository.UpdateById(&item); err != nil { return err } @@ -63,7 +63,7 @@ func JobUpdateEndpoint(c echo.Context) error { func JobChangeStatusEndpoint(c echo.Context) error { id := c.Param("id") status := c.QueryParam("status") - if err := model.ChangeJobStatusById(id, status); err != nil { + if err := jobService.ChangeStatusById(id, status); err != nil { return err } return Success(c, "") @@ -71,7 +71,7 @@ func JobChangeStatusEndpoint(c echo.Context) error { func JobExecEndpoint(c echo.Context) error { id := c.Param("id") - if err := model.ExecJobById(id); err != nil { + if err := jobService.ExecJobById(id); err != nil { return err } return Success(c, "") @@ -83,7 +83,7 @@ func JobDeleteEndpoint(c echo.Context) error { split := strings.Split(ids, ",") for i := range split { jobId := split[i] - if err := model.DeleteJobById(jobId); err != nil { + if err := jobRepository.DeleteJobById(jobId); err != nil { return err } } @@ -94,7 +94,7 @@ func JobDeleteEndpoint(c echo.Context) error { func JobGetEndpoint(c echo.Context) error { id := c.Param("id") - item, err := model.FindJobById(id) + item, err := jobRepository.FindById(id) if err != nil { return err } @@ -105,7 +105,7 @@ func JobGetEndpoint(c echo.Context) error { func JobGetLogsEndpoint(c echo.Context) error { id := c.Param("id") - items, err := model.FindJobLogs(id) + items, err := jobLogRepository.FindByJobId(id) if err != nil { return err } @@ -115,7 +115,7 @@ func JobGetLogsEndpoint(c echo.Context) error { func JobDeleteLogsEndpoint(c echo.Context) error { id := c.Param("id") - if err := model.DeleteJobLogByJobId(id); err != nil { + if err := jobLogRepository.DeleteByJobId(id); err != nil { return err } return Success(c, "") diff --git a/server/api/overview.go b/server/api/overview.go index 1fb31bc..aa21611 100644 --- a/server/api/overview.go +++ b/server/api/overview.go @@ -2,7 +2,7 @@ package api import ( "next-terminal/server/constant" - "next-terminal/server/model" + "next-terminal/server/repository" "github.com/labstack/echo/v4" ) @@ -25,14 +25,14 @@ func OverviewCounterEndPoint(c echo.Context) error { ) if constant.TypeUser == account.Type { countUser, _ = userRepository.CountOnlineUser() - countOnlineSession, _ = model.CountOnlineSession() - credential, _ = model.CountCredentialByUserId(account.ID) - asset, _ = model.CountAssetByUserId(account.ID) + countOnlineSession, _ = sessionRepository.CountOnlineSession() + credential, _ = credentialRepository.CountByUserId(account.ID) + asset, _ = assetRepository.CountByUserId(account.ID) } else { countUser, _ = userRepository.CountOnlineUser() - countOnlineSession, _ = model.CountOnlineSession() - credential, _ = model.CountCredential() - asset, _ = model.CountAsset() + countOnlineSession, _ = sessionRepository.CountOnlineSession() + credential, _ = credentialRepository.Count() + asset, _ = assetRepository.Count() } counter := Counter{ User: countUser, @@ -46,11 +46,11 @@ func OverviewCounterEndPoint(c echo.Context) error { func OverviewSessionPoint(c echo.Context) (err error) { d := c.QueryParam("d") - var results []model.D + var results []repository.D if d == "m" { - results, err = model.CountSessionByDay(30) + results, err = sessionRepository.CountSessionByDay(30) } else { - results, err = model.CountSessionByDay(7) + results, err = sessionRepository.CountSessionByDay(7) } if err != nil { return err diff --git a/server/api/property.go b/server/api/property.go index 3852249..f5e6310 100644 --- a/server/api/property.go +++ b/server/api/property.go @@ -11,7 +11,7 @@ import ( ) func PropertyGetEndpoint(c echo.Context) error { - properties := model.FindAllPropertiesMap() + properties := propertyRepository.FindAllMap() return Success(c, properties) } @@ -32,13 +32,15 @@ func PropertyUpdateEndpoint(c echo.Context) error { Value: value, } - _, err := model.FindPropertyByName(key) + _, err := propertyRepository.FindByName(key) if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { - if err := model.CreateNewProperty(&property); err != nil { + if err := propertyRepository.Create(&property); err != nil { return err } } else { - model.UpdatePropertyByName(&property, key) + if err := propertyRepository.UpdateByName(&property, key); err != nil { + return err + } } } return Success(c, nil) diff --git a/server/api/resource-sharer.go b/server/api/resource-sharer.go index 6f9d9d4..77a5201 100644 --- a/server/api/resource-sharer.go +++ b/server/api/resource-sharer.go @@ -1,8 +1,6 @@ package api import ( - "next-terminal/server/model" - "github.com/labstack/echo/v4" ) @@ -21,7 +19,7 @@ type UR struct { func RSGetSharersEndPoint(c echo.Context) error { resourceId := c.QueryParam("resourceId") - userIds, err := model.FindUserIdsByResourceId(resourceId) + userIds, err := resourceSharerRepository.FindUserIdsByResourceId(resourceId) if err != nil { return err } @@ -34,7 +32,7 @@ func RSOverwriteSharersEndPoint(c echo.Context) error { return err } - if err := model.OverwriteUserIdsByResourceId(ur.ResourceId, ur.ResourceType, ur.UserIds); err != nil { + if err := resourceSharerRepository.OverwriteUserIdsByResourceId(ur.ResourceId, ur.ResourceType, ur.UserIds); err != nil { return err } @@ -47,7 +45,7 @@ func ResourceRemoveByUserIdAssignEndPoint(c echo.Context) error { return err } - if err := model.DeleteByUserIdAndResourceTypeAndResourceIdIn(ru.UserGroupId, ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil { + if err := resourceSharerRepository.DeleteByUserIdAndResourceTypeAndResourceIdIn(ru.UserGroupId, ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil { return err } @@ -60,7 +58,7 @@ func ResourceAddByUserIdAssignEndPoint(c echo.Context) error { return err } - if err := model.AddSharerResources(ru.UserGroupId, ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil { + if err := resourceSharerRepository.AddSharerResources(ru.UserGroupId, ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil { return err } diff --git a/server/api/routes.go b/server/api/routes.go index e907648..39b765f 100644 --- a/server/api/routes.go +++ b/server/api/routes.go @@ -1,9 +1,13 @@ package api import ( + "fmt" "github.com/patrickmn/go-cache" "github.com/sirupsen/logrus" + "gorm.io/driver/mysql" + "gorm.io/driver/sqlite" "gorm.io/gorm" + "gorm.io/gorm/logger" "net/http" "next-terminal/server/global" "next-terminal/server/log" @@ -40,6 +44,7 @@ var ( propertyService *service.PropertyService userService *service.UserService sessionService *service.SessionService + mailService *service.MailService ) func SetupRoutes(db *gorm.DB) *echo.Echo { @@ -47,7 +52,9 @@ func SetupRoutes(db *gorm.DB) *echo.Echo { InitRepository(db) InitService() - InitDBData() + if err := InitDBData(); err != nil { + logrus.WithError(err).Error("初始化数据异常") + } e := echo.New() e.HideBanner = true @@ -225,8 +232,9 @@ func InitRepository(db *gorm.DB) { func InitService() { jobService = service.NewJobService(jobRepository, jobLogRepository, assetRepository, credentialRepository) propertyService = service.NewPropertyService(propertyRepository) - userService = service.NewUserService(userRepository) + userService = service.NewUserService(userRepository, loginLogRepository) sessionService = service.NewSessionService(sessionRepository) + mailService = service.NewMailService(propertyRepository) } func InitDBData() (err error) { @@ -244,8 +252,11 @@ func InitDBData() (err error) { } sessionService.Fix() + if err := ReloadAccessSecurity(); err != nil { + return err + } nums, _ := numRepository.FindAll() - if nums == nil || len(nums) == 0 { + if nums == nil { for i := 0; i <= 30; i++ { if err := numRepository.Create(&model.Num{I: strconv.Itoa(i)}); err != nil { return err @@ -291,3 +302,44 @@ func SetupCache() *cache.Cache { }) return mCache } + +func SetupDB() *gorm.DB { + + var logMode logger.Interface + if global.Config.Debug { + logMode = logger.Default.LogMode(logger.Info) + } else { + logMode = logger.Default.LogMode(logger.Silent) + } + + fmt.Printf("当前数据库模式为:%v\n", global.Config.DB) + var err error + var db *gorm.DB + if global.Config.DB == "mysql" { + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", + global.Config.Mysql.Username, + global.Config.Mysql.Password, + global.Config.Mysql.Hostname, + global.Config.Mysql.Port, + global.Config.Mysql.Database, + ) + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ + Logger: logMode, + }) + } else { + db, err = gorm.Open(sqlite.Open(global.Config.Sqlite.File), &gorm.Config{ + Logger: logMode, + }) + } + + if err != nil { + logrus.WithError(err).Panic("连接数据库异常") + } + + if err := db.AutoMigrate(&model.User{}, &model.Asset{}, &model.AssetAttribute{}, &model.Session{}, &model.Command{}, + &model.Credential{}, &model.Property{}, &model.ResourceSharer{}, &model.UserGroup{}, &model.UserGroupMember{}, + &model.LoginLog{}, &model.Num{}, &model.Job{}, &model.JobLog{}, &model.AccessSecurity{}); err != nil { + logrus.WithError(err).Panic("初始化数据库表结构异常") + } + return db +} diff --git a/server/api/session.go b/server/api/session.go index b295873..1a8edaa 100644 --- a/server/api/session.go +++ b/server/api/session.go @@ -32,7 +32,7 @@ func SessionPagingEndpoint(c echo.Context) error { assetId := c.QueryParam("assetId") protocol := c.QueryParam("protocol") - items, total, err := model.FindPageSession(pageIndex, pageSize, status, userId, clientIp, assetId, protocol) + items, total, err := sessionRepository.Find(pageIndex, pageSize, status, userId, clientIp, assetId, protocol) if err != nil { return err @@ -67,7 +67,7 @@ func SessionPagingEndpoint(c echo.Context) error { func SessionDeleteEndpoint(c echo.Context) error { sessionIds := c.Param("id") split := strings.Split(sessionIds, ",") - err := model.DeleteSessionByIds(split) + err := sessionRepository.DeleteByIds(split) if err != nil { return err } @@ -83,7 +83,7 @@ func SessionConnectEndpoint(c echo.Context) error { session.Status = constant.Connected session.ConnectedTime = utils.NowJsonTime() - if err := model.UpdateSessionById(&session, sessionId); err != nil { + if err := sessionRepository.UpdateById(&session, sessionId); err != nil { return err } return Success(c, nil) @@ -116,7 +116,7 @@ func CloseSessionById(sessionId string, code int, reason string) { } global.Store.Del(sessionId) - s, err := model.FindSessionById(sessionId) + s, err := sessionRepository.FindById(sessionId) if err != nil { return } @@ -127,7 +127,7 @@ func CloseSessionById(sessionId string, code int, reason string) { if s.Status == constant.Connecting { // 会话还未建立成功,无需保留数据 - _ = model.DeleteSessionById(sessionId) + _ = sessionRepository.DeleteById(sessionId) return } @@ -138,7 +138,7 @@ func CloseSessionById(sessionId string, code int, reason string) { session.Code = code session.Message = reason - _ = model.UpdateSessionById(&session, sessionId) + _ = sessionRepository.UpdateById(&session, sessionId) } func SessionResizeEndpoint(c echo.Context) error { @@ -154,7 +154,7 @@ func SessionResizeEndpoint(c echo.Context) error { intHeight, _ := strconv.Atoi(height) - if err := model.UpdateSessionWindowSizeById(intWidth, intHeight, sessionId); err != nil { + if err := sessionRepository.UpdateWindowSizeById(intWidth, intHeight, sessionId); err != nil { return err } return Success(c, "") @@ -174,7 +174,7 @@ func SessionCreateEndpoint(c echo.Context) error { if constant.TypeUser == user.Type { // 检测是否有访问权限 - assetIds, err := model.FindAssetIdsByUserId(user.ID) + assetIds, err := resourceSharerRepository.FindAssetIdsByUserId(user.ID) if err != nil { return err } @@ -184,7 +184,7 @@ func SessionCreateEndpoint(c echo.Context) error { } } - asset, err := model.FindAssetById(assetId) + asset, err := assetRepository.FindById(assetId) if err != nil { return err } @@ -206,7 +206,7 @@ func SessionCreateEndpoint(c echo.Context) error { } if asset.AccountType == "credential" { - credential, err := model.FindCredentialById(asset.CredentialId) + credential, err := credentialRepository.FindById(asset.CredentialId) if err != nil { return err } @@ -221,7 +221,7 @@ func SessionCreateEndpoint(c echo.Context) error { } } - if err := model.CreateNewSession(session); err != nil { + if err := sessionRepository.Create(session); err != nil { return err } @@ -230,7 +230,7 @@ func SessionCreateEndpoint(c echo.Context) error { func SessionUploadEndpoint(c echo.Context) error { sessionId := c.Param("id") - session, err := model.FindSessionById(sessionId) + session, err := sessionRepository.FindById(sessionId) if err != nil { return err } @@ -280,7 +280,7 @@ func SessionUploadEndpoint(c echo.Context) error { return Fail(c, -1, ":) 您的IP已被记录,请去向管理员自首。") } - drivePath, err := model.GetDrivePath() + drivePath, err := propertyRepository.GetDrivePath() if err != nil { return err } @@ -304,7 +304,7 @@ func SessionUploadEndpoint(c echo.Context) error { func SessionDownloadEndpoint(c echo.Context) error { sessionId := c.Param("id") - session, err := model.FindSessionById(sessionId) + session, err := sessionRepository.FindById(sessionId) if err != nil { return err } @@ -337,7 +337,7 @@ func SessionDownloadEndpoint(c echo.Context) error { SafetyRuleTrigger(c) return Fail(c, -1, ":) 您的IP已被记录,请去向管理员自首。") } - drivePath, err := model.GetDrivePath() + drivePath, err := propertyRepository.GetDrivePath() if err != nil { return err } @@ -359,7 +359,7 @@ type File struct { func SessionLsEndpoint(c echo.Context) error { sessionId := c.Param("id") - session, err := model.FindSessionById(sessionId) + session, err := sessionRepository.FindById(sessionId) if err != nil { return err } @@ -419,7 +419,7 @@ func SessionLsEndpoint(c echo.Context) error { SafetyRuleTrigger(c) return Fail(c, -1, ":) 您的IP已被记录,请去向管理员自首。") } - drivePath, err := model.GetDrivePath() + drivePath, err := propertyRepository.GetDrivePath() if err != nil { return err } @@ -458,12 +458,12 @@ func SafetyRuleTrigger(c echo.Context) { Rule: constant.AccessRuleReject, } - _ = model.CreateNewSecurity(&security) + _ = accessSecurityRepository.Create(&security) } func SessionMkDirEndpoint(c echo.Context) error { sessionId := c.Param("id") - session, err := model.FindSessionById(sessionId) + session, err := sessionRepository.FindById(sessionId) if err != nil { return err } @@ -482,7 +482,7 @@ func SessionMkDirEndpoint(c echo.Context) error { SafetyRuleTrigger(c) return Fail(c, -1, ":) 您的IP已被记录,请去向管理员自首。") } - drivePath, err := model.GetDrivePath() + drivePath, err := propertyRepository.GetDrivePath() if err != nil { return err } @@ -498,7 +498,7 @@ func SessionMkDirEndpoint(c echo.Context) error { func SessionRmEndpoint(c echo.Context) error { sessionId := c.Param("id") - session, err := model.FindSessionById(sessionId) + session, err := sessionRepository.FindById(sessionId) if err != nil { return err } @@ -543,7 +543,7 @@ func SessionRmEndpoint(c echo.Context) error { SafetyRuleTrigger(c) return Fail(c, -1, ":) 您的IP已被记录,请去向管理员自首。") } - drivePath, err := model.GetDrivePath() + drivePath, err := propertyRepository.GetDrivePath() if err != nil { return err } @@ -560,7 +560,7 @@ func SessionRmEndpoint(c echo.Context) error { func SessionRenameEndpoint(c echo.Context) error { sessionId := c.Param("id") - session, err := model.FindSessionById(sessionId) + session, err := sessionRepository.FindById(sessionId) if err != nil { return err } @@ -584,7 +584,7 @@ func SessionRenameEndpoint(c echo.Context) error { SafetyRuleTrigger(c) return Fail(c, -1, ":) 您的IP已被记录,请去向管理员自首。") } - drivePath, err := model.GetDrivePath() + drivePath, err := propertyRepository.GetDrivePath() if err != nil { return err } @@ -600,7 +600,7 @@ func SessionRenameEndpoint(c echo.Context) error { func SessionRecordingEndpoint(c echo.Context) error { sessionId := c.Param("id") - session, err := model.FindSessionById(sessionId) + session, err := sessionRepository.FindById(sessionId) if err != nil { return err } diff --git a/server/api/ssh.go b/server/api/ssh.go index 1285a89..61de926 100644 --- a/server/api/ssh.go +++ b/server/api/ssh.go @@ -54,7 +54,7 @@ func SSHEndpoint(c echo.Context) (err error) { cols, _ := strconv.Atoi(c.QueryParam("cols")) rows, _ := strconv.Atoi(c.QueryParam("rows")) - session, err := model.FindSessionById(sessionId) + session, err := sessionRepository.FindById(sessionId) if err != nil { msg := Message{ Type: Closed, @@ -67,7 +67,7 @@ func SSHEndpoint(c echo.Context) (err error) { user, _ := GetCurrentAccount(c) if constant.TypeUser == user.Type { // 检测是否有访问权限 - assetIds, err := model.FindAssetIdsByUserId(user.ID) + assetIds, err := resourceSharerRepository.FindAssetIdsByUserId(user.ID) if err != nil { return err } @@ -91,7 +91,7 @@ func SSHEndpoint(c echo.Context) (err error) { ) recording := "" - propertyMap := model.FindAllPropertiesMap() + propertyMap := propertyRepository.FindAllMap() if propertyMap[guacd.EnableRecording] == "true" { recording = path.Join(propertyMap[guacd.RecordingPath], sessionId, "recording.cast") } @@ -145,7 +145,7 @@ func SSHEndpoint(c echo.Context) (err error) { } // 创建新会话 logrus.Debugf("创建新会话 %v", sess.ConnectionId) - if err := model.UpdateSessionById(&sess, sessionId); err != nil { + if err := sessionRepository.UpdateById(&sess, sessionId); err != nil { return err } diff --git a/server/api/tunnel.go b/server/api/tunnel.go index 975f1e5..db8c46e 100644 --- a/server/api/tunnel.go +++ b/server/api/tunnel.go @@ -42,12 +42,12 @@ func TunEndpoint(c echo.Context) error { configuration := guacd.NewConfiguration() - propertyMap := model.FindAllPropertiesMap() + propertyMap := propertyRepository.FindAllMap() var session model.Session if len(connectionId) > 0 { - session, err = model.FindSessionByConnectionId(connectionId) + session, err = sessionRepository.FindByConnectionId(connectionId) if err != nil { logrus.Warnf("会话不存在") return err @@ -65,7 +65,7 @@ func TunEndpoint(c echo.Context) error { configuration.SetParameter("width", width) configuration.SetParameter("height", height) configuration.SetParameter("dpi", dpi) - session, err = model.FindSessionById(sessionId) + session, err = sessionRepository.FindById(sessionId) if err != nil { CloseSessionById(sessionId, NotFoundSession, "会话不存在") return err @@ -143,7 +143,7 @@ func TunEndpoint(c echo.Context) error { configuration.SetParameter("port", strconv.Itoa(session.Port)) // 加载资产配置的属性,优先级比全局配置的高,因此最后加载,覆盖掉全局配置 - attributes, _ := model.FindAssetAttributeByAssetId(session.AssetId) + attributes, _ := assetRepository.FindAttrById(session.AssetId) if len(attributes) > 0 { for i := range attributes { attribute := attributes[i] @@ -195,7 +195,7 @@ func TunEndpoint(c echo.Context) error { } // 创建新会话 logrus.Debugf("创建新会话 %v", sess.ConnectionId) - if err := model.UpdateSessionById(&sess, sessionId); err != nil { + if err := sessionRepository.UpdateById(&sess, sessionId); err != nil { return err } } else { diff --git a/server/api/user-group.go b/server/api/user-group.go index a20d13d..650f0ac 100644 --- a/server/api/user-group.go +++ b/server/api/user-group.go @@ -4,7 +4,6 @@ import ( "strconv" "strings" - "next-terminal/server/global" "next-terminal/server/model" "next-terminal/server/utils" @@ -29,7 +28,7 @@ func UserGroupCreateEndpoint(c echo.Context) error { Name: item.Name, } - if err := model.CreateNewUserGroup(&userGroup, item.Members); err != nil { + if err := userGroupRepository.Create(&userGroup, item.Members); err != nil { return err } @@ -44,7 +43,7 @@ func UserGroupPagingEndpoint(c echo.Context) error { order := c.QueryParam("order") field := c.QueryParam("field") - items, total, err := model.FindPageUserGroup(pageIndex, pageSize, name, order, field) + items, total, err := userGroupRepository.Find(pageIndex, pageSize, name, order, field) if err != nil { return err } @@ -66,7 +65,7 @@ func UserGroupUpdateEndpoint(c echo.Context) error { Name: item.Name, } - if err := model.UpdateUserGroupById(&userGroup, item.Members, id); err != nil { + if err := userGroupRepository.Update(&userGroup, item.Members, id); err != nil { return err } @@ -78,7 +77,9 @@ func UserGroupDeleteEndpoint(c echo.Context) error { split := strings.Split(ids, ",") for i := range split { userId := split[i] - model.DeleteUserGroupById(userId) + if err := userGroupRepository.DeleteById(userId); err != nil { + return err + } } return Success(c, nil) @@ -87,12 +88,12 @@ func UserGroupDeleteEndpoint(c echo.Context) error { func UserGroupGetEndpoint(c echo.Context) error { id := c.Param("id") - item, err := model.FindUserGroupById(id) + item, err := userGroupRepository.FindById(id) if err != nil { return err } - members, err := model.FindUserGroupMembersByUserGroupId(id) + members, err := userGroupRepository.FindMembersById(id) if err != nil { return err } @@ -105,32 +106,3 @@ func UserGroupGetEndpoint(c echo.Context) error { return Success(c, userGroup) } - -func UserGroupAddMembersEndpoint(c echo.Context) error { - id := c.Param("id") - - var items []string - if err := c.Bind(&items); err != nil { - return err - } - - if err := model.AddUserGroupMembers(global.DB, items, id); err != nil { - return err - } - return Success(c, "") -} - -func UserGroupDelMembersEndpoint(c echo.Context) (err error) { - id := c.Param("id") - memberIdsStr := c.Param("memberId") - memberIds := strings.Split(memberIdsStr, ",") - for i := range memberIds { - memberId := memberIds[i] - err = global.DB.Where("user_group_id = ? and user_id = ?", id, memberId).Delete(&model.UserGroupMember{}).Error - if err != nil { - return err - } - } - - return Success(c, "") -} diff --git a/server/api/user.go b/server/api/user.go index a510f83..ed51513 100644 --- a/server/api/user.go +++ b/server/api/user.go @@ -34,7 +34,7 @@ func UserCreateEndpoint(c echo.Context) error { } if item.Mail != "" { - go model.SendMail(item.Mail, "[Next Terminal] 注册通知", "你好,"+item.Nickname+"。管理员为你注册了账号:"+item.Username+" 密码:"+password) + go mailService.SendMail(item.Mail, "[Next Terminal] 注册通知", "你好,"+item.Nickname+"。管理员为你注册了账号:"+item.Username+" 密码:"+password) } return Success(c, item) } @@ -89,14 +89,14 @@ func UserDeleteEndpoint(c echo.Context) error { return Fail(c, -1, "不允许删除自身账户") } // 将用户强制下线 - loginLogs, err := model.FindAliveLoginLogsByUserId(userId) + loginLogs, err := loginLogRepository.FindAliveLoginLogsByUserId(userId) if err != nil { return err } for j := range loginLogs { global.Cache.Delete(loginLogs[j].ID) - if err := model.Logout(loginLogs[j].ID); err != nil { + if err := userService.Logout(loginLogs[j].ID); err != nil { logrus.WithError(err).WithField("id:", loginLogs[j].ID).Error("Cache Deleted Error") return Fail(c, 500, "强制下线错误") } @@ -144,7 +144,7 @@ func UserChangePasswordEndpoint(c echo.Context) error { } if user.Mail != "" { - go model.SendMail(user.Mail, "[Next Terminal] 密码修改通知", "你好,"+user.Nickname+"。管理员已将你的密码修改为:"+password) + go mailService.SendMail(user.Mail, "[Next Terminal] 密码修改通知", "你好,"+user.Nickname+"。管理员已将你的密码修改为:"+password) } return Success(c, "") @@ -161,3 +161,37 @@ func UserResetTotpEndpoint(c echo.Context) error { } return Success(c, "") } + +func ReloadToken() error { + loginLogs, err := loginLogRepository.FindAliveLoginLogs() + if err != nil { + return err + } + + for i := range loginLogs { + loginLog := loginLogs[i] + token := loginLog.ID + user, err := userRepository.FindById(loginLog.UserId) + if err != nil { + logrus.Debugf("用户「%v」获取失败,忽略", loginLog.UserId) + continue + } + + authorization := Authorization{ + Token: token, + Remember: loginLog.Remember, + User: user, + } + + cacheKey := BuildCacheKeyByToken(token) + + if authorization.Remember { + // 记住登录有效期两周 + global.Cache.Set(cacheKey, authorization, RememberEffectiveTime) + } else { + global.Cache.Set(cacheKey, authorization, NotRememberEffectiveTime) + } + logrus.Debugf("重新加载用户「%v」授权Token「%v」到缓存", user.Nickname, token) + } + return nil +} diff --git a/server/config/db.go b/server/config/db.go deleted file mode 100644 index 3ef9428..0000000 --- a/server/config/db.go +++ /dev/null @@ -1,53 +0,0 @@ -package config - -import ( - "fmt" - "github.com/sirupsen/logrus" - "gorm.io/driver/mysql" - "gorm.io/driver/sqlite" - "gorm.io/gorm" - "gorm.io/gorm/logger" - "next-terminal/server/global" - "next-terminal/server/model" -) - -func SetupDB() *gorm.DB { - - var logMode logger.Interface - if global.Config.Debug { - logMode = logger.Default.LogMode(logger.Info) - } else { - logMode = logger.Default.LogMode(logger.Silent) - } - - fmt.Printf("当前数据库模式为:%v\n", global.Config.DB) - var err error - var db *gorm.DB - if global.Config.DB == "mysql" { - dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", - global.Config.Mysql.Username, - global.Config.Mysql.Password, - global.Config.Mysql.Hostname, - global.Config.Mysql.Port, - global.Config.Mysql.Database, - ) - db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ - Logger: logMode, - }) - } else { - db, err = gorm.Open(sqlite.Open(global.Config.Sqlite.File), &gorm.Config{ - Logger: logMode, - }) - } - - if err != nil { - logrus.WithError(err).Panic("连接数据库异常") - } - - if err := db.AutoMigrate(&model.User{}, &model.Asset{}, &model.AssetAttribute{}, &model.Session{}, &model.Command{}, - &model.Credential{}, &model.Property{}, &model.ResourceSharer{}, &model.UserGroup{}, &model.UserGroupMember{}, - &model.LoginLog{}, &model.Num{}, &model.Job{}, &model.JobLog{}, &model.AccessSecurity{}); err != nil { - logrus.WithError(err).Panic("初始化数据库表结构异常") - } - return db -} diff --git a/server/repository/access_security.go b/server/repository/access_security.go index 469675f..84e8a86 100644 --- a/server/repository/access_security.go +++ b/server/repository/access_security.go @@ -2,7 +2,6 @@ package repository import ( "gorm.io/gorm" - "next-terminal/server/global" "next-terminal/server/model" ) @@ -63,19 +62,19 @@ func (r AccessSecurityRepository) Find(pageIndex, pageSize int, ip, rule, order, } func (r AccessSecurityRepository) Create(o *model.AccessSecurity) error { - return global.DB.Create(o).Error + return r.DB.Create(o).Error } func (r AccessSecurityRepository) UpdateById(o *model.AccessSecurity, id string) error { o.ID = id - return global.DB.Updates(o).Error + return r.DB.Updates(o).Error } func (r AccessSecurityRepository) DeleteById(id string) error { - return global.DB.Where("id = ?", id).Delete(model.AccessSecurity{}).Error + return r.DB.Where("id = ?", id).Delete(model.AccessSecurity{}).Error } func (r AccessSecurityRepository) FindById(id string) (o *model.AccessSecurity, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error + err = r.DB.Where("id = ?", id).First(&o).Error return } diff --git a/server/repository/asset.go b/server/repository/asset.go index e0b383c..845b3eb 100644 --- a/server/repository/asset.go +++ b/server/repository/asset.go @@ -169,7 +169,7 @@ func (r AssetRepository) DeleteById(id string) error { return r.DB.Where("id = ?", id).Delete(&model.Asset{}).Error } -func (r AssetRepository) CountAsset() (total int64, err error) { +func (r AssetRepository) Count() (total int64, err error) { err = r.DB.Find(&model.Asset{}).Count(&total).Error return } diff --git a/server/repository/command.go b/server/repository/command.go index a47112c..2dd8b22 100644 --- a/server/repository/command.go +++ b/server/repository/command.go @@ -3,7 +3,6 @@ package repository import ( "gorm.io/gorm" "next-terminal/server/constant" - "next-terminal/server/global" "next-terminal/server/model" ) @@ -68,13 +67,13 @@ func (r CommandRepository) Create(o *model.Command) (err error) { } func (r CommandRepository) FindById(id string) (o model.Command, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error + err = r.DB.Where("id = ?", id).First(&o).Error return } -func (r CommandRepository) UpdateById(o *model.Command, id string) { +func (r CommandRepository) UpdateById(o *model.Command, id string) error { o.ID = id - global.DB.Updates(o) + return r.DB.Updates(o).Error } func (r CommandRepository) DeleteById(id string) error { diff --git a/server/repository/credential.go b/server/repository/credential.go index 25d5915..a5526db 100644 --- a/server/repository/credential.go +++ b/server/repository/credential.go @@ -15,7 +15,7 @@ func NewCredentialRepository(db *gorm.DB) *CredentialRepository { return credentialRepository } -func (r CredentialRepository) FindAllByUser(account model.User) (o []model.CredentialSimpleVo, err error) { +func (r CredentialRepository) FindByUser(account model.User) (o []model.CredentialSimpleVo, err error) { db := r.DB.Table("credentials").Select("DISTINCT credentials.id,credentials.name").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id") if account.Type == constant.TypeUser { db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", account.ID, account.ID) diff --git a/server/repository/job.go b/server/repository/job.go index 08086cb..518b3e5 100644 --- a/server/repository/job.go +++ b/server/repository/job.go @@ -2,7 +2,6 @@ package repository import ( "gorm.io/gorm" - "next-terminal/server/global" "next-terminal/server/model" "next-terminal/server/utils" ) @@ -64,19 +63,6 @@ func (r JobRepository) FindByFunc(function string) (o []model.Job, err error) { } func (r JobRepository) Create(o *model.Job) (err error) { - // - //if o.Status == constant.JobStatusRunning { - // j, err := getJob(o) - // if err != nil { - // return err - // } - // jobId, err := global.Cron.AddJob(o.Cron, j) - // if err != nil { - // return err - // } - // o.CronJobId = int(jobId) - //} - return r.DB.Create(o).Error } @@ -104,5 +90,5 @@ func (r JobRepository) DeleteJobById(id string) error { // return err // } //} - return global.DB.Where("id = ?", id).Delete(model.Job{}).Error + return r.DB.Where("id = ?", id).Delete(model.Job{}).Error } diff --git a/server/repository/num.go b/server/repository/num.go index 7309127..9792b89 100644 --- a/server/repository/num.go +++ b/server/repository/num.go @@ -2,7 +2,6 @@ package repository import ( "gorm.io/gorm" - "next-terminal/server/global" "next-terminal/server/model" ) @@ -21,6 +20,6 @@ func (r NumRepository) FindAll() (o []model.Num, err error) { } func (r NumRepository) Create(o *model.Num) (err error) { - err = global.DB.Create(o).Error + err = r.DB.Create(o).Error return } diff --git a/server/repository/property.go b/server/repository/property.go index d8320af..e77382e 100644 --- a/server/repository/property.go +++ b/server/repository/property.go @@ -1,11 +1,7 @@ package repository import ( - "github.com/jordan-wright/email" - "github.com/sirupsen/logrus" "gorm.io/gorm" - "net/smtp" - "next-terminal/server/constant" "next-terminal/server/guacd" "next-terminal/server/model" ) @@ -31,7 +27,7 @@ func (r PropertyRepository) Create(o *model.Property) (err error) { return } -func (r PropertyRepository) UpdatePropertyByName(o *model.Property, name string) error { +func (r PropertyRepository) UpdateByName(o *model.Property, name string) error { o.Name = name return r.DB.Updates(o).Error } @@ -65,26 +61,3 @@ func (r PropertyRepository) GetRecordingPath() (string, error) { } return property.Value, nil } - -func (r PropertyRepository) SendMail(to, subject, text string) { - propertiesMap := r.FindAllMap() - host := propertiesMap[constant.MailHost] - port := propertiesMap[constant.MailPort] - username := propertiesMap[constant.MailUsername] - password := propertiesMap[constant.MailPassword] - - if host == "" || port == "" || username == "" || password == "" { - logrus.Debugf("邮箱信息不完整,跳过发送邮件。") - return - } - - e := email.NewEmail() - e.From = "Next Terminal <" + username + ">" - e.To = []string{to} - e.Subject = subject - e.Text = []byte(text) - err := e.Send(host+":"+port, smtp.PlainAuth("", username, password, host)) - if err != nil { - logrus.Errorf("邮件发送失败: %v", err.Error()) - } -} diff --git a/server/repository/user_group.go b/server/repository/user_group.go index 64323c1..fc6afe3 100644 --- a/server/repository/user_group.go +++ b/server/repository/user_group.go @@ -2,7 +2,6 @@ package repository import ( "gorm.io/gorm" - "next-terminal/server/global" "next-terminal/server/model" "next-terminal/server/utils" ) @@ -56,7 +55,7 @@ func (r UserGroupRepository) Find(pageIndex, pageSize int, name, order, field st } func (r UserGroupRepository) FindById(id string) (o model.UserGroup, err error) { - err = global.DB.Where("id = ?", id).First(&o).Error + err = r.DB.Where("id = ?", id).First(&o).Error return } @@ -66,7 +65,7 @@ func (r UserGroupRepository) FindUserGroupIdsByUserId(userId string) (o []string return } -func (r UserGroupRepository) FindUserGroupMembersByUserGroupId(userGroupId string) (o []string, err error) { +func (r UserGroupRepository) FindMembersById(userGroupId string) (o []string, err error) { err = r.DB.Table("user_group_members").Select("user_id").Where("user_group_id = ?", userGroupId).Find(&o).Error return } @@ -112,9 +111,12 @@ func (r UserGroupRepository) Update(o *model.UserGroup, members []string, id str }) } -func (r UserGroupRepository) DeleteById(id string) { - r.DB.Where("id = ?", id).Delete(&model.UserGroup{}) - r.DB.Where("user_group_id = ?", id).Delete(&model.UserGroupMember{}) +func (r UserGroupRepository) DeleteById(id string) (err error) { + err = r.DB.Where("id = ?", id).Delete(&model.UserGroup{}).Error + if err != nil { + return err + } + return r.DB.Where("user_group_id = ?", id).Delete(&model.UserGroupMember{}).Error } func AddUserGroupMembers(tx *gorm.DB, userIds []string, userGroupId string) error { diff --git a/server/service/job.go b/server/service/job.go index 731f5a9..02827d6 100644 --- a/server/service/job.go +++ b/server/service/job.go @@ -6,7 +6,6 @@ import ( "fmt" "github.com/robfig/cron/v3" "github.com/sirupsen/logrus" - "next-terminal/server/api" "next-terminal/server/constant" "next-terminal/server/global" "next-terminal/server/model" @@ -28,7 +27,7 @@ func NewJobService(jobRepository *repository.JobRepository, jobLogRepository *re return &JobService{jobRepository: jobRepository, jobLogRepository: jobLogRepository, assetRepository: assetRepository, credentialRepository: credentialRepository} } -func (r JobService) ChangeJobStatusById(id, status string) error { +func (r JobService) ChangeStatusById(id, status string) error { job, err := r.jobRepository.FindById(id) if err != nil { return err @@ -259,7 +258,7 @@ func (r JobService) ExecJobById(id string) (err error) { func (r JobService) InitJob() error { jobs, _ := r.jobRepository.FindByFunc(constant.FuncCheckAssetStatusJob) - if jobs == nil || len(jobs) == 0 { + if jobs == nil { job := model.Job{ ID: utils.UUID(), Name: "资产状态检测", @@ -277,7 +276,7 @@ func (r JobService) InitJob() error { } else { for i := range jobs { if jobs[i].Status == constant.JobStatusRunning { - err := r.ChangeJobStatusById(jobs[i].ID, constant.JobStatusRunning) + err := r.ChangeStatusById(jobs[i].ID, constant.JobStatusRunning) if err != nil { return err } @@ -288,37 +287,32 @@ func (r JobService) InitJob() error { return nil } -// TODO 可能存在循环引用 -func (r UserService) ReloadToken() error { - loginLogs, err := r.loginLogRepository.FindAliveLoginLogs() +func (r JobService) Create(o *model.Job) (err error) { + + if o.Status == constant.JobStatusRunning { + j, err := getJob(o, &r) + if err != nil { + return err + } + jobId, err := global.Cron.AddJob(o.Cron, j) + if err != nil { + return err + } + o.CronJobId = int(jobId) + } + + return r.jobRepository.Create(o) +} + +func (r JobService) DeleteJobById(id string) error { + job, err := r.jobRepository.FindById(id) if err != nil { return err } - - for i := range loginLogs { - loginLog := loginLogs[i] - token := loginLog.ID - user, err := r.userRepository.FindById(loginLog.UserId) - if err != nil { - logrus.Debugf("用户「%v」获取失败,忽略", loginLog.UserId) - continue + if job.Status == constant.JobStatusRunning { + if err := r.ChangeStatusById(id, constant.JobStatusNotRunning); err != nil { + return err } - - authorization := api.Authorization{ - Token: token, - Remember: loginLog.Remember, - User: user, - } - - cacheKey := api.BuildCacheKeyByToken(token) - - if authorization.Remember { - // 记住登录有效期两周 - global.Cache.Set(cacheKey, authorization, api.RememberEffectiveTime) - } else { - global.Cache.Set(cacheKey, authorization, api.NotRememberEffectiveTime) - } - logrus.Debugf("重新加载用户「%v」授权Token「%v」到缓存", user.Nickname, token) } - return nil + return r.jobRepository.DeleteJobById(id) } diff --git a/server/service/mail.go b/server/service/mail.go new file mode 100644 index 0000000..b0fc156 --- /dev/null +++ b/server/service/mail.go @@ -0,0 +1,40 @@ +package service + +import ( + "github.com/jordan-wright/email" + "github.com/sirupsen/logrus" + "net/smtp" + "next-terminal/server/constant" + "next-terminal/server/repository" +) + +type MailService struct { + propertyRepository *repository.PropertyRepository +} + +func NewMailService(propertyRepository *repository.PropertyRepository) *MailService { + return &MailService{propertyRepository: propertyRepository} +} + +func (r MailService) SendMail(to, subject, text string) { + propertiesMap := r.propertyRepository.FindAllMap() + host := propertiesMap[constant.MailHost] + port := propertiesMap[constant.MailPort] + username := propertiesMap[constant.MailUsername] + password := propertiesMap[constant.MailPassword] + + if host == "" || port == "" || username == "" || password == "" { + logrus.Debugf("邮箱信息不完整,跳过发送邮件。") + return + } + + e := email.NewEmail() + e.From = "Next Terminal <" + username + ">" + e.To = []string{to} + e.Subject = subject + e.Text = []byte(text) + err := e.Send(host+":"+port, smtp.PlainAuth("", username, password, host)) + if err != nil { + logrus.Errorf("邮件发送失败: %v", err.Error()) + } +} diff --git a/server/service/user.go b/server/service/user.go index 25a4ed1..43b03d9 100644 --- a/server/service/user.go +++ b/server/service/user.go @@ -13,8 +13,8 @@ type UserService struct { loginLogRepository *repository.LoginLogRepository } -func NewUserService(userRepository *repository.UserRepository) *UserService { - return &UserService{userRepository: userRepository} +func NewUserService(userRepository *repository.UserRepository, loginLogRepository *repository.LoginLogRepository) *UserService { + return &UserService{userRepository: userRepository, loginLogRepository: loginLogRepository} } func (r UserService) InitUser() (err error) { diff --git a/web/src/components/asset/Asset.js b/web/src/components/asset/Asset.js index fb1e328..4da5233 100644 --- a/web/src/components/asset/Asset.js +++ b/web/src/components/asset/Asset.js @@ -13,7 +13,6 @@ import { Menu, Modal, notification, - PageHeader, Row, Select, Space, @@ -27,7 +26,7 @@ import qs from "qs"; import AssetModal from "./AssetModal"; import request from "../../common/request"; import {message} from "antd/es"; -import {getHeaders, isEmpty, itemRender} from "../../utils/utils"; +import {getHeaders, isEmpty} from "../../utils/utils"; import dayjs from 'dayjs'; import { DeleteOutlined, @@ -881,7 +880,7 @@ class Asset extends Component {