From 44110722b252ae9b6b6165185bbf79e7dc9ec6a8 Mon Sep 17 00:00:00 2001 From: dushixiang Date: Sat, 16 Jan 2021 01:27:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=8C=87=E4=BB=A4=E7=9A=84?= =?UTF-8?q?=E6=8E=88=E6=9D=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/api/asset.go | 16 +- pkg/api/command.go | 30 +- pkg/api/credential.go | 16 +- pkg/api/routes.go | 1 + pkg/model/asset.go | 18 +- pkg/model/command.go | 39 ++- pkg/model/credential.go | 15 +- web/package.json | 2 +- web/src/App.js | 56 ++-- web/src/components/asset/Asset.js | 7 +- web/src/components/command/DynamicCommand.js | 326 +++++++++++++++++-- web/src/components/credential/Credential.js | 2 +- web/src/utils/utils.js | 14 + 13 files changed, 440 insertions(+), 102 deletions(-) diff --git a/pkg/api/asset.go b/pkg/api/asset.go index 1606813..03f4113 100644 --- a/pkg/api/asset.go +++ b/pkg/api/asset.go @@ -15,6 +15,8 @@ func AssetCreateEndpoint(c echo.Context) error { return err } + account, _ := GetCurrentAccount(c) + item.Owner = account.ID item.ID = utils.UUID() item.Created = utils.NowJsonTime() @@ -32,17 +34,8 @@ func AssetPagingEndpoint(c echo.Context) error { protocol := c.QueryParam("protocol") tags := c.QueryParam("tags") - var ( - total int64 - items []model.AssetVo - ) - account, _ := GetCurrentAccount(c) - if account.Role == model.RoleUser { - items, total, _ = model.FindPageAsset(pageIndex, pageSize, name, protocol, tags, account.ID) - } else { - items, total, _ = model.FindPageAsset(pageIndex, pageSize, name, protocol, tags, "") - } + items, total, _ := model.FindPageAsset(pageIndex, pageSize, name, protocol, tags, account) return Success(c, H{ "total": total, @@ -52,7 +45,8 @@ func AssetPagingEndpoint(c echo.Context) error { func AssetAllEndpoint(c echo.Context) error { protocol := c.QueryParam("protocol") - items, _ := model.FindAssetByConditions(protocol) + account, _ := GetCurrentAccount(c) + items, _ := model.FindAssetByConditions(protocol, account) return Success(c, items) } diff --git a/pkg/api/command.go b/pkg/api/command.go index dcdac43..721955b 100644 --- a/pkg/api/command.go +++ b/pkg/api/command.go @@ -1,6 +1,7 @@ package api import ( + "errors" "github.com/labstack/echo/v4" "next-terminal/pkg/model" "next-terminal/pkg/utils" @@ -14,6 +15,8 @@ func CommandCreateEndpoint(c echo.Context) error { return err } + account, _ := GetCurrentAccount(c) + item.Owner = account.ID item.ID = utils.UUID() item.Created = utils.NowJsonTime() @@ -29,8 +32,9 @@ func CommandPagingEndpoint(c echo.Context) error { pageSize, _ := strconv.Atoi(c.QueryParam("pageSize")) name := c.QueryParam("name") content := c.QueryParam("content") + account, _ := GetCurrentAccount(c) - items, total, _ := model.FindPageCommand(pageIndex, pageSize, name, content) + items, total, _ := model.FindPageCommand(pageIndex, pageSize, name, content, account) return Success(c, H{ "total": total, @@ -68,3 +72,27 @@ func CommandGetEndpoint(c echo.Context) (err error) { } return Success(c, item) } + +func CommandChangeOwnerEndpoint(c echo.Context) (err error) { + id := c.Param("id") + + if err := PreCheckCommandPermission(c, id); err != nil { + return err + } + + owner := c.QueryParam("owner") + model.UpdateCommandById(&model.Command{Owner: owner}, id) + return Success(c, "") +} + +func PreCheckCommandPermission(c echo.Context, id string) error { + item, err := model.FindCommandById(id) + if err != nil { + return err + } + + if !HasPermission(c, item.Owner) { + return errors.New("permission denied") + } + return nil +} diff --git a/pkg/api/credential.go b/pkg/api/credential.go index 9c3e3ed..7e482c8 100644 --- a/pkg/api/credential.go +++ b/pkg/api/credential.go @@ -10,7 +10,8 @@ import ( ) func CredentialAllEndpoint(c echo.Context) error { - items, _ := model.FindAllCredential() + account, _ := GetCurrentAccount(c) + items, _ := model.FindAllCredential(account) return Success(c, items) } func CredentialCreateEndpoint(c echo.Context) error { @@ -19,6 +20,8 @@ func CredentialCreateEndpoint(c echo.Context) error { return err } + account, _ := GetCurrentAccount(c) + item.Owner = account.ID item.ID = utils.UUID() item.Created = utils.NowJsonTime() @@ -59,17 +62,8 @@ func CredentialPagingEndpoint(c echo.Context) error { pageSize, _ := strconv.Atoi(c.QueryParam("pageSize")) name := c.QueryParam("name") - var ( - total int64 - items []model.CredentialVo - ) - account, _ := GetCurrentAccount(c) - if account.Role == model.RoleUser { - items, total, _ = model.FindPageCredential(pageIndex, pageSize, name, account.ID) - } else { - items, total, _ = model.FindPageCredential(pageIndex, pageSize, name, "") - } + items, total, _ := model.FindPageCredential(pageIndex, pageSize, name, account) return Success(c, H{ "total": total, diff --git a/pkg/api/routes.go b/pkg/api/routes.go index b49e347..eefd944 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -72,6 +72,7 @@ func SetupRoutes() *echo.Echo { commands.PUT("/:id", CommandUpdateEndpoint) commands.DELETE("/:id", CommandDeleteEndpoint) commands.GET("/:id", CommandGetEndpoint) + commands.POST("/:id/change-owner", CommandChangeOwnerEndpoint) } credentials := e.Group("/credentials") diff --git a/pkg/model/asset.go b/pkg/model/asset.go index bdda511..72aa7b5 100644 --- a/pkg/model/asset.go +++ b/pkg/model/asset.go @@ -48,21 +48,27 @@ func FindAllAsset() (o []Asset, err error) { return } -func FindAssetByConditions(protocol string) (o []Asset, err error) { - db := global.DB +func FindAssetByConditions(protocol string, account User) (o []Asset, err error) { + db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resources on assets.id = resources.resource_id").Group("assets.id") + + if RoleUser == account.Role { + owner := account.ID + db = db.Where("assets.owner = ? or resources.user_id = ?", owner, owner) + } if len(protocol) > 0 { - db = db.Where("protocol = ?", protocol) + db = db.Where("assets.protocol = ?", protocol) } err = db.Find(&o).Error return } -func FindPageAsset(pageIndex, pageSize int, name, protocol, tags, owner string) (o []AssetVo, total int64, err error) { +func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account User) (o []AssetVo, total int64, err error) { db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resources on assets.id = resources.resource_id").Group("assets.id") - dbCounter := global.DB.Table("assets").Select("DISTINCT assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name").Joins("left join users on assets.owner = users.id").Joins("left join resources on assets.id = resources.resource_id") + dbCounter := global.DB.Table("assets").Select("DISTINCT assets.id").Joins("left join resources on assets.id = resources.resource_id") - if len(owner) > 0 { + if RoleUser == account.Role { + owner := account.ID db = db.Where("assets.owner = ? or resources.user_id = ?", owner, owner) dbCounter = dbCounter.Where("assets.owner = ? or resources.user_id = ?", owner, owner) } diff --git a/pkg/model/command.go b/pkg/model/command.go index 0d85f17..e6b9cee 100644 --- a/pkg/model/command.go +++ b/pkg/model/command.go @@ -10,27 +10,52 @@ type Command struct { Name string `json:"name"` Content string `json:"content"` Created utils.JsonTime `json:"created"` - Creator string `json:"creator"` + Owner string `json:"owner"` +} + +type CommandVo struct { + ID string `gorm:"primary_key" json:"id"` + Name string `json:"name"` + Content string `json:"content"` + Created utils.JsonTime `json:"created"` + Owner string `json:"owner"` + OwnerName string `json:"ownerName"` + SharerCount int64 `json:"sharerCount"` } func (r *Command) TableName() string { return "commands" } -func FindPageCommand(pageIndex, pageSize int, name, content string) (o []Command, total int64, err error) { +func FindPageCommand(pageIndex, pageSize int, name, content string, account User) (o []CommandVo, total int64, err error) { + + db := global.DB.Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resources on commands.id = resources.resource_id").Group("commands.id") + dbCounter := global.DB.Table("commands").Select("DISTINCT commands.id").Joins("left join resources on commands.id = resources.resource_id") + + if RoleUser == account.Role { + owner := account.ID + db = db.Where("commands.owner = ? or resources.user_id = ?", owner, owner) + dbCounter = dbCounter.Where("commands.owner = ? or resources.user_id = ?", owner, owner) + } - db := global.DB if len(name) > 0 { - db = db.Where("name like ?", "%"+name+"%") + db = db.Where("commands.name like ?", "%"+name+"%") + dbCounter = dbCounter.Where("commands.name like ?", "%"+name+"%") } if len(content) > 0 { - db = db.Where("content like ?", "%"+content+"%") + db = db.Where("commands.content like ?", "%"+content+"%") + dbCounter = dbCounter.Where("commands.content like ?", "%"+content+"%") } - err = db.Offset((pageIndex - 1) * pageSize).Limit(pageSize).Count(&total).Find(&o).Error + err = dbCounter.Count(&total).Error + if err != nil { + return nil, 0, err + } + + err = db.Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error if o == nil { - o = make([]Command, 0) + o = make([]CommandVo, 0) } return } diff --git a/pkg/model/credential.go b/pkg/model/credential.go index 415d535..db15a09 100644 --- a/pkg/model/credential.go +++ b/pkg/model/credential.go @@ -43,16 +43,21 @@ type CredentialSimpleVo struct { Name string `json:"name"` } -func FindAllCredential() (o []CredentialSimpleVo, err error) { - err = global.DB.Find(&o).Error +func FindAllCredential(account User) (o []CredentialSimpleVo, err error) { + db := global.DB.Table("credentials").Select("DISTINCT credentials.id,credentials.name").Joins("left join resources on credentials.id = resources.resource_id") + if account.Role == RoleUser { + db = db.Where("credentials.owner = ? or resources.user_id = ?", account.ID, account.ID) + } + err = db.Find(&o).Error return } -func FindPageCredential(pageIndex, pageSize int, name, owner string) (o []CredentialVo, total int64, err error) { +func FindPageCredential(pageIndex, pageSize int, name string, account User) (o []CredentialVo, total int64, err error) { db := global.DB.Table("credentials").Select("credentials.id,credentials.name,credentials.type,credentials.username,credentials.owner,credentials.created,users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on credentials.owner = users.id").Joins("left join resources on credentials.id = resources.resource_id").Group("credentials.id") - dbCounter := global.DB.Table("credentials").Select("DISTINCT credentials.id,credentials.name,credentials.type,credentials.username,credentials.owner,credentials.created,users.nickname as owner_name").Joins("left join users on credentials.owner = users.id").Joins("left join resources on credentials.id = resources.resource_id") + dbCounter := global.DB.Table("credentials").Select("DISTINCT credentials.id").Joins("left join resources on credentials.id = resources.resource_id") - if len(owner) > 0 { + if RoleUser == account.Role { + owner := account.ID db = db.Where("credentials.owner = ? or resources.user_id = ?", owner, owner) dbCounter = dbCounter.Where("credentials.owner = ? or resources.user_id = ?", owner, owner) } diff --git a/web/package.json b/web/package.json index 017cb4b..2c87f64 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "next-terminal", - "version": "0.0.7", + "version": "0.0.8", "private": true, "dependencies": { "@ant-design/icons": "^4.3.0", diff --git a/web/src/App.js b/web/src/App.js index d6a671b..cba9498 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -34,6 +34,7 @@ import {message} from "antd/es"; import Setting from "./components/setting/Setting"; import BatchCommand from "./components/command/BatchCommand"; import {NT_PACKAGE} from "./utils/utils"; +import {isAdmin} from "./service/permission"; const {Footer, Sider} = Layout; @@ -170,25 +171,31 @@ class App extends Component { */} - }> - }> - - 在线会话 - - + { + isAdmin() ? + <> + }> + }> + + 在线会话 + + - }> - - 历史会话 - - - + }> + + 历史会话 + + + + + }> + + 用户管理 + + + : undefined + } - }> - - 用户管理 - - }> @@ -196,11 +203,16 @@ class App extends Component { - }> - - 系统设置 - - + { + isAdmin() ? + <> + }> + + 系统设置 + + + : undefined + }
diff --git a/web/src/components/asset/Asset.js b/web/src/components/asset/Asset.js index 3af0480..10e3b64 100644 --- a/web/src/components/asset/Asset.js +++ b/web/src/components/asset/Asset.js @@ -455,11 +455,13 @@ class Asset extends Component { @@ -497,6 +499,7 @@ class Asset extends Component { @@ -703,7 +706,7 @@ class Asset extends Component { message.success('操作成功'); this.loadTableData(); } else { - message.success(result['message'], 10); + message.error(result['message'], 10); changeOwnerModalVisible = true; } }) @@ -737,7 +740,7 @@ class Asset extends Component { value={d.id}>{d.nickname})} - + diff --git a/web/src/components/command/DynamicCommand.js b/web/src/components/command/DynamicCommand.js index fd37cdd..2962847 100644 --- a/web/src/components/command/DynamicCommand.js +++ b/web/src/components/command/DynamicCommand.js @@ -1,18 +1,24 @@ import React, {Component} from 'react'; import { + Alert, Button, Checkbox, Col, Divider, + Dropdown, + Form, Input, Layout, + Menu, Modal, PageHeader, Row, + Select, Space, Table, Tooltip, + Transfer, Typography } from "antd"; import qs from "qs"; @@ -20,22 +26,22 @@ import request from "../../common/request"; import {message} from "antd/es"; import DynamicCommandModal from "./DynamicCommandModal"; import { - CodeTwoTone, DeleteOutlined, - DeleteTwoTone, - EditTwoTone, + DownOutlined, ExclamationCircleOutlined, PlusOutlined, SyncOutlined, UndoOutlined } from '@ant-design/icons'; -import {itemRender} from "../../utils/utils"; +import {compare, itemRender} from "../../utils/utils"; import Logout from "../user/Logout"; +import {hasPermission, isAdmin} from "../../service/permission"; const confirm = Modal.confirm; const {Content} = Layout; const {Title, Text} = Typography; const {Search} = Input; +const CheckboxGroup = Checkbox.Group; const routes = [ { path: '', @@ -51,6 +57,7 @@ class DynamicCommand extends Component { inputRefOfName = React.createRef(); inputRefOfContent = React.createRef(); + changeOwnerFormRef = React.createRef(); state = { items: [], @@ -70,6 +77,15 @@ class DynamicCommand extends Component { model: null, selectedRowKeys: [], delBtnLoading: false, + changeOwnerModalVisible: false, + changeSharerModalVisible: false, + changeOwnerConfirmLoading: false, + changeSharerConfirmLoading: false, + users: [], + selected: {}, + selectedSharers: [], + indeterminate: true, + checkAllChecked: false }; componentDidMount() { @@ -185,23 +201,6 @@ class DynamicCommand extends Component { }); }; - handleChecked = e => { - let checkedAssets = this.state.checkedAssets; - if (e.target.checked) { - checkedAssets.push(e.target.value); - } else { - for (let i = 0; i < checkedAssets.length; i++) { - if (checkedAssets[i].id === e.target.value.id) { - checkedAssets.splice(i, 1); - } - } - } - - this.setState({ - checkedAssets: checkedAssets - }); - }; - executeCommand = e => { let checkedAssets = this.state.checkedAssets; if (checkedAssets.length === 0) { @@ -209,14 +208,22 @@ class DynamicCommand extends Component { return; } - let assets = checkedAssets.map(item => { + let assets = this.state.assets; + let cAssets = checkedAssets.map(item => { + let name = ''; + for (let i = 0; i < assets.length; i++) { + if (assets[i]['id'] === item) { + name = assets[i]['name']; + break; + } + } return { - id: item.id, - name: item.name + id: item, + name: name } }); - window.location.href = '#/batch-command?command=' + this.state.command + '&assets=' + JSON.stringify(assets); + window.location.href = '#/batch-command?command=' + this.state.command + '&assets=' + JSON.stringify(cAssets); }; handleOk = async (formData) => { @@ -280,6 +287,65 @@ class DynamicCommand extends Component { } } + handleSearchByNickname = async nickname => { + const result = await request.get(`/users/paging?pageIndex=1&pageSize=100&nickname=${nickname}`); + if (result.code !== 1) { + message.error(result.message, 10); + return; + } + + const items = result['data']['items'].map(item => { + return {'key': item['id'], ...item} + }) + + this.setState({ + users: items + }) + } + + handleSharersChange = async targetKeys => { + this.setState({ + selectedSharers: targetKeys + }) + } + + handleShowSharer = async (record) => { + let r1 = this.handleSearchByNickname(''); + let r2 = request.get(`/resources/${record['id']}/assign`); + + await r1; + let result = await r2; + + let selectedSharers = []; + if (result['code'] !== 1) { + message.error(result['message']); + } else { + selectedSharers = result['data']; + } + + this.setState({ + selectedSharers: selectedSharers, + selected: record, + changeSharerModalVisible: true + }) + } + + onCheckAllChange = (event) => { + this.setState({ + checkedAssets: event.target.checked ? this.state.assets.map(item => item['id']) : [], + indeterminate: false, + checkAllChecked: event.target.checked + }) + } + + onChange = (list) => { + this.setState({ + checkedAssets: list, + indeterminate: !!list.length && list.length < this.state.assets.length, + checkAllChecked: list.length === this.state.assets.length + }) + } + render() { const columns = [{ @@ -320,18 +386,70 @@ class DynamicCommand extends Component { ); } + }, { + title: '所有者', + dataIndex: 'ownerName', + key: 'ownerName' + }, { + title: '创建日期', + dataIndex: 'created', + key: 'created' }, { title: '操作', key: 'action', render: (text, record) => { + const menu = ( + + + + + + {isAdmin() ? + + + : undefined + } + + + + + + + + + + + + ); + return (
- - - + + + +
) }, } ]; + if (isAdmin()) { + columns.splice(4, 0, { + title: '授权人数', + dataIndex: 'sharerCount', + key: 'sharerCount', + render: (text, record, index) => { + return + } + }); + } + const selectedRowKeys = this.state.selectedRowKeys; const rowSelection = { selectedRowKeys: this.state.selectedRowKeys, @@ -502,10 +641,127 @@ class DynamicCommand extends Component { }); }} > - {this.state.assets.map(item => { - return ({item.name}); - })} + + 全选 + + + + { + return { + label: item.name, + value: item.id, + key: item.id, + } + })} value={this.state.checkedAssets} onChange={this.onChange}/> + + + + 更换资源「{this.state.selected['name']}」的所有者 + } + visible={this.state.changeOwnerModalVisible} + confirmLoading={this.state.changeOwnerConfirmLoading} + onOk={() => { + this.setState({ + changeOwnerConfirmLoading: true + }); + + let changeOwnerModalVisible = false; + this.changeOwnerFormRef + .current + .validateFields() + .then(async values => { + let result = await request.post(`/commands/${this.state.selected['id']}/change-owner?owner=${values['owner']}`); + if (result['code'] === 1) { + message.success('操作成功'); + this.loadTableData(); + } else { + message.error(result['message'], 10); + changeOwnerModalVisible = true; + } + }) + .catch(info => { + + }) + .finally(() => { + this.setState({ + changeOwnerConfirmLoading: false, + changeOwnerModalVisible: changeOwnerModalVisible + }) + }); + }} + onCancel={() => { + this.setState({ + changeOwnerModalVisible: false + }) + }} + > + +
+ + + + + + + +
+ + 更新资源「{this.state.selected['name']}」的授权人 + } + visible={this.state.changeSharerModalVisible} + confirmLoading={this.state.changeSharerConfirmLoading} + onOk={async () => { + this.setState({ + changeSharerConfirmLoading: true + }); + + let changeSharerModalVisible = false; + + let result = await request.post(`/resources/${this.state.selected['id']}/assign?type=command&userIds=${this.state.selectedSharers.join(',')}`); + if (result['code'] === 1) { + message.success('操作成功'); + this.loadTableData(); + } else { + message.error(result['message'], 10); + changeSharerModalVisible = true; + } + + this.setState({ + changeSharerConfirmLoading: false, + changeSharerModalVisible: changeSharerModalVisible + }) + }} + onCancel={() => { + this.setState({ + changeSharerModalVisible: false + }) + }} + okButtonProps={{disabled: !hasPermission(this.state.selected['owner'])}} + > + + `${item.nickname}`} + /> diff --git a/web/src/components/credential/Credential.js b/web/src/components/credential/Credential.js index 1422f6d..f2a87a1 100644 --- a/web/src/components/credential/Credential.js +++ b/web/src/components/credential/Credential.js @@ -591,7 +591,7 @@ class Credential extends Component { message.success('操作成功'); this.loadTableData(); } else { - message.success(result['message'], 10); + message.error(result['message'], 10); changeOwnerModalVisible = true; } }) diff --git a/web/src/utils/utils.js b/web/src/utils/utils.js index f737ffd..4a650f9 100644 --- a/web/src/utils/utils.js +++ b/web/src/utils/utils.js @@ -157,3 +157,17 @@ export const NT_PACKAGE = () => { version: version } } + +export function compare(p) { + return function (m, n) { + const a = m[p]; + const b = n[p]; + if (a > b) { + return 1; + } + if (a < b) { + return -1; + } + return 0; + } +} \ No newline at end of file