wenlele 3 years ago
parent
commit
a79a64f3cf
  1. 4
      api/app/lib/controllers/alarm/app.js
  2. 218
      api/app/lib/controllers/project/bind.js
  3. 17
      api/app/lib/controllers/project/index.js
  4. 2
      api/app/lib/controllers/push/config.js
  5. 8
      api/app/lib/middlewares/authenticator.js
  6. 3
      api/app/lib/routes/project/index.js
  7. 35
      web/client/src/sections/install/actions/roles.js
  8. 170
      web/client/src/sections/install/components/systemModal.jsx
  9. 2
      web/client/src/sections/install/containers/roles.jsx
  10. 188
      web/client/src/sections/install/containers/system.jsx
  11. 7
      web/client/src/utils/webapi.js

4
api/app/lib/controllers/alarm/app.js

@ -108,10 +108,10 @@ async function notedInspection (ctx) {
try {
const models = ctx.fs.dc.models;
const { inspectionId } = ctx.request.body
const { userId } = ctx.fs.api
const { userId, pepUserId } = ctx.fs.api
await models.AppInspection.update({
notedPepUserId: userId,
notedPepUserId: pepUserId,
notedTime: moment().format()
}, {
where: {

218
api/app/lib/controllers/project/bind.js

@ -1,74 +1,148 @@
'use strict';
const moment = require('moment')
async function bindAnxin2pep (ctx) {
const transaction = await ctx.fs.dc.orm.transaction();
try {
// const models = ctx.fs.dc.models;
// const { clickHouse } = ctx.app.fs
// const { bindId, name, pepProjectId, anxinProjectId = [], app = [] } = ctx.request.body
const models = ctx.fs.dc.models;
const { clickHouse } = ctx.app.fs
const { userId, pepUserId } = ctx.fs.api
const { bindId, name, pepProjectId, anxinProjectId = [], app = [] } = ctx.request.body
// let bindId_ = bindId
// const existRes = await models.ProjectCorrelation.findOne({
// where: {
// pepProjectId: pepProjectId
// }
// })
let bindId_ = bindId
const now = moment()
const existRes = await models.ProjectCorrelation.findOne({
where: {
pepProjectId: pepProjectId
},
include: {
model: models.ProjectApp
}
})
// let storageData = {
// name, pepProjectId, anxinProjectId,
// }
// if (bindId_) {
// if (!existRes) {
// throw '尚无已绑定的项企项目'
// }
// // 修改
// await models.ProjectCorrelation.update(storageData, {
// where: {
// pepProjectId: pepProjectId
// },
// transaction
// })
// } else {
// // 新增
// if (existRes) {
// // 但是有之前的数据
// if (existRes.del) {
// // 不过之前的删除了
let storageData = {
name, pepProjectId, anxinProjectId,
del: false,
}
// 已经创建过的 app
let existApp = []
// 新增的
let createAppData = []
// url 相同需要更新的 解除 lock 状态
let updateAppData = []
// 没有出现但存在的 叠加 lock 状态
let lockAppData = []
// } else {
// // 没有删除 重复添加
// throw '当前项企项目已绑定'
// }
// } else {
// const createRes = await models.ProjectCorrelation.create(storageData, {
// transaction
// })
// bindId_ = createRes.id
if (bindId_) {
if (!existRes) {
throw '尚无已绑定的项企项目'
}
// 修改
await models.ProjectCorrelation.update(storageData, {
where: {
pepProjectId: pepProjectId
},
transaction
})
existApp = existRes.projectApps
} else {
// 新增
if (existRes) {
// 但是有之前的数据
if (existRes.del) {
// 不过之前的删除了
await models.ProjectCorrelation.update(storageData, {
where: {
pepProjectId: pepProjectId
},
transaction
})
existApp = existRes.projectApps
} else {
// 没有删除 重复添加
throw '当前项企项目已绑定'
}
} else {
storageData.createUser = userId;
storageData.createTime = now.format()
const createRes = await models.ProjectCorrelation.create(storageData, {
transaction
})
bindId_ = createRes.id
}
}
// await models.ProjectApp.bulkCreate(app.map((a, i, arr) => {
// if (!a.name || !a.url) {
// throw `${a.name} ${a.url} 缺少必要参数`
// }
// let curUrlArr = a.url.split('://')
// if(curUrlArr.length < 2){
// throw `${a.name} ${a.url} url 地址错误`
// }
// for (let ii = i; ii < arr.length; ii++) {
// let
// }
app.forEach((a, i, arr) => {
if (!a.name || !a.url) {
throw `${a.name} ${a.url} 缺少必要参数`
}
let curUrlArr = a.url.split('://')
if (curUrlArr.length < 2) {
throw `${a.name} ${a.url} url 地址错误`
}
curUrlArr.shift()
let curUrl = curUrlArr.join('://')
// 先判断传过来的数据有没有重复
for (let ii = i + 1; ii < arr.length; ii++) {
let curForUrlArr = arr[ii].url.split('://')
curForUrlArr.shift()
if (curUrl == curForUrlArr.join('://')) {
throw `${a.name} ${a.url}${arr[ii].name} ${arr[ii].url} 地址重复`
}
}
// return {
// name: a.name,
// url: a.url,
// 再判断和已有的有没有重复
let existSameApp = existApp.find(ea => {
let curForUrlArr = ea.url.split('://')
curForUrlArr.shift()
return curUrl == curForUrlArr.join('://')
})
if (existSameApp) {
updateAppData.push({
id: existSameApp.id,
name: a.name,
url: a.url,
projectId: bindId_,
lock: false,
})
existSameApp.addAgain = true
} else {
createAppData.push({
name: a.name,
url: a.url,
projectId: bindId_,
lock: false,
})
}
lockAppData = existApp.filter(esa => !esa.addAgain).map(esa => {
return {
id: esa.id,
// name: esa.name,
// url: esa.url,
// projectId: bindId_,
// lock: false,
// }
// }))
// }
// }
lock: true,
}
})
})
createAppData.length ?
await models.ProjectApp.bulkCreate(createAppData, {
transaction
}) : null
// await transaction.commit();
// ctx.status = 204;
updateAppData.length || lockAppData.length ?
await Promise.all(updateAppData.concat(lockAppData).map(ud => {
return models.ProjectApp.update(ud, {
where: {
id: ud.id
},
transaction
})
}))
: null
await transaction.commit();
ctx.status = 204;
} catch (error) {
await transaction.rollback();
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
@ -79,7 +153,31 @@ async function bindAnxin2pep (ctx) {
}
}
async function del (ctx) {
try {
const models = ctx.fs.dc.models;
const { bindId } = ctx.query
await models.ProjectCorrelation.update({
del: true
}, {
where: {
id: bindId
}
})
ctx.status = 20;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = {
bindAnxin2pep
bindAnxin2pep,
del
};

17
api/app/lib/controllers/project/index.js

@ -24,13 +24,14 @@ async function pomsProject (ctx) {
try {
const models = ctx.fs.dc.models;
const { clickHouse } = ctx.app.fs
const { userId } = ctx.fs.api
const { userId, pepUserId } = ctx.fs.api
const { limit, page } = ctx.query
let findOption = {
where: {
del: false
},
distinct: true,
include: {
model: models.ProjectApp,
where: {
@ -49,14 +50,11 @@ async function pomsProject (ctx) {
findOption.offset = page * limit
}
const proRes = await models.ProjectCorrelation.findAll(findOption)
delete findOption.limit
delete findOption.offset
const proCount = await models.ProjectCorrelation.count(findOption)
const proRes = await models.ProjectCorrelation.findAndCountAll(findOption)
let pepProjectIds = new Set()
let anxinProjectIds = new Set()
for (let p of proRes) {
for (let p of proRes.rows) {
pepProjectIds.add(p.pepProjectId)
for (let ap of p.anxinProjectId) {
anxinProjectIds.add(ap)
@ -71,7 +69,7 @@ async function pomsProject (ctx) {
[]
for (let p of proRes) {
for (let p of proRes.rows) {
const corPro = pepProjectRes.find(pp => pp.id == p.pepProjectId)
p.dataValues.pepProjectName = corPro.project_name
let nextAnxinProject = anxinProjectRes.filter(ap => p.anxinProjectId.includes(ap.id))
@ -79,10 +77,7 @@ async function pomsProject (ctx) {
delete p.dataValues.anxinProjectId
}
ctx.status = 200;
ctx.body = {
count: proCount,
rows: proRes
}
ctx.body = proRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;

2
api/app/lib/controllers/push/config.js

@ -48,7 +48,7 @@ async function list (ctx) {
async function edit (ctx) {
try {
const models = ctx.fs.dc.models;
const { userId } = ctx.fs.api
const { userId, pepUserId } = ctx.fs.api
const { pushId, name, pepProjectId = [], alarmType = [], receiverPepUserId = [], timeType = [], disable } = ctx.request.body
let storageData = {

8
api/app/lib/middlewares/authenticator.js

@ -76,11 +76,17 @@ let authorizeToken = async function (ctx, token) {
})
const { userInfo, expired } = authorizeRes;
if (expired && moment().valueOf() <= moment(expired).valueOf()) {
const pomsUser = await ctx.app.fs.dc.models.User.findOne({
where: {
pepUserId: userInfo.id
}
})
rslt = {
'authorized': userInfo.authorized,
'resources': (userInfo || {}).resources || [],
};
ctx.fs.api.userId = userInfo.id;
ctx.fs.api.userId = pomsUser.id;
ctx.fs.api.pepUserId = userInfo.id;
ctx.fs.api.userInfo = userInfo;
ctx.fs.api.token = token;
}

3
api/app/lib/routes/project/index.js

@ -10,6 +10,9 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['POST/project/bind'] = { content: '绑定安心云、项目管理项目', visible: true };
router.post('/project/bind', projectBind.bindAnxin2pep);
app.fs.api.logAttr['DEL/project/bind/:bindId'] = { content: '删除安心云、项目管理项目绑定关系', visible: true };
router.delete('/project/bind/:bindId', projectBind.del);
app.fs.api.logAttr['GET/project/anxincloud'] = { content: '获取安心云项目', visible: true };
router.get('/project/anxincloud', project.projectAnxincloud);

35
web/client/src/sections/install/actions/roles.js

@ -60,7 +60,7 @@ export function postOrganizationUser (data) {//添加/编辑成员
reducer: { name: "" },
});
}
export function deteleOrganizationAdmin(data) {
export function deteleOrganizationAdmin (data) {//删除管理员
let pomsUserId = ''
let msg = ''
if (data) {
@ -77,3 +77,36 @@ export function deteleOrganizationAdmin(data) {
reducer: {},
});
}
export function getProjectPoms (query) {//获取已绑定项目
return (dispatch) => basicAction({
type: "get",
dispatch: dispatch,
actionType: "GET_PROJECT_POMS",
query: query,
url: `${ApiTable.getProjectPoms}`,
msg: { option: "获取已绑定项目" },
reducer: { name: "ProjectPoms", params: { noClear: true } },
});
}
export function getProjectAnxincloud (query) {//获取安心云项目
return (dispatch) => basicAction({
type: "get",
dispatch: dispatch,
actionType: "GET_PROJECT_ANXINCLOUD",
query: query,
url: `${ApiTable.getProjectAnxincloud}`,
msg: { option: "获取安心云项目" },
reducer: { name: "ProjectPoms", params: { noClear: true } },
});
}
export function getProjectPmanage (query) {//获取PEP项目管理项目
return (dispatch) => basicAction({
type: "get",
dispatch: dispatch,
actionType: "GET_PROJECT_PMANAGE",
query: query,
url: `${ApiTable.getProjectPmanage}`,
msg: { option: "获取PEP项目管理项目" },
reducer: { name: "ProjectPoms", params: { noClear: true } },
});
}

170
web/client/src/sections/install/components/systemModal.jsx

@ -0,0 +1,170 @@
import React, { useState, useRef, useEffect } from "react";
import { connect } from "react-redux";
import { Modal, Form } from "@douyinfe/semi-ui";
import { IconAlertCircle } from '@douyinfe/semi-icons';
function adminModal (props) {
const {
close,
visible,
dispatch,
pepList,
actions,
adminEdit,//
editObj,
} = props;
const { install } = actions;
const form = useRef();//
const [disablePeople, setDisablePeople] = useState(true); //
const [peopleList, setPeopleList] = useState([]); //List
const [departmentId, setDepartmentId] = useState(); //id
const [peopleId, setPeopleId] = useState(); //id
//
useEffect(() => {
if (editObj.id) {
let departmentList = []
for (let i = 0; i < pepList.length; i++) {
if (pepList[i].id == editObj.departments[0].id) {
departmentList = pepList[i].users
}
}
setPeopleList(departmentList)
setDepartmentId(editObj.departments[0].id)
setPeopleId(editObj.pepUserId)
setDisablePeople(false)
}
}, []);
function handleOk () {
//
form.current
.validate()
.then((values) => {
if (adminEdit) {
dispatch(install.deteleOrganizationAdmin({id:editObj.id,msg:''})).then(
dispatch(install.postOrganizationUser({ role: ['admin'], pepUserId: values.pepUserId, msg: '修改管理员' })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
)
}
else {
dispatch(install.postOrganizationUser({ role: ['admin'], pepUserId: values.pepUserId, msg: '新增管理员' })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
}
})
}
function handleCancel () {
close();
//
}
return (
<>
<Modal
title="管理员设置"
okText="确定"
cancelText="取消"
visible={visible}
onOk={handleOk}
width={607}
onCancel={handleCancel}
>
<div style={{ margin: "0px 25px" }}>
<div style={{ width: '100%', height: 32, background: '#F4F8FF', borderRadius: 2, border: '1px solid #C7E1FF', display: 'flex', alignItems: 'center' }}>
<div style={{ display: 'flex', alignItems: 'center', marginLeft: 12 }}><IconAlertCircle style={{ color: '#0F7EFB' }} /></div>
<div style={{ color: '#0F7EFB', fontSize: 12, marginLeft: 8 }}>成员成为管理员后拥有平台所有权限和项目成员的普通角色会被禁用</div>
</div>
<Form
allowEmpty
labelPosition="left"
labelAlign="right"
labelWidth="90px"
onValueChange={(values, field) => {
for (var key in field) {
if (key == 'department') {
if (values.department >= 0) {
let departmentList = []
for (let i = 0; i < pepList.length; i++) {
if (pepList[i].id == values.department) {
departmentList = pepList[i].users
}
}
setPeopleList(departmentList)
setDisablePeople(false)
form.current.setValue('pepUserId', undefined);
}
else {
setPeopleList([])
setDisablePeople(true)
form.current.setValue('pepUserId', undefined);
}
}
}
}}
getFormApi={(formApi) => (form.current = formApi)}
>
<div>
<Form.Select
label="选择部门:"
field="department"
placeholder="请选择部门"
style={{ width: 417 }}
rules={[{ required: true, message: "请选择部门" }]}
initValue={departmentId || ""}
showClear
>
{
pepList.map((item, index) => {
return (
<Form.Select.Option key={index} value={item.id}>
{item.name}
</Form.Select.Option>
)
})
}
</Form.Select>
</div>
<div>
<Form.Select
label="选择人员:"
field="pepUserId"
placeholder="请选择人员"
style={{ width: 417 }}
rules={[{ required: true, message: "请选择人员" }]}
initValue={peopleId || ""}
showClear
disabled={disablePeople}
>
{
peopleList.map((item, index) => {
return (
<Form.Select.Option key={item.id} value={item.id}>
{item.name}
</Form.Select.Option>
)
})
}
</Form.Select>
</div>
</Form>
</div>
</Modal>
</>
);
}
function mapStateToProps (state) {
const { auth, global, members } = state;
return {
// loading: members.isRequesting,
user: auth.user,
actions: global.actions,
// members: members.data,
};
}
export default connect(mapStateToProps)(adminModal);

2
web/client/src/sections/install/containers/roles.jsx

@ -266,7 +266,7 @@ const Roles = (props) => {
},
])
// const [data, setdata] = useState([])//
const tableData = useRef([]); //
const tableData = useRef([]); //
const page = useRef(query.page);//
const [limits, setLimits] = useState()//
const mylimits = useRef(); //

188
web/client/src/sections/install/containers/system.jsx

@ -1,16 +1,142 @@
import React, { useEffect } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Button } from '@douyinfe/semi-ui';
import { Skeleton, Button, Pagination, Table, Popconfirm } from '@douyinfe/semi-ui';
import { SkeletonScreen, } from "$components";
import '../style.less'
const Example = (props) => {
const { dispatch, actions, user, loading, socket } = props
const { install } = actions;
const [query, setQuery] = useState({ limit: 10, page: 0 }); //
const [limits, setLimits] = useState()//
const mylimits = useRef(); //
const [selected, setSelected] = useState([]) //
const tableData = useRef([]); //
useEffect(() => {
// dispatch(actions.example.getMembers(user.orgId))
// getProjectAnxincloud//
// getProjectPmanage//PEP
dispatch(install.getProjectAnxincloud(query)).then((res) => {//
console.log('getProjectAnxincloud',res.payload.data);
})
dispatch(install.getProjectPmanage(query)).then((res) => {//PEP
console.log('getProjectPmanage',res.payload.data);
})
}, [])
useEffect(() => {
getProjectPomsList();
}, [query]);
function getProjectPomsList () {
dispatch(install.getProjectPoms(query)).then((res) => {//
if (res.success) {
let mytableData = res.payload.data.rows;
for (let index = 0; index < mytableData.length; index++) {
mytableData[index].key = mytableData[index].id
}
tableData.current = mytableData;
setLimits(res.payload.data.count)
mylimits.current = res.payload.data.rows.length
}
})
}
const [columns, setColumns] = useState([//
{
title: "序号",
dataIndex: "index",
render: (text, r, index) => {
return index + 1;
},
},
// {
// title: '',
// render: (_, row) => {
// let departmentsArr = []
// for (let i = 0; i < row.departments.length; i++) {
// departmentsArr.push(row.departments[i].name)
// }
// return (
// <div style={{ display: 'flex', alignItems: 'center' }}>
// <div>
// {row.name}
// </div>
// <div style={{ padding: '2px 4px', background: '#E8F3FF', borderRadius: 2, fontSize: 12, color: '#005ABD', marginLeft: 4 }}>
// {row.departments[0].name}
// </div>
// {
// row.departments.length > 1 ? (
// <Tooltip content={departmentsArr.join(',')} trigger="click" style={{ lineHeight: 2 }}>
// <div style={{ fontSize: 14, color: '#005ABD', marginLeft: 8, cursor: "pointer", }}>
// +{row.departments.length - 1}
// </div>
// </Tooltip>
// ) : ('')
// }
// </div>
// )
// }
// },
{
title: '关联时间',
dataIndex: "createTime",
render: (_, row) => {
return (
<div>
{row.createTime}
</div>
)
}
},
{
title: "管理",
width: "20%",
dataIndex: "text",
render: (_, row) => {
return (
<div style={{ display: "flex" }}>
<Button
theme="borderless"
onClick={() => {
setMemberModal(true);
setEditObj(row)
setMemberEdit(true)
}}
>
修改
</Button>
<Popconfirm
title="删除后,成员将无法登录。"
arrowPointAtCenter={false}
showArrow={true}
position="topRight"
onConfirm={() => {
dispatch(install.putOrganizationUser({ pomsUserId: row?.id, deleted: true, msg: '删除成员' })).then(() => {
if (page.current > 0 && mylimits.current < 2) {
setQuery({ limit: 10, page: page.current - 1 })
} else {
setQuery({ limit: 10, page: page.current })
}
})
}}
>
<Button theme="borderless">删除</Button>
</Popconfirm>
</div>
);
},
},
])
const rowSelection = {
selectedRowKeys: selected,
onSelect: (record, selected) => {
console.log(`select row: ${selected}`, record);
},
onSelectAll: (selected, selectedRows) => {
console.log(`select all rows: ${selected}`, selectedRows);
},
onChange: (selectedRowKeys, selectedRows) => {
setSelected(selectedRows.map(v => v.key))
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
}
return (
<>
<div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px' }}>
@ -41,7 +167,55 @@ const Example = (props) => {
</div>
</div>
<div style={{ marginTop: 20 }}>
111
<Skeleton
loading={false}
active={true}
placeholder={SkeletonScreen()}
>
<Table
columns={columns}
dataSource={tableData.current}
bordered={false}
empty="暂无数据"
style={{ padding: "0px 20px", }}
pagination={false}
onRow={(record, index) => {
if (index % 1 === 0) {
return { style: { background: '#FAFCFF' } }
}
}}
rowSelection={rowSelection}
/>
</Skeleton>
<div
style={{
display: "flex",
justifyContent: "space-between",
padding: "20px 20px",
}}
>
<div>
<div style={{ display: 'inline-block', lineHeight: '30px' }}>勾选<span style={{ fontWeight: 400, color: '#0F7EFB', }}>{selected.length}</span>问题</div>
<Button onClick={() => setSelected([])} style={{ width: 93, height: 24, borderRadius: '1px', border: '1px solid #0F7EFB', color: '#0F7EFB', fontWeight: 400, margin: '0 10px' }}>全选</Button>
<Button type='primary' theme='solid' style={{ width: 93, height: 24, borderRadius: '1px', border: '1px solid #0F7EFB', color: '#FFFFFF', fontWeight: 400, }}>批量确认</Button>
</div>
<div style={{ display: 'flex', }}>
<span style={{ lineHeight: "30px" }}>
{limits}个问题
</span>
<Pagination
className="22"
total={limits}
showSizeChanger
currentPage={query.page + 1}
pageSizeOpts={[10, 20, 30, 40]}
onChange={(currentPage, pageSize) => {
setQuery({ limit: pageSize, page: currentPage - 1 });
page.current = currentPage - 1
}}
/>
</div>
</div>
</div>
</div>
</>

7
web/client/src/utils/webapi.js

@ -16,12 +16,19 @@ export const ApiTable = {
login: "login",
logout: "logout",
//设置-鉴权管理
getOrganizationDeps: 'organization/deps',//获取项企(PEP)全部部门及其下用户
getOrganizationUser: 'organization/user',//获取成员列表
putOrganizationUser: 'organization/user/{pomsUserId}',//更新成员状态
postOrganizationUser: 'organization/user',//添加/编辑成员
deteleOrganizationAdmin: 'organization/admin/{pomsUserId}',//删除管理员
//设置-关系映射
getProjectPoms:'project/poms',//获取已绑定项目列表
getProjectAnxincloud:'project/anxincloud',//获取安心云项目
getProjectPmanage:'project/pmanage',//获取PEP项目管理项目
//告警
getProjectPoms: 'project/poms', //获取已绑定项目

Loading…
Cancel
Save