feat: 添加数据库资产、命令拦截器、授权资产等功能,修复GitHub Actions工作流

This commit is contained in:
2026-04-18 07:44:18 +08:00
parent 6e2e2f9387
commit 3c217ab039
64 changed files with 3308 additions and 760 deletions
@@ -9,11 +9,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Private Actions Checkout - name: Private Actions Checkout
uses: actions/checkout@v2.3.4 uses: actions/checkout@v4
- name: Docker Setup QEMU - name: Docker Setup QEMU
uses: docker/setup-qemu-action@v1.2.0 uses: docker/setup-qemu-action@v3
- name: Docker Setup Buildx - name: Docker Setup Buildx
uses: docker/setup-buildx-action@v1.6.0 uses: docker/setup-buildx-action@v3
- name: Get resources - name: Get resources
run: | run: |
rm -rf * rm -rf *
@@ -22,15 +22,15 @@ jobs:
mv /tmp/guacamole-server-master/* . mv /tmp/guacamole-server-master/* .
- name: Docker Login - name: Docker Login
uses: docker/login-action@v1.10.0 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }} password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build and push Docker images - name: Build and push Docker images
uses: docker/build-push-action@v2.7.0 uses: docker/build-push-action@v6
with: with:
context: . context: .
platforms: linux/arm64,linux/arm/v7,linux/amd64 platforms: linux/arm64,linux/amd64
push: true push: true
tags: | tags: |
${{ secrets.DOCKERHUB_USERNAME }}/guacamole-server:latest ${{ secrets.DOCKERHUB_USERNAME }}/guacamole-server:latest
+7 -7
View File
@@ -9,28 +9,28 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Private Actions Checkout - name: Private Actions Checkout
uses: actions/checkout@v2.3.4 uses: actions/checkout@v4
- name: Docker Setup QEMU - name: Docker Setup QEMU
uses: docker/setup-qemu-action@v1.2.0 uses: docker/setup-qemu-action@v3
- name: Docker Setup Buildx - name: Docker Setup Buildx
uses: docker/setup-buildx-action@v1.6.0 uses: docker/setup-buildx-action@v3
- name: Docker Login - name: Docker Login
uses: docker/login-action@v1.10.0 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }} password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Docker Aliyun Login - name: Docker Aliyun Login
uses: docker/login-action@v1.10.0 uses: docker/login-action@v3
with: with:
registry: registry.cn-beijing.aliyuncs.com registry: registry.cn-beijing.aliyuncs.com
username: ${{ secrets.ALI_USERNAME }} username: ${{ secrets.ALI_USERNAME }}
password: ${{ secrets.ALI_PASSWORD }} password: ${{ secrets.ALI_PASSWORD }}
- name: Build and push Docker images - name: Build and push Docker images
uses: docker/build-push-action@v2.7.0 uses: docker/build-push-action@v6
with: with:
context: . context: .
platforms: linux/arm64,linux/arm/v7,linux/amd64 platforms: linux/arm64,linux/amd64
file: guacd/Dockerfile file: guacd/Dockerfile
push: true push: true
tags: | tags: |
+12 -12
View File
@@ -13,17 +13,17 @@ jobs:
steps: steps:
- name: Get version - name: Get version
id: get_version id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Private Actions Checkout - name: Private Actions Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Docker Setup QEMU - name: Docker Setup QEMU
uses: docker/setup-qemu-action@v1.2.0 uses: docker/setup-qemu-action@v3
- name: Docker Setup Buildx - name: Docker Setup Buildx
uses: docker/setup-buildx-action@v1.6.0 uses: docker/setup-buildx-action@v3
- name: node Setup - name: node Setup
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with: with:
node-version: '16' node-version: '20'
- name: npm install - name: npm install
run: | run: |
cd web cd web
@@ -31,21 +31,21 @@ jobs:
yarn yarn
yarn build yarn build
- name: Docker Login - name: Docker Login
uses: docker/login-action@v1.10.0 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }} password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Docker Aliyun Login - name: Docker Aliyun Login
uses: docker/login-action@v1.10.0 uses: docker/login-action@v3
with: with:
registry: registry.cn-beijing.aliyuncs.com registry: registry.cn-beijing.aliyuncs.com
username: ${{ secrets.ALI_USERNAME }} username: ${{ secrets.ALI_USERNAME }}
password: ${{ secrets.ALI_PASSWORD }} password: ${{ secrets.ALI_PASSWORD }}
- name: Build and push Docker images - name: Build and push Docker images
uses: docker/build-push-action@v2.7.0 uses: docker/build-push-action@v6
with: with:
context: . context: .
platforms: linux/arm64,linux/arm/v7,linux/amd64 platforms: linux/arm64,linux/amd64
file: Dockerfile file: Dockerfile
push: true push: true
tags: | tags: |
@@ -62,9 +62,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Private Actions Checkout - name: Private Actions Checkout
uses: actions/checkout@v2.3.4 uses: actions/checkout@v4
- name: Docker Hub Description - name: Docker Hub Description
uses: peter-evans/dockerhub-description@v2 uses: peter-evans/dockerhub-description@v4
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }} password: ${{ secrets.DOCKERHUB_PASSWORD }}
+8 -5
View File
@@ -13,10 +13,13 @@ jobs:
name: lint name: lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: golangci-lint - uses: actions/setup-go@v5
uses: golangci/golangci-lint-action@v2
with: with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version go-version: '1.22'
version: v1.42.1 - name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest
args: --timeout=5m args: --timeout=5m
working-directory: server
+7 -7
View File
@@ -13,22 +13,22 @@ jobs:
steps: steps:
- name: Get version - name: Get version
id: get_version id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Private Actions Checkout - name: Private Actions Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: node Setup - name: node Setup
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with: with:
node-version: '16' node-version: '20'
- name: npm install - name: npm install
run: | run: |
cd web cd web
npm install --global yarn npm install --global yarn
yarn yarn
- name: go Setup - name: go Setup
uses: actions/setup-go@v4 uses: actions/setup-go@v5
with: with:
go-version: '1.20' go-version: '1.22'
- name: Build package Linux - name: Build package Linux
run: | run: |
sh build.sh sh build.sh
@@ -39,7 +39,7 @@ jobs:
cp LICENSE next-terminal/ cp LICENSE next-terminal/
tar zcvf next-terminal.tar.gz next-terminal/ tar zcvf next-terminal.tar.gz next-terminal/
- name: release - name: release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
with: with:
files: next-terminal.tar.gz files: next-terminal.tar.gz
env: env:
-3
View File
@@ -5,7 +5,6 @@ linters-settings:
linters: linters:
disable-all: true disable-all: true
enable: enable:
- deadcode
- errcheck - errcheck
- gofmt - gofmt
- goimports - goimports
@@ -13,10 +12,8 @@ linters:
- govet - govet
- ineffassign - ineffassign
- staticcheck - staticcheck
- structcheck
- typecheck - typecheck
- unused - unused
- varcheck
run: run:
skip-files: skip-files:
+18 -3
View File
@@ -122,9 +122,14 @@ func (api AccountApi) LoginEndpoint(c echo.Context) error {
Username: user.Username, Username: user.Username,
Nickname: user.Nickname, Nickname: user.Nickname,
Type: user.Type, Type: user.Type,
EnableTotp: user.TOTPSecret != "" && user.TOTPSecret != "-", EnabledTotp: user.TOTPSecret != "" && user.TOTPSecret != "-",
MfaEnabled: user.TOTPSecret != "" && user.TOTPSecret != "-",
Roles: user.Roles, Roles: user.Roles,
Menus: menus, Menus: menus,
Language: "zh-CN",
ForceTotpEnabled: false,
NeedChangePassword: false,
Dev: config.GlobalCfg.Debug,
} }
return Success(c, maps.Map{ return Success(c, maps.Map{
@@ -273,9 +278,14 @@ type AccountInfo struct {
Username string `json:"username"` Username string `json:"username"`
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
Type string `json:"type"` Type string `json:"type"`
EnableTotp bool `json:"enableTotp"` EnabledTotp bool `json:"enabledTotp"`
MfaEnabled bool `json:"mfaEnabled"`
Roles []string `json:"roles"` Roles []string `json:"roles"`
Menus []UserMenu `json:"menus"` Menus []UserMenu `json:"menus"`
Language string `json:"language"`
ForceTotpEnabled bool `json:"forceTotpEnabled"`
NeedChangePassword bool `json:"needChangePassword"`
Dev bool `json:"dev"`
} }
type UserMenu struct { type UserMenu struct {
@@ -321,9 +331,14 @@ func (api AccountApi) InfoEndpoint(c echo.Context) error {
Username: user.Username, Username: user.Username,
Nickname: user.Nickname, Nickname: user.Nickname,
Type: user.Type, Type: user.Type,
EnableTotp: user.TOTPSecret != "" && user.TOTPSecret != "-", EnabledTotp: user.TOTPSecret != "" && user.TOTPSecret != "-",
MfaEnabled: user.TOTPSecret != "" && user.TOTPSecret != "-",
Roles: user.Roles, Roles: user.Roles,
Menus: menus, Menus: menus,
Language: "zh-CN",
ForceTotpEnabled: false,
NeedChangePassword: false,
Dev: config.GlobalCfg.Debug,
} }
return Success(c, info) return Success(c, info)
} }
+36 -17
View File
@@ -32,9 +32,15 @@ func (api AssetGroupApi) GroupsSetEndpoint(c echo.Context) error {
ctx := context.TODO() ctx := context.TODO()
repository.AssetGroupRepository.DeleteByParentId(ctx, "") repository.AssetGroupRepository.DeleteByParentId(ctx, "")
for i, item := range req { for i, item := range req {
name := ""
if v, ok := item["name"].(string); ok {
name = v
} else if v, ok := item["title"].(string); ok {
name = v
}
group := model.AssetGroup{ group := model.AssetGroup{
ID: utils.UUID(), ID: utils.UUID(),
Name: item["name"].(string), Name: name,
ParentId: "", ParentId: "",
Sort: i, Sort: i,
Created: time.Now().UnixMilli(), Created: time.Now().UnixMilli(),
@@ -58,9 +64,15 @@ func (api AssetGroupApi) GroupsDeleteEndpoint(c echo.Context) error {
func saveChildren(ctx context.Context, children []interface{}, parentId string) { func saveChildren(ctx context.Context, children []interface{}, parentId string) {
for i, item := range children { for i, item := range children {
m := item.(map[string]interface{}) m := item.(map[string]interface{})
name := ""
if v, ok := m["name"].(string); ok {
name = v
} else if v, ok := m["title"].(string); ok {
name = v
}
group := model.AssetGroup{ group := model.AssetGroup{
ID: utils.UUID(), ID: utils.UUID(),
Name: m["name"].(string), Name: name,
ParentId: parentId, ParentId: parentId,
Sort: i, Sort: i,
Created: time.Now().UnixMilli(), Created: time.Now().UnixMilli(),
@@ -109,27 +121,15 @@ func (api AssetGroupApi) TreeEndpoint(c echo.Context) error {
func buildAssetTree(assets []model.Asset, groups []model.AssetGroup, groupId string) []maps.Map { func buildAssetTree(assets []model.Asset, groups []model.AssetGroup, groupId string) []maps.Map {
var nodes []maps.Map var nodes []maps.Map
var groupAssets []model.Asset
for _, a := range assets {
groupAssets = append(groupAssets, a)
}
for _, a := range groupAssets {
nodes = append(nodes, maps.Map{
"id": a.ID,
"name": a.Name,
"key": a.ID,
"isLeaf": true,
"protocol": a.Protocol,
"ip": a.IP,
"port": a.Port,
})
}
for _, g := range groups { for _, g := range groups {
if g.ParentId == groupId { if g.ParentId == groupId {
node := maps.Map{ node := maps.Map{
"id": g.ID, "id": g.ID,
"name": g.Name, "name": g.Name,
"key": g.ID, "key": g.ID,
"title": g.Name,
"value": g.ID,
} }
children := buildAssetTree(assets, groups, g.ID) children := buildAssetTree(assets, groups, g.ID)
if len(children) > 0 { if len(children) > 0 {
@@ -138,6 +138,25 @@ func buildAssetTree(assets []model.Asset, groups []model.AssetGroup, groupId str
nodes = append(nodes, node) nodes = append(nodes, node)
} }
} }
if groupId == "" {
for _, a := range assets {
nodes = append(nodes, maps.Map{
"id": a.ID,
"name": a.Name,
"key": a.ID,
"title": a.Name,
"value": a.ID,
"isLeaf": true,
"protocol": a.Protocol,
"ip": a.IP,
"port": a.Port,
"extra": maps.Map{
"network": a.IP + ":" + strconv.Itoa(a.Port),
},
})
}
}
return nodes return nodes
} }
+56 -5
View File
@@ -2,8 +2,12 @@ package api
import ( import (
"context" "context"
"crypto/rand"
"crypto/rsa"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix"
"encoding/pem" "encoding/pem"
"math/big"
"strconv" "strconv"
"time" "time"
@@ -65,6 +69,43 @@ func parseCertificate(certPEM string) (commonName, subject, issuer string, notBe
return commonName, subject, issuer, notBefore, notAfter, nil return commonName, subject, issuer, notBefore, notAfter, nil
} }
func generateSelfSignedCertificate(commonName string) (certPEM, keyPEM string, notBefore, notAfter time.Time, err error) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return "", "", time.Time{}, time.Time{}, err
}
notBefore = time.Now()
notAfter = notBefore.Add(365 * 24 * time.Hour)
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return "", "", time.Time{}, time.Time{}, err
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: commonName,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return "", "", time.Time{}, time.Time{}, err
}
certPEM = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}))
keyPEM = string(pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}))
return certPEM, keyPEM, notBefore, notAfter, nil
}
func (api CertificateApi) CreateEndpoint(c echo.Context) error { func (api CertificateApi) CreateEndpoint(c echo.Context) error {
var req struct { var req struct {
CommonName string `json:"commonName"` CommonName string `json:"commonName"`
@@ -89,7 +130,21 @@ func (api CertificateApi) CreateEndpoint(c echo.Context) error {
UpdatedAt: common.NowJsonTime(), UpdatedAt: common.NowJsonTime(),
} }
if req.Certificate != "" { if item.Type == "" {
item.Type = "imported"
}
if req.Type == "self-signed" && req.Certificate == "" {
certPEM, keyPEM, notBefore, notAfter, err := generateSelfSignedCertificate(req.CommonName)
if err == nil {
item.Certificate = certPEM
item.PrivateKey = keyPEM
item.NotBefore = common.NewJsonTime(notBefore)
item.NotAfter = common.NewJsonTime(notAfter)
item.Subject = "CN=" + req.CommonName
item.Issuer = "CN=" + req.CommonName
}
} else if req.Certificate != "" {
commonName, subject, issuer, notBefore, notAfter, err := parseCertificate(req.Certificate) commonName, subject, issuer, notBefore, notAfter, err := parseCertificate(req.Certificate)
if err == nil { if err == nil {
if item.CommonName == "" { if item.CommonName == "" {
@@ -102,10 +157,6 @@ func (api CertificateApi) CreateEndpoint(c echo.Context) error {
} }
} }
if item.Type == "" {
item.Type = "imported"
}
if err := repository.CertificateRepository.Create(context.TODO(), item); err != nil { if err := repository.CertificateRepository.Create(context.TODO(), item); err != nil {
return err return err
} }
+114 -18
View File
@@ -1,7 +1,12 @@
package api package api
import ( import (
"context"
"strconv"
"next-terminal/server/common/maps" "next-terminal/server/common/maps"
"next-terminal/server/model"
"next-terminal/server/repository"
"next-terminal/server/utils" "next-terminal/server/utils"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@@ -10,59 +15,150 @@ import (
type CommandFilterApi struct{} type CommandFilterApi struct{}
func (api CommandFilterApi) AllEndpoint(c echo.Context) error { func (api CommandFilterApi) AllEndpoint(c echo.Context) error {
return Success(c, []interface{}{}) items, err := repository.CommandFilterRepository.FindAll(context.TODO())
if err != nil {
return err
}
return Success(c, items)
} }
func (api CommandFilterApi) PagingEndpoint(c echo.Context) error { func (api CommandFilterApi) PagingEndpoint(c echo.Context) error {
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
name := c.QueryParam("name")
order := c.QueryParam("order")
field := c.QueryParam("field")
items, total, err := repository.CommandFilterRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field)
if err != nil {
return err
}
return Success(c, maps.Map{ return Success(c, maps.Map{
"items": []interface{}{}, "items": items,
"total": 0, "total": total,
}) })
} }
func (api CommandFilterApi) CreateEndpoint(c echo.Context) error { func (api CommandFilterApi) CreateEndpoint(c echo.Context) error {
var item map[string]interface{} var item model.CommandFilter
if err := c.Bind(&item); err != nil { if err := c.Bind(&item); err != nil {
return err return err
} }
item["id"] = utils.LongUUID() item.ID = utils.UUID()
if err := repository.CommandFilterRepository.Create(context.TODO(), &item); err != nil {
return err
}
return Success(c, item) return Success(c, item)
} }
func (api CommandFilterApi) UpdateEndpoint(c echo.Context) error { func (api CommandFilterApi) UpdateEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
var item map[string]interface{} var item model.CommandFilter
if err := c.Bind(&item); err != nil { if err := c.Bind(&item); err != nil {
return err return err
} }
item["id"] = id item.ID = id
if err := repository.CommandFilterRepository.UpdateById(context.TODO(), &item); err != nil {
return err
}
return Success(c, item) return Success(c, item)
} }
func (api CommandFilterApi) DeleteEndpoint(c echo.Context) error { func (api CommandFilterApi) DeleteEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
_ = id if err := repository.CommandFilterRepository.DeleteById(context.TODO(), id); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
func (api CommandFilterApi) GetEndpoint(c echo.Context) error { func (api CommandFilterApi) GetEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
_ = id item, err := repository.CommandFilterRepository.FindById(context.TODO(), id)
return Success(c, maps.Map{ if err != nil {
"id": id, return err
"name": "Default Filter", }
"createdAt": 1700000000000, return Success(c, item)
})
} }
func (api CommandFilterApi) BindEndpoint(c echo.Context) error { func (api CommandFilterApi) BindEndpoint(c echo.Context) error {
id := c.Param("id")
_ = id
return Success(c, nil) return Success(c, nil)
} }
func (api CommandFilterApi) UnbindEndpoint(c echo.Context) error { func (api CommandFilterApi) UnbindEndpoint(c echo.Context) error {
id := c.Param("id")
_ = id
return Success(c, nil) return Success(c, nil)
} }
type CommandFilterRuleApi struct{}
func (api CommandFilterRuleApi) AllEndpoint(c echo.Context) error {
commandFilterId := c.QueryParam("commandFilterId")
items, err := repository.CommandFilterRuleRepository.FindByCommandFilterId(context.TODO(), commandFilterId)
if err != nil {
return err
}
return Success(c, items)
}
func (api CommandFilterRuleApi) PagingEndpoint(c echo.Context) error {
commandFilterId := c.QueryParam("commandFilterId")
items, err := repository.CommandFilterRuleRepository.FindByCommandFilterId(context.TODO(), commandFilterId)
if err != nil {
return err
}
return Success(c, maps.Map{
"items": items,
"total": len(items),
})
}
func (api CommandFilterRuleApi) CreateEndpoint(c echo.Context) error {
var item model.CommandFilterRule
if err := c.Bind(&item); err != nil {
return err
}
item.ID = utils.UUID()
if err := repository.CommandFilterRuleRepository.Create(context.TODO(), &item); err != nil {
return err
}
return Success(c, item)
}
func (api CommandFilterRuleApi) UpdateEndpoint(c echo.Context) error {
id := c.Param("id")
var item model.CommandFilterRule
if err := c.Bind(&item); err != nil {
return err
}
item.ID = id
if err := repository.CommandFilterRuleRepository.UpdateById(context.TODO(), &item); err != nil {
return err
}
return Success(c, item)
}
func (api CommandFilterRuleApi) DeleteEndpoint(c echo.Context) error {
id := c.Param("id")
if err := repository.CommandFilterRuleRepository.DeleteById(context.TODO(), id); err != nil {
return err
}
return Success(c, nil)
}
func (api CommandFilterRuleApi) GetEndpoint(c echo.Context) error {
id := c.Param("id")
item, err := repository.CommandFilterRuleRepository.FindById(context.TODO(), id)
if err != nil {
return err
}
return Success(c, item)
}
+3 -1
View File
@@ -19,6 +19,7 @@ func (api DashboardApi) GetTimeCounterEndpoint(c echo.Context) error {
totalAsset int64 totalAsset int64
activeAsset int64 activeAsset int64
failLoginCount int64 failLoginCount int64
totalWebsite int64
) )
totalUser, _ = repository.UserRepository.Count(context.TODO()) totalUser, _ = repository.UserRepository.Count(context.TODO())
@@ -29,6 +30,7 @@ func (api DashboardApi) GetTimeCounterEndpoint(c echo.Context) error {
failLoginCount, _ = repository.LoginLogRepository.CountByState(context.TODO(), "0") failLoginCount, _ = repository.LoginLogRepository.CountByState(context.TODO(), "0")
gatewayList, _ := repository.GatewayRepository.FindAll(context.TODO()) gatewayList, _ := repository.GatewayRepository.FindAll(context.TODO())
gatewayActiveCount := int64(len(gatewayList)) gatewayActiveCount := int64(len(gatewayList))
totalWebsite, _ = repository.WebsiteRepository.Count(context.TODO())
counter := map[string]interface{}{ counter := map[string]interface{}{
"loginFailedTimes": failLoginCount, "loginFailedTimes": failLoginCount,
@@ -39,7 +41,7 @@ func (api DashboardApi) GetTimeCounterEndpoint(c echo.Context) error {
"assetActiveCount": activeAsset, "assetActiveCount": activeAsset,
"assetTotalCount": totalAsset, "assetTotalCount": totalAsset,
"websiteActiveCount": 0, "websiteActiveCount": 0,
"websiteTotalCount": 0, "websiteTotalCount": totalWebsite,
"gatewayActiveCount": gatewayActiveCount, "gatewayActiveCount": gatewayActiveCount,
"gatewayTotalCount": gatewayActiveCount, "gatewayTotalCount": gatewayActiveCount,
} }
+131 -28
View File
@@ -1,7 +1,14 @@
package api package api
import ( import (
"context"
"strconv"
"strings"
"next-terminal/server/common"
"next-terminal/server/common/maps" "next-terminal/server/common/maps"
"next-terminal/server/model"
"next-terminal/server/repository"
"next-terminal/server/utils" "next-terminal/server/utils"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@@ -10,65 +17,161 @@ import (
type DatabaseAssetApi struct{} type DatabaseAssetApi struct{}
func (api DatabaseAssetApi) AllEndpoint(c echo.Context) error { func (api DatabaseAssetApi) AllEndpoint(c echo.Context) error {
return Success(c, []interface{}{}) dbType := c.QueryParam("type")
items, err := repository.DatabaseAssetRepository.FindByType(context.TODO(), dbType)
if err != nil {
return err
}
return Success(c, items)
} }
func (api DatabaseAssetApi) PagingEndpoint(c echo.Context) error { func (api DatabaseAssetApi) PagingEndpoint(c echo.Context) error {
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
name := c.QueryParam("keyword")
dbType := c.QueryParam("type")
order := c.QueryParam("order")
field := c.QueryParam("field")
items, total, err := repository.DatabaseAssetRepository.Find(context.TODO(), pageIndex, pageSize, name, dbType, order, field)
if err != nil {
return err
}
return Success(c, maps.Map{ return Success(c, maps.Map{
"items": []interface{}{}, "items": items,
"total": 0, "total": total,
}) })
} }
func (api DatabaseAssetApi) CreateEndpoint(c echo.Context) error { func (api DatabaseAssetApi) CreateEndpoint(c echo.Context) error {
var item map[string]interface{} var req struct {
if err := c.Bind(&item); err != nil { Name string `json:"name"`
Type string `json:"type"`
Host string `json:"host"`
Port int `json:"port"`
Database string `json:"database"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
GatewayType string `json:"gatewayType"`
GatewayId string `json:"gatewayId"`
Tags []string `json:"tags"`
}
if err := c.Bind(&req); err != nil {
return err return err
} }
item["id"] = utils.LongUUID()
account, _ := GetCurrentAccount(c)
item := &model.DatabaseAsset{
ID: utils.UUID(),
Name: req.Name,
Type: req.Type,
Host: req.Host,
Port: req.Port,
Database: req.Database,
Username: req.Username,
Password: req.Password,
Description: req.Description,
GatewayType: req.GatewayType,
GatewayId: req.GatewayId,
Tags: strings.Join(req.Tags, ","),
Owner: account.ID,
Created: common.NowJsonTime(),
Updated: common.NowJsonTime(),
}
if item.Port == 0 {
item.Port = repository.DatabaseAssetRepository.GetDBPort(item.Type)
}
if err := repository.DatabaseAssetRepository.Create(context.TODO(), item); err != nil {
return err
}
return Success(c, item) return Success(c, item)
} }
func (api DatabaseAssetApi) UpdateEndpoint(c echo.Context) error { func (api DatabaseAssetApi) UpdateEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
var item map[string]interface{} var req struct {
if err := c.Bind(&item); err != nil { Name string `json:"name"`
Type string `json:"type"`
Host string `json:"host"`
Port int `json:"port"`
Database string `json:"database"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
GatewayType string `json:"gatewayType"`
GatewayId string `json:"gatewayId"`
Tags []string `json:"tags"`
}
if err := c.Bind(&req); err != nil {
return err return err
} }
item["id"] = id
return Success(c, item) existing, err := repository.DatabaseAssetRepository.FindById(context.TODO(), id)
if err != nil {
return err
}
existing.Name = req.Name
existing.Type = req.Type
existing.Host = req.Host
existing.Port = req.Port
existing.Database = req.Database
existing.Username = req.Username
if req.Password != "" {
existing.Password = req.Password
}
existing.Description = req.Description
existing.GatewayType = req.GatewayType
existing.GatewayId = req.GatewayId
existing.Tags = strings.Join(req.Tags, ",")
existing.Updated = common.NowJsonTime()
if err := repository.DatabaseAssetRepository.UpdateById(context.TODO(), &existing); err != nil {
return err
}
return Success(c, existing)
} }
func (api DatabaseAssetApi) DeleteEndpoint(c echo.Context) error { func (api DatabaseAssetApi) DeleteEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
_ = id if err := repository.DatabaseAssetRepository.DeleteById(context.TODO(), id); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
func (api DatabaseAssetApi) GetEndpoint(c echo.Context) error { func (api DatabaseAssetApi) GetEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
_ = id item, err := repository.DatabaseAssetRepository.FindById(context.TODO(), id)
return Success(c, maps.Map{ if err != nil {
"id": id, return err
"name": "MySQL Server", }
"type": "mysql", return Success(c, item)
"host": "localhost",
"port": 3306,
"database": "test_db",
"username": "root",
"description": "Test database",
"status": "active",
"statusText": "Active",
"createdAt": 1700000000000,
"updatedAt": 1700000000000,
})
} }
func (api DatabaseAssetApi) DecryptEndpoint(c echo.Context) error { func (api DatabaseAssetApi) DecryptEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
_ = id item, err := repository.DatabaseAssetRepository.FindById(context.TODO(), id)
if err != nil {
return err
}
return Success(c, maps.Map{ return Success(c, maps.Map{
"id": id, "id": id,
"password": "decrypted_password", "password": item.Password,
}) })
} }
func (api DatabaseAssetApi) TagsEndpoint(c echo.Context) error {
tags, err := repository.DatabaseAssetRepository.FindAllTags(context.TODO())
if err != nil {
return err
}
return Success(c, tags)
}
+103 -25
View File
@@ -1,7 +1,12 @@
package api package api
import ( import (
"context"
"strconv"
"next-terminal/server/common/maps" "next-terminal/server/common/maps"
"next-terminal/server/model"
"next-terminal/server/repository"
"next-terminal/server/utils" "next-terminal/server/utils"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@@ -10,77 +15,150 @@ import (
type DepartmentApi struct{} type DepartmentApi struct{}
func (api DepartmentApi) AllEndpoint(c echo.Context) error { func (api DepartmentApi) AllEndpoint(c echo.Context) error {
return Success(c, []interface{}{}) items, err := repository.DepartmentRepository.FindAll(context.TODO())
if err != nil {
return err
}
return Success(c, items)
} }
func (api DepartmentApi) PagingEndpoint(c echo.Context) error { func (api DepartmentApi) PagingEndpoint(c echo.Context) error {
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
name := c.QueryParam("keyword")
order := c.QueryParam("order")
field := c.QueryParam("field")
items, total, err := repository.DepartmentRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field)
if err != nil {
return err
}
departmentMap := make(map[string]model.Department)
allDepts, _ := repository.DepartmentRepository.FindAll(context.TODO())
for _, dept := range allDepts {
departmentMap[dept.ID] = dept
}
for i := range items {
if items[i].ParentId != "" {
if parent, ok := departmentMap[items[i].ParentId]; ok {
items[i].ParentName = parent.Name
}
}
userCount, _ := repository.UserDepartmentRepository.CountByDepartmentId(context.TODO(), items[i].ID)
items[i].UserCount = userCount
}
return Success(c, maps.Map{ return Success(c, maps.Map{
"items": []interface{}{}, "items": items,
"total": 0, "total": total,
}) })
} }
func (api DepartmentApi) CreateEndpoint(c echo.Context) error { func (api DepartmentApi) CreateEndpoint(c echo.Context) error {
var item map[string]interface{} var item model.Department
if err := c.Bind(&item); err != nil { if err := c.Bind(&item); err != nil {
return err return err
} }
item["id"] = utils.LongUUID() item.ID = utils.UUID()
if err := repository.DepartmentRepository.Create(context.TODO(), &item); err != nil {
return err
}
return Success(c, item) return Success(c, item)
} }
func (api DepartmentApi) UpdateEndpoint(c echo.Context) error { func (api DepartmentApi) UpdateEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
var item map[string]interface{} var item model.Department
if err := c.Bind(&item); err != nil { if err := c.Bind(&item); err != nil {
return err return err
} }
item["id"] = id item.ID = id
if err := repository.DepartmentRepository.UpdateById(context.TODO(), &item); err != nil {
return err
}
return Success(c, item) return Success(c, item)
} }
func (api DepartmentApi) DeleteEndpoint(c echo.Context) error { func (api DepartmentApi) DeleteEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
_ = id
count, err := repository.DepartmentRepository.CountByParentId(context.TODO(), id)
if err != nil {
return err
}
if count > 0 {
return Fail(c, -1, "该部门下存在子部门,无法删除")
}
userCount, err := repository.UserDepartmentRepository.CountByDepartmentId(context.TODO(), id)
if err != nil {
return err
}
if userCount > 0 {
return Fail(c, -1, "该部门下存在用户,无法删除")
}
if err := repository.DepartmentRepository.DeleteById(context.TODO(), id); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
func (api DepartmentApi) GetEndpoint(c echo.Context) error { func (api DepartmentApi) GetEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
_ = id item, err := repository.DepartmentRepository.FindById(context.TODO(), id)
return Success(c, maps.Map{ if err != nil {
"id": id, return err
"name": "Default", }
"parentId": "", return Success(c, item)
})
} }
func (api DepartmentApi) GetTreeEndpoint(c echo.Context) error { func (api DepartmentApi) GetTreeEndpoint(c echo.Context) error {
tree := []map[string]interface{}{ departments, err := repository.DepartmentRepository.FindAll(context.TODO())
{ if err != nil {
"title": "Default", return err
"key": "default",
"value": "default",
"children": []interface{}{},
},
} }
tree := repository.DepartmentRepository.BuildTree(departments)
return Success(c, tree) return Success(c, tree)
} }
func (api DepartmentApi) GetDepartmentUsersEndpoint(c echo.Context) error { func (api DepartmentApi) GetDepartmentUsersEndpoint(c echo.Context) error {
departmentId := c.Param("id") departmentId := c.Param("id")
_ = departmentId users, err := repository.UserDepartmentRepository.FindUsersByDepartmentId(context.TODO(), departmentId)
return Success(c, []string{}) if err != nil {
return err
}
return Success(c, users)
} }
func (api DepartmentApi) SetDepartmentUsersEndpoint(c echo.Context) error { func (api DepartmentApi) SetDepartmentUsersEndpoint(c echo.Context) error {
departmentId := c.Param("id") departmentId := c.Param("id")
_ = departmentId var userIds []string
if err := c.Bind(&userIds); err != nil {
return err
}
if err := repository.UserDepartmentRepository.SaveDepartmentUsers(context.TODO(), departmentId, userIds); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
func (api DepartmentApi) RemoveUsersFromDepartmentEndpoint(c echo.Context) error { func (api DepartmentApi) RemoveUsersFromDepartmentEndpoint(c echo.Context) error {
departmentId := c.Param("id") departmentId := c.Param("id")
_ = departmentId var userIds []string
if err := c.Bind(&userIds); err != nil {
return err
}
if err := repository.UserDepartmentRepository.RemoveUsersFromDepartment(context.TODO(), departmentId, userIds); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
+4
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"strconv" "strconv"
"next-terminal/server/common"
"next-terminal/server/common/maps" "next-terminal/server/common/maps"
"next-terminal/server/model" "next-terminal/server/model"
"next-terminal/server/repository" "next-terminal/server/repository"
@@ -43,6 +44,8 @@ func (api GatewayGroupApi) CreateEndpoint(c echo.Context) error {
return err return err
} }
item.ID = utils.UUID() item.ID = utils.UUID()
item.Created = common.NowJsonTime()
item.Updated = common.NowJsonTime()
if err := repository.GatewayGroupRepository.Create(context.TODO(), &item); err != nil { if err := repository.GatewayGroupRepository.Create(context.TODO(), &item); err != nil {
return err return err
@@ -56,6 +59,7 @@ func (api GatewayGroupApi) UpdateEndpoint(c echo.Context) error {
if err := c.Bind(&item); err != nil { if err := c.Bind(&item); err != nil {
return err return err
} }
item.Updated = common.NowJsonTime()
if err := repository.GatewayGroupRepository.UpdateById(context.TODO(), &item, id); err != nil { if err := repository.GatewayGroupRepository.UpdateById(context.TODO(), &item, id); err != nil {
return err return err
} }
+1 -1
View File
@@ -93,7 +93,7 @@ func (api LoginPolicyApi) GetUserPageEndpoint(c echo.Context) error {
order := c.QueryParam("order") order := c.QueryParam("order")
field := c.QueryParam("field") field := c.QueryParam("field")
items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, "", id, order, field) items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, "", "", id, order, field)
if err != nil { if err != nil {
return err return err
} }
+2 -2
View File
@@ -5,10 +5,10 @@ import (
"encoding/base64" "encoding/base64"
"strings" "strings"
"next-terminal/server/model"
"next-terminal/server/repository"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"next-terminal/server/model"
"next-terminal/server/repository"
) )
type LogoApi struct{} type LogoApi struct{}
+1 -1
View File
@@ -4,8 +4,8 @@ import (
"context" "context"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"encoding/pem"
"crypto/x509" "crypto/x509"
"encoding/pem"
"next-terminal/server/repository" "next-terminal/server/repository"
"next-terminal/server/service" "next-terminal/server/service"
+339
View File
@@ -0,0 +1,339 @@
package api
import (
"context"
"encoding/json"
"strconv"
"strings"
"next-terminal/server/common"
"next-terminal/server/common/maps"
"next-terminal/server/log"
"next-terminal/server/model"
"next-terminal/server/repository"
"next-terminal/server/service"
"next-terminal/server/utils"
"github.com/labstack/echo/v4"
"github.com/robfig/cron/v3"
)
type ScheduledTaskApi struct{}
type ScheduledTaskDTO struct {
ID string `json:"id"`
EntryId int `json:"entryId"`
Name string `json:"name"`
Spec string `json:"spec"`
Type string `json:"type"`
AssetIdList []string `json:"assetIdList"`
Mode string `json:"mode"`
Script string `json:"script"`
Enabled bool `json:"enabled"`
CreatedAt common.JsonTime `json:"createdAt"`
UpdatedAt common.JsonTime `json:"updatedAt"`
LastExecAt *common.JsonTime `json:"lastExecAt,omitempty"`
}
type JobLogDTO struct {
ID string `json:"id"`
JobId string `json:"jobId"`
JobType string `json:"jobType"`
Results []interface{} `json:"results"`
CreatedAt common.JsonTime `json:"createdAt"`
}
func JobLogToDTO(log model.JobLog, jobType string) JobLogDTO {
var results []interface{}
if log.Results != "" {
json.Unmarshal([]byte(log.Results), &results)
}
if results == nil {
results = []interface{}{}
}
return JobLogDTO{
ID: log.ID,
JobId: log.JobId,
JobType: jobType,
Results: results,
CreatedAt: log.Timestamp,
}
}
func JobToDTO(job model.Job) ScheduledTaskDTO {
dto := ScheduledTaskDTO{
ID: job.ID,
EntryId: job.CronJobId,
Name: job.Name,
Spec: job.Cron,
Type: job.Func,
Mode: job.Mode,
Enabled: job.Status == "enabled",
CreatedAt: job.Created,
UpdatedAt: job.Updated,
}
if job.ResourceIds != "" {
dto.AssetIdList = strings.Split(job.ResourceIds, ",")
}
if job.Metadata != "" && job.Func == "asset-exec-command" {
var metadataShell struct {
Shell string `json:"shell"`
}
if err := json.Unmarshal([]byte(job.Metadata), &metadataShell); err == nil {
dto.Script = metadataShell.Shell
} else {
dto.Script = job.Metadata
}
} else {
dto.Script = job.Metadata
}
if !job.LastExecAt.IsZero() {
dto.LastExecAt = &job.LastExecAt
}
return dto
}
func DTOToJob(dto ScheduledTaskDTO) model.Job {
status := "disabled"
if dto.Enabled {
status = "enabled"
}
metadata := dto.Script
if dto.Type == "asset-exec-command" && dto.Script != "" {
metadataJSON, _ := json.Marshal(map[string]string{"shell": dto.Script})
metadata = string(metadataJSON)
}
return model.Job{
ID: dto.ID,
CronJobId: dto.EntryId,
Name: dto.Name,
Cron: dto.Spec,
Func: dto.Type,
ResourceIds: strings.Join(dto.AssetIdList, ","),
Mode: dto.Mode,
Metadata: metadata,
Status: status,
}
}
func (api ScheduledTaskApi) AllEndpoint(c echo.Context) error {
items, err := repository.JobRepository.FindAll(context.TODO())
if err != nil {
return err
}
var dtos []ScheduledTaskDTO
for _, item := range items {
dtos = append(dtos, JobToDTO(item))
}
if dtos == nil {
dtos = []ScheduledTaskDTO{}
}
return Success(c, dtos)
}
func (api ScheduledTaskApi) PagingEndpoint(c echo.Context) error {
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
name := c.QueryParam("name")
status := c.QueryParam("status")
order := c.QueryParam("order")
field := c.QueryParam("field")
items, total, err := repository.JobRepository.Find(context.TODO(), pageIndex, pageSize, name, status, order, field)
if err != nil {
return err
}
var dtos []ScheduledTaskDTO
for _, item := range items {
dtos = append(dtos, JobToDTO(item))
}
if dtos == nil {
dtos = []ScheduledTaskDTO{}
}
return Success(c, maps.Map{
"total": total,
"items": dtos,
})
}
func (api ScheduledTaskApi) CreateEndpoint(c echo.Context) error {
var dto ScheduledTaskDTO
if err := c.Bind(&dto); err != nil {
return err
}
log.Info("Create ScheduledTask", log.Any("dto", dto))
job := DTOToJob(dto)
job.ID = utils.UUID()
job.Created = common.NowJsonTime()
job.Updated = common.NowJsonTime()
if job.Status == "" {
job.Status = "disabled"
}
if err := repository.JobRepository.Create(context.TODO(), &job); err != nil {
return err
}
return Success(c, job.ID)
}
func (api ScheduledTaskApi) UpdateEndpoint(c echo.Context) error {
id := c.Param("id")
var dto ScheduledTaskDTO
if err := c.Bind(&dto); err != nil {
return err
}
job := DTOToJob(dto)
job.ID = id
job.Updated = common.NowJsonTime()
if err := repository.JobRepository.UpdateById(context.TODO(), &job); err != nil {
return err
}
return Success(c, nil)
}
func (api ScheduledTaskApi) DeleteEndpoint(c echo.Context) error {
id := c.Param("id")
if err := repository.JobRepository.DeleteJobById(context.TODO(), id); err != nil {
return err
}
return Success(c, nil)
}
func (api ScheduledTaskApi) GetEndpoint(c echo.Context) error {
id := c.Param("id")
item, err := repository.JobRepository.FindById(context.TODO(), id)
if err != nil {
return err
}
dto := JobToDTO(item)
log.Info("Get ScheduledTask", log.Any("dto", dto))
return Success(c, dto)
}
func (api ScheduledTaskApi) ChangeStatusEndpoint(c echo.Context) error {
id := c.Param("id")
enabled := c.QueryParam("enabled") == "true"
var status string
if enabled {
status = "enabled"
} else {
status = "disabled"
}
job := model.Job{
ID: id,
Status: status,
}
if err := repository.JobRepository.UpdateById(context.TODO(), &job); err != nil {
return err
}
return Success(c, nil)
}
func (api ScheduledTaskApi) ExecEndpoint(c echo.Context) error {
id := c.Param("id")
job, err := repository.JobRepository.FindById(context.TODO(), id)
if err != nil {
return err
}
now := common.NowJsonTime()
jobUpdate := model.Job{
ID: id,
LastExecAt: now,
}
if err := repository.JobRepository.UpdateById(context.TODO(), &jobUpdate); err != nil {
return err
}
switch job.Func {
case "asset-exec-command":
shellJob := service.ShellJob{
ID: job.ID,
Mode: job.Mode,
ResourceIds: job.ResourceIds,
Metadata: job.Metadata,
}
shellJob.Run()
default:
jobLog := &model.JobLog{
ID: utils.UUID(),
JobId: id,
Timestamp: now,
Message: "任务执行成功",
}
_ = repository.JobLogRepository.Create(context.TODO(), jobLog)
}
return Success(c, nil)
}
func (api ScheduledTaskApi) GetLogsEndpoint(c echo.Context) error {
jobId := c.Param("id")
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
if pageIndex == 0 {
pageIndex = 1
}
if pageSize == 0 {
pageSize = 10
}
job, err := repository.JobRepository.FindById(context.TODO(), jobId)
if err != nil {
return err
}
items, total, err := repository.JobLogRepository.FindByJobId(context.TODO(), jobId, pageIndex, pageSize)
if err != nil {
return err
}
var dtos []JobLogDTO
for _, item := range items {
dtos = append(dtos, JobLogToDTO(item, job.Func))
}
if dtos == nil {
dtos = []JobLogDTO{}
}
return Success(c, maps.Map{
"total": total,
"items": dtos,
})
}
func (api ScheduledTaskApi) DeleteLogsEndpoint(c echo.Context) error {
jobId := c.Param("id")
if err := repository.JobLogRepository.DeleteByJobId(context.TODO(), jobId); err != nil {
return err
}
return Success(c, nil)
}
func (api ScheduledTaskApi) NextTenRunsEndpoint(c echo.Context) error {
var req struct {
Spec string `json:"spec"`
}
if err := c.Bind(&req); err != nil {
return err
}
var results []string
if req.Spec == "" {
return Success(c, results)
}
parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
schedule, err := parser.Parse(req.Spec)
if err != nil {
return Success(c, results)
}
now := common.NowJsonTime().Time
for i := 0; i < 10; i++ {
next := schedule.Next(now)
results = append(results, next.Format("2006-01-02 15:04:05"))
}
return Success(c, results)
}
-90
View File
@@ -3,71 +3,10 @@ package api
import ( import (
"context" "context"
"next-terminal/server/common/maps" "next-terminal/server/common/maps"
"strconv"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
) )
type ScheduledTaskApi struct{}
func (api ScheduledTaskApi) AllEndpoint(c echo.Context) error {
return Success(c, []interface{}{})
}
func (api ScheduledTaskApi) PagingEndpoint(c echo.Context) error {
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
if pageIndex == 0 {
pageIndex = 1
}
if pageSize == 0 {
pageSize = 10
}
return Success(c, maps.Map{
"total": 0,
"items": []interface{}{},
})
}
func (api ScheduledTaskApi) CreateEndpoint(c echo.Context) error {
return Success(c, "")
}
func (api ScheduledTaskApi) UpdateEndpoint(c echo.Context) error {
return Success(c, nil)
}
func (api ScheduledTaskApi) DeleteEndpoint(c echo.Context) error {
return Success(c, nil)
}
func (api ScheduledTaskApi) GetEndpoint(c echo.Context) error {
return Success(c, nil)
}
func (api ScheduledTaskApi) ChangeStatusEndpoint(c echo.Context) error {
return Success(c, nil)
}
func (api ScheduledTaskApi) ExecEndpoint(c echo.Context) error {
return Success(c, nil)
}
func (api ScheduledTaskApi) GetLogsEndpoint(c echo.Context) error {
return Success(c, maps.Map{
"total": 0,
"items": []interface{}{},
})
}
func (api ScheduledTaskApi) DeleteLogsEndpoint(c echo.Context) error {
return Success(c, nil)
}
func (api ScheduledTaskApi) NextTenRunsEndpoint(c echo.Context) error {
return Success(c, []string{})
}
type SessionCommandApi struct{} type SessionCommandApi struct{}
func (api SessionCommandApi) AllEndpoint(c echo.Context) error { func (api SessionCommandApi) AllEndpoint(c echo.Context) error {
@@ -186,35 +125,6 @@ func (api FilesystemLogApi) ClearEndpoint(c echo.Context) error {
return Success(c, nil) return Success(c, nil)
} }
type CommandFilterRuleApi struct{}
func (api CommandFilterRuleApi) AllEndpoint(c echo.Context) error {
return Success(c, []interface{}{})
}
func (api CommandFilterRuleApi) PagingEndpoint(c echo.Context) error {
return Success(c, maps.Map{
"total": 0,
"items": []interface{}{},
})
}
func (api CommandFilterRuleApi) CreateEndpoint(c echo.Context) error {
return Success(c, "")
}
func (api CommandFilterRuleApi) UpdateEndpoint(c echo.Context) error {
return Success(c, nil)
}
func (api CommandFilterRuleApi) DeleteEndpoint(c echo.Context) error {
return Success(c, nil)
}
func (api CommandFilterRuleApi) GetEndpoint(c echo.Context) error {
return Success(c, nil)
}
type AgentGatewayTokenApi struct{} type AgentGatewayTokenApi struct{}
func (api AgentGatewayTokenApi) AllEndpoint(c echo.Context) error { func (api AgentGatewayTokenApi) AllEndpoint(c echo.Context) error {
+424 -12
View File
@@ -1,7 +1,13 @@
package api package api
import ( import (
"context"
"strconv"
"next-terminal/server/common/maps" "next-terminal/server/common/maps"
"next-terminal/server/model"
"next-terminal/server/repository"
"next-terminal/server/utils"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
) )
@@ -9,87 +15,493 @@ import (
type AuthorisedAssetApi struct{} type AuthorisedAssetApi struct{}
func (api AuthorisedAssetApi) PagingEndpoint(c echo.Context) error { func (api AuthorisedAssetApi) PagingEndpoint(c echo.Context) error {
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
userId := c.QueryParam("userId")
departmentId := c.QueryParam("departmentId")
assetGroupId := c.QueryParam("assetGroupId")
assetId := c.QueryParam("assetId")
items, total, err := repository.AuthorisedAssetRepository.FindWithDetails(context.TODO(), pageIndex, pageSize, userId, departmentId, assetGroupId, assetId)
if err != nil {
return err
}
if items == nil {
items = []map[string]interface{}{}
}
return Success(c, maps.Map{ return Success(c, maps.Map{
"total": 0, "total": total,
"items": []interface{}{}, "items": items,
}) })
} }
func (api AuthorisedAssetApi) AuthorisedAssetsEndpoint(c echo.Context) error { func (api AuthorisedAssetApi) AuthorisedAssetsEndpoint(c echo.Context) error {
var req struct {
UserIds []string `json:"userIds"`
DepartmentIds []string `json:"departmentIds"`
AssetIds []string `json:"assetIds"`
AssetGroupIds []string `json:"assetGroupIds"`
CommandFilterId string `json:"commandFilterId"`
StrategyId string `json:"strategyId"`
ExpiredAt int64 `json:"expiredAt"`
}
if err := c.Bind(&req); err != nil {
return err
}
var items []model.AuthorisedAsset
for _, userId := range req.UserIds {
for _, assetId := range req.AssetIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
UserId: userId,
AssetId: assetId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
for _, assetGroupId := range req.AssetGroupIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
UserId: userId,
AssetGroupId: assetGroupId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
for _, departmentId := range req.DepartmentIds {
for _, assetId := range req.AssetIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
DepartmentId: departmentId,
AssetId: assetId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
for _, assetGroupId := range req.AssetGroupIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
DepartmentId: departmentId,
AssetGroupId: assetGroupId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
if len(items) > 0 {
if err := repository.AuthorisedAssetRepository.CreateInBatches(context.TODO(), items); err != nil {
return err
}
}
return Success(c, nil) return Success(c, nil)
} }
func (api AuthorisedAssetApi) AuthorisedUsersEndpoint(c echo.Context) error { func (api AuthorisedAssetApi) AuthorisedUsersEndpoint(c echo.Context) error {
var req struct {
AssetIds []string `json:"assetIds"`
AssetGroupIds []string `json:"assetGroupIds"`
UserIds []string `json:"userIds"`
CommandFilterId string `json:"commandFilterId"`
StrategyId string `json:"strategyId"`
ExpiredAt int64 `json:"expiredAt"`
}
if err := c.Bind(&req); err != nil {
return err
}
var items []model.AuthorisedAsset
for _, userId := range req.UserIds {
for _, assetId := range req.AssetIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
UserId: userId,
AssetId: assetId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
for _, assetGroupId := range req.AssetGroupIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
UserId: userId,
AssetGroupId: assetGroupId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
if len(items) > 0 {
if err := repository.AuthorisedAssetRepository.CreateInBatches(context.TODO(), items); err != nil {
return err
}
}
return Success(c, nil) return Success(c, nil)
} }
func (api AuthorisedAssetApi) AuthorisedDepartmentsEndpoint(c echo.Context) error { func (api AuthorisedAssetApi) AuthorisedDepartmentsEndpoint(c echo.Context) error {
var req struct {
AssetIds []string `json:"assetIds"`
AssetGroupIds []string `json:"assetGroupIds"`
DepartmentIds []string `json:"departmentIds"`
CommandFilterId string `json:"commandFilterId"`
StrategyId string `json:"strategyId"`
ExpiredAt int64 `json:"expiredAt"`
}
if err := c.Bind(&req); err != nil {
return err
}
var items []model.AuthorisedAsset
for _, departmentId := range req.DepartmentIds {
for _, assetId := range req.AssetIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
DepartmentId: departmentId,
AssetId: assetId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
for _, assetGroupId := range req.AssetGroupIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
DepartmentId: departmentId,
AssetGroupId: assetGroupId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
if len(items) > 0 {
if err := repository.AuthorisedAssetRepository.CreateInBatches(context.TODO(), items); err != nil {
return err
}
}
return Success(c, nil) return Success(c, nil)
} }
func (api AuthorisedAssetApi) SelectedEndpoint(c echo.Context) error { func (api AuthorisedAssetApi) SelectedEndpoint(c echo.Context) error {
return Success(c, []string{}) expect := c.QueryParam("expect")
userId := c.QueryParam("userId")
departmentId := c.QueryParam("departmentId")
assetId := c.QueryParam("assetId")
result, err := repository.AuthorisedAssetRepository.Selected(context.TODO(), expect, userId, departmentId, assetId)
if err != nil {
return err
}
return Success(c, result)
} }
func (api AuthorisedAssetApi) DeleteEndpoint(c echo.Context) error { func (api AuthorisedAssetApi) DeleteEndpoint(c echo.Context) error {
id := c.Param("id")
if err := repository.AuthorisedAssetRepository.DeleteById(context.TODO(), id); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
func (api AuthorisedAssetApi) GetEndpoint(c echo.Context) error { func (api AuthorisedAssetApi) GetEndpoint(c echo.Context) error {
return Success(c, nil) id := c.Param("id")
item, err := repository.AuthorisedAssetRepository.FindById(context.TODO(), id)
if err != nil {
return err
}
return Success(c, item)
} }
func (api AuthorisedAssetApi) UpdateEndpoint(c echo.Context) error { func (api AuthorisedAssetApi) UpdateEndpoint(c echo.Context) error {
return Success(c, nil) id := c.Param("id")
var item model.AuthorisedAsset
if err := c.Bind(&item); err != nil {
return err
}
item.ID = id
if err := repository.AuthorisedAssetRepository.UpdateById(context.TODO(), &item); err != nil {
return err
}
return Success(c, item)
} }
func (api AuthorisedAssetApi) CreateEndpoint(c echo.Context) error { func (api AuthorisedAssetApi) CreateEndpoint(c echo.Context) error {
var req struct {
UserIds []string `json:"userIds"`
DepartmentIds []string `json:"departmentIds"`
AssetIds []string `json:"assetIds"`
AssetGroupIds []string `json:"assetGroupIds"`
CommandFilterId string `json:"commandFilterId"`
StrategyId string `json:"strategyId"`
ExpiredAt int64 `json:"expiredAt"`
}
if err := c.Bind(&req); err != nil {
return err
}
var items []model.AuthorisedAsset
for _, userId := range req.UserIds {
for _, assetId := range req.AssetIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
UserId: userId,
AssetId: assetId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
for _, assetGroupId := range req.AssetGroupIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
UserId: userId,
AssetGroupId: assetGroupId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
for _, departmentId := range req.DepartmentIds {
for _, assetId := range req.AssetIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
DepartmentId: departmentId,
AssetId: assetId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
for _, assetGroupId := range req.AssetGroupIds {
items = append(items, model.AuthorisedAsset{
ID: utils.UUID(),
DepartmentId: departmentId,
AssetGroupId: assetGroupId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
if len(items) > 0 {
if err := repository.AuthorisedAssetRepository.CreateInBatches(context.TODO(), items); err != nil {
return err
}
}
return Success(c, nil) return Success(c, nil)
} }
type AuthorisedDatabaseAssetApi struct{} type AuthorisedDatabaseAssetApi struct{}
func (api AuthorisedDatabaseAssetApi) PagingEndpoint(c echo.Context) error { func (api AuthorisedDatabaseAssetApi) PagingEndpoint(c echo.Context) error {
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
userId := c.QueryParam("userId")
departmentId := c.QueryParam("departmentId")
databaseAssetId := c.QueryParam("assetId")
items, total, err := repository.AuthorisedDatabaseAssetRepository.FindWithDetails(context.TODO(), pageIndex, pageSize, userId, departmentId, databaseAssetId)
if err != nil {
return err
}
if items == nil {
items = []map[string]interface{}{}
}
return Success(c, maps.Map{ return Success(c, maps.Map{
"total": 0, "total": total,
"items": []interface{}{}, "items": items,
}) })
} }
func (api AuthorisedDatabaseAssetApi) SelectedEndpoint(c echo.Context) error { func (api AuthorisedDatabaseAssetApi) SelectedEndpoint(c echo.Context) error {
return Success(c, []string{}) expect := c.QueryParam("expect")
userId := c.QueryParam("userId")
departmentId := c.QueryParam("departmentId")
databaseAssetId := c.QueryParam("assetId")
result, err := repository.AuthorisedDatabaseAssetRepository.Selected(context.TODO(), expect, userId, departmentId, databaseAssetId)
if err != nil {
return err
}
return Success(c, result)
} }
func (api AuthorisedDatabaseAssetApi) DeleteEndpoint(c echo.Context) error { func (api AuthorisedDatabaseAssetApi) DeleteEndpoint(c echo.Context) error {
id := c.Param("id")
if err := repository.AuthorisedDatabaseAssetRepository.DeleteById(context.TODO(), id); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
func (api AuthorisedDatabaseAssetApi) GetEndpoint(c echo.Context) error { func (api AuthorisedDatabaseAssetApi) GetEndpoint(c echo.Context) error {
return Success(c, nil) id := c.Param("id")
item, err := repository.AuthorisedDatabaseAssetRepository.FindById(context.TODO(), id)
if err != nil {
return err
}
return Success(c, item)
} }
func (api AuthorisedDatabaseAssetApi) UpdateEndpoint(c echo.Context) error { func (api AuthorisedDatabaseAssetApi) UpdateEndpoint(c echo.Context) error {
return Success(c, nil) id := c.Param("id")
var item model.AuthorisedDatabaseAsset
if err := c.Bind(&item); err != nil {
return err
}
item.ID = id
if err := repository.AuthorisedDatabaseAssetRepository.UpdateById(context.TODO(), &item); err != nil {
return err
}
return Success(c, item)
} }
func (api AuthorisedDatabaseAssetApi) CreateEndpoint(c echo.Context) error { func (api AuthorisedDatabaseAssetApi) CreateEndpoint(c echo.Context) error {
var req struct {
UserIds []string `json:"userIds"`
DepartmentIds []string `json:"departmentIds"`
DatabaseAssetIds []string `json:"assetIds"`
CommandFilterId string `json:"commandFilterId"`
StrategyId string `json:"strategyId"`
ExpiredAt int64 `json:"expiredAt"`
}
if err := c.Bind(&req); err != nil {
return err
}
var items []model.AuthorisedDatabaseAsset
for _, userId := range req.UserIds {
for _, databaseAssetId := range req.DatabaseAssetIds {
items = append(items, model.AuthorisedDatabaseAsset{
ID: utils.UUID(),
UserId: userId,
DatabaseAssetId: databaseAssetId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
for _, departmentId := range req.DepartmentIds {
for _, databaseAssetId := range req.DatabaseAssetIds {
items = append(items, model.AuthorisedDatabaseAsset{
ID: utils.UUID(),
DepartmentId: departmentId,
DatabaseAssetId: databaseAssetId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
if len(items) > 0 {
if err := repository.AuthorisedDatabaseAssetRepository.CreateInBatches(context.TODO(), items); err != nil {
return err
}
}
return Success(c, nil) return Success(c, nil)
} }
type AuthorisedWebsiteApi struct{} type AuthorisedWebsiteApi struct{}
func (api AuthorisedWebsiteApi) PagingEndpoint(c echo.Context) error { func (api AuthorisedWebsiteApi) PagingEndpoint(c echo.Context) error {
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
userId := c.QueryParam("userId")
departmentId := c.QueryParam("departmentId")
websiteId := c.QueryParam("websiteId")
items, total, err := repository.AuthorisedWebsiteRepository.FindWithDetails(context.TODO(), pageIndex, pageSize, userId, departmentId, websiteId)
if err != nil {
return err
}
if items == nil {
items = []map[string]interface{}{}
}
return Success(c, maps.Map{ return Success(c, maps.Map{
"total": 0, "total": total,
"items": []interface{}{}, "items": items,
}) })
} }
func (api AuthorisedWebsiteApi) DeleteEndpoint(c echo.Context) error { func (api AuthorisedWebsiteApi) DeleteEndpoint(c echo.Context) error {
id := c.Param("id")
if err := repository.AuthorisedWebsiteRepository.DeleteById(context.TODO(), id); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
func (api AuthorisedWebsiteApi) CreateEndpoint(c echo.Context) error { func (api AuthorisedWebsiteApi) CreateEndpoint(c echo.Context) error {
var req struct {
UserIds []string `json:"userIds"`
DepartmentIds []string `json:"departmentIds"`
WebsiteIds []string `json:"websiteIds"`
CommandFilterId string `json:"commandFilterId"`
StrategyId string `json:"strategyId"`
ExpiredAt int64 `json:"expiredAt"`
}
if err := c.Bind(&req); err != nil {
return err
}
var items []model.AuthorisedWebsite
for _, userId := range req.UserIds {
for _, websiteId := range req.WebsiteIds {
items = append(items, model.AuthorisedWebsite{
ID: utils.UUID(),
UserId: userId,
WebsiteId: websiteId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
for _, departmentId := range req.DepartmentIds {
for _, websiteId := range req.WebsiteIds {
items = append(items, model.AuthorisedWebsite{
ID: utils.UUID(),
DepartmentId: departmentId,
WebsiteId: websiteId,
CommandFilterId: req.CommandFilterId,
StrategyId: req.StrategyId,
ExpiredAt: req.ExpiredAt,
})
}
}
if len(items) > 0 {
if err := repository.AuthorisedWebsiteRepository.CreateInBatches(context.TODO(), items); err != nil {
return err
}
}
return Success(c, nil) return Success(c, nil)
} }
+161
View File
@@ -0,0 +1,161 @@
package api
import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"net/http"
"os/exec"
"runtime"
"strconv"
"strings"
"time"
"github.com/labstack/echo/v4"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
type ToolsApi struct{}
func (api ToolsApi) TcpingEndpoint(c echo.Context) error {
host := c.QueryParam("host")
port := c.QueryParam("port")
attemptsStr := c.QueryParam("attempts")
attempts := 4
if attemptsStr != "" {
attempts, _ = strconv.Atoi(attemptsStr)
}
c.Response().Header().Set("Content-Type", "text/event-stream")
c.Response().Header().Set("Cache-Control", "no-cache")
c.Response().Header().Set("Connection", "keep-alive")
flusher, ok := c.Response().Writer.(http.Flusher)
if !ok {
return echo.NewHTTPError(500, "Streaming unsupported")
}
for i := 0; i < attempts; i++ {
start := time.Now()
conn, err := net.DialTimeout("tcp", host+":"+port, 5*time.Second)
elapsed := time.Since(start)
var result string
if err != nil {
result = fmt.Sprintf("TCP ping %s:%s - Failed: %v", host, port, err)
} else {
conn.Close()
result = fmt.Sprintf("TCP ping %s:%s - Connected, time=%v", host, port, elapsed.Round(time.Millisecond))
}
fmt.Fprintf(c.Response(), "data: %s\n\n", result)
flusher.Flush()
if i < attempts-1 {
time.Sleep(1 * time.Second)
}
}
fmt.Fprintf(c.Response(), "event: done\ndata: completed\n\n")
flusher.Flush()
return nil
}
func (api ToolsApi) PingEndpoint(c echo.Context) error {
host := c.QueryParam("host")
attemptsStr := c.QueryParam("attempts")
attempts := 4
if attemptsStr != "" {
attempts, _ = strconv.Atoi(attemptsStr)
}
c.Response().Header().Set("Content-Type", "text/event-stream")
c.Response().Header().Set("Cache-Control", "no-cache")
c.Response().Header().Set("Connection", "keep-alive")
flusher, ok := c.Response().Writer.(http.Flusher)
if !ok {
return echo.NewHTTPError(500, "Streaming unsupported")
}
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("ping", "-n", strconv.Itoa(attempts), host)
} else {
cmd = exec.Command("ping", "-c", strconv.Itoa(attempts), host)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Fprintf(c.Response(), "data: Error: %v\n\n", err)
flusher.Flush()
return nil
}
if err := cmd.Start(); err != nil {
fmt.Fprintf(c.Response(), "data: Error: %v\n\n", err)
flusher.Flush()
return nil
}
var reader io.Reader = stdout
if runtime.GOOS == "windows" {
reader = transform.NewReader(stdout, simplifiedchinese.GBK.NewDecoder())
}
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
if strings.TrimSpace(line) != "" {
fmt.Fprintf(c.Response(), "data: %s\n\n", line)
flusher.Flush()
}
}
cmd.Wait()
fmt.Fprintf(c.Response(), "event: done\ndata: completed\n\n")
flusher.Flush()
return nil
}
func (api ToolsApi) TelnetEndpoint(c echo.Context) error {
host := c.QueryParam("host")
port := c.QueryParam("port")
c.Response().Header().Set("Content-Type", "text/event-stream")
c.Response().Header().Set("Cache-Control", "no-cache")
c.Response().Header().Set("Connection", "keep-alive")
flusher, ok := c.Response().Writer.(http.Flusher)
if !ok {
return echo.NewHTTPError(500, "Streaming unsupported")
}
start := time.Now()
conn, err := net.DialTimeout("tcp", host+":"+port, 10*time.Second)
elapsed := time.Since(start)
var result bytes.Buffer
if err != nil {
result.WriteString(fmt.Sprintf("Telnet %s:%s - Connection failed: %v", host, port, err))
} else {
conn.Close()
result.WriteString(fmt.Sprintf("Telnet %s:%s - Connected successfully in %v", host, port, elapsed.Round(time.Millisecond)))
}
fmt.Fprintf(c.Response(), "data: %s\n\n", result.String())
flusher.Flush()
fmt.Fprintf(c.Response(), "event: done\ndata: completed\n\n")
flusher.Flush()
return nil
}
+2 -1
View File
@@ -41,8 +41,9 @@ func (userApi UserApi) PagingEndpoint(c echo.Context) error {
order := c.QueryParam("order") order := c.QueryParam("order")
field := c.QueryParam("field") field := c.QueryParam("field")
online := c.QueryParam("online") online := c.QueryParam("online")
userType := c.QueryParam("type")
items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, online, "", order, field) items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, online, userType, "", order, field)
if err != nil { if err != nil {
return err return err
} }
+108 -1
View File
@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"strconv" "strconv"
"next-terminal/server/common"
"next-terminal/server/common/maps" "next-terminal/server/common/maps"
"next-terminal/server/dto" "next-terminal/server/dto"
"next-terminal/server/model" "next-terminal/server/model"
@@ -152,18 +153,124 @@ func (api WebsiteApi) GetEndpoint(c echo.Context) error {
} }
func (api WebsiteApi) GroupsGetEndpoint(c echo.Context) error { func (api WebsiteApi) GroupsGetEndpoint(c echo.Context) error {
return Success(c, []interface{}{}) groups, err := repository.WebsiteGroupRepository.FindAll(context.TODO())
if err != nil {
return err
}
tree := buildWebsiteGroupTree(groups, "")
return Success(c, tree)
}
func buildWebsiteGroupTree(groups []model.WebsiteGroup, parentId string) []maps.Map {
var tree []maps.Map
for _, g := range groups {
if g.ParentId == parentId {
node := maps.Map{
"id": g.ID,
"name": g.Name,
"title": g.Name,
"key": g.ID,
"value": g.ID,
}
children := buildWebsiteGroupTree(groups, g.ID)
if len(children) > 0 {
node["children"] = children
}
tree = append(tree, node)
}
}
return tree
} }
func (api WebsiteApi) GroupsSetEndpoint(c echo.Context) error { func (api WebsiteApi) GroupsSetEndpoint(c echo.Context) error {
var req []map[string]interface{}
if err := c.Bind(&req); err != nil {
return err
}
ctx := context.TODO()
repository.WebsiteGroupRepository.DeleteAll(ctx)
for i, item := range req {
name := ""
if v, ok := item["name"].(string); ok {
name = v
} else if v, ok := item["title"].(string); ok {
name = v
}
group := model.WebsiteGroup{
ID: utils.UUID(),
Name: name,
ParentId: "",
Sort: i,
Created: common.NowJsonTime(),
}
repository.WebsiteGroupRepository.Create(ctx, &group)
if subChildren, ok := item["children"].([]interface{}); ok {
saveWebsiteGroupChildren(ctx, subChildren, group.ID)
}
}
return Success(c, nil) return Success(c, nil)
} }
func saveWebsiteGroupChildren(ctx context.Context, children []interface{}, parentId string) {
for i, child := range children {
m, ok := child.(map[string]interface{})
if !ok {
continue
}
name := ""
if v, ok := m["name"].(string); ok {
name = v
} else if v, ok := m["title"].(string); ok {
name = v
}
group := model.WebsiteGroup{
ID: utils.UUID(),
Name: name,
ParentId: parentId,
Sort: i,
Created: common.NowJsonTime(),
}
repository.WebsiteGroupRepository.Create(ctx, &group)
if subChildren, ok := m["children"].([]interface{}); ok {
saveWebsiteGroupChildren(ctx, subChildren, group.ID)
}
}
}
func (api WebsiteApi) GroupsDeleteEndpoint(c echo.Context) error { func (api WebsiteApi) GroupsDeleteEndpoint(c echo.Context) error {
id := c.Param("id")
count, err := repository.WebsiteGroupRepository.CountByParentId(context.TODO(), id)
if err != nil {
return err
}
if count > 0 {
return Fail(c, -1, "该分组下存在子分组,无法删除")
}
if err := repository.WebsiteGroupRepository.DeleteById(context.TODO(), id); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
func (api WebsiteApi) ChangeGroupEndpoint(c echo.Context) error { func (api WebsiteApi) ChangeGroupEndpoint(c echo.Context) error {
var req struct {
WebsiteIds []string `json:"websiteIds"`
GroupId string `json:"groupId"`
}
if err := c.Bind(&req); err != nil {
return err
}
for _, websiteId := range req.WebsiteIds {
website, err := repository.WebsiteRepository.FindById(context.TODO(), websiteId)
if err != nil {
continue
}
website.GroupId = req.GroupId
repository.WebsiteRepository.UpdateById(context.TODO(), &website, websiteId)
}
return Success(c, nil) return Success(c, nil)
} }
+2
View File
@@ -57,6 +57,8 @@ var allowUrls = []urlpath.Path{
urlpath.New("/api/portal/websites"), urlpath.New("/api/portal/websites"),
urlpath.New("/api/portal/info"), urlpath.New("/api/portal/info"),
urlpath.New("/api/license"), urlpath.New("/api/license"),
urlpath.New("/api/admin/tools/tcping"),
urlpath.New("/api/admin/tools/ping"),
} }
func Auth(next echo.HandlerFunc) echo.HandlerFunc { func Auth(next echo.HandlerFunc) echo.HandlerFunc {
+7
View File
@@ -133,6 +133,7 @@ func setupRoutes() *echo.Echo {
GatewayGroupApi := new(api.GatewayGroupApi) GatewayGroupApi := new(api.GatewayGroupApi)
WebsiteApi := new(api.WebsiteApi) WebsiteApi := new(api.WebsiteApi)
CertificateApi := new(api.CertificateApi) CertificateApi := new(api.CertificateApi)
ToolsApi := new(api.ToolsApi)
adminGroup.GET("/login-status", setupApi.LoginStatusEndpoint) adminGroup.GET("/login-status", setupApi.LoginStatusEndpoint)
adminGroup.POST("/validate-totp", setupApi.ValidateTOTPEndpoint) adminGroup.POST("/validate-totp", setupApi.ValidateTOTPEndpoint)
@@ -279,6 +280,12 @@ func setupRoutes() *echo.Echo {
adminGroup.GET("/tags", AssetApi.AssetTagsEndpoint) adminGroup.GET("/tags", AssetApi.AssetTagsEndpoint)
tools := adminGroup.Group("/tools")
{
tools.GET("/tcping", ToolsApi.TcpingEndpoint)
tools.GET("/ping", ToolsApi.PingEndpoint)
}
commands := adminGroup.Group("/commands") commands := adminGroup.Group("/commands")
{ {
commands.GET("", CommandApi.CommandAllEndpoint) commands.GET("", CommandApi.CommandAllEndpoint)
+1 -1
View File
@@ -55,7 +55,7 @@ func setupDB() *gorm.DB {
&model.Role{}, &model.RoleMenuRef{}, &model.UserRoleRef{}, &model.Role{}, &model.RoleMenuRef{}, &model.UserRoleRef{},
&model.LoginPolicy{}, &model.LoginPolicyUserRef{}, &model.TimePeriod{}, &model.LoginPolicy{}, &model.LoginPolicyUserRef{}, &model.TimePeriod{},
&model.StorageLog{}, &model.Authorised{}, &model.Logo{}, &model.AssetGroup{}, &model.StorageLog{}, &model.Authorised{}, &model.Logo{}, &model.AssetGroup{},
&model.AgentGateway{}, &model.SshGateway{}, &model.GatewayGroup{}, &model.Website{}, &model.Certificate{}, &model.Snippet{}, &model.SessionAudit{}, &model.Department{}, &model.UserDepartmentRef{}); err != nil { &model.AgentGateway{}, &model.SshGateway{}, &model.GatewayGroup{}, &model.Website{}, &model.Certificate{}, &model.Snippet{}, &model.SessionAudit{}, &model.Department{}, &model.UserDepartmentRef{}, &model.DatabaseAsset{}, &model.CommandFilter{}, &model.CommandFilterRule{}, &model.AuthorisedAsset{}, &model.AuthorisedDatabaseAsset{}, &model.AuthorisedWebsite{}, &model.WebsiteGroup{}); err != nil {
panic(fmt.Errorf("初始化数据库表结构异常: %v", err.Error())) panic(fmt.Errorf("初始化数据库表结构异常: %v", err.Error()))
} }
return db return db
+49
View File
@@ -0,0 +1,49 @@
package model
import "next-terminal/server/common"
type AuthorisedAsset struct {
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
UserId string `gorm:"index,type:varchar(36)" json:"userId"`
DepartmentId string `gorm:"index,type:varchar(36)" json:"departmentId"`
AssetId string `gorm:"index,type:varchar(36)" json:"assetId"`
AssetGroupId string `gorm:"index,type:varchar(36)" json:"assetGroupId"`
CommandFilterId string `gorm:"index,type:varchar(36)" json:"commandFilterId"`
StrategyId string `gorm:"index,type:varchar(36)" json:"strategyId"`
ExpiredAt int64 `json:"expiredAt"`
Created common.JsonTime `gorm:"type:datetime" json:"createdAt"`
}
func (m AuthorisedAsset) TableName() string {
return "authorised_assets"
}
type AuthorisedDatabaseAsset struct {
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
UserId string `gorm:"index,type:varchar(36)" json:"userId"`
DepartmentId string `gorm:"index,type:varchar(36)" json:"departmentId"`
DatabaseAssetId string `gorm:"index,type:varchar(36)" json:"databaseAssetId"`
CommandFilterId string `gorm:"index,type:varchar(36)" json:"commandFilterId"`
StrategyId string `gorm:"index,type:varchar(36)" json:"strategyId"`
ExpiredAt int64 `json:"expiredAt"`
Created common.JsonTime `gorm:"type:datetime" json:"createdAt"`
}
func (m AuthorisedDatabaseAsset) TableName() string {
return "authorised_database_assets"
}
type AuthorisedWebsite struct {
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
UserId string `gorm:"index,type:varchar(36)" json:"userId"`
DepartmentId string `gorm:"index,type:varchar(36)" json:"departmentId"`
WebsiteId string `gorm:"index,type:varchar(36)" json:"websiteId"`
CommandFilterId string `gorm:"index,type:varchar(36)" json:"commandFilterId"`
StrategyId string `gorm:"index,type:varchar(36)" json:"strategyId"`
ExpiredAt int64 `json:"expiredAt"`
Created common.JsonTime `gorm:"type:datetime" json:"createdAt"`
}
func (m AuthorisedWebsite) TableName() string {
return "authorised_websites"
}
+4 -4
View File
@@ -7,8 +7,8 @@ type Certificate struct {
CommonName string `gorm:"type:varchar(500)" json:"commonName"` CommonName string `gorm:"type:varchar(500)" json:"commonName"`
Subject string `gorm:"type:varchar(500)" json:"subject"` Subject string `gorm:"type:varchar(500)" json:"subject"`
Issuer string `gorm:"type:varchar(500)" json:"issuer"` Issuer string `gorm:"type:varchar(500)" json:"issuer"`
NotBefore common.JsonTime `json:"notBefore"` NotBefore common.JsonTime `gorm:"type:datetime" json:"notBefore"`
NotAfter common.JsonTime `json:"notAfter"` NotAfter common.JsonTime `gorm:"type:datetime" json:"notAfter"`
Type string `gorm:"type:varchar(20);default:'imported'" json:"type"` Type string `gorm:"type:varchar(20);default:'imported'" json:"type"`
StorageKey string `gorm:"type:varchar(100)" json:"storageKey"` StorageKey string `gorm:"type:varchar(100)" json:"storageKey"`
Certificate string `gorm:"type:text" json:"certificate"` Certificate string `gorm:"type:text" json:"certificate"`
@@ -16,9 +16,9 @@ type Certificate struct {
RequireClientAuth bool `gorm:"default:false" json:"requireClientAuth"` RequireClientAuth bool `gorm:"default:false" json:"requireClientAuth"`
IssuedStatus string `gorm:"type:varchar(20);default:'success'" json:"issuedStatus"` IssuedStatus string `gorm:"type:varchar(20);default:'success'" json:"issuedStatus"`
IssuedError string `gorm:"type:text" json:"issuedError"` IssuedError string `gorm:"type:text" json:"issuedError"`
UpdatedAt common.JsonTime `json:"updatedAt"` UpdatedAt common.JsonTime `gorm:"type:datetime" json:"updatedAt"`
IsDefault bool `gorm:"default:false" json:"isDefault"` IsDefault bool `gorm:"default:false" json:"isDefault"`
Created common.JsonTime `json:"createdAt"` Created common.JsonTime `gorm:"type:datetime" json:"createdAt"`
} }
func (r *Certificate) TableName() string { func (r *Certificate) TableName() string {
+27
View File
@@ -0,0 +1,27 @@
package model
import "next-terminal/server/common"
type CommandFilter struct {
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
Name string `gorm:"type:varchar(200)" json:"name"`
Created common.JsonTime `gorm:"type:datetime" json:"createdAt"`
}
func (r *CommandFilter) TableName() string {
return "command_filters"
}
type CommandFilterRule struct {
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
CommandFilterId string `gorm:"type:varchar(36);index" json:"commandFilterId"`
Type string `gorm:"type:varchar(20)" json:"type"`
Pattern string `gorm:"type:text" json:"pattern"`
Priority int `json:"priority"`
Action string `gorm:"type:varchar(20)" json:"action"`
Created common.JsonTime `gorm:"type:datetime" json:"createdAt"`
}
func (r *CommandFilterRule) TableName() string {
return "command_filter_rules"
}
+27
View File
@@ -0,0 +1,27 @@
package model
import "next-terminal/server/common"
type DatabaseAsset struct {
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
Name string `gorm:"type:varchar(500)" json:"name"`
Type string `gorm:"type:varchar(20)" json:"type"`
Host string `gorm:"type:varchar(200)" json:"host"`
Port int `json:"port"`
Database string `gorm:"type:varchar(200)" json:"database"`
Username string `gorm:"type:varchar(200)" json:"username"`
Password string `gorm:"type:varchar(500)" json:"password"`
Description string `json:"description"`
GatewayType string `gorm:"type:varchar(20)" json:"gatewayType"`
GatewayId string `gorm:"type:varchar(36)" json:"gatewayId"`
Tags string `json:"tags"`
Owner string `gorm:"index,type:varchar(36)" json:"owner"`
Encrypted bool `json:"encrypted"`
Created common.JsonTime `gorm:"type:datetime" json:"createdAt"`
Updated common.JsonTime `gorm:"type:datetime" json:"updatedAt"`
Sort int `json:"sort" gorm:"default:0"`
}
func (r *DatabaseAsset) TableName() string {
return "database_assets"
}
+4 -2
View File
@@ -6,8 +6,10 @@ type Department struct {
ID string `gorm:"primary_key,type:varchar(36)" json:"id"` ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
Name string `gorm:"type:varchar(200)" json:"name"` Name string `gorm:"type:varchar(200)" json:"name"`
ParentId string `gorm:"type:varchar(36)" json:"parentId"` ParentId string `gorm:"type:varchar(36)" json:"parentId"`
Sort int `json:"sort"` Sort int `json:"weight"`
Created common.JsonTime `json:"createdAt"` ParentName string `gorm:"-" json:"parentName"`
UserCount int64 `gorm:"-" json:"userCount"`
Created common.JsonTime `gorm:"type:datetime" json:"createdAt"`
} }
func (r *Department) TableName() string { func (r *Department) TableName() string {
+2
View File
@@ -16,6 +16,7 @@ type Job struct {
Metadata string `json:"metadata"` Metadata string `json:"metadata"`
Created common.JsonTime `json:"created"` Created common.JsonTime `json:"created"`
Updated common.JsonTime `json:"updated"` Updated common.JsonTime `json:"updated"`
LastExecAt common.JsonTime `json:"lastExecAt"`
} }
func (r *Job) TableName() string { func (r *Job) TableName() string {
@@ -27,6 +28,7 @@ type JobLog struct {
Timestamp common.JsonTime `json:"timestamp"` Timestamp common.JsonTime `json:"timestamp"`
JobId string `json:"jobId"` JobId string `json:"jobId"`
Message string `json:"message"` Message string `json:"message"`
Results string `json:"results"`
} }
func (r *JobLog) TableName() string { func (r *JobLog) TableName() string {
+1 -1
View File
@@ -46,7 +46,7 @@ type UserForPage struct {
Watermark string `json:"watermark"` Watermark string `json:"watermark"`
Remark string `json:"remark"` Remark string `json:"remark"`
SharerAssetCount int64 `json:"sharerAssetCount"` SharerAssetCount int64 `json:"sharerAssetCount"`
Departments []UserDepartment `json:"departments"` Departments []UserDepartment `gorm:"-" json:"departments"`
LastLoginAt *common.JsonTime `json:"lastLoginAt"` LastLoginAt *common.JsonTime `json:"lastLoginAt"`
} }
+15
View File
@@ -0,0 +1,15 @@
package model
import "next-terminal/server/common"
type WebsiteGroup struct {
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
Name string `gorm:"type:varchar(200)" json:"name"`
ParentId string `gorm:"type:varchar(36)" json:"parentId"`
Sort int `json:"sort"`
Created common.JsonTime `gorm:"type:datetime" json:"createdAt"`
}
func (r *WebsiteGroup) TableName() string {
return "website_groups"
}
+389
View File
@@ -0,0 +1,389 @@
package repository
import (
"context"
"strconv"
"next-terminal/server/common"
"next-terminal/server/model"
)
var AuthorisedAssetRepository = new(authorisedAssetRepository)
type authorisedAssetRepository struct {
baseRepository
}
func (r authorisedAssetRepository) Find(c context.Context, pageIndex, pageSize int, userId, departmentId, assetGroupId, assetId string) (o []model.AuthorisedAsset, total int64, err error) {
db := r.GetDB(c).Model(&model.AuthorisedAsset{})
if userId != "" {
db = db.Where("user_id = ?", userId)
}
if departmentId != "" {
db = db.Where("department_id = ?", departmentId)
}
if assetGroupId != "" {
db = db.Where("asset_group_id = ?", assetGroupId)
}
if assetId != "" {
db = db.Where("asset_id = ?", assetId)
}
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Order("created desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
return
}
func (r authorisedAssetRepository) FindById(c context.Context, id string) (o model.AuthorisedAsset, err error) {
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
return
}
func (r authorisedAssetRepository) Create(c context.Context, o *model.AuthorisedAsset) error {
o.Created = common.NowJsonTime()
return r.GetDB(c).Create(o).Error
}
func (r authorisedAssetRepository) CreateInBatches(c context.Context, items []model.AuthorisedAsset) error {
for i := range items {
items[i].Created = common.NowJsonTime()
}
return r.GetDB(c).CreateInBatches(items, 100).Error
}
func (r authorisedAssetRepository) UpdateById(c context.Context, o *model.AuthorisedAsset) error {
return r.GetDB(c).Updates(o).Error
}
func (r authorisedAssetRepository) DeleteById(c context.Context, id string) error {
return r.GetDB(c).Where("id = ?", id).Delete(&model.AuthorisedAsset{}).Error
}
func (r authorisedAssetRepository) Selected(c context.Context, expect, userId, departmentId, assetId string) (result []string, err error) {
var items []model.AuthorisedAsset
db := r.GetDB(c)
switch expect {
case "userId":
db = db.Select("user_id")
case "departmentId":
db = db.Select("department_id")
case "assetId":
db = db.Select("asset_id")
case "assetGroupId":
db = db.Select("asset_group_id")
}
if userId != "" {
db = db.Where("user_id = ?", userId)
}
if departmentId != "" {
db = db.Where("department_id = ?", departmentId)
}
if assetId != "" {
db = db.Where("asset_id = ?", assetId)
}
err = db.Find(&items).Error
if err != nil {
return
}
for _, item := range items {
switch expect {
case "userId":
if item.UserId != "" {
result = append(result, item.UserId)
}
case "departmentId":
if item.DepartmentId != "" {
result = append(result, item.DepartmentId)
}
case "assetId":
if item.AssetId != "" {
result = append(result, item.AssetId)
}
case "assetGroupId":
if item.AssetGroupId != "" {
result = append(result, item.AssetGroupId)
}
}
}
return
}
func (r authorisedAssetRepository) FindWithDetails(c context.Context, pageIndex, pageSize int, userId, departmentId, assetGroupId, assetId string) (o []map[string]interface{}, total int64, err error) {
db := r.GetDB(c).Table("authorised_assets").
Select(`authorised_assets.id,
strftime('%s', authorised_assets.created) * 1000 as "createdAt",
authorised_assets.expired_at as "expiredAt",
authorised_assets.user_id as "userId", users.nickname as "userName",
authorised_assets.department_id as "departmentId", departments.name as "departmentName",
authorised_assets.asset_id as "assetId", assets.name as "assetName",
authorised_assets.asset_group_id as "assetGroupId", asset_groups.name as "assetGroupName",
authorised_assets.strategy_id as "strategyId", strategies.name as "strategyName"`).
Joins("left join users on users.id = authorised_assets.user_id").
Joins("left join departments on departments.id = authorised_assets.department_id").
Joins("left join assets on assets.id = authorised_assets.asset_id").
Joins("left join asset_groups on asset_groups.id = authorised_assets.asset_group_id").
Joins("left join strategies on strategies.id = authorised_assets.strategy_id")
dbCounter := r.GetDB(c).Model(&model.AuthorisedAsset{})
if userId != "" {
db = db.Where("authorised_assets.user_id = ?", userId)
dbCounter = dbCounter.Where("user_id = ?", userId)
}
if departmentId != "" {
db = db.Where("authorised_assets.department_id = ?", departmentId)
dbCounter = dbCounter.Where("department_id = ?", departmentId)
}
if assetGroupId != "" {
db = db.Where("authorised_assets.asset_group_id = ?", assetGroupId)
dbCounter = dbCounter.Where("asset_group_id = ?", assetGroupId)
}
if assetId != "" {
db = db.Where("authorised_assets.asset_id = ?", assetId)
dbCounter = dbCounter.Where("asset_id = ?", assetId)
}
err = dbCounter.Count(&total).Error
if err != nil {
return
}
err = db.Order("authorised_assets.created desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
return
}
var AuthorisedDatabaseAssetRepository = new(authorisedDatabaseAssetRepository)
type authorisedDatabaseAssetRepository struct {
baseRepository
}
func (r authorisedDatabaseAssetRepository) Find(c context.Context, pageIndex, pageSize int, userId, departmentId, databaseAssetId string) (o []model.AuthorisedDatabaseAsset, total int64, err error) {
db := r.GetDB(c).Model(&model.AuthorisedDatabaseAsset{})
if userId != "" {
db = db.Where("user_id = ?", userId)
}
if departmentId != "" {
db = db.Where("department_id = ?", departmentId)
}
if databaseAssetId != "" {
db = db.Where("database_asset_id = ?", databaseAssetId)
}
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Order("created desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
return
}
func (r authorisedDatabaseAssetRepository) FindById(c context.Context, id string) (o model.AuthorisedDatabaseAsset, err error) {
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
return
}
func (r authorisedDatabaseAssetRepository) Create(c context.Context, o *model.AuthorisedDatabaseAsset) error {
o.Created = common.NowJsonTime()
return r.GetDB(c).Create(o).Error
}
func (r authorisedDatabaseAssetRepository) CreateInBatches(c context.Context, items []model.AuthorisedDatabaseAsset) error {
for i := range items {
items[i].Created = common.NowJsonTime()
}
return r.GetDB(c).CreateInBatches(items, 100).Error
}
func (r authorisedDatabaseAssetRepository) UpdateById(c context.Context, o *model.AuthorisedDatabaseAsset) error {
return r.GetDB(c).Updates(o).Error
}
func (r authorisedDatabaseAssetRepository) DeleteById(c context.Context, id string) error {
return r.GetDB(c).Where("id = ?", id).Delete(&model.AuthorisedDatabaseAsset{}).Error
}
func (r authorisedDatabaseAssetRepository) Selected(c context.Context, expect, userId, departmentId, databaseAssetId string) (result []string, err error) {
var items []model.AuthorisedDatabaseAsset
db := r.GetDB(c)
switch expect {
case "userId":
db = db.Select("user_id")
case "departmentId":
db = db.Select("department_id")
case "databaseAssetId":
db = db.Select("database_asset_id")
}
if userId != "" {
db = db.Where("user_id = ?", userId)
}
if departmentId != "" {
db = db.Where("department_id = ?", departmentId)
}
if databaseAssetId != "" {
db = db.Where("database_asset_id = ?", databaseAssetId)
}
err = db.Find(&items).Error
if err != nil {
return
}
for _, item := range items {
switch expect {
case "userId":
if item.UserId != "" {
result = append(result, item.UserId)
}
case "departmentId":
if item.DepartmentId != "" {
result = append(result, item.DepartmentId)
}
case "databaseAssetId":
if item.DatabaseAssetId != "" {
result = append(result, item.DatabaseAssetId)
}
}
}
return
}
func (r authorisedDatabaseAssetRepository) FindWithDetails(c context.Context, pageIndex, pageSize int, userId, departmentId, databaseAssetId string) (o []map[string]interface{}, total int64, err error) {
db := r.GetDB(c).Table("authorised_database_assets").
Select(`authorised_database_assets.id,
strftime('%s', authorised_database_assets.created) * 1000 as "createdAt",
authorised_database_assets.expired_at as "expiredAt",
authorised_database_assets.user_id as "userId", users.nickname as "userName",
authorised_database_assets.department_id as "departmentId", departments.name as "departmentName",
authorised_database_assets.database_asset_id as "databaseAssetId", database_assets.name as "databaseAssetName",
authorised_database_assets.strategy_id as "strategyId", strategies.name as "strategyName"`).
Joins("left join users on users.id = authorised_database_assets.user_id").
Joins("left join departments on departments.id = authorised_database_assets.department_id").
Joins("left join database_assets on database_assets.id = authorised_database_assets.database_asset_id").
Joins("left join strategies on strategies.id = authorised_database_assets.strategy_id")
dbCounter := r.GetDB(c).Model(&model.AuthorisedDatabaseAsset{})
if userId != "" {
db = db.Where("authorised_database_assets.user_id = ?", userId)
dbCounter = dbCounter.Where("user_id = ?", userId)
}
if departmentId != "" {
db = db.Where("authorised_database_assets.department_id = ?", departmentId)
dbCounter = dbCounter.Where("department_id = ?", departmentId)
}
if databaseAssetId != "" {
db = db.Where("authorised_database_assets.database_asset_id = ?", databaseAssetId)
dbCounter = dbCounter.Where("database_asset_id = ?", databaseAssetId)
}
err = dbCounter.Count(&total).Error
if err != nil {
return
}
err = db.Order("authorised_database_assets.created desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
return
}
var AuthorisedWebsiteRepository = new(authorisedWebsiteRepository)
type authorisedWebsiteRepository struct {
baseRepository
}
func (r authorisedWebsiteRepository) Find(c context.Context, pageIndex, pageSize int, userId, departmentId, websiteId string) (o []model.AuthorisedWebsite, total int64, err error) {
db := r.GetDB(c).Model(&model.AuthorisedWebsite{})
if userId != "" {
db = db.Where("user_id = ?", userId)
}
if departmentId != "" {
db = db.Where("department_id = ?", departmentId)
}
if websiteId != "" {
db = db.Where("website_id = ?", websiteId)
}
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Order("created desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
return
}
func (r authorisedWebsiteRepository) FindById(c context.Context, id string) (o model.AuthorisedWebsite, err error) {
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
return
}
func (r authorisedWebsiteRepository) Create(c context.Context, o *model.AuthorisedWebsite) error {
o.Created = common.NowJsonTime()
return r.GetDB(c).Create(o).Error
}
func (r authorisedWebsiteRepository) CreateInBatches(c context.Context, items []model.AuthorisedWebsite) error {
for i := range items {
items[i].Created = common.NowJsonTime()
}
return r.GetDB(c).CreateInBatches(items, 100).Error
}
func (r authorisedWebsiteRepository) DeleteById(c context.Context, id string) error {
return r.GetDB(c).Where("id = ?", id).Delete(&model.AuthorisedWebsite{}).Error
}
func (r authorisedWebsiteRepository) FindWithDetails(c context.Context, pageIndex, pageSize int, userId, departmentId, websiteId string) (o []map[string]interface{}, total int64, err error) {
db := r.GetDB(c).Table("authorised_websites").
Select(`authorised_websites.id,
strftime('%s', authorised_websites.created) * 1000 as "createdAt",
authorised_websites.expired_at as "expiredAt",
authorised_websites.user_id as "userId", users.nickname as "userName",
authorised_websites.department_id as "departmentId", departments.name as "departmentName",
authorised_websites.website_id as "websiteId", websites.name as "websiteName",
authorised_websites.strategy_id as "strategyId", strategies.name as "strategyName"`).
Joins("left join users on users.id = authorised_websites.user_id").
Joins("left join departments on departments.id = authorised_websites.department_id").
Joins("left join websites on websites.id = authorised_websites.website_id").
Joins("left join strategies on strategies.id = authorised_websites.strategy_id")
dbCounter := r.GetDB(c).Model(&model.AuthorisedWebsite{})
if userId != "" {
db = db.Where("authorised_websites.user_id = ?", userId)
dbCounter = dbCounter.Where("user_id = ?", userId)
}
if departmentId != "" {
db = db.Where("authorised_websites.department_id = ?", departmentId)
dbCounter = dbCounter.Where("department_id = ?", departmentId)
}
if websiteId != "" {
db = db.Where("authorised_websites.website_id = ?", websiteId)
dbCounter = dbCounter.Where("website_id = ?", websiteId)
}
err = dbCounter.Count(&total).Error
if err != nil {
return
}
err = db.Order("authorised_websites.created desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
return
}
func init() {
_ = strconv.Itoa(0)
}
+110
View File
@@ -0,0 +1,110 @@
package repository
import (
"context"
"next-terminal/server/common"
"next-terminal/server/model"
)
var CommandFilterRepository = new(commandFilterRepository)
type commandFilterRepository struct {
baseRepository
}
func (r commandFilterRepository) FindAll(c context.Context) (o []model.CommandFilter, err error) {
err = r.GetDB(c).Order("name asc").Find(&o).Error
return
}
func (r commandFilterRepository) Find(c context.Context, pageIndex, pageSize int, name, order, field string) (o []model.CommandFilter, total int64, err error) {
db := r.GetDB(c).Model(&model.CommandFilter{})
if len(name) > 0 {
db = db.Where("name like ?", "%"+name+"%")
}
err = db.Count(&total).Error
if err != nil {
return
}
if order == "" {
order = "asc"
}
if field == "" {
field = "name"
}
err = db.Order(field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
return
}
func (r commandFilterRepository) FindById(c context.Context, id string) (o model.CommandFilter, err error) {
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
return
}
func (r commandFilterRepository) Create(c context.Context, o *model.CommandFilter) error {
o.Created = common.NowJsonTime()
return r.GetDB(c).Create(o).Error
}
func (r commandFilterRepository) UpdateById(c context.Context, o *model.CommandFilter) error {
return r.GetDB(c).Updates(o).Error
}
func (r commandFilterRepository) DeleteById(c context.Context, id string) error {
tx := r.GetDB(c).Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Where("command_filter_id = ?", id).Delete(&model.CommandFilterRule{}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Where("id = ?", id).Delete(&model.CommandFilter{}).Error; err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
}
var CommandFilterRuleRepository = new(commandFilterRuleRepository)
type commandFilterRuleRepository struct {
baseRepository
}
func (r commandFilterRuleRepository) FindByCommandFilterId(c context.Context, commandFilterId string) (o []model.CommandFilterRule, err error) {
err = r.GetDB(c).Where("command_filter_id = ?", commandFilterId).Order("priority asc").Find(&o).Error
return
}
func (r commandFilterRuleRepository) FindById(c context.Context, id string) (o model.CommandFilterRule, err error) {
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
return
}
func (r commandFilterRuleRepository) Create(c context.Context, o *model.CommandFilterRule) error {
o.Created = common.NowJsonTime()
return r.GetDB(c).Create(o).Error
}
func (r commandFilterRuleRepository) UpdateById(c context.Context, o *model.CommandFilterRule) error {
return r.GetDB(c).Updates(o).Error
}
func (r commandFilterRuleRepository) DeleteById(c context.Context, id string) error {
return r.GetDB(c).Where("id = ?", id).Delete(&model.CommandFilterRule{}).Error
}
func (r commandFilterRuleRepository) DeleteByCommandFilterId(c context.Context, commandFilterId string) error {
return r.GetDB(c).Where("command_filter_id = ?", commandFilterId).Delete(&model.CommandFilterRule{}).Error
}
+148
View File
@@ -0,0 +1,148 @@
package repository
import (
"context"
"strconv"
"strings"
"next-terminal/server/model"
)
var DatabaseAssetRepository = new(databaseAssetRepository)
type databaseAssetRepository struct {
baseRepository
}
func (r databaseAssetRepository) FindAll(c context.Context) (o []model.DatabaseAsset, err error) {
err = r.GetDB(c).Order("name asc").Find(&o).Error
return
}
func (r databaseAssetRepository) FindByType(c context.Context, dbType string) (o []model.DatabaseAsset, err error) {
db := r.GetDB(c)
if dbType != "" {
db = db.Where("type = ?", dbType)
}
err = db.Order("name asc").Find(&o).Error
return
}
func (r databaseAssetRepository) Find(c context.Context, pageIndex, pageSize int, name, dbType, order, field string) (o []model.DatabaseAsset, total int64, err error) {
db := r.GetDB(c).Model(&model.DatabaseAsset{})
if len(name) > 0 {
db = db.Where("name like ?", "%"+name+"%")
}
if len(dbType) > 0 {
db = db.Where("type = ?", dbType)
}
err = db.Count(&total).Error
if err != nil {
return
}
if order == "" {
order = "asc"
}
if field == "" {
field = "name"
}
orderBy := field + " " + order
if field == "created" {
orderBy = "created " + order
}
err = db.Order(orderBy).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
return
}
func (r databaseAssetRepository) FindById(c context.Context, id string) (o model.DatabaseAsset, err error) {
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
return
}
func (r databaseAssetRepository) Create(c context.Context, o *model.DatabaseAsset) error {
return r.GetDB(c).Create(o).Error
}
func (r databaseAssetRepository) UpdateById(c context.Context, o *model.DatabaseAsset) error {
return r.GetDB(c).Updates(o).Error
}
func (r databaseAssetRepository) DeleteById(c context.Context, id string) error {
return r.GetDB(c).Where("id = ?", id).Delete(&model.DatabaseAsset{}).Error
}
func (r databaseAssetRepository) Count(c context.Context) (total int64, err error) {
err = r.GetDB(c).Model(&model.DatabaseAsset{}).Count(&total).Error
return
}
func (r databaseAssetRepository) FindAllTags(c context.Context) (o []string, err error) {
var assets []model.DatabaseAsset
err = r.GetDB(c).Select("tags").Find(&assets).Error
if err != nil {
return
}
tagSet := make(map[string]bool)
for _, asset := range assets {
if asset.Tags == "" {
continue
}
tags := strings.Split(asset.Tags, ",")
for _, tag := range tags {
tag = strings.TrimSpace(tag)
if tag != "" {
tagSet[tag] = true
}
}
}
for tag := range tagSet {
o = append(o, tag)
}
return
}
func (r databaseAssetRepository) ParseTags(tags string) []string {
if tags == "" {
return nil
}
return strings.Split(tags, ",")
}
func (r databaseAssetRepository) FormatTags(tags []string) string {
if len(tags) == 0 {
return ""
}
return strings.Join(tags, ",")
}
func (r databaseAssetRepository) GetDBPort(dbType string) int {
switch dbType {
case "mysql":
return 3306
case "pg":
return 5432
case "sqlserver":
return 1433
case "oracle":
return 1521
default:
return 3306
}
}
func (r databaseAssetRepository) UpdateOwner(c context.Context, ownerId string, newOwnerId string) error {
return r.GetDB(c).Model(&model.DatabaseAsset{}).Where("owner = ?", ownerId).Update("owner", newOwnerId).Error
}
func init() {
_ = strconv.Itoa(0)
_ = strings.Join(nil, "")
}
+109
View File
@@ -0,0 +1,109 @@
package repository
import (
"context"
"next-terminal/server/common"
"next-terminal/server/model"
)
var DepartmentRepository = new(departmentRepository)
type departmentRepository struct {
baseRepository
}
func (r departmentRepository) FindAll(c context.Context) (o []model.Department, err error) {
err = r.GetDB(c).Order("sort asc, name asc").Find(&o).Error
return
}
func (r departmentRepository) Find(c context.Context, pageIndex, pageSize int, name, order, field string) (o []model.Department, total int64, err error) {
db := r.GetDB(c).Model(&model.Department{})
if len(name) > 0 {
db = db.Where("name like ?", "%"+name+"%")
}
err = db.Count(&total).Error
if err != nil {
return
}
if order == "" {
order = "asc"
}
if field == "" {
field = "sort"
}
err = db.Order(field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
return
}
func (r departmentRepository) FindById(c context.Context, id string) (o model.Department, err error) {
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
return
}
func (r departmentRepository) FindByParentId(c context.Context, parentId string) (o []model.Department, err error) {
db := r.GetDB(c)
if parentId == "" {
db = db.Where("parent_id = '' or parent_id is null")
} else {
db = db.Where("parent_id = ?", parentId)
}
err = db.Order("sort asc, name asc").Find(&o).Error
return
}
func (r departmentRepository) Create(c context.Context, o *model.Department) error {
o.Created = common.NowJsonTime()
return r.GetDB(c).Create(o).Error
}
func (r departmentRepository) UpdateById(c context.Context, o *model.Department) error {
return r.GetDB(c).Updates(o).Error
}
func (r departmentRepository) DeleteById(c context.Context, id string) error {
return r.GetDB(c).Where("id = ?", id).Delete(&model.Department{}).Error
}
func (r departmentRepository) CountByParentId(c context.Context, parentId string) (total int64, err error) {
err = r.GetDB(c).Model(&model.Department{}).Where("parent_id = ?", parentId).Count(&total).Error
return
}
func (r departmentRepository) BuildTree(departments []model.Department) []map[string]interface{} {
departmentMap := make(map[string][]map[string]interface{})
var roots []map[string]interface{}
for _, dept := range departments {
node := map[string]interface{}{
"title": dept.Name,
"key": dept.ID,
"value": dept.ID,
"children": []map[string]interface{}{},
}
if dept.ParentId == "" {
roots = append(roots, node)
} else {
departmentMap[dept.ParentId] = append(departmentMap[dept.ParentId], node)
}
}
var buildChildren func(nodes []map[string]interface{})
buildChildren = func(nodes []map[string]interface{}) {
for i := range nodes {
id := nodes[i]["key"].(string)
if children, ok := departmentMap[id]; ok {
nodes[i]["children"] = children
buildChildren(children)
}
}
}
buildChildren(roots)
return roots
}
+14 -1
View File
@@ -7,6 +7,8 @@ import (
"next-terminal/server/model" "next-terminal/server/model"
) )
const SuperAdminID = `abcdefghijklmnopqrstuvwxyz`
var UserRepository = new(userRepository) var UserRepository = new(userRepository)
type userRepository struct { type userRepository struct {
@@ -18,7 +20,7 @@ func (r userRepository) FindAll(c context.Context) (o []model.User, err error) {
return return
} }
func (r userRepository) Find(c context.Context, pageIndex, pageSize int, username, nickname, mail, online, loginPolicyId, order, field string) (o []model.UserForPage, total int64, err error) { func (r userRepository) Find(c context.Context, pageIndex, pageSize int, username, nickname, mail, online, userType, loginPolicyId, order, field string) (o []model.UserForPage, total int64, err error) {
db := r.GetDB(c).Table("users").Select("users.id,users.username,users.nickname,users.mail,users.phone,users.online,users.created,users.type,users.status,users.source,users.recording,users.watermark, users.totp_secret") db := r.GetDB(c).Table("users").Select("users.id,users.username,users.nickname,users.mail,users.phone,users.online,users.created,users.type,users.status,users.source,users.recording,users.watermark, users.totp_secret")
dbCounter := r.GetDB(c).Table("users") dbCounter := r.GetDB(c).Table("users")
@@ -54,6 +56,17 @@ func (r userRepository) Find(c context.Context, pageIndex, pageSize int, usernam
dbCounter = dbCounter.Where("users.online = ?", _online) dbCounter = dbCounter.Where("users.online = ?", _online)
} }
if userType == "super-admin" {
db = db.Where("users.id = ?", SuperAdminID)
dbCounter = dbCounter.Where("id = ?", SuperAdminID)
} else if userType == "admin" {
db = db.Where("users.type = ? and users.id != ?", "admin", SuperAdminID)
dbCounter = dbCounter.Where("type = ? and id != ?", "admin", SuperAdminID)
} else if userType == "user" {
db = db.Where("users.type = ?", "user")
dbCounter = dbCounter.Where("type = ?", "user")
}
err = dbCounter.Count(&total).Error err = dbCounter.Count(&total).Error
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
+106
View File
@@ -0,0 +1,106 @@
package repository
import (
"context"
"next-terminal/server/model"
)
var UserDepartmentRepository = new(userDepartmentRepository)
type userDepartmentRepository struct {
baseRepository
}
func (r userDepartmentRepository) FindByUserId(c context.Context, userId string) (o []model.UserDepartmentRef, err error) {
err = r.GetDB(c).Where("user_id = ?", userId).Find(&o).Error
return
}
func (r userDepartmentRepository) FindByDepartmentId(c context.Context, departmentId string) (o []model.UserDepartmentRef, err error) {
err = r.GetDB(c).Where("department_id = ?", departmentId).Find(&o).Error
return
}
func (r userDepartmentRepository) FindUsersByDepartmentId(c context.Context, departmentId string) (userIds []string, err error) {
var refs []model.UserDepartmentRef
err = r.GetDB(c).Where("department_id = ?", departmentId).Find(&refs).Error
if err != nil {
return
}
for _, ref := range refs {
userIds = append(userIds, ref.UserId)
}
return
}
func (r userDepartmentRepository) SaveUserDepartments(c context.Context, userId string, departmentIds []string) error {
tx := r.GetDB(c).Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Where("user_id = ?", userId).Delete(&model.UserDepartmentRef{}).Error; err != nil {
tx.Rollback()
return err
}
for _, deptId := range departmentIds {
ref := model.UserDepartmentRef{
UserId: userId,
DepartmentId: deptId,
}
if err := tx.Create(&ref).Error; err != nil {
tx.Rollback()
return err
}
}
return tx.Commit().Error
}
func (r userDepartmentRepository) SaveDepartmentUsers(c context.Context, departmentId string, userIds []string) error {
tx := r.GetDB(c).Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Where("department_id = ?", departmentId).Delete(&model.UserDepartmentRef{}).Error; err != nil {
tx.Rollback()
return err
}
for _, userId := range userIds {
ref := model.UserDepartmentRef{
UserId: userId,
DepartmentId: departmentId,
}
if err := tx.Create(&ref).Error; err != nil {
tx.Rollback()
return err
}
}
return tx.Commit().Error
}
func (r userDepartmentRepository) RemoveUsersFromDepartment(c context.Context, departmentId string, userIds []string) error {
return r.GetDB(c).Where("department_id = ? AND user_id IN ?", departmentId, userIds).Delete(&model.UserDepartmentRef{}).Error
}
func (r userDepartmentRepository) DeleteByUserId(c context.Context, userId string) error {
return r.GetDB(c).Where("user_id = ?", userId).Delete(&model.UserDepartmentRef{}).Error
}
func (r userDepartmentRepository) DeleteByDepartmentId(c context.Context, departmentId string) error {
return r.GetDB(c).Where("department_id = ?", departmentId).Delete(&model.UserDepartmentRef{}).Error
}
func (r userDepartmentRepository) CountByDepartmentId(c context.Context, departmentId string) (total int64, err error) {
err = r.GetDB(c).Model(&model.UserDepartmentRef{}).Where("department_id = ?", departmentId).Count(&total).Error
return
}
+9 -1
View File
@@ -3,6 +3,7 @@ package repository
import ( import (
"context" "context"
"next-terminal/server/common"
"next-terminal/server/model" "next-terminal/server/model"
) )
@@ -18,7 +19,8 @@ func (r websiteRepository) FindAll(c context.Context) (o []model.Website, err er
} }
func (r websiteRepository) Find(c context.Context, pageIndex, pageSize int, keyword string) (o []model.WebsiteForPage, total int64, err error) { func (r websiteRepository) Find(c context.Context, pageIndex, pageSize int, keyword string) (o []model.WebsiteForPage, total int64, err error) {
db := r.GetDB(c).Table("websites").Select("id,name,enabled,target_url,target_host,target_port,domain,status,status_text,created,group_id,sort") db := r.GetDB(c).Table("websites").Select(`id,name,enabled,target_url,target_host,target_port,domain,status,status_text,
strftime('%s', created) * 1000 as created,group_id,sort`)
dbCounter := r.GetDB(c).Table("websites") dbCounter := r.GetDB(c).Table("websites")
if len(keyword) > 0 { if len(keyword) > 0 {
@@ -39,6 +41,7 @@ func (r websiteRepository) Find(c context.Context, pageIndex, pageSize int, keyw
} }
func (r websiteRepository) Create(c context.Context, o *model.Website) error { func (r websiteRepository) Create(c context.Context, o *model.Website) error {
o.Created = common.NowJsonTime()
return r.GetDB(c).Create(o).Error return r.GetDB(c).Create(o).Error
} }
@@ -55,3 +58,8 @@ func (r websiteRepository) FindById(c context.Context, id string) (o model.Websi
err = r.GetDB(c).Where("id = ?", id).First(&o).Error err = r.GetDB(c).Where("id = ?", id).First(&o).Error
return return
} }
func (r websiteRepository) Count(c context.Context) (total int64, err error) {
err = r.GetDB(c).Model(&model.Website{}).Count(&total).Error
return
}
+50
View File
@@ -0,0 +1,50 @@
package repository
import (
"context"
"next-terminal/server/common"
"next-terminal/server/model"
)
var WebsiteGroupRepository = new(websiteGroupRepository)
type websiteGroupRepository struct {
baseRepository
}
func (r websiteGroupRepository) FindAll(c context.Context) (o []model.WebsiteGroup, err error) {
err = r.GetDB(c).Order("sort asc, name asc").Find(&o).Error
return
}
func (r websiteGroupRepository) FindById(c context.Context, id string) (o model.WebsiteGroup, err error) {
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
return
}
func (r websiteGroupRepository) Create(c context.Context, o *model.WebsiteGroup) error {
o.Created = common.NowJsonTime()
return r.GetDB(c).Create(o).Error
}
func (r websiteGroupRepository) UpdateById(c context.Context, o *model.WebsiteGroup) error {
return r.GetDB(c).Updates(o).Error
}
func (r websiteGroupRepository) DeleteById(c context.Context, id string) error {
return r.GetDB(c).Where("id = ?", id).Delete(&model.WebsiteGroup{}).Error
}
func (r websiteGroupRepository) DeleteByParentId(c context.Context, parentId string) error {
return r.GetDB(c).Where("parent_id = ?", parentId).Delete(&model.WebsiteGroup{}).Error
}
func (r websiteGroupRepository) CountByParentId(c context.Context, parentId string) (total int64, err error) {
err = r.GetDB(c).Model(&model.WebsiteGroup{}).Where("parent_id = ?", parentId).Count(&total).Error
return
}
func (r websiteGroupRepository) DeleteAll(c context.Context) error {
return r.GetDB(c).Where("1 = 1").Delete(&model.WebsiteGroup{}).Error
}
+62 -7
View File
@@ -30,6 +30,14 @@ type MetadataShell struct {
Shell string Shell string
} }
type ExecScriptResult struct {
Name string `json:"name"`
Success bool `json:"success"`
UsedTime int64 `json:"usedTime"`
UsedTimeStr string `json:"usedTimeStr"`
Result string `json:"result"`
}
func (r ShellJob) Run() { func (r ShellJob) Run() {
if r.ID == "" { if r.ID == "" {
return return
@@ -49,6 +57,14 @@ func (r ShellJob) Run() {
func (r ShellJob) executeShellByAssets(assets []model.Asset) { func (r ShellJob) executeShellByAssets(assets []model.Asset) {
if len(assets) == 0 { if len(assets) == 0 {
jobLog := model.JobLog{
ID: utils.UUID(),
JobId: r.ID,
Timestamp: common.NowJsonTime(),
Message: "没有找到符合条件的SSH资产",
Results: "[]",
}
_ = repository.JobLogRepository.Create(context.TODO(), &jobLog)
return return
} }
@@ -59,12 +75,23 @@ func (r ShellJob) executeShellByAssets(assets []model.Asset) {
return return
} }
msgChan := make(chan string) type execResult struct {
msg string
result ExecScriptResult
}
resultChan := make(chan execResult)
for i := range assets { for i := range assets {
asset, err := AssetService.FindByIdAndDecrypt(context.TODO(), assets[i].ID) asset, err := AssetService.FindByIdAndDecrypt(context.TODO(), assets[i].ID)
if err != nil { if err != nil {
msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询数据异常「%v」", assets[i].Name, err.Error()) resultChan <- execResult{
return msg: fmt.Sprintf("资产「%v」Shell执行失败,查询数据异常「%v」", assets[i].Name, err.Error()),
result: ExecScriptResult{
Name: assets[i].Name,
Success: false,
Result: err.Error(),
},
}
continue
} }
var ( var (
@@ -79,8 +106,15 @@ func (r ShellJob) executeShellByAssets(assets []model.Asset) {
if asset.AccountType == "credential" { if asset.AccountType == "credential" {
credential, err := CredentialService.FindByIdAndDecrypt(context.TODO(), asset.CredentialId) credential, err := CredentialService.FindByIdAndDecrypt(context.TODO(), asset.CredentialId)
if err != nil { if err != nil {
msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询授权凭证数据异常「%v」", assets[i].Name, err.Error()) resultChan <- execResult{
return msg: fmt.Sprintf("资产「%v」Shell执行失败,查询授权凭证数据异常「%v」", assets[i].Name, err.Error()),
result: ExecScriptResult{
Name: asset.Name,
Success: false,
Result: err.Error(),
},
}
continue
} }
if credential.Type == nt.Custom { if credential.Type == nt.Custom {
@@ -98,33 +132,54 @@ func (r ShellJob) executeShellByAssets(assets []model.Asset) {
result, err := execute(metadataShell.Shell, asset.AccessGatewayId, ip, port, username, password, privateKey, passphrase) result, err := execute(metadataShell.Shell, asset.AccessGatewayId, ip, port, username, password, privateKey, passphrase)
elapsed := time.Since(t1) elapsed := time.Since(t1)
var msg string var msg string
var execRes ExecScriptResult
if err != nil { if err != nil {
if errors.Is(gorm.ErrRecordNotFound, err) { if errors.Is(gorm.ErrRecordNotFound, err) {
msg = fmt.Sprintf("资产「%v」Shell执行失败,请检查资产所关联接入网关是否存在,耗时「%v」", asset.Name, elapsed) msg = fmt.Sprintf("资产「%v」Shell执行失败,请检查资产所关联接入网关是否存在,耗时「%v」", asset.Name, elapsed)
} else { } else {
msg = fmt.Sprintf("资产「%v」Shell执行失败,错误内容为:「%v」,耗时「%v」", asset.Name, err.Error(), elapsed) msg = fmt.Sprintf("资产「%v」Shell执行失败,错误内容为:「%v」,耗时「%v」", asset.Name, err.Error(), elapsed)
} }
execRes = ExecScriptResult{
Name: asset.Name,
Success: false,
UsedTime: elapsed.Milliseconds(),
UsedTimeStr: elapsed.String(),
Result: err.Error(),
}
log.Debug(msg) log.Debug(msg)
} else { } else {
msg = fmt.Sprintf("资产「%v」Shell执行成功,返回值「%v」,耗时「%v」", asset.Name, result, elapsed) msg = fmt.Sprintf("资产「%v」Shell执行成功,返回值「%v」,耗时「%v」", asset.Name, result, elapsed)
execRes = ExecScriptResult{
Name: asset.Name,
Success: true,
UsedTime: elapsed.Milliseconds(),
UsedTimeStr: elapsed.String(),
Result: result,
}
log.Debug(msg) log.Debug(msg)
} }
msgChan <- msg resultChan <- execResult{msg: msg, result: execRes}
}() }()
} }
var message = "" var message = ""
var results []ExecScriptResult
for i := 0; i < len(assets); i++ { for i := 0; i < len(assets); i++ {
message += <-msgChan + "\n" res := <-resultChan
message += res.msg + "\n"
results = append(results, res.result)
} }
resultsJSON, _ := json.Marshal(results)
_ = repository.JobRepository.UpdateLastUpdatedById(context.TODO(), r.ID) _ = repository.JobRepository.UpdateLastUpdatedById(context.TODO(), r.ID)
jobLog := model.JobLog{ jobLog := model.JobLog{
ID: utils.UUID(), ID: utils.UUID(),
JobId: r.ID, JobId: r.ID,
Timestamp: common.NowJsonTime(), Timestamp: common.NowJsonTime(),
Message: message, Message: message,
Results: string(resultsJSON),
} }
_ = repository.JobLogRepository.Create(context.TODO(), &jobLog) _ = repository.JobLogRepository.Create(context.TODO(), &jobLog)
@@ -1,4 +1,4 @@
import React, {useEffect, useMemo, useState} from 'react'; import React, {useEffect, useMemo, useRef, useState} from 'react';
import {Button, Card, Input, Modal, Result, Space, Spin} from "antd"; import {Button, Card, Input, Modal, Result, Space, Spin} from "antd";
import accountApi, {AuthType} from "@/api/account-api"; import accountApi, {AuthType} from "@/api/account-api";
import {startAuthentication} from "@simplewebauthn/browser"; import {startAuthentication} from "@simplewebauthn/browser";
@@ -26,7 +26,8 @@ const MultiFactorAuthentication = ({open, handleOk, handleCancel, forceReauth =
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [otpValue, setOtpValue] = useState(''); const [otpValue, setOtpValue] = useState('');
const [showSelector, setShowSelector] = useState(false); const [showSelector, setShowSelector] = useState(false);
const [otpKey, setOtpKey] = useState(0); // 用于强制重新挂载 InputOTP const [otpKey, setOtpKey] = useState(0);
const handledRef = useRef(false);
// 查询支持的认证类型 // 查询支持的认证类型
const {data: supportedAuthTypes = []} = useQuery({ const {data: supportedAuthTypes = []} = useQuery({
@@ -58,6 +59,7 @@ const MultiFactorAuthentication = ({open, handleOk, handleCancel, forceReauth =
setOtpValue(''); setOtpValue('');
setShowSelector(false); setShowSelector(false);
setAuthType(''); setAuthType('');
handledRef.current = false;
}; };
// 清除输入 // 清除输入
@@ -81,12 +83,10 @@ const MultiFactorAuthentication = ({open, handleOk, handleCancel, forceReauth =
const validateSecurityToken = () => { const validateSecurityToken = () => {
const securityToken = sessionStorage.getItem('securityToken'); const securityToken = sessionStorage.getItem('securityToken');
if (securityToken) { if (securityToken) {
// 检查 Token 是否有效,有效则直接使用
accountApi.validateSecurityToken(securityToken).then((ok) => { accountApi.validateSecurityToken(securityToken).then((ok) => {
if (ok) { if (ok) {
storeSecurityToken(securityToken); storeSecurityToken(securityToken);
} else { } else {
// 无效则清除
sessionStorage.removeItem('securityToken'); sessionStorage.removeItem('securityToken');
} }
}); });
@@ -121,7 +121,7 @@ const MultiFactorAuthentication = ({open, handleOk, handleCancel, forceReauth =
onSuccess: (token) => token && storeSecurityToken(token), onSuccess: (token) => token && storeSecurityToken(token),
onError: (e: any) => { onError: (e: any) => {
setOtpValue(''); setOtpValue('');
setOtpKey(prev => prev + 1); // 强制重新挂载以重新获得焦点 setOtpKey(prev => prev + 1);
const code = typeof e?.code === 'number' ? e.code : 1; const code = typeof e?.code === 'number' ? e.code : 1;
setError({code, message: e?.message || String(e)}); setError({code, message: e?.message || String(e)});
} }
@@ -141,8 +141,13 @@ const MultiFactorAuthentication = ({open, handleOk, handleCancel, forceReauth =
return; return;
} }
if (handledRef.current) {
return;
}
// 没有可用的认证方式,直接通过 // 没有可用的认证方式,直接通过
if (supportedAuthTypes.length === 0) { if (supportedAuthTypes.length === 0) {
handledRef.current = true;
handleOk(''); handleOk('');
return; return;
} }
@@ -153,7 +158,6 @@ const MultiFactorAuthentication = ({open, handleOk, handleCancel, forceReauth =
setAuthType(supportedAuthTypes[0]); setAuthType(supportedAuthTypes[0]);
setShowSelector(false); setShowSelector(false);
} else { } else {
// 有可用的认证方式
if (!authType || authType === 'none') { if (!authType || authType === 'none') {
setAuthType(''); setAuthType('');
setShowSelector(true); setShowSelector(true);
@@ -170,8 +174,7 @@ const MultiFactorAuthentication = ({open, handleOk, handleCancel, forceReauth =
}, [authType, showSelector, open]); }, [authType, showSelector, open]);
useEffect(() => { useEffect(() => {
if (open && !forceReauth) { if (open && !forceReauth && !handledRef.current) {
// 检查是否有存储的 securityToken
validateSecurityToken(); validateSecurityToken();
} }
}, [open, forceReauth]); }, [open, forceReauth]);
@@ -93,24 +93,14 @@ const AuthorisedAssetPost = () => {
}} }}
request={async () => { request={async () => {
let items = await assetApi.getGroups(); let items = await assetApi.getGroups();
// let selected = await authorisedAssetApi.selected('assetId', userId, userGroupId, ''); function processNode(item: any) {
// 递归把 key 字段设置为 value,并且非叶子节点全部 disabled item.value = item.id;
function setKeyAndDisabled(item: any) {
item.value = item.key;
if (!item.isLeaf) {
// 递归处理子节点
if (item.children) { if (item.children) {
item.children.forEach(setKeyAndDisabled); item.children.forEach(processNode);
} }
} }
// if (selected.includes(item.key)) {
// item.disabled = true;
// }
}
// 对获取到的所有节点进行处理
items.forEach((item: any) => { items.forEach((item: any) => {
setKeyAndDisabled(item); processNode(item);
}); });
return items; return items;
}} }}
@@ -127,28 +117,19 @@ const AuthorisedAssetPost = () => {
}} }}
request={async () => { request={async () => {
let items = await assetApi.tree(); let items = await assetApi.tree();
// let selected = await authorisedAssetApi.selected('assetId', userId, userGroupId, ''); function processNode(item: any) {
item.value = item.id;
// 递归把 key 字段设置为 value,并且非叶子节点全部 disabled
function setKeyAndDisabled(item: any) {
item.value = item.key;
if (!item.isLeaf) { if (!item.isLeaf) {
item.disabled = true; item.disabled = true;
// 递归处理子节点
if (item.children) {
item.children.forEach(setKeyAndDisabled);
}
} else { } else {
item.title = item.title + ' (' + item.extra?.network + ')'; item.title = item.title + ' (' + item.extra?.network + ')';
} }
// if (selected.includes(item.key)) { if (item.children) {
// item.disabled = true; item.children.forEach(processNode);
// } }
} }
// 对获取到的所有节点进行处理
items.forEach((item: any) => { items.forEach((item: any) => {
setKeyAndDisabled(item); processNode(item);
}); });
return items; return items;
}} }}
+18 -3
View File
@@ -29,7 +29,18 @@ const GatewayGroupDrawer: React.FC<Props> = ({open, group, onClose}) => {
useEffect(() => { useEffect(() => {
if (open && group) { if (open && group) {
form.setFieldsValue(group); let members = group.members;
if (typeof members === 'string') {
try {
members = JSON.parse(members);
} catch {
members = [];
}
}
form.setFieldsValue({
...group,
members,
});
} else if (open) { } else if (open) {
form.resetFields(); form.resetFields();
form.setFieldsValue({ form.setFieldsValue({
@@ -41,10 +52,14 @@ const GatewayGroupDrawer: React.FC<Props> = ({open, group, onClose}) => {
const handleSubmit = async (values: any) => { const handleSubmit = async (values: any) => {
try { try {
const submitData = {
...values,
members: JSON.stringify(values.members || []),
};
if (group?.id) { if (group?.id) {
await gatewayGroupApi.updateById(group.id, values); await gatewayGroupApi.updateById(group.id, submitData);
} else { } else {
await gatewayGroupApi.create(values); await gatewayGroupApi.create(submitData);
} }
message.success(t('general.success')); message.success(t('general.success'));
onClose(true); onClose(true);
+13 -2
View File
@@ -43,6 +43,16 @@ const GatewayGroupPage: React.FC = () => {
} }
}; };
const parseMembers = (members: string | undefined): any[] => {
if (!members) return [];
if (Array.isArray(members)) return members;
try {
return JSON.parse(members);
} catch {
return [];
}
};
const columns: ProColumns<GatewayGroup>[] = [ const columns: ProColumns<GatewayGroup>[] = [
{ {
title: t('gateway_group.name'), title: t('gateway_group.name'),
@@ -69,8 +79,9 @@ const GatewayGroupPage: React.FC = () => {
dataIndex: 'members', dataIndex: 'members',
width: 100, width: 100,
render: (_, record) => { render: (_, record) => {
const enabledCount = record.members?.filter(m => m.enabled).length || 0; const members = parseMembers(record.members as unknown as string);
const totalCount = record.members?.length || 0; const enabledCount = members.filter(m => m.enabled).length;
const totalCount = members.length;
return `${enabledCount}/${totalCount}`; return `${enabledCount}/${totalCount}`;
}, },
}, },
+20 -1
View File
@@ -194,7 +194,25 @@ const ScheduledTaskLogPage = ({open, jobId, handleCancel}: Props) => {
title: t('sysops.logs.result'), title: t('sysops.logs.result'),
dataIndex: 'result', dataIndex: 'result',
key: 'result', key: 'result',
valueType: 'code', width: 200,
render: (text: string) => {
if (!text) return <Text type="secondary">-</Text>
return (
<div style={{
maxHeight: 60,
overflow: 'auto',
backgroundColor: '#f5f5f5',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
fontFamily: 'monospace',
whiteSpace: 'pre-wrap',
wordBreak: 'break-all',
}}>
{text}
</div>
)
}
}, },
] ]
case 'renew-certificate': case 'renew-certificate':
@@ -244,6 +262,7 @@ const ScheduledTaskLogPage = ({open, jobId, handleCancel}: Props) => {
options={false} options={false}
dataSource={record.results} dataSource={record.results}
pagination={false} pagination={false}
rowKey={(r: any, index: number) => r.name || r.id || index}
/> />
} }
+34 -33
View File
@@ -13,6 +13,7 @@ import {Col, Modal, Popover, Row} from "antd";
import scheduledTaskApi, {ScheduledTask} from "@/api/scheduled-task-api"; import scheduledTaskApi, {ScheduledTask} from "@/api/scheduled-task-api";
import assetApi from "@/api/asset-api"; import assetApi from "@/api/asset-api";
import ScheduledTaskRuntime from "@/pages/sysops/ScheduledTaskRuntime"; import ScheduledTaskRuntime from "@/pages/sysops/ScheduledTaskRuntime";
import {useQuery} from "@tanstack/react-query";
const ScheduledTaskModal = ({ const ScheduledTaskModal = ({
open, open,
@@ -23,13 +24,33 @@ const ScheduledTaskModal = ({
}: Props) => { }: Props) => {
let {t} = useTranslation(); let {t} = useTranslation();
const formRef = useRef<ProFormInstance>(null); const formRef = useRef<ProFormInstance>(null);
let [spec, setSpec] = useState('');
let [runtimeOpen, setRuntimeOpen] = useState(false); let [runtimeOpen, setRuntimeOpen] = useState(false);
let [specForRuntime, setSpecForRuntime] = useState('');
const {data: assetTreeData} = useQuery({
queryKey: ['asset-tree', 'ssh'],
queryFn: async () => {
let items = await assetApi.tree('ssh');
function processItem(item: any) {
item.value = item.value || item.key || item.id;
item.title = item.title || item.name || '';
if (item.isLeaf && item.extra?.network) {
item.title = item.title + ' (' + item.extra.network + ')';
}
if (item.children && item.children.length > 0) {
item.children.forEach(processItem);
}
}
if (items && items.length > 0) {
items.forEach(processItem);
}
return items || [];
},
});
const get = async () => { const get = async () => {
if (id) { if (id) {
let data = await scheduledTaskApi.getById(id); let data = await scheduledTaskApi.getById(id);
setSpec(data.spec);
return data return data
} }
return { return {
@@ -38,6 +59,14 @@ const ScheduledTaskModal = ({
} as ScheduledTask; } as ScheduledTask;
} }
const handleRuntimeOpenChange = (open: boolean) => {
if (open) {
const currentSpec = formRef.current?.getFieldValue('spec') || '';
setSpecForRuntime(currentSpec);
}
setRuntimeOpen(open);
}
return ( return (
<Modal <Modal
@@ -82,20 +111,14 @@ const ScheduledTaskModal = ({
rules={[{required: true}]} rules={[{required: true}]}
tooltip={t('sysops.spec_tooltip')} tooltip={t('sysops.spec_tooltip')}
fieldProps={{ fieldProps={{
value: spec,
onChange: (e) => {
setSpec(e.target.value);
},
addonAfter: <div className={'cursor-pointer'}> addonAfter: <div className={'cursor-pointer'}>
<Popover <Popover
content={<ScheduledTaskRuntime open={runtimeOpen} spec={spec}/>} content={runtimeOpen ? <ScheduledTaskRuntime open={runtimeOpen} spec={specForRuntime}/> : null}
title={t('sysops.spec_run_time')} title={t('sysops.spec_run_time')}
trigger="click" trigger="click"
placement="rightTop" placement="rightTop"
open={runtimeOpen} open={runtimeOpen}
onOpenChange={(open) => { onOpenChange={handleRuntimeOpenChange}
setRuntimeOpen(open);
}}
> >
{t('sysops.spec_run')} {t('sysops.spec_run')}
</Popover> </Popover>
@@ -135,34 +158,12 @@ const ScheduledTaskModal = ({
name='assetIdList' name='assetIdList'
rules={[{required: true}]} rules={[{required: true}]}
fieldProps={{ fieldProps={{
treeData: assetTreeData || [],
multiple: true, multiple: true,
showSearch: true, showSearch: true,
treeDefaultExpandAll: true, treeDefaultExpandAll: true,
treeNodeFilterProp: "title", treeNodeFilterProp: "title",
}} }}
request={async () => {
let items = await assetApi.tree('ssh');
// 递归把 key 字段设置为 value,并且非叶子节点全部 disabled
function setKeyAndDisabled(item: any) {
item.value = item.key;
if (!item.isLeaf) {
// item.disabled = true;
// 递归处理子节点
if (item.children) {
item.children.forEach(setKeyAndDisabled);
}
} else {
item.title = item.title + ' (' + item.extra?.network + ')';
}
}
// 对获取到的所有节点进行处理
items.forEach((item: any) => {
setKeyAndDisabled(item);
});
return items;
}}
/> />
} }
return <></> return <></>
+4 -10
View File
@@ -1,4 +1,4 @@
import React, {useEffect} from 'react'; import React from 'react';
import {useQuery} from "@tanstack/react-query"; import {useQuery} from "@tanstack/react-query";
import scheduledTaskApi from '@/api/scheduled-task-api'; import scheduledTaskApi from '@/api/scheduled-task-api';
@@ -14,22 +14,16 @@ const ScheduledTaskRuntime = ({open, spec}: Props) => {
queryFn: () => { queryFn: () => {
return scheduledTaskApi.getNextTenRuns(spec); return scheduledTaskApi.getNextTenRuns(spec);
}, },
enabled: open, enabled: open && !!spec,
retry: false, retry: false,
}); });
useEffect(() => {
if (open) {
query.refetch();
}
}, [open]);
return ( return (
<div className={''}> <div className={''}>
{query.isError && <div className={'text-red-500'}>Error: {query.error?.message}</div>} {query.isError && <div className={'text-red-500'}>Error: {query.error?.message}</div>}
<div className={'space-y-1'}> <div className={'space-y-1'}>
{Array.isArray(query.data) ? query.data.map((item: any) => { {Array.isArray(query.data) ? query.data.map((item: any, index: number) => {
return <div key={item.id}>{item}</div> return <div key={index}>{item}</div>
}) : []} }) : []}
</div> </div>
</div> </div>
+7 -2
View File
@@ -23,6 +23,11 @@ const ToolsPing = () => {
setLogs((prevLogs) => [...prevLogs, event.data]); setLogs((prevLogs) => [...prevLogs, event.data]);
}; };
eventSource.addEventListener('done', () => {
eventSource?.close();
setRunning(false);
});
eventSource.onerror = () => { eventSource.onerror = () => {
eventSource?.close(); eventSource?.close();
setRunning(false); setRunning(false);
@@ -67,8 +72,8 @@ const ToolsPing = () => {
</Form.Item> </Form.Item>
</Form> </Form>
<div className='border rounded-lg p-4 flex-grow'> <div className='border rounded-lg p-4 flex-grow overflow-auto'>
<pre>{logs.join("\n")}</pre> <pre className='whitespace-pre-wrap break-all'>{logs.join("\n")}</pre>
</div> </div>
</div> </div>
); );
+9 -4
View File
@@ -1,4 +1,4 @@
import {Button, Form, Input, InputNumber, Space} from 'antd'; import {Button, Form, Input, InputNumber} from 'antd';
import React, {useState} from 'react'; import React, {useState} from 'react';
import {baseUrl, getToken} from "@/api/core/requests"; import {baseUrl, getToken} from "@/api/core/requests";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
@@ -15,7 +15,7 @@ const ToolsTcping = () => {
let eventSource: EventSource | null = null; let eventSource: EventSource | null = null;
const onSearch = (host: string, port: number) => { const onSearch = (host: string, port: number) => {
if (running) return; // 防止重复启动 if (running) return;
setRunning(true); setRunning(true);
setLogs([]); setLogs([]);
@@ -25,6 +25,11 @@ const ToolsTcping = () => {
setLogs((prevLogs) => [...prevLogs, event.data]); setLogs((prevLogs) => [...prevLogs, event.data]);
}; };
eventSource.addEventListener('done', () => {
eventSource?.close();
setRunning(false);
});
eventSource.onerror = () => { eventSource.onerror = () => {
eventSource?.close(); eventSource?.close();
setRunning(false); setRunning(false);
@@ -79,8 +84,8 @@ const ToolsTcping = () => {
</Form.Item> </Form.Item>
</Form> </Form>
<div className='border rounded-lg p-4 flex-grow'> <div className='border rounded-lg p-4 flex-grow overflow-auto'>
<pre>{logs.join("\n")}</pre> <pre className='whitespace-pre-wrap break-all'>{logs.join("\n")}</pre>
</div> </div>
</div> </div>
); );
+98 -1
View File
@@ -50,7 +50,8 @@
"upload": "上传", "upload": "上传",
"offline": "离线", "offline": "离线",
"online": "在线", "online": "在线",
"search_placeholder": "关键词搜索" "search_placeholder": "关键词搜索",
"max_length": "最大长度为 {{max}} 个字符"
}, },
"actions": { "actions": {
"new": "新建", "new": "新建",
@@ -1834,5 +1835,101 @@
"403": "没有权限执行此操作", "403": "没有权限执行此操作",
"404": "请求的资源不存在", "404": "请求的资源不存在",
"500": "服务器内部错误" "500": "服务器内部错误"
},
"permissions": {
"dashboard": "数据概览",
"asset-access": "访问资产",
"asset-add": "新增资产",
"asset-edit": "编辑资产",
"asset-del": "删除资产",
"asset-copy": "复制资产",
"asset-conn-test": "连接测试",
"asset-import": "导入资产",
"asset-change-owner": "修改所有者",
"asset-authorised-user-add": "授权用户",
"asset-authorised-user-del": "取消授权用户",
"asset-authorised-user-group-add": "授权用户组",
"asset-authorised-user-group-del": "取消授权用户组",
"credential-add": "新增凭证",
"credential-del": "删除凭证",
"credential-edit": "编辑凭证",
"command-add": "新增命令",
"command-edit": "编辑命令",
"command-del": "删除命令",
"command-exec": "执行命令",
"command-change-owner": "修改所有者",
"access-gateway-add": "新增网关",
"access-gateway-del": "删除网关",
"access-gateway-edit": "编辑网关",
"online-session-disconnect": "断开会话",
"online-session-monitor": "监控会话",
"offline-session-playback": "回放会话",
"offline-session-del": "删除会话",
"offline-session-clear": "清空会话",
"offline-session-command": "命令记录",
"offline-session-reviewed": "标记已阅",
"offline-session-unreviewed": "标记未读",
"offline-session-reviewed-all": "全部标记已阅",
"login-log-del": "删除登录日志",
"login-log-clear": "清空登录日志",
"storage-log-del": "删除文件日志",
"storage-log-clear": "清空文件日志",
"session-command": "命令日志",
"job-add": "新增任务",
"job-del": "删除任务",
"job-edit": "编辑任务",
"job-run": "执行任务",
"job-change-status": "修改状态",
"job-log": "任务日志",
"job-log-clear": "清空日志",
"storage-add": "新增存储",
"storage-del": "删除存储",
"storage-edit": "编辑存储",
"storage-browse-download": "下载文件",
"storage-browse-upload": "上传文件",
"storage-browse-mkdir": "创建目录",
"storage-browse-rm": "删除文件",
"storage-browse-rename": "重命名",
"storage-browse-edit": "编辑文件",
"monitoring": "系统监控",
"access-security-add": "新增规则",
"access-security-del": "删除规则",
"access-security-edit": "编辑规则",
"login-policy-add": "新增策略",
"login-policy-del": "删除策略",
"login-policy-edit": "编辑策略",
"login-policy-bind-user": "绑定用户",
"user-add": "新增用户",
"user-del": "删除用户",
"user-edit": "编辑用户",
"user-change-password": "修改密码",
"user-enable-disable": "启用/禁用",
"user-reset-totp": "重置OTP",
"user-bind-asset": "绑定资产",
"user-unbind-asset": "解绑资产",
"login-policy-unbind-user": "解绑用户",
"user-unbind-login-policy": "解绑策略",
"role-add": "新增角色",
"role-del": "删除角色",
"role-edit": "编辑角色",
"role-detail": "角色详情",
"user-group-add": "新增用户组",
"user-group-del": "删除用户组",
"user-group-edit": "编辑用户组",
"user-group-detail": "用户组详情",
"user-group-bind-asset": "绑定资产",
"user-group-unbind-asset": "解绑资产",
"command-filter-add": "新增拦截器",
"command-filter-del": "删除拦截器",
"command-filter-edit": "编辑拦截器",
"command-filter-rule-add": "新增规则",
"command-filter-rule-put": "修改规则",
"command-filter-rule-del": "删除规则",
"strategy-add": "新增策略",
"strategy-edit": "编辑策略",
"strategy-del": "删除策略",
"strategy-detail": "策略详情",
"setting": "系统设置",
"info": "系统信息"
} }
} }