wenlele 1 year ago
parent
commit
85e19f7251
  1. 3
      api/.vscode/launch.json
  2. 2
      api/app/lib/controllers/backups/index.js
  3. 98
      api/app/lib/controllers/homepage/index.js
  4. 46
      api/app/lib/controllers/latestMetadata/index.js
  5. 134
      api/app/lib/index.js
  6. 70
      api/app/lib/models/dbStatistics.js
  7. 7
      api/app/lib/routes/homepage/index.js
  8. 8
      api/config.js
  9. 2
      api/sequelize-automate.config.js
  10. 4
      web/client/src/sections/backups/containers/backupTask.js
  11. 12
      web/client/src/sections/homePage/components/dataShare.js
  12. 67
      web/client/src/sections/homePage/components/dataTop5.js
  13. 11
      web/client/src/sections/homePage/components/nodeResource.js
  14. 2
      web/client/src/sections/homePage/components/style.less
  15. 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}`);

98
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) {
@ -60,8 +61,103 @@ async function getCPUUsage() {
});
}
//查询后端同步数据库数据量总量和top5
function getDataTotalTop5(opts) {
return async function (ctx, next) {
const models = ctx.fs.dc.models;
try {
let total = await models.DbStatistics.sum('dbRecordCount')
let top5 = await models.DbStatistics.findAll({
order: [["dbRecordCount", "desc"]],
limit: 5,
offset: 0,
include: [{
model: models.DataSource,
include: [
{
model: models.ResourceCatalog,
attributes: ['id', 'name'],
}]
}],
})
ctx.status = 200;
ctx.body = { total, top5 };
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '查询后端同步数据库数据量总量和top5' }
}
}
}
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,
getClusterInfo
}

46
api/app/lib/controllers/latestMetadata/index.js

@ -3,7 +3,7 @@
const moment = require("moment/moment");
//获取资源目录
async function getResourceCatalog (ctx) {
async function getResourceCatalog(ctx) {
try {
const models = ctx.fs.dc.models;
const rslt = await models.ResourceCatalog.findAll({
@ -20,7 +20,7 @@ async function getResourceCatalog (ctx) {
}
}
//新建资源目录
async function postResourceCatalog (ctx) {
async function postResourceCatalog(ctx) {
try {
const { name, code } = ctx.request.body;
const models = ctx.fs.dc.models;
@ -49,7 +49,7 @@ async function postResourceCatalog (ctx) {
}
}
//修改资源目录
async function putResourceCatalog (ctx) {
async function putResourceCatalog(ctx) {
try {
const { id } = ctx.params;
const { name, code, description } = ctx.request.body;
@ -78,7 +78,7 @@ async function putResourceCatalog (ctx) {
}
}
//删除资源目录
async function delResourceCatalog (ctx) {
async function delResourceCatalog(ctx) {
const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
@ -117,7 +117,7 @@ async function delResourceCatalog (ctx) {
}
}
//获取库表元数据列表
async function getMetadataDatabases (ctx) {
async function getMetadataDatabases(ctx) {
try {
const models = ctx.fs.dc.models;
const { catalog, limit, offset, keywords, orderBy = 'createAt', orderDirection = 'desc', id = null, resourceId } = ctx.query;
@ -169,7 +169,7 @@ async function getMetadataDatabases (ctx) {
}
}
//获取文件元数据列表
async function getMetadataFiles (ctx) {
async function getMetadataFiles(ctx) {
try {
const models = ctx.fs.dc.models;
const { catalog, limit, offset, keywords, orderBy = 'updateAt', orderDirection = 'desc', resourceId } = ctx.query;
@ -214,7 +214,7 @@ async function getMetadataFiles (ctx) {
}
}
//获取接口元数据列表
async function getMetadataRestapis (ctx) {
async function getMetadataRestapis(ctx) {
try {
const models = ctx.fs.dc.models;
const { catalog, limit, offset, keywords, orderBy = 'createAt', orderDirection = 'desc', resourceId } = ctx.query;
@ -257,7 +257,7 @@ async function getMetadataRestapis (ctx) {
}
}
//获取元数据模型
async function getMetadataModels (ctx) {
async function getMetadataModels(ctx) {
try {
const models = ctx.fs.dc.models;
const { modelTypes } = ctx.query;
@ -281,7 +281,7 @@ async function getMetadataModels (ctx) {
}
//新建库表元数据
async function postMetadataDatabases (ctx) {
async function postMetadataDatabases(ctx) {
try {
const { name, code, catalog, parent } = ctx.request.body;
const models = ctx.fs.dc.models;
@ -318,7 +318,7 @@ async function postMetadataDatabases (ctx) {
}
//修改库表元数据
async function putMetadataDatabases (ctx) {
async function putMetadataDatabases(ctx) {
try {
const { id } = ctx.params;
const { catalog, name, code } = ctx.request.body;
@ -354,7 +354,7 @@ async function putMetadataDatabases (ctx) {
}
}
//删除库表元数据
async function delMetadataDatabases (ctx) {
async function delMetadataDatabases(ctx) {
const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
@ -409,7 +409,7 @@ async function delMetadataDatabases (ctx) {
}
}
//获取库表元数据基本信息
async function getMetadataDatabasesById (ctx) {
async function getMetadataDatabasesById(ctx) {
try {
const models = ctx.fs.dc.models;
const { id } = ctx.params;
@ -440,7 +440,7 @@ async function getMetadataDatabasesById (ctx) {
}
//打标元数据
async function postTagMetadata (ctx) {
async function postTagMetadata(ctx) {
const transaction = await ctx.fs.dc.orm.transaction();
try {
const { tags, database, file, restapi } = ctx.request.body;
@ -481,7 +481,7 @@ async function postTagMetadata (ctx) {
}
//获取元数据已打标数据
async function getTagMetadata (ctx) {
async function getTagMetadata(ctx) {
try {
const models = ctx.fs.dc.models;
const { id } = ctx.params;
@ -525,7 +525,7 @@ async function getTagMetadata (ctx) {
}
}
//申请资源
async function postMetadataResourceApplications (ctx) {
async function postMetadataResourceApplications(ctx) {
try {
const { resourceName, applyBy, resourceType, resourceId } = ctx.request.body;
if (!resourceName || !applyBy || !resourceType || !resourceId) {
@ -555,7 +555,7 @@ async function postMetadataResourceApplications (ctx) {
}
//获取元数据资源申请记录
async function getMetadataResourceApplications (ctx) {
async function getMetadataResourceApplications(ctx) {
try {
const models = ctx.fs.dc.models;
const { resourceNames, type } = ctx.query;
@ -577,7 +577,7 @@ async function getMetadataResourceApplications (ctx) {
}
//新建文件元数据
async function postMetadataFiles (ctx) {
async function postMetadataFiles(ctx) {
try {
const { name, catalog, type, fileName } = ctx.request.body;
const models = ctx.fs.dc.models;
@ -606,7 +606,7 @@ async function postMetadataFiles (ctx) {
}
}
//修改文件元数据
async function putMetadataFiles (ctx) {
async function putMetadataFiles(ctx) {
try {
const { id } = ctx.params;
const { updateFileName } = ctx.query;
@ -646,7 +646,7 @@ async function putMetadataFiles (ctx) {
}
}
//删除文件元数据
async function delMetadataFiles (ctx) {
async function delMetadataFiles(ctx) {
const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
@ -694,7 +694,7 @@ async function delMetadataFiles (ctx) {
}
//新建接口元数据
async function postMetadataRestapis (ctx) {
async function postMetadataRestapis(ctx) {
try {
const { name, catalog, method, url } = ctx.request.body;
const models = ctx.fs.dc.models;
@ -724,7 +724,7 @@ async function postMetadataRestapis (ctx) {
}
//修改接口元数据
async function putMetadataRestapis (ctx) {
async function putMetadataRestapis(ctx) {
try {
const { id } = ctx.params;
const { catalog, name, method, url } = ctx.request.body;
@ -758,7 +758,7 @@ async function putMetadataRestapis (ctx) {
}
}
//删除接口元数据
async function delMetadataRestapis (ctx) {
async function delMetadataRestapis(ctx) {
const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
@ -806,7 +806,7 @@ async function delMetadataRestapis (ctx) {
}
//获取对表的库与字段信息
async function listStructuredData (ctx) {
async function listStructuredData(ctx) {
try {
const models = ctx.fs.dc.models;
const { id, parent } = ctx.query;

134
api/app/lib/index.js

@ -13,86 +13,88 @@ const schedule = require('./schedule')
// const apiLog = require('./middlewares/api-log');
module.exports.entry = function (app, router, opts) {
app.fs.logger.log('info', '[FS-AUTH]', 'Inject auth and api mv into router.');
app.fs.logger.log('info', '[FS-AUTH]', 'Inject auth and api mv into router.');
app.fs.api = app.fs.api || {};
app.fs.opts = opts || {};
app.fs.utils = app.fs.utils || {};
app.fs.api.authAttr = app.fs.api.authAttr || {};
app.fs.api.logAttr = app.fs.api.logAttr || {};
app.fs.api = app.fs.api || {};
app.fs.opts = opts || {};
app.fs.utils = app.fs.utils || {};
app.fs.api.authAttr = app.fs.api.authAttr || {};
app.fs.api.logAttr = app.fs.api.logAttr || {};
// 顺序固定 ↓
//redisConnect(app, opts)
socketConect(app, opts)
// 顺序固定 ↓
//redisConnect(app, opts)
socketConect(app, opts)
// 实例其他平台请求方法
paasRequest(app, opts)
// 实例其他平台请求方法
paasRequest(app, opts)
// clickHouse 数据库 client
// clickHouseClient(app, opts)
// clickHouse 数据库 client
// clickHouseClient(app, opts)
// 工具类函数
utils(app, opts)
// 工具类函数
utils(app, opts)
// 定时任务
schedule(app, opts)
// 定时任务
schedule(app, opts)
//鉴权中间件
router.use(authenticator(app, opts));
//鉴权中间件
router.use(authenticator(app, opts));
// 日志记录
// router.use(apiLog(app, opts));
// 日志记录
// router.use(apiLog(app, opts));
router = routes(app, router, opts);
router = routes(app, router, opts);
};
module.exports.models = function (dc) {
// dc = { orm: Sequelize对象, ORM: Sequelize, models: {} }
// 模型关系摘出来 初始化之后再定义关系才行
fs.readdirSync(path.join(__dirname, '/models')).forEach((filename) => {
require(`./models/${filename}`)(dc)
});
const {
DataSource, AcquisitionTask, Adapter, User, MetadataDatabase, MetadataFile, MetadataRestapi, AcquisitionLog, ResourceCatalog,
BusinessMetadataDatabase, BusinessMetadataFile, BusinessMetadataRestapi,ResourceConsumption,BusinessRule,StandardDoc,RestfulApi,RestfulApiRecord
} = dc.models;
AcquisitionTask.belongsTo(DataSource, { foreignKey: 'dataSourceId', targetKey: 'id' });
DataSource.hasMany(AcquisitionTask, { foreignKey: 'dataSourceId', sourceKey: 'id' });
AcquisitionLog.belongsTo(AcquisitionTask, { foreignKey: 'task', targetKey: 'id' });
AcquisitionTask.hasMany(AcquisitionLog, { foreignKey: 'task', sourceKey: 'id' });
DataSource.belongsTo(ResourceCatalog, { foreignKey: 'mountPath', targetKey: 'id' });
ResourceCatalog.hasMany(DataSource, { foreignKey: 'mountPath', sourceKey: 'id' });
DataSource.belongsTo(Adapter, { foreignKey: 'adapterId', targetKey: 'id' });
Adapter.hasMany(DataSource, { foreignKey: 'adapterId', sourceKey: 'id' });
MetadataDatabase.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
MetadataFile.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
MetadataRestapi.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
BusinessMetadataDatabase.belongsTo(MetadataDatabase, { foreignKey: 'metadataDatabaseId', targetKey: 'id' });
MetadataDatabase.hasMany(BusinessMetadataDatabase, { foreignKey: 'metadataDatabaseId', sourceKey: 'id' });
BusinessMetadataFile.belongsTo(MetadataFile, { foreignKey: 'metadataFileId', targetKey: 'id' });
MetadataFile.hasMany(BusinessMetadataFile, { foreignKey: 'metadataFileId', sourceKey: 'id' });
BusinessMetadataRestapi.belongsTo(MetadataRestapi, { foreignKey: 'metadataRestapiId', targetKey: 'id' });
MetadataRestapi.hasMany(BusinessMetadataRestapi, { foreignKey: 'metadataRestapiId', sourceKey: 'id' });
BusinessMetadataDatabase.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
BusinessMetadataFile.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
BusinessMetadataRestapi.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
ResourceConsumption.belongsTo(User, { foreignKey: 'applyBy', targetKey: 'id' ,as:"applyUser"});
ResourceConsumption.belongsTo(User, { foreignKey: 'approveBy', targetKey: 'id',as:'approveUser' });
// dc = { orm: Sequelize对象, ORM: Sequelize, models: {} }
// 模型关系摘出来 初始化之后再定义关系才行
fs.readdirSync(path.join(__dirname, '/models')).forEach((filename) => {
require(`./models/${filename}`)(dc)
});
const {
DataSource, AcquisitionTask, Adapter, User, MetadataDatabase, MetadataFile, MetadataRestapi, AcquisitionLog, ResourceCatalog,
BusinessMetadataDatabase, BusinessMetadataFile, BusinessMetadataRestapi, ResourceConsumption, BusinessRule, StandardDoc, DbStatistics
, RestfulApi, RestfulApiRecord
} = dc.models;
AcquisitionTask.belongsTo(DataSource, { foreignKey: 'dataSourceId', targetKey: 'id' });
DataSource.hasMany(AcquisitionTask, { foreignKey: 'dataSourceId', sourceKey: 'id' });
AcquisitionLog.belongsTo(AcquisitionTask, { foreignKey: 'task', targetKey: 'id' });
AcquisitionTask.hasMany(AcquisitionLog, { foreignKey: 'task', sourceKey: 'id' });
DataSource.belongsTo(ResourceCatalog, { foreignKey: 'mountPath', targetKey: 'id' });
ResourceCatalog.hasMany(DataSource, { foreignKey: 'mountPath', sourceKey: 'id' });
DataSource.belongsTo(Adapter, { foreignKey: 'adapterId', targetKey: 'id' });
Adapter.hasMany(DataSource, { foreignKey: 'adapterId', sourceKey: 'id' });
MetadataDatabase.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
MetadataFile.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
MetadataRestapi.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
BusinessMetadataDatabase.belongsTo(MetadataDatabase, { foreignKey: 'metadataDatabaseId', targetKey: 'id' });
MetadataDatabase.hasMany(BusinessMetadataDatabase, { foreignKey: 'metadataDatabaseId', sourceKey: 'id' });
BusinessMetadataFile.belongsTo(MetadataFile, { foreignKey: 'metadataFileId', targetKey: 'id' });
MetadataFile.hasMany(BusinessMetadataFile, { foreignKey: 'metadataFileId', sourceKey: 'id' });
BusinessMetadataRestapi.belongsTo(MetadataRestapi, { foreignKey: 'metadataRestapiId', targetKey: 'id' });
MetadataRestapi.hasMany(BusinessMetadataRestapi, { foreignKey: 'metadataRestapiId', sourceKey: 'id' });
BusinessMetadataDatabase.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
BusinessMetadataFile.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
BusinessMetadataRestapi.belongsTo(User, { foreignKey: 'createBy', targetKey: 'id' });
ResourceConsumption.belongsTo(User, { foreignKey: 'applyBy', targetKey: 'id', as: "applyUser" });
ResourceConsumption.belongsTo(User, { foreignKey: 'approveBy', targetKey: 'id', as: 'approveUser' });
BusinessRule.belongsTo(StandardDoc, { foreignKey: 'ruleBasis', targetKey: 'id' });
StandardDoc.hasMany(BusinessRule, { foreignKey: 'ruleBasis', targetKey: 'id' });
RestfulApi.belongsTo(RestfulApiRecord, { foreignKey: 'restServiceId', targetKey: 'id' });
RestfulApiRecord.belongsTo(RestfulApi, { foreignKey: 'restServiceId', targetKey: 'id' });
DbStatistics.belongsTo(DataSource, { foreignKey: 'sourceId', targetKey: 'id' });
DataSource.hasMany(DbStatistics, { foreignKey: 'sourceId', sourceKey: 'id' });
RestfulApiRecord.belongsTo(RestfulApi, { foreignKey: 'restServiceId', targetKey: 'id' });
};

70
api/app/lib/models/dbStatistics.js

@ -0,0 +1,70 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const DbStatistics = sequelize.define("dbStatistics", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: "nextval(\"dbStatistics_id_seq\"::regclass)",
comment: null,
primaryKey: true,
field: "id",
autoIncrement: false
},
dbName: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "dbName",
autoIncrement: false
},
sourceId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "sourceId",
autoIncrement: false
},
dbRecordCount: {
type: DataTypes.BIGINT,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "dbRecordCount",
autoIncrement: false
},
time: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "time",
autoIncrement: false
},
description: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "description",
autoIncrement: false
}
}, {
tableName: "dbStatistics",
comment: "",
indexes: []
});
dc.models.DbStatistics = DbStatistics;
return DbStatistics;
};

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

@ -7,4 +7,11 @@ module.exports = function (app, router, opts, AuthCode) {
app.fs.api.logAttr['GET/homepage/node/resources'] = { content: '获取节点资源信息', visible: true };
router.get('/homepage/node/resources', backups.getNodeResources(opts))
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
}
}
],

2
api/sequelize-automate.config.js

@ -26,7 +26,7 @@ module.exports = {
dir: './app/lib/models', // 指定输出 models 文件的目录
typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义
emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir`
// tables: ['safety_cultivate'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
tables: ['dbStatistics'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性
tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中
ignorePrefix: ['t_',], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面

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 = [

67
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: {
@ -44,6 +37,27 @@ function DataTop5(props) {
show: false,
},
},
tooltip: {
confine: true,
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
backgroundColor: 'rgba(13,30,44, 0.7)',
borderColor: 'rgba(3, 65, 118, 0.8)',
textStyle: {
color: '#fff',
},
formatter: function (params) {
var name = params[0].name
if (name.length > 20) {
name = name.replace(/(.{20})/g, '$1<br>') // 每 30 个字符添加一个换行符
}
var content = name
return content + ' : <b>' + params[0].value + '</b>万条'
}
},
grid: {
top: 13,
bottom: -10,
@ -64,14 +78,15 @@ function DataTop5(props) {
},
formatter(value, index) {
let str = '', num = 'TOP' + (index + 1)
let valueHandle = value.length > 10 ? value.substring(0, 10) + '...' : value
if (index === 0) {
str = '{a| ' + num + '}{title| ' + value + '}'
str = '{a| ' + num + '}{title| ' + valueHandle + '}'
} else if (index === 1) {
str = '{b| ' + num + '}{title| ' + value + '}'
str = '{b| ' + num + '}{title| ' + valueHandle + '}'
} else if (index === 2) {
str = '{c| ' + num + '}{title| ' + value + '}'
str = '{c| ' + num + '}{title| ' + valueHandle + '}'
} else {
str = '{d| ' + num + '}{title| ' + value + '}'
str = '{d| ' + num + '}{title| ' + valueHandle + '}'
}
return str
},

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 * 20,
});
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>

2
web/client/src/sections/homePage/components/style.less

@ -123,7 +123,7 @@
.data_total {
position: absolute;
bottom: 13%;
right: 7%;
left: 79%;
.data_number {
line-height: 25px;

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