Browse Source

(*)大屏数据接入

master
peng.peng 2 years ago
parent
commit
35b0cd59b8
  1. 3
      api/.vscode/launch.json
  2. 2
      api/app/lib/controllers/backups/index.js
  3. 70
      api/app/lib/controllers/homepage/index.js
  4. 4
      api/app/lib/routes/homepage/index.js
  5. 8
      api/config.js
  6. 4
      web/client/src/sections/backups/containers/backupTask.js
  7. 12
      web/client/src/sections/homePage/components/dataShare.js
  8. 37
      web/client/src/sections/homePage/components/dataTop5.js
  9. 11
      web/client/src/sections/homePage/components/nodeResource.js
  10. 3
      web/client/src/sections/homePage/components/util.js

3
api/.vscode/launch.json

@ -17,7 +17,8 @@
//
"-g postgres://FashionAdmin:123456@10.8.30.39:5432/GovernmentDataResourceCenter",
// "-g postgres://FashionAdmin:123456@10.8.30.156:5432/gdrcenter",
"-b http://10.8.30.161:31420"
"-b http://10.8.30.161:31420",
"-s http://10.8.30.161:32258"
]
},
{

2
api/app/lib/controllers/backups/index.js

@ -134,6 +134,7 @@ function restore(opts) {
const { id, source, databases: { database, host, password, port, user } } = ctx.request.body
await models.Backups.update({
state: '恢复中',
restoreStart: moment()
}, { where: { id: id } })
//调用后端备份接口
const url = backupsUrl + `/restoreDB?dbHost=${host}&dbPort=${port}&user=${user}&password=${password}&dbName=${database}&backFileName=${source}`;
@ -142,6 +143,7 @@ function restore(opts) {
models.Backups.update({
state: code == 200 ? '恢复成功' : '恢复失败',
log: code == 200 ? '' : message,
restoreEnd: moment(),
restoreDatabases: ctx.request.body.databases
}, { where: { id: id } })
if (code != 200) ctx.fs.logger.error(`path: ${ctx.path}, error: ${message}`);

70
api/app/lib/controllers/homepage/index.js

@ -2,6 +2,7 @@
const moment = require('moment');
const diskinfo = require('diskinfo');
const os = require('os-utils');
const request = require("superagent");
function getNodeResources(opts) {
return async function (ctx, next) {
@ -90,8 +91,73 @@ function getDataTotalTop5(opts) {
}
async function queryToken_(ctx) {
let rslt = null;
try {
const { k8s } = ctx;
const params = {
grant_type: 'password',
username: 'admin',
password: 'Fashion123',
client_id: 'kubesphere',
client_secret: 'kubesphere',
}
const url = k8s + '/oauth/token'
rslt = await request.post(url).send(params)
.set('Content-Type', 'application/x-www-form-urlencoded')
return (rslt || {}).body || null;
} catch (err) {
throw err
}
}
function mathRound(use, total) {
return Math.round(parseFloat(use) / parseFloat(total) * 1000) / 10
}
function getClusterInfo(opts) {
return async function (ctx, next) {
const { k8s } = opts;
let errMsg = { message: '获取节点资源失败' }
try {
const token = await queryToken_(opts);
const url = k8s + '/kapis/monitoring.kubesphere.io/v1alpha3/cluster?metrics_filter=cluster_cpu_usage%7Ccluster_cpu_total%7Ccluster_memory_usage_wo_cache%7Ccluster_memory_total%7Ccluster_disk_size_usage%7Ccluster_disk_size_capacity%7Ccluster_pod_running_count%7Ccluster_pod_quota%24'
let res = await request.get(url)
.set('Authorization', `Bearer ${token.access_token}`)
.set('Content-Type', 'application/json;charset=utf-8');
if (res.body && res.body.results) {
let results = res.body.results;
let cpuUsage = ((results.find(s => s.metric_name == 'cluster_cpu_usage') || {}).data || {}).result[0].value[1];
let cpuTotal = ((results.find(s => s.metric_name == 'cluster_cpu_total') || {}).data || {}).result[0].value[1];
let diskUsage = ((results.find(s => s.metric_name == 'cluster_disk_size_usage') || {}).data || {}).result[0].value[1];
let diskTotal = ((results.find(s => s.metric_name == 'cluster_disk_size_capacity') || {}).data || {}).result[0].value[1];
let memoryUsage = ((results.find(s => s.metric_name == 'cluster_memory_usage_wo_cache') || {}).data || {}).result[0].value[1];
let memoryTotal = ((results.find(s => s.metric_name == 'cluster_memory_total') || {}).data || {}).result[0].value[1];
ctx.status = 200;
ctx.body = {
cpu: mathRound(cpuUsage, cpuTotal),
disk: mathRound(diskUsage, diskTotal),
memory: mathRound(memoryUsage, memoryTotal),
};
} else {
ctx.status = 400;
ctx.body = errMsg;
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = errMsg
}
}
}
module.exports = {
getNodeResources,
getDataTotalTop5
getDataTotalTop5,
getClusterInfo
}

4
api/app/lib/routes/homepage/index.js

@ -10,4 +10,8 @@ module.exports = function (app, router, opts, AuthCode) {
app.fs.api.logAttr['GET/homepage/datatotal/top5'] = { content: '获取数据总量和top5', visible: true };
router.get('/homepage/datatotal/top5', backups.getDataTotalTop5(opts))
app.fs.api.logAttr['GET/homepage/cluters'] = { content: '获取集群资源节点信息', visible: true };
router.get('/homepage/datatotal/cluters', backups.getClusterInfo(opts))
};

8
api/config.js

@ -11,6 +11,7 @@ const dev = process.env.NODE_ENV == 'development';
args.option(['p', 'port'], '启动端口');
args.option(['g', 'pg'], 'postgre 服务 URL');
args.option(['b', 'backups'], '后端数据库备份恢复接口地址');
args.option(['s', 'kubesphere'], 'kubesphere地址');
const flags = args.parse(process.argv);
@ -23,7 +24,9 @@ const QINIU_AK = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak;
const QINIU_SK = process.env.ANXINCLOUD_QINIU_SECRETKEY || flags.qnsk;
const BACKUPS_URL = process.env.BACKUPS_URL || flags.backups;
if (!DB || !BACKUPS_URL) {
const KUBESPHERE_URL = process.env.KUBESPHERE_URL || flags.kubesphere;
if (!DB || !BACKUPS_URL || !KUBESPHERE_URL) {
console.log('缺少启动参数,异常退出');
args.showHelp();
process.exit(-1);
@ -68,7 +71,8 @@ const product = {
}
},
pssaRequest: [],
backupsUrl: BACKUPS_URL
backupsUrl: BACKUPS_URL,
k8s: KUBESPHERE_URL
}
}
],

4
web/client/src/sections/backups/containers/backupTask.js

@ -96,17 +96,19 @@ function Member(props) {
title: '恢复数据源',
dataIndex: 'restoreDatabases',
width: '8%',
render: (text, record) => record?.restoreDatabases?.database
render: (text, record) => record?.restoreDatabases?.displayName
},
{
title: '恢复开始时间',
width: '7%',
dataIndex: 'restoreStart',
render: (text, record) => { return record?.restoreStart ? moment(record?.restoreStart).format('YYYY-MM-DD HH:mm:ss') : '-' }
},
{
title: '恢复结束时间',
width: '7%',
dataIndex: 'restoreEnd',
render: (text, record) => { return record?.restoreEnd ? moment(record?.restoreEnd).format('YYYY-MM-DD HH:mm:ss') : '-' }
},
{
title: '异常日志',

12
web/client/src/sections/homePage/components/dataShare.js

@ -1,8 +1,15 @@
import React, { useEffect, useState } from 'react'
import Box from './public/table-card';
import { ApiTable, useFsRequest } from '$utils';
import { mathRound } from './util';
function DataShare(props) {
const { data: dataTotal = {} } = useFsRequest({
url: 'homepage/datatotal/top5',
pollingInterval: 1000 * 60,
cacheKey: 'datatotal',
});
const renderItem = (s) => {
return <div className='_item_content'>
<div className={'_item_icon' + s.key} />
@ -14,8 +21,9 @@ function DataShare(props) {
</div>
</div>
}
const leftData = [
{ key: '1', data: 2000, unit: '条', title: '共享库数据总量' },
{ key: '1', data: mathRound(dataTotal?.total), unit: '条', title: '共享库数据总量' },
{ key: '2', data: 2000, unit: '次', title: '访问接口总次数' },
{ key: '3', data: 2000, unit: '个', title: '访问接口用户总数' }]
const rightData = [

37
web/client/src/sections/homePage/components/dataTop5.js

@ -2,32 +2,25 @@ import React, { useEffect, useState } from 'react'
import Box from './public/table-card';
import ReactEcharts from 'echarts-for-react';
import './style.less';
import { useFsRequest } from '$utils';
import { mathRound } from './util';
function DataTop5(props) {
const { cardContentHeight } = props;
const { data: dataTotal = {} } = useFsRequest({
url: 'homepage/datatotal/top5',
pollingInterval: 1000 * 60,
cacheKey: 'datatotal',
});
const renderBody = () => {
let chartData = [
{
name: '工商局',
value: 12245,
},
{
name: '人社局',
value: 11211,
},
{
name: '市政单位',
value: 11165,
},
{
name: '住建局',
value: 11145,
},
{
name: '环保局',
value: 11128,
},
]
let chartData = dataTotal?.top5?.map(x => {
return {
name: x?.dataSource?.resourceCatalog?.name,
value: mathRound(x.dbRecordCount),
}
}) || []
let options = {
xAxis: {
splitLine: {

11
web/client/src/sections/homePage/components/nodeResource.js

@ -1,22 +1,27 @@
import React, { useEffect, useState } from 'react'
import Box from './public/table-card';
import { ApiTable, useFsRequest } from '$utils';
import './style.less';
function NodeResource(props) {
const { data: cluters = {} } = useFsRequest({
url: 'homepage/datatotal/cluters',
pollingInterval: 1000 * 10,
});
const renderBody = () => {
return <div className='node-resource-container'>
<div className='_item'>
<div className='_noderesource_data'>97.2<span className='_percent'>%</span></div>
<div className='_noderesource_data'>{cluters?.disk}<span className='_percent'>%</span></div>
<div className='_noderesource_title'>硬盘</div>
<div className='disk_icon' />
</div>
<div className='_item'>
<div className='_noderesource_data'>97.2<span className='_percent'>%</span></div>
<div className='_noderesource_data'>{cluters?.memory}<span className='_percent'>%</span></div>
<div className='_noderesource_title'>内存</div>
<div className='memory_icon' />
</div>
<div className='_item'>
<div className='_noderesource_data'>97.2<span className='_percent'>%</span></div>
<div className='_noderesource_data'>{cluters?.cpu}<span className='_percent'>%</span></div>
<div className='_noderesource_title'>CPU</div>
<div className='cpu_icon' />
</div>

3
web/client/src/sections/homePage/components/util.js

@ -0,0 +1,3 @@
export const mathRound = (number) => {
return number ? Math.round(number / 1000) / 10 : 0
}
Loading…
Cancel
Save