diff --git a/.gitignore b/.gitignore index 6a6e7cd..6f4c7f7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ web/build *.log *.db .DS_Store -.eslintcache \ No newline at end of file +.eslintcache +.env \ No newline at end of file diff --git a/pkg/api/asset.go b/pkg/api/asset.go index cc53d28..9c50390 100644 --- a/pkg/api/asset.go +++ b/pkg/api/asset.go @@ -53,9 +53,10 @@ func AssetPagingEndpoint(c echo.Context) error { owner := c.QueryParam("owner") sharer := c.QueryParam("sharer") userGroupId := c.QueryParam("userGroupId") + ip := c.QueryParam("ip") account, _ := GetCurrentAccount(c) - items, total, err := model.FindPageAsset(pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId) + items, total, err := model.FindPageAsset(pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip) if err != nil { return err } diff --git a/pkg/api/middleware.go b/pkg/api/middleware.go index 533998e..f0ea3c0 100644 --- a/pkg/api/middleware.go +++ b/pkg/api/middleware.go @@ -27,7 +27,7 @@ func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc { func Auth(next echo.HandlerFunc) echo.HandlerFunc { - urls := []string{"download", "recording", "login", "static", "favicon", "logo"} + urls := []string{"download", "recording", "login", "static", "favicon", "logo", "asciinema"} return func(c echo.Context) error { // 路由拦截 - 登录身份、资源权限判断等 diff --git a/pkg/api/overview.go b/pkg/api/overview.go index c35c977..cacac77 100644 --- a/pkg/api/overview.go +++ b/pkg/api/overview.go @@ -22,12 +22,12 @@ func OverviewCounterEndPoint(c echo.Context) error { asset int64 ) if model.TypeUser == account.Type { - countUser, _ = model.CountUser() + countUser, _ = model.CountOnlineUser() countOnlineSession, _ = model.CountOnlineSession() credential, _ = model.CountCredentialByUserId(account.ID) asset, _ = model.CountAssetByUserId(account.ID) } else { - countUser, _ = model.CountUser() + countUser, _ = model.CountOnlineUser() countOnlineSession, _ = model.CountOnlineSession() credential, _ = model.CountCredential() asset, _ = model.CountAsset() diff --git a/pkg/model/asset.go b/pkg/model/asset.go index 6f3c236..2613fe3 100644 --- a/pkg/model/asset.go +++ b/pkg/model/asset.go @@ -63,8 +63,8 @@ func FindAssetByConditions(protocol string, account User) (o []Asset, err error) return } -func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account User, owner, sharer, userGroupId string) (o []AssetVo, total int64, err error) { - db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id") +func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account User, owner, sharer, userGroupId, ip string) (o []AssetVo, total int64, err error) { + db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created,assets.tags, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id") dbCounter := global.DB.Table("assets").Select("DISTINCT assets.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id") if TypeUser == account.Type { @@ -103,6 +103,11 @@ func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account dbCounter = dbCounter.Where("assets.name like ?", "%"+name+"%") } + if len(ip) > 0 { + db = db.Where("assets.ip like ?", "%"+ip+"%") + dbCounter = dbCounter.Where("assets.ip like ?", "%"+ip+"%") + } + if len(protocol) > 0 { db = db.Where("assets.protocol = ?", protocol) dbCounter = dbCounter.Where("assets.protocol = ?", protocol) diff --git a/pkg/model/user.go b/pkg/model/user.go index de3de6e..d26f147 100644 --- a/pkg/model/user.go +++ b/pkg/model/user.go @@ -107,7 +107,7 @@ func DeleteUserById(id string) { global.DB.Where("user_id = ?", id).Delete(&ResourceSharer{}) } -func CountUser() (total int64, err error) { - err = global.DB.Find(&User{}).Count(&total).Error +func CountOnlineUser() (total int64, err error) { + err = global.DB.Where("online = ?", true).Find(&User{}).Count(&total).Error return } diff --git a/web/.env b/web/.env new file mode 100644 index 0000000..2d9fca9 --- /dev/null +++ b/web/.env @@ -0,0 +1 @@ +REACT_APP_ENV=development \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index 03e5238..ce996c9 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "next-terminal", - "version": "0.1.1", + "version": "0.2.4", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5363,9 +5363,9 @@ "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==" }, "dayjs": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.9.6.tgz", - "integrity": "sha512-HngNLtPEBWRo8EFVmHFmSXAjtCX8rGNqeXQI0Gh7wCTSqwaKgPIDqu9m07wABVopNwzvOeCb+2711vQhDlcIXw==" + "version": "1.10.4", + "resolved": "https://registry.npm.taobao.org/dayjs/download/dayjs-1.10.4.tgz?cache=0&sync_timestamp=1611309982734&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdayjs%2Fdownload%2Fdayjs-1.10.4.tgz", + "integrity": "sha1-jlRKm4aD9heD9XCYCoqA6vVKseI=" }, "debug": { "version": "3.1.0", diff --git a/web/package.json b/web/package.json index 2963780..2fc0300 100644 --- a/web/package.json +++ b/web/package.json @@ -6,6 +6,7 @@ "@ant-design/icons": "^4.3.0", "antd": "^4.8.4", "axios": "^0.21.1", + "dayjs": "^1.10.4", "guacamole-common-js": "^1.2.0", "qs": "^6.9.4", "react": "^16.14.0", diff --git a/web/src/common/constants.js b/web/src/common/constants.js index 2d8289f..2c22a59 100644 --- a/web/src/common/constants.js +++ b/web/src/common/constants.js @@ -1,22 +1,5 @@ -// prod -let wsPrefix; -if (window.location.protocol === 'https:') { - wsPrefix = 'wss:' -} else { - wsPrefix = 'ws:' -} - -export const server = ''; -export const wsServer = wsPrefix + window.location.host; -export const prefix = window.location.protocol + '//' + window.location.host; - -// dev -// export const server = '//127.0.0.1:8088'; -// export const wsServer = 'ws://127.0.0.1:8088'; -// export const prefix = ''; - export const PROTOCOL_COLORS = { - 'rdp': 'red', + 'rdp': 'cyan', 'ssh': 'blue', 'telnet': 'geekblue', 'vnc': 'purple' diff --git a/web/src/common/env.js b/web/src/common/env.js new file mode 100644 index 0000000..4c947ff --- /dev/null +++ b/web/src/common/env.js @@ -0,0 +1,28 @@ +function env() { + if (process.env.REACT_APP_ENV === 'development') { + // 本地开发环境 + return { + server: '//127.0.0.1:8088', + wsServer: 'ws://127.0.0.1:8088', + prefix: '', + } + } else { + // 生产环境 + let wsPrefix; + if (window.location.protocol === 'https:') { + wsPrefix = 'wss:' + } else { + wsPrefix = 'ws:' + } + return { + server: '', + wsServer: wsPrefix + window.location.host, + prefix: window.location.protocol + '//' + window.location.host, + } + } +} +export default env(); + +export const server = env().server; +export const wsServer = env().wsServer; +export const prefix = env().prefix; \ No newline at end of file diff --git a/web/src/common/request.js b/web/src/common/request.js index de55ed6..8384553 100644 --- a/web/src/common/request.js +++ b/web/src/common/request.js @@ -1,5 +1,5 @@ import axios from 'axios' -import {server} from "./constants"; +import {server} from "./env"; import {message} from 'antd'; import {getHeaders} from "../utils/utils"; diff --git a/web/src/components/access/Access.js b/web/src/components/access/Access.js index 980ff5e..d748635 100644 --- a/web/src/components/access/Access.js +++ b/web/src/components/access/Access.js @@ -3,7 +3,7 @@ import Guacamole from 'guacamole-common-js'; import {Affix, Button, Col, Drawer, Dropdown, Form, Input, Menu, message, Modal, Row} from 'antd' import qs from "qs"; import request from "../../common/request"; -import {wsServer} from "../../common/constants"; +import {wsServer} from "../../common/env"; import { AppstoreTwoTone, CopyTwoTone, diff --git a/web/src/components/access/Console.js b/web/src/components/access/Console.js index a9178ad..ce87321 100644 --- a/web/src/components/access/Console.js +++ b/web/src/components/access/Console.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import "xterm/css/xterm.css" import {Terminal} from "xterm"; import qs from "qs"; -import {wsServer} from "../../common/constants"; +import {wsServer} from "../../common/env"; import "./Console.css" import {getToken, isEmpty} from "../../utils/utils"; import {FitAddon} from 'xterm-addon-fit' diff --git a/web/src/components/access/FileSystem.js b/web/src/components/access/FileSystem.js index 7c6eca5..f3104be 100644 --- a/web/src/components/access/FileSystem.js +++ b/web/src/components/access/FileSystem.js @@ -22,7 +22,7 @@ import { } from "@ant-design/icons"; import qs from "qs"; import request from "../../common/request"; -import {server} from "../../common/constants"; +import {server} from "../../common/env"; import Upload from "antd/es/upload"; import {download, getFileName, getToken, isEmpty, renderSize} from "../../utils/utils"; import './FileSystem.css' @@ -107,7 +107,7 @@ class FileSystem extends Component { if (isEmpty(key)) { key = '/'; } - let result = await request.get(`${server}/sessions/${this.state.sessionId}/ls?dir=${key}`); + let result = await request.get(`/sessions/${this.state.sessionId}/ls?dir=${key}`); if (result['code'] !== 1) { message.error(result['message']); return; diff --git a/web/src/components/access/Monitor.js b/web/src/components/access/Monitor.js index 6d6d2b4..d3c2bdd 100644 --- a/web/src/components/access/Monitor.js +++ b/web/src/components/access/Monitor.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import Guacamole from 'guacamole-common-js'; import {Modal, Result, Spin} from 'antd' import qs from "qs"; -import {wsServer} from "../../common/constants"; +import {wsServer} from "../../common/env"; import {getToken} from "../../utils/utils"; import './Access.css' diff --git a/web/src/components/access/Term.js b/web/src/components/access/Term.js index a63022f..8cc6ed1 100644 --- a/web/src/components/access/Term.js +++ b/web/src/components/access/Term.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import "xterm/css/xterm.css" import {Terminal} from "xterm"; import qs from "qs"; -import {wsServer} from "../../common/constants"; +import {wsServer} from "../../common/env"; import {getToken, isEmpty} from "../../utils/utils"; import {FitAddon} from 'xterm-addon-fit'; import "./Access.css" diff --git a/web/src/components/asset/Asset.js b/web/src/components/asset/Asset.js index e857615..27901e2 100644 --- a/web/src/components/asset/Asset.js +++ b/web/src/components/asset/Asset.js @@ -26,9 +26,8 @@ import qs from "qs"; import AssetModal from "./AssetModal"; import request from "../../common/request"; import {message} from "antd/es"; -import {itemRender} from "../../utils/utils"; - - +import {isEmpty, itemRender} from "../../utils/utils"; +import dayjs from 'dayjs'; import { DeleteOutlined, DownOutlined, @@ -41,6 +40,7 @@ import {PROTOCOL_COLORS} from "../../common/constants"; import Logout from "../user/Logout"; import {hasPermission, isAdmin} from "../../service/permission"; + const confirm = Modal.confirm; const {Search} = Input; const {Content} = Layout; @@ -59,6 +59,7 @@ const routes = [ class Asset extends Component { inputRefOfName = React.createRef(); + inputRefOfIp = React.createRef(); changeOwnerFormRef = React.createRef(); state = { @@ -172,6 +173,17 @@ class Asset extends Component { this.loadTableData(query); }; + handleSearchByIp = ip => { + let query = { + ...this.state.queryParams, + 'pageIndex': 1, + 'pageSize': this.state.queryParams.pageSize, + 'ip': ip, + } + + this.loadTableData(query); + }; + handleTagsChange = tags => { this.setState({ selectedTags: tags @@ -459,18 +471,48 @@ class Asset extends Component { dataIndex: 'protocol', key: 'protocol', render: (text, record) => { - - return ({text}); + const title = `${record['ip'] + ':' + record['port']}` + return ( + + {text} + + ) + } + }, { + title: '标签', + dataIndex: 'tags', + key: 'tags', + render: tags => { + if (!isEmpty(tags)) { + let tagDocuments = [] + let tagArr = tags.split(','); + for (let i = 0; i < tagArr.length; i++) { + if (tags[i] === '-') { + continue; + } + tagDocuments.push({tagArr[i]}) + } + return tagDocuments; + } } }, { title: '状态', dataIndex: 'active', key: 'active', render: text => { + if (text) { - return (); + return ( + + + + ) } else { - return (); + return ( + + + + ) } } }, { @@ -480,7 +522,14 @@ class Asset extends Component { }, { title: '创建日期', dataIndex: 'created', - key: 'created' + key: 'created', + render: (text, record) => { + return ( + + {dayjs(text).fromNow()} + + ) + } }, { title: '操作', @@ -598,10 +647,10 @@ class Asset extends Component {
- + 资产列表 - + + +