运维服务中台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
14 KiB

2 years ago
'use strict';
const fs = require('fs');
2 years ago
const moment = require('moment')
const { alarmConfirmLog } = require('./alarmConfirmLog');
async function inspection (ctx) {
// 巡查
2 years ago
try {
const models = ctx.fs.dc.models;
2 years ago
const { projectAppId, screenshot = [], } = ctx.request.body
2 years ago
const now = moment().format()
const storageData = screenshot.map(s => {
return {
projectAppId,
2 years ago
screenshot: s.url,
2 years ago
createTime: now,
2 years ago
description: s.description,
2 years ago
router: s.router
2 years ago
}
})
await models.AppInspection.bulkCreate(storageData)
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function inspectionList (ctx) {
2 years ago
try {
const models = ctx.fs.dc.models;
const { clickHouse } = ctx.app.fs
const { utils: { anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs
const { pepProjectId, timeStart, timeEnd, projectId, appId, noted } = ctx.query
2 years ago
let pomsProjectIds = null
if (!projectId && !appId) {
let pomsProject = await pomsProjectRange({
ctx, pepProjectId
})
pomsProjectIds = pomsProject.map(p => p.id)
}
2 years ago
let findOption = {
where: {
},
order: [['id', 'DESC']],
include: [{
model: models.App,
required: true,
2 years ago
where: appId ? {
id: appId
} : undefined,
include: {
model: models.ProjectCorrelation,
required: true,
where: Object.assign({},
{
del: false
},
projectId ?
{
id: projectId
} : {},
pomsProjectIds ? {
id: { $in: pomsProjectIds }
} : {}
),
2 years ago
attributes: {
exclude: ['anxinProjectId', 'createTime', 'createUser']
}
}
}]
}
if (timeStart && timeEnd) {
findOption.where.createTime = { $between: [moment(timeStart).format(), moment(timeEnd).format()] }
}
2 years ago
if (noted) {
if (noted == 'noted') {
findOption.where.notedTime = { $ne: null }
} else if (noted == 'unnote') {
findOption.where.notedTime = null
}
}
const inspectionRes = await models.AppInspection.findAll(findOption)
let notedUserIds = new Set()
for (let ins of inspectionRes) {
if (ins.notedPepUserId) {
notedUserIds.add(ins.notedPepUserId)
}
}
let userPepRes = notedUserIds.size ?
await clickHouse.pepEmis.query(`SELECT DISTINCT user.id AS id, "user"."name" AS name FROM user WHERE user.id IN (${[...notedUserIds].join(',')}, -1)`).toPromise() :
2 years ago
[]
for (let ins of inspectionRes) {
if (ins.notedPepUserId) {
const corUser = userPepRes.find(up => up.id == ins.notedPepUserId)
ins.dataValues.notedPepUser = corUser ? corUser.name : ''
}
}
ctx.status = 200;
ctx.body = inspectionRes
} catch (error) {
2 years ago
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
2 years ago
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function notedInspection (ctx) {
2 years ago
try {
const models = ctx.fs.dc.models;
const { inspectionId } = ctx.request.body
const { userId, pepUserId } = ctx.fs.api
2 years ago
await models.AppInspection.update({
notedPepUserId: pepUserId,
2 years ago
notedTime: moment().format()
}, {
where: {
id: inspectionId
}
})
ctx.status = 204;
} catch (error) {
2 years ago
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
2 years ago
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function apiError (ctx) {
2 years ago
try {
const { utils: { sendAppearToWeb } } = ctx.app.fs
2 years ago
const models = ctx.fs.dc.models;
const { projectAppId, alarmContent, router, statusCode, screenshot = '', type } = ctx.request.body
2 years ago
const now = moment().format()
if (!type) {
throw '请标明告警类型 type'
}
const isRestore = statusCode == 200 || statusCode == 204
if (isRestore) {
const existRes = await models.AppAlarm.findAll({
2 years ago
where: {
projectAppId, router, alarmContent,
confirmTime: null, type
2 years ago
}
})
if (existRes.length) {
await models.AppAlarm.update({
updateTime: now,
confirmTime: now,
confirmAuto: true,
}, {
where: {
id: { $in: existRes.map(e => e.id) }
}
})
}
2 years ago
} else {
let storageData = {
projectAppId, alarmContent, router, statusCode, type
}
const existRes = await models.AppAlarm.findOne({
2 years ago
where: {
projectAppId, alarmContent, router, statusCode,
confirmTime: null, type,
2 years ago
}
})
if (existRes) {
await models.AppAlarm.update({
updateTime: now
}, {
where: {
id: existRes.id
}
})
} else {
const existCount = await models.AppAlarm.count({
where: {
}
})
storageData.serialNumber = 'WEB' + (existCount < 9 ? '0' + (existCount + 1) : existCount)
storageData.createTime = now
storageData.screenshot = screenshot
await models.AppAlarm.create(storageData)
//存告警记录
let constTypes = { 'element': "元素异常", 'apiError': "接口报错 ", 'timeout': "加载超时" }
let belongsTo = await models.ProjectApp.findOne({
where: {
id: projectAppId
},
include: [{
model: models.ProjectCorrelation,
attributes: ['id'],
where: { del: false }
}]
})
if (belongsTo) {
let appName = await models.App.findOne({
where: {
id: belongsTo.appId
},
attributes: ['name'],
})
let pId = belongsTo.projectCorrelation.dataValues.id;//归属项目
let data = {
projectCorrelationId: pId,
alarmInfo: { messageMode: 'AlarmGeneration', sourceName: appName.name, content: alarmContent, type: constTypes[type] },//AlarmGeneration代表告警首次产生
time: now,
type: '应用异常'
}
let r = await models.AlarmAppearRecord.create(data, { returning: true });
let dynamic = {
time: r.dataValues.time,
alarmAppearId: r.dataValues.id,
projectCorrelationId: r.dataValues.projectCorrelationId,
type: 1//发现
}
await models.LatestDynamicList.create(dynamic);
//消息推送到前端
await sendAppearToWeb([data], 'app');
}
}
2 years ago
}
ctx.status = 200;
} catch (error) {
2 years ago
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
2 years ago
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function apiErrorList (ctx) {
2 years ago
try {
const models = ctx.fs.dc.models;
const { clickHouse } = ctx.app.fs
const { utils: { anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs
const { keyword, errType, confirmState, state, sustainTimeStart, sustainTimeEnd, limit, page, pepProjectId, toExport } = ctx.query
let pomsProject = await pomsProjectRange({
ctx, pepProjectId,
keywordTarget: 'pepProject',
keyword,
})
const pomsProjectIds = pomsProject.map(p => p.id)
let findOption = {
distinct: true,
where: {
$or: []
},
include: [{
model: models.App,
where: {
},
attributes: {
exclude: ['projectId']
},
include: [{
model: models.ProjectCorrelation,
where: {
},
attributes: {
exclude: ['createTime', 'createUser', 'anxinProjectId',]
},
}]
}]
}
if (keyword) {
findOption.where.$or.push(
{
'$app->projectCorrelations.id$': {
$in: pomsProjectIds
},
}
)
findOption.where.$or.push(
{
'$app.name$': { $like: `%${keyword}%` }
},
)
} else {
// findOption.where['$app->projectCorrelations.id$'] = {
// $in: pomsProjectIds
// }
findOption.include[0].include[0].where.id = { $in: pomsProjectIds }
}
if (errType) {
findOption.where.type = errType // element / apiError
}
if (confirmState || state) {
if (confirmState == 'confirmd' || state == 'histroy') {
findOption.where.confirmTime = { $ne: null }
} else if (confirmState == 'unconfirmed' || state == 'new') {
findOption.where.confirmTime = null
}
}
if (sustainTimeStart && sustainTimeEnd) {
findOption.where.$or = findOption.where.$or.concat([
{
createTime: { $between: [moment(sustainTimeStart).format(), moment(sustainTimeEnd).format()] },
},
{
updateTime: { $between: [moment(sustainTimeStart).format(), moment(sustainTimeEnd).format()] }
},
{
createTime: { $lte: moment(sustainTimeStart).format() },
updateTime: { $gte: moment(sustainTimeEnd).format() },
}
])
}
if (!toExport) {//非导出时考虑分页
if (limit) {
findOption.limit = limit
}
if (page && limit) {
findOption.offset = page * limit
}
}
if (!findOption.where.$or.length) {
delete findOption.where.$or
}
const listRes = await models.AppAlarm.findAndCountAll(findOption)
for (let lr of listRes.rows) {
if (lr.app && lr.app.projectCorrelations) {
for (let p of lr.app.projectCorrelations) {
let corProjectCorrelations = pomsProject.find(pr => pr.id == p.id)
if (corProjectCorrelations) {
p.dataValues = corProjectCorrelations
}
}
}
}
if (toExport) {//数据导出相关
await exportAppAlarms(ctx, listRes);
} else {
ctx.status = 200;
ctx.body = listRes
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
2 years ago
async function exportAppAlarms (ctx, listRes) {
let typeData = { element: "元素异常", apiError: "接口报错 ", timeout: "加载超时" }
try {
const { utils: { simpleExcelDown, getExportAlarmHeader } } = ctx.app.fs;
let header = await getExportAlarmHeader('app');
let exportData = []
for (let { dataValues: item } of listRes.rows) {
let projectName = [];
for (let project of item.app.projectCorrelations) {
if (project.dataValues.name) {
projectName.push(project.dataValues.name)
} else {
projectName.push(project.dataValues.pepProject.projectName)
}
}
item.projectName = projectName.join('\r\n');//项目名称
item.appName = item.app.dataValues.name;
item.url = item.app.dataValues.url;
item.type = item.type ? typeData[item.type] : '无';//异常类型
let time = moment(item.confirmTime || item.updateTime || moment().format("YYYY-MM-DD HH:mm:ss")).diff(moment(item.createTime), 'seconds')
item.sustainTime = time < 60 ? '< 1分钟' : time > 3600 ? Math.floor(time / 3600) + '小时' + Math.floor((time - Math.floor(time / 3600) * 3600) / 60) + '分钟' : Math.floor((time - Math.floor(time / 3600) * 3600) / 60) + '分钟';
item.alarmContent = item.alarmContent || '无';
item.updateTime = item.updateTime || '无';
item.confirm = item.confirm || '无';
item.confirmTime = item.confirmTime || '无';
exportData.push(item)
}
const fileName = `应用异常列表_${moment().format('YYYYMMDDHHmmss')}` + '.xlsx'
const filePath = await simpleExcelDown({ data: exportData, header, fileName: fileName })
const fileData = fs.readFileSync(filePath);
2 years ago
ctx.status = 200;
ctx.set('Content-Type', 'application/x-xls');
ctx.set('Content-disposition', 'attachment; filename=' + encodeURI(fileName));
ctx.body = fileData;
2 years ago
} catch (error) {
2 years ago
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
2 years ago
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function confirmApiError (ctx) {
2 years ago
try {
const models = ctx.fs.dc.models;
const { confirm, appAlarmId = [], confirmPost } = ctx.request.body
2 years ago
await models.AppAlarm.update({
confirm,
confirmTime: moment().format()
2 years ago
}, {
where: {
id: { $in: appAlarmId }
2 years ago
}
})
await alarmConfirmLog(ctx, confirmPost, confirm);//告警确认日志
2 years ago
ctx.status = 204;
} catch (error) {
2 years ago
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
2 years ago
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
2 years ago
module.exports = {
inspection,
2 years ago
inspectionList,
2 years ago
notedInspection,
apiError,
2 years ago
apiErrorList,
2 years ago
confirmApiError,
2 years ago
};