Compare commits

...

30 Commits

Author SHA1 Message Date
巴林闲侠 02e3f7393b API_ANXINYUN_URL 2 years ago
巴林闲侠 3871adf4bf 当前登录用户IP: 2 years ago
巴林闲侠 2111b014e2 API_ANXINYUN_URL 2 years ago
巴林闲侠 9f41ef4445 MQTT_VIDEO_SERVER 暂时注释 2 years ago
巴林闲侠 225b6b5ec2 MQTT_VIDEO_SERVER 暂时注释 2 years ago
巴林闲侠 fddf05c742 sql 2 years ago
巴林闲侠 14f8d00ea5 font 2 years ago
巴林闲侠 bfba015319 font 2 years ago
deartibers de3eba495e Merge branch 'dev' of 2 years ago
deartibers 7e43ef5d7f 导航栏 2 years ago
wenlele f033f4cc6b Merge branch 'dev' of https://gitea.anxinyun.cn/gao.zhiyuan/OperationalService into dev 2 years ago
wenlele 156643b093 策略,视频,应用 2 years ago
巴林闲侠 cfc316897f app.js cat 2 years ago
巴林闲侠 a38d71ec5a A CAT 2 years ago
巴林闲侠 7f8a7f83ff app巡检接口 2 years ago
deartibers ccfda4ed32 Merge branch 'dev' of ssh://gitea.anxinyun.cn:2022/gao.zhiyuan/OperationalService into dev 2 years ago
deartibers fa84a546c6 导航栏联动 2 years ago
巴林闲侠 f7f9b309f0 Merge branch 'dev' of https://gitea.anxinyun.cn/gao.zhiyuan/OperationalService into dev 2 years ago
巴林闲侠 4ae13f8ff6 用户登录时长统计入库 2 years ago
deartibers 8596e5c6a0 Merge branch 'dev' of 2 years ago
deartibers f5bda27f5e 导航 2 years ago
巴林闲侠 b9b0b28645 登录及其配置 2 years ago
wenlele 981b25f0c2 Merge branch 'dev' of https://gitea.anxinyun.cn/gao.zhiyuan/Operation 2 years ago
wenlele 22daa473aa 告警 2 years ago
deartibers 80676e5ed2 导航联动 2 years ago
巴林闲侠 538bc90235 51la统计 2 years ago
巴林闲侠 431a3350f8 reinit 2 years ago
deartibers 72c12478dd 导航 2 years ago
巴林闲侠 8728e7f2ab reinit 2 years ago
巴林闲侠 4d47205fa6 reinit 2 years ago
  1. 20
      api/.vscode/launch.json
  2. 128
      api/app/lib/controllers/alarm/app.js
  3. 108
      api/app/lib/controllers/auth/index.js
  4. 157
      api/app/lib/controllers/organization/index.js
  5. 25
      api/app/lib/controllers/project/index.js
  6. 71
      api/app/lib/index.js
  7. 218
      api/app/lib/middlewares/authenticator.js
  8. 65
      api/app/lib/models/action_log.js
  9. 61
      api/app/lib/models/alarm_push_config.js
  10. 106
      api/app/lib/models/app_alarm.js
  11. 74
      api/app/lib/models/app_inspection.js
  12. 35
      api/app/lib/models/ax_project.js
  13. 56
      api/app/lib/models/project_app.js
  14. 70
      api/app/lib/models/project_correlation.js
  15. 47
      api/app/lib/models/quick_link.js
  16. 115
      api/app/lib/models/user.js
  17. 20
      api/app/lib/routes/alarm/index.js
  18. 11
      api/app/lib/routes/auth/index.js
  19. 17
      api/app/lib/routes/organization/index.js
  20. 8
      api/app/lib/routes/project/index.js
  21. 36
      api/app/lib/schedule/index.js
  22. 45
      api/app/lib/service/mqttVideoServer.js
  23. 51
      api/app/lib/service/socket.js
  24. 116
      api/config.js
  25. 4
      api/sequelize-automate.config.js
  26. 3
      script/.vscode/settings.json
  27. 543
      script/0.0.1/0_init_db.sql
  28. 186
      script/1.0.0/1.init_tables.sql
  29. 1
      script/1.0.0/2.init_superadmin.sql
  30. 14
      script/1.1.0/1.camera_remark.sql
  31. 17
      script/1.1.1/data/1_update_status_code_data/.vscode/launch.json
  32. 69
      script/1.1.1/data/1_update_status_code_data/index.js
  33. 16
      script/1.1.1/data/1_update_status_code_data/package.json
  34. BIN
      script/1.1.1/data/1_update_status_code_data/云录制错误码.xlsx
  35. 141
      script/1.1.1/data/2.insert_camera_status.sql
  36. 140
      script/1.1.1/schema/1.create_camera_status_table.sql
  37. 21
      script/1.1.2/schema/1.update_camera_status_config.sql
  38. 5
      script/1.1.2/schema/2.update_push_log.sql
  39. 4
      script/1.1.2/schema/3.update_camera_channel_no.sql
  40. 13
      script/1.1.2/schema/4.create_camera_status_offline_log.sql
  41. 19
      script/1.2.1/schema/1.create_application.sql
  42. 4
      script/1.3.0/schema/1.alert_gbcamera_did.sql
  43. 90
      script/1.3.0/schema/2.create_mirror_table.sql
  44. 17
      script/1.3.1/data/1_sync_camera_data/.vscode/launch.json
  45. 8
      script/1.3.1/data/1_sync_camera_data/Dockerfile
  46. 156
      script/1.3.1/data/1_sync_camera_data/index.js
  47. 18
      script/1.3.1/data/1_sync_camera_data/package.json
  48. BIN
      web/client/assets/fonts/YouSheBiaoTiHei-2.ttf
  49. BIN
      web/client/assets/images/problem/setup.png
  50. 3
      web/client/index.ejs
  51. 10
      web/client/index.html
  52. 19
      web/client/src/app.jsx
  53. 5
      web/client/src/components/setup.jsx
  54. 2
      web/client/src/index.jsx
  55. 5
      web/client/src/index.less
  56. 2
      web/client/src/layout/actions/global.js
  57. 13
      web/client/src/layout/actions/webSocket.js
  58. 162
      web/client/src/layout/components/header/index.jsx
  59. 14
      web/client/src/layout/components/header/index.less
  60. 202
      web/client/src/layout/components/sider/index.jsx
  61. 15
      web/client/src/layout/components/sider/index.less
  62. 432
      web/client/src/layout/containers/layout/index.jsx
  63. 53
      web/client/src/layout/index.jsx
  64. 34
      web/client/src/sections/auth/actions/auth.js
  65. 12
      web/client/src/sections/auth/containers/login.jsx
  66. 14
      web/client/src/sections/console/actions/console.js
  67. 7
      web/client/src/sections/console/actions/index.js
  68. 49
      web/client/src/sections/console/containers/console.jsx
  69. 5
      web/client/src/sections/console/containers/index.js
  70. 15
      web/client/src/sections/console/index.js
  71. 15
      web/client/src/sections/console/nav-item.jsx
  72. 5
      web/client/src/sections/console/reducers/index.js
  73. 13
      web/client/src/sections/console/routes.js
  74. 7
      web/client/src/sections/console/style.less
  75. 17
      web/client/src/sections/example/nav-item.jsx
  76. 2
      web/client/src/sections/example/routes.js
  77. 12
      web/client/src/sections/example1/nav-item.jsx
  78. 3
      web/client/src/sections/noMatch/containers/noMatch.jsx
  79. 14
      web/client/src/sections/problem/actions/dataAlarm.jsx
  80. 7
      web/client/src/sections/problem/actions/index.js
  81. 42
      web/client/src/sections/problem/components/statistics.jsx
  82. 167
      web/client/src/sections/problem/components/tableData.jsx
  83. 261
      web/client/src/sections/problem/containers/dataAlarm.jsx
  84. 5
      web/client/src/sections/problem/containers/index.js
  85. 15
      web/client/src/sections/problem/index.js
  86. 33
      web/client/src/sections/problem/nav-item.jsx
  87. 5
      web/client/src/sections/problem/reducers/index.js
  88. 61
      web/client/src/sections/problem/routes.js
  89. 7
      web/client/src/sections/problem/style.less
  90. 25
      web/client/src/utils/func.js
  91. 13
      web/client/src/utils/index.js
  92. 14
      web/client/src/utils/webapi.js
  93. 36
      web/config.js
  94. 4
      web/middlewares/vite-dev.js
  95. 8
      web/middlewares/webpack-dev.js
  96. 4
      web/package.json
  97. 2
      web/webpack.config.js

20
api/.vscode/launch.json

@ -13,23 +13,21 @@
"NODE_ENV": "development"
},
"args": [
"-p 4000",
"-f http://localhost:4000",
"-g postgres://postgres:123@10.8.30.32:5432/video_access",
"-p 4600",
"-f http://localhost:4600",
"-g postgres://postgres:123@10.8.30.32:5432/orational_service",
"--redisHost 10.8.30.112",
"--redisPort 6379",
"--axyApiUrl http://127.0.0.1:4100",
"--iotAuthApi http://127.0.0.1:4200",
"--apiEmisUrl http://10.8.30.112:14000",
"--godUrl https://restapi.amap.com/v3",
"--godKey 21c2d970e1646bb9a795900dd00093ce",
"--mqttVideoServer mqtt://10.8.30.71:30883",
"--iotVideoServerUrl http://221.230.55.27:8081",
// "--iotVideoServerUrl http://10.8.30.59:8080",
"--cameraPlayWsHost ws://221.230.55.27:8081",
"--cameraPlayHttpFlvHost http://221.230.55.27:2020",
"--cameraPlayHlsHost http://221.230.55.27:8081",
"--cameraPlayRtmpHost rtmp://221.230.55.27:1935",
"--cameraPlayRtspHost rtsp://221.230.55.27:554"
"--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5",
"--qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa",
"--qnbkt dev-operational-service",
// "--qndmn http://resources.anxinyun.cn",
"--qndmn http://rhvqdivo5.hn-bkt.clouddn.com",
]
},
{

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

@ -0,0 +1,128 @@
'use strict';
const moment = require('moment')
async function inspection (ctx) {
try {
const models = ctx.fs.dc.models;
const { projectAppId, screenshot = [] } = ctx.request.body
const now = moment().format()
const storageData = screenshot.map(s => {
return {
projectAppId,
screenshot: [s],
createTime: now,
}
})
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 notedInspection (ctx) {
try {
const models = ctx.fs.dc.models;
const { inspectionId } = ctx.request.body
const { userId } = ctx.fs.api
await models.AppInspection.update({
notedPepUserId: userId,
notedTime: moment().format()
}, {
where: {
id: inspectionId
}
})
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 apiError (ctx) {
try {
const models = ctx.fs.dc.models;
const { projectAppId, alarmContent, router, statusCode } = ctx.request.body
const now = moment().format()
let storageData = {
projectAppId, alarmContent, router, statusCode
}
const existRes = await models.AppAlarm.findOne({
where: {
projectAppId, alarmContent, router, statusCode,
confirm: null
}
})
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
await models.AppAlarm.create(storageData)
}
ctx.status = 200;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function confirmApiError (ctx) {
try {
const models = ctx.fs.dc.models;
const { confirm, appAlarmId } = ctx.request.body
await models.AppAlarm.update({
confirm,
}, {
where: {
id: appAlarmId
}
})
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
}
}
}
module.exports = {
inspection,
notedInspection,
apiError,
confirmApiError,
};

108
api/app/lib/controllers/auth/index.js

@ -0,0 +1,108 @@
'use strict';
const Hex = require('crypto-js/enc-hex');
const MD5 = require('crypto-js/md5');
const moment = require('moment');
const uuid = require('uuid');
async function login (ctx, next) {
// const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
const params = ctx.request.body;
const emisLoginRes = await ctx.app.fs.emisRequest.post('login', {
data: params
})
if (!emisLoginRes) {
throw "账号或密码错误"
} else {
const pomsRegisterRes = await models.User.findOne({
where: {
pepUserId: emisLoginRes.id,
$or: {
deleted: false,
role: { $contains: ['admin'] }
}
}
})
if (!pomsRegisterRes) {
throw '当前账号尚未在此系统启用'
} else if (
pomsRegisterRes.disable && (!pomsRegisterRes.role || !pomsRegisterRes.role.includes('admin'))
) {
throw '当前账号已禁用'
}
emisLoginRes.pomsUserInfo = pomsRegisterRes.dataValues
let userUpdateData = {
lastInTime: moment().format(),
inTimes: pomsRegisterRes.inTimes + 1,
lastInAddress: ''
}
try {
// 获取ip转为地点并记录
let ip =
// '117.90.39.49' ||
ctx.ip
console.log(`当前登录用户IP:${ip}`);
if (ip && /^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$/.test(ip)) {
const ipLocationRes = await ctx.app.fs.godRequest.post('ip', {
query: {
ip,
}
})
if (ipLocationRes) {
userUpdateData.lastInAddress = ipLocationRes.province + ipLocationRes.city
}
}
} catch (error) {
ctx.fs.logger.error(`IP GET, error: ${error}`);
}
await models.User.update(userUpdateData, {
where: {
id: emisLoginRes.id
}
})
ctx.status = 200;
ctx.body = emisLoginRes;
}
// await transaction.commit();
} catch (error) {
// await transaction.rollback();
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message:
typeof error == 'string' ? error
: error.response.body.message || "登录失败"
}
}
}
async function logout (ctx) {
try {
const models = ctx.fs.dc.models;
const params = ctx.request.body;
await ctx.app.fs.emisRequest.put('logout', {
data: params
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
}
}
}
module.exports = {
login,
logout,
};

157
api/app/lib/controllers/organization/index.js

@ -0,0 +1,157 @@
'use strict';
async function allDeps (ctx) {
try {
const models = ctx.fs.dc.models;
const { redis } = ctx.app
let depRes = await redis.get('allDepartments')
if (depRes) {
depRes = JSON.parse(depRes)
depRes = depRes.departments
}
ctx.status = 200;
ctx.body = depRes || []
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
}
}
}
async function editUser (ctx) {
try {
const models = ctx.fs.dc.models;
const { pepUserId, role = [], correlationProject = [] } = ctx.request.body
const existUserRes = await models.User.findOne({
where: {
pepUserId
}
})
let storageData = {
pepUserId,
role,
correlationProject,
updateTime: moment().format()
}
if (existUserRes) {
// 存在且传递id 或者 不传id也存在
// 修改 update
if (
role.includes('admin')
) {
storageData.role = [...new Set([...existUserRes.role, ...role])]
storageData.disabled = false
}
await models.User.update(storageData, {
where: {
pepUserId
}
})
} else {
// 新增
await models.User.create(storageData)
}
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
}
}
}
async function putUser (ctx) {
try {
const models = ctx.fs.dc.models;
const { pomsUserId } = ctx.params
const { disabled = undefined, deleted = undefined } = ctx.request.body
const updateData = {
disabled,
deleted,
}
for (let k in updateData) {
if (updateData[k] == undefined) {
delete updateData[k]
}
}
await models.User.update(updateData, {
where: {
id: pomsUserId
}
})
ctx.status = 204
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
}
}
}
async function user (ctx) {
try {
const models = ctx.fs.dc.models;
const { role, limit, page, } = ctx.query
const excludeField = ['lastInTime', 'inTimes', 'onlineDuration', 'lastInAddress', 'deleted', 'updateTime']
let findOption = {
attributes: {
exclude: excludeField,
},
where: {
deleted: false,
},
order: [['updateTime', 'DESC']]
}
if (role) {
findOption.where.role = { $contains: [role] }
}
if (limit) {
findOption.limit = limit
}
if (page && limit) {
findOption.offset = page * limit
}
const userRes = await models.User.findAndCountAll(findOption)
const adminRes = await models.User.findAll({
where: {
role: { $contains: ['admin'] }
},
attributes: {
exclude: excludeField,
},
order: [['updateTime', 'DESC']]
})
ctx.status = 200
ctx.body = {
admin: adminRes,
users: userRes
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
}
}
}
module.exports = {
allDeps,
editUser,
putUser,
user,
};

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

@ -0,0 +1,25 @@
'use strict';
async function appList (ctx) {
try {
const models = ctx.fs.dc.models;
const appRes = await models.ProjectApp.findAll({
attributes: {
exclude: ['projectId']
}
})
ctx.status = 200;
ctx.body = appRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = {
appList
};

71
api/app/lib/index.js

@ -44,8 +44,6 @@ module.exports.entry = function (app, router, opts) {
};
module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Sequelize, models: {} }
// 加载定义模型 历史写法
// require('./models/nvr')(dc);
// 模型关系摘出来 初始化之后再定义关系才行
fs.readdirSync(path.join(__dirname, '/models')).forEach((filename) => {
@ -53,74 +51,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
});
const {
Nvr, Camera, CameraAbility, CameraAbilityBind, CameraKind, CameraRemark,
GbCamera, SecretYingshi, Vender, CameraStatus, CameraStatusResolve, CameraStatusLog,
CameraStatusPushConfig, CameraStatusPushMonitor, CameraStatusPushLog, CameraStatusPushReceiver, CameraStatusOfflineLog,
Mirror, MirrorTree, MirrorFilterGroup, MirrorFilter, MirrorCamera
} = dc.models;
// Nvr.belongsTo(User, { foreignKey: 'userId', targetKey: 'id' });
// User.hasMany(Nvr, { foreignKey: 'userId', sourceKey: 'id' });
Camera.belongsToMany(CameraAbility, { through: CameraAbilityBind, foreignKey: 'cameraId', otherKey: 'abilityId' });
CameraRemark.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' });
Camera.hasMany(CameraRemark, { foreignKey: 'cameraId', sourceKey: 'id' });
Camera.belongsTo(CameraKind, { foreignKey: 'kindId', targetKey: 'id' });
CameraKind.hasMany(Camera, { foreignKey: 'kindId', sourceKey: 'id' });
Camera.belongsTo(Nvr, { foreignKey: 'nvrId', targetKey: 'id' });
Nvr.hasMany(Camera, { foreignKey: 'nvrId', sourceKey: 'id' });
Nvr.belongsTo(GbCamera, { foreignKey: 'serialNo', targetKey: 'streamid', as: 'gbNvr' });
GbCamera.hasMany(Nvr, { foreignKey: 'serialNo', sourceKey: 'streamid', as: 'gbNvr' });
Camera.belongsTo(GbCamera, { foreignKey: 'gbId', targetKey: 'id' });
GbCamera.hasMany(Camera, { foreignKey: 'gbId', sourceKey: 'id' });
Camera.belongsTo(SecretYingshi, { foreignKey: 'yingshiSecretId', targetKey: 'id' });
SecretYingshi.hasMany(Camera, { foreignKey: 'yingshiSecretId', sourceKey: 'id' });
Camera.belongsTo(Vender, { foreignKey: 'venderId', targetKey: 'id' });
Vender.hasMany(Camera, { foreignKey: 'venderId', sourceKey: 'id' });
Nvr.belongsTo(Vender, { foreignKey: 'venderId', targetKey: 'id' });
Vender.hasMany(Nvr, { foreignKey: 'venderId', sourceKey: 'id' });
CameraStatusResolve.belongsTo(CameraStatus, { foreignKey: 'statusId', targetKey: 'id' });
CameraStatus.hasMany(CameraStatusResolve, { foreignKey: 'statusId', sourceKey: 'id' });
CameraStatusLog.belongsTo(CameraStatus, { foreignKey: 'statusId', targetKey: 'id' });
CameraStatus.hasMany(CameraStatusLog, { foreignKey: 'statusId', sourceKey: 'id' });
CameraStatusPushMonitor.belongsTo(CameraStatusPushConfig, { foreignKey: 'configId', targetKey: 'id' });
CameraStatusPushConfig.hasMany(CameraStatusPushMonitor, { foreignKey: 'configId', sourceKey: 'id' });
CameraStatusPushMonitor.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' });
Camera.hasMany(CameraStatusPushMonitor, { foreignKey: 'cameraId', sourceKey: 'id' });
CameraStatusPushLog.belongsTo(CameraStatusPushConfig, { foreignKey: 'pushConfigId', targetKey: 'id' });
CameraStatusPushConfig.hasMany(CameraStatusPushLog, { foreignKey: 'pushConfigId', sourceKey: 'id' });
CameraStatusPushReceiver.belongsTo(CameraStatusPushConfig, { foreignKey: 'configId', targetKey: 'id' });
CameraStatusPushConfig.hasMany(CameraStatusPushReceiver, { foreignKey: 'configId', sourceKey: 'id' });
CameraStatusOfflineLog.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' });
Camera.hasMany(CameraStatusOfflineLog, { foreignKey: 'cameraId', sourceKey: 'id' });
MirrorTree.belongsTo(Mirror, { foreignKey: 'mirrorId', targetKey: 'id' });
Mirror.hasMany(MirrorTree, { foreignKey: 'mirrorId', sourceKey: 'id' });
MirrorFilterGroup.belongsTo(Mirror, { foreignKey: 'mirrorId', targetKey: 'id' });
Mirror.hasMany(MirrorFilterGroup, { foreignKey: 'mirrorId', sourceKey: 'id' });
MirrorFilter.belongsTo(MirrorFilterGroup, { foreignKey: 'groupId', targetKey: 'id' });
MirrorFilterGroup.hasMany(MirrorFilter, { foreignKey: 'groupId', sourceKey: 'id' });
MirrorCamera.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' });
Camera.hasMany(MirrorCamera, { foreignKey: 'cameraId', sourceKey: 'id' });
MirrorCamera.belongsTo(Mirror, { foreignKey: 'mirrorId', targetKey: 'id' });
Mirror.hasMany(MirrorCamera, { foreignKey: 'mirrorId', sourceKey: 'id' });
};

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

@ -8,42 +8,42 @@ const util = require('util');
const moment = require('moment');
class ExcludesUrls {
constructor(opts) {
this.allUrls = undefined;
this.reload(opts);
}
constructor(opts) {
this.allUrls = undefined;
this.reload(opts);
}
sanitizePath (path) {
if (!path) return '/';
const p = '/' + path.replace(/^\/+/i, '').replace(/\/+$/, '').replace(/\/{2,}/, '/');
return p;
}
sanitizePath (path) {
if (!path) return '/';
const p = '/' + path.replace(/^\/+/i, '').replace(/\/+$/, '').replace(/\/{2,}/, '/');
return p;
}
reload (opts) {
// load all url
if (!this.allUrls) {
this.allUrls = opts;
let that = this;
this.allUrls.forEach(function (url, i, arr) {
if (typeof url === "string") {
url = { p: url, o: '*' };
arr[i] = url;
}
const keys = [];
let eachPath = url.p;
url.p = (!eachPath || eachPath === '(.*)' || util.isRegExp(eachPath)) ? eachPath : that.sanitizePath(eachPath);
url.pregexp = pathToRegexp(eachPath, keys);
});
}
}
reload (opts) {
// load all url
if (!this.allUrls) {
this.allUrls = opts;
let that = this;
this.allUrls.forEach(function (url, i, arr) {
if (typeof url === "string") {
url = { p: url, o: '*' };
arr[i] = url;
}
const keys = [];
let eachPath = url.p;
url.p = (!eachPath || eachPath === '(.*)' || util.isRegExp(eachPath)) ? eachPath : that.sanitizePath(eachPath);
url.pregexp = pathToRegexp(eachPath, keys);
});
}
}
isExcluded (path, method) {
return this.allUrls.some(function (url) {
return !url.auth
&& url.pregexp.test(path)
&& (url.o === '*' || url.o.indexOf(method) !== -1);
});
}
isExcluded (path, method) {
return this.allUrls.some(function (url) {
return !url.auth
&& url.pregexp.test(path)
&& (url.o === '*' || url.o.indexOf(method) !== -1);
});
}
}
/**
@ -53,92 +53,94 @@ class ExcludesUrls {
* @param {*} method 当前request的method
*/
let isPathExcluded = function (opts, path, method) {
let excludeAll = Boolean(opts.exclude && opts.exclude.length && opts.exclude[0] == '*');
let excludes = null;
if (!excludeAll) {
let excludeOpts = opts.exclude || [];
excludeOpts.push({ p: '/login', o: 'POST' });
excludeOpts.push({ p: '/logout', o: 'PUT' });
excludes = new ExcludesUrls(excludeOpts);
}
let excluded = excludeAll || excludes.isExcluded(path, method);
return excluded;
let excludeAll = Boolean(opts.exclude && opts.exclude.length && opts.exclude[0] == '*');
let excludes = null;
if (!excludeAll) {
let excludeOpts = opts.exclude || [];
excludeOpts.push({ p: '/login', o: 'POST' });
excludeOpts.push({ p: '/logout', o: 'PUT' });
excludes = new ExcludesUrls(excludeOpts);
}
let excluded = excludeAll || excludes.isExcluded(path, method);
return excluded;
};
let authorizeToken = async function (ctx, token) {
let rslt = null;
const tokenFormatRegexp = /^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/g;
if (token && tokenFormatRegexp.test(token)) {
try {
const expired = await ctx.redis.hget(token, 'expired');
let rslt = null;
const tokenFormatRegexp = /^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/g;
if (token && tokenFormatRegexp.test(token)) {
try {
if (expired && moment().valueOf() <= moment(expired).valueOf()) {
const userInfo = JSON.parse(await ctx.redis.hget(token, 'userInfo'));
rslt = {
'authorized': userInfo.authorized,
'resources': (userInfo || {}).resources || [],
};
ctx.fs.api.userId = userInfo.id;
ctx.fs.api.userInfo = userInfo;
ctx.fs.api.token = token;
}
} catch (err) {
const { error } = err.response || {};
ctx.fs.logger.log('[anxinyun]', '[AUTH] failed', (error || {}).message || `cannot GET /users/${token}`);
}
}
return rslt;
const authorizeRes = await ctx.app.fs.emisRequest.get('authorize', {
query: { token }
})
const { userInfo, expired } = authorizeRes;
if (expired && moment().valueOf() <= moment(expired).valueOf()) {
rslt = {
'authorized': userInfo.authorized,
'resources': (userInfo || {}).resources || [],
};
ctx.fs.api.userId = userInfo.id;
ctx.fs.api.userInfo = userInfo;
ctx.fs.api.token = token;
}
} catch (err) {
const { error } = err.response || {};
ctx.fs.logger.log('[anxinyun]', '[AUTH] failed', (error || {}).message || `cannot GET /users/${token}`);
}
}
return rslt;
};
let isResourceAvailable = function (resources, options) {
let authCode = null;
// authorize user by authorization attribute
const { authAttr, method, path } = options;
for (let prop in authAttr) {
let keys = [];
let re = pathToRegexp(prop.replace(/\:[A-Za-z_\-]+\b/g, '(\\d+)'), keys);
if (re.test(`${method}${path}`)) {
authCode = authAttr[prop];
break;
}
}
return !authCode || (resources || []).some(code => code === authCode);
let authCode = null;
// authorize user by authorization attribute
const { authAttr, method, path } = options;
for (let prop in authAttr) {
let keys = [];
let re = pathToRegexp(prop.replace(/\:[A-Za-z_\-]+\b/g, '(\\d+)'), keys);
if (re.test(`${method}${path}`)) {
authCode = authAttr[prop];
break;
}
}
return !authCode || (resources || []).some(code => code === authCode);
};
function factory (app, opts) {
return async function auth (ctx, next) {
const { path, method, header, query } = ctx;
ctx.fs.logger.log('[AUTH] start', path, method);
ctx.fs.api = ctx.fs.api || {};
ctx.fs.port = opts.port;
ctx.redis = app.redis;
ctx.redisTools = app.redisTools;
let error = null;
if (path) {
if (!isPathExcluded(opts, path, method)) {
const user = await authorizeToken(ctx, header.token || query.token);
if (user && user.authorized) {
// if (!isResourceAvailable(user.resources, { authAttr: app.fs.auth.authAttr, path, method })) {
// error = { status: 403, name: 'Forbidden' }
// } else {
// error = { status: 401, name: 'Unauthorized' }
// }
} else {
error = { status: 401, name: 'Unauthorized' }
}
return async function auth (ctx, next) {
const { path, method, header, query } = ctx;
ctx.fs.logger.log('[AUTH] start', path, method);
ctx.fs.api = ctx.fs.api || {};
ctx.fs.port = opts.port;
ctx.redis = app.redis;
ctx.redisTools = app.redisTools;
let error = null;
if (path) {
if (!isPathExcluded(opts, path, method)) {
const user = await authorizeToken(ctx, header.token || query.token);
if (user && user.authorized) {
// if (!isResourceAvailable(user.resources, { authAttr: app.fs.auth.authAttr, path, method })) {
// error = { status: 403, name: 'Forbidden' }
// } else {
// error = { status: 401, name: 'Unauthorized' }
// }
} else {
error = { status: 401, name: 'Unauthorized' }
}
} else {
error = { status: 401, name: 'Unauthorized' };
}
if (error) {
ctx.fs.logger.log('[AUTH] failed', path, method);
ctx.status = error.status;
ctx.body = error.name;
} else {
ctx.fs.logger.log('[AUTH] passed', path, method);
await next();
}
}
}
} else {
error = { status: 401, name: 'Unauthorized' };
}
if (error) {
ctx.fs.logger.log('[AUTH] failed', path, method);
ctx.status = error.status;
ctx.body = error.name;
} else {
ctx.fs.logger.log('[AUTH] passed', path, method);
await next();
}
}
}
module.exports = factory;

65
api/app/lib/models/action_log.js

@ -0,0 +1,65 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const ActionLog = sequelize.define("actionLog", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "action_log_id_uindex"
},
userId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "user_id",
autoIncrement: false,
references: {
key: "id",
model: "user"
}
},
time: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "time",
autoIncrement: false
},
action: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "动态内容",
primaryKey: false,
field: "action",
autoIncrement: false
},
expandParams: {
type: DataTypes.JSONB,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "expand_params",
autoIncrement: false
}
}, {
tableName: "action_log",
comment: "",
indexes: []
});
dc.models.ActionLog = ActionLog;
return ActionLog;
};

61
api/app/lib/models/application.js → api/app/lib/models/alarm_push_config.js

@ -4,7 +4,7 @@
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const Application = sequelize.define("application", {
const AlarmPushConfig = sequelize.define("alarmPushConfig", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
@ -13,7 +13,7 @@ module.exports = dc => {
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "application_id_uindex"
unique: "alarm_push_config_id_uindex"
},
name: {
type: DataTypes.STRING,
@ -24,65 +24,74 @@ module.exports = dc => {
field: "name",
autoIncrement: false
},
type: {
pepProjectId: {
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "pep_project_id",
autoIncrement: false
},
alarmType: {
type: DataTypes.ARRAY(DataTypes.STRING),
allowNull: true,
defaultValue: null,
comment: "web / app / wxapp / other",
comment: "监听的告警类型",
primaryKey: false,
field: "type",
field: "alarm_type",
autoIncrement: false
},
appKey: {
type: DataTypes.STRING,
allowNull: false,
receiverPepUserId: {
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: true,
defaultValue: null,
comment: null,
comment: "接收人id 项企",
primaryKey: false,
field: "app_key",
field: "receiver_pep_user_id",
autoIncrement: false
},
appSecret: {
type: DataTypes.STRING,
allowNull: false,
timeType: {
type: DataTypes.ARRAY(DataTypes.STRING),
allowNull: true,
defaultValue: null,
comment: null,
comment: "通知时效",
primaryKey: false,
field: "app_secret",
field: "time_type",
autoIncrement: false
},
createUserId: {
type: DataTypes.INTEGER,
createTime: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_user_id",
field: "create_time",
autoIncrement: false
},
createTime: {
type: DataTypes.DATE,
createUserId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_time",
field: "create_user_id",
autoIncrement: false
},
forbidden: {
disable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "forbidden",
field: "disable",
autoIncrement: false
}
}, {
tableName: "application",
tableName: "alarm_push_config",
comment: "",
indexes: []
});
dc.models.Application = Application;
return Application;
dc.models.AlarmPushConfig = AlarmPushConfig;
return AlarmPushConfig;
};

106
api/app/lib/models/app_alarm.js

@ -0,0 +1,106 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const AppAlarm = sequelize.define("appAlarm", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "app_alarm_id_uindex"
},
serialNumber: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "自定义编号",
primaryKey: false,
field: "serial_number",
autoIncrement: false
},
projectAppId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: "对应的项目id",
primaryKey: false,
field: "project_app_id",
autoIncrement: false
},
appDomain: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "应用域名",
primaryKey: false,
field: "app_domain",
autoIncrement: false
},
alarmContent: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "告警信息",
primaryKey: false,
field: "alarm_content",
autoIncrement: false
},
createTime: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_time",
autoIncrement: false
},
updateTime: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "update_time",
autoIncrement: false
},
confirm: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "确认信息",
primaryKey: false,
field: "confirm",
autoIncrement: false
},
router: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "路由",
primaryKey: false,
field: "router",
autoIncrement: false
},
statusCode: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "状态码",
primaryKey: false,
field: "status_code",
autoIncrement: false
}
}, {
tableName: "app_alarm",
comment: "",
indexes: []
});
dc.models.AppAlarm = AppAlarm;
return AppAlarm;
};

74
api/app/lib/models/app_inspection.js

@ -0,0 +1,74 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const AppInspection = sequelize.define("appInspection", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "app_inspection_id_uindex"
},
projectAppId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "project_app_id",
autoIncrement: false,
references: {
key: "id",
model: "projectApp"
}
},
createTime: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_time",
autoIncrement: false
},
screenshot: {
type: DataTypes.ARRAY(DataTypes.STRING),
allowNull: true,
defaultValue: null,
comment: "截图存储路径",
primaryKey: false,
field: "screenshot",
autoIncrement: false
},
notedPepUserId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: "核验人员",
primaryKey: false,
field: "noted_pep_user_id",
autoIncrement: false
},
notedTime: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "noted_time",
autoIncrement: false
}
}, {
tableName: "app_inspection",
comment: "",
indexes: []
});
dc.models.AppInspection = AppInspection;
return AppInspection;
};

35
api/app/lib/models/ax_project.js

@ -1,35 +0,0 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const AxProject = sequelize.define("axProject", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: false,
unique: "ax_project_id_uindex"
},
name: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "name",
autoIncrement: false
}
}, {
tableName: "ax_project",
comment: "",
indexes: []
});
dc.models.AxProject = AxProject;
return AxProject;
};

56
api/app/lib/models/project_app.js

@ -0,0 +1,56 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const ProjectApp = sequelize.define("projectApp", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "project_app_id_uindex"
},
name: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "name",
autoIncrement: false
},
url: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "url",
autoIncrement: false
},
projectId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "project_id",
autoIncrement: false,
references: {
key: "id",
model: "projectCorrelation"
}
}
}, {
tableName: "project_app",
comment: "",
indexes: []
});
dc.models.ProjectApp = ProjectApp;
return ProjectApp;
};

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

@ -0,0 +1,70 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const ProjectCorrelation = sequelize.define("projectCorrelation", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "project_correlation_id_uindex"
},
anxinProjectId: {
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "anxin_project_id",
autoIncrement: false
},
pepProjectId: {
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: false,
defaultValue: null,
comment: "项目管理的项目id",
primaryKey: false,
field: "pep_project_id",
autoIncrement: false
},
createTime: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_time",
autoIncrement: false
},
createUser: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_user",
autoIncrement: false
},
name: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "name",
autoIncrement: false
}
}, {
tableName: "project_correlation",
comment: "",
indexes: []
});
dc.models.ProjectCorrelation = ProjectCorrelation;
return ProjectCorrelation;
};

47
api/app/lib/models/quick_link.js

@ -0,0 +1,47 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const QuickLink = sequelize.define("quickLink", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "quick_link_id_uindex"
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "user_id",
autoIncrement: false,
references: {
key: "id",
model: "user"
}
},
link: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "link",
autoIncrement: false
}
}, {
tableName: "quick_link",
comment: "",
indexes: []
});
dc.models.QuickLink = QuickLink;
return QuickLink;
};

115
api/app/lib/models/user.js

@ -0,0 +1,115 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const User = sequelize.define("user", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "user_id_uindex"
},
pepUserId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: "项企对应用户id",
primaryKey: false,
field: "pep_user_id",
autoIncrement: false
},
role: {
type: DataTypes.ARRAY(DataTypes.STRING),
allowNull: true,
defaultValue: null,
comment: "角色 也对应权限 admin 管理员 / all 全部角色 / data_analyst 数据分析 / after_sale 售后运维 / resource_manage 资源管理 / customer_service 客户服务",
primaryKey: false,
field: "role",
autoIncrement: false
},
correlationProject: {
type: DataTypes.ARRAY(DataTypes.INTEGER),
allowNull: true,
defaultValue: null,
comment: "关联的项目管理的项目id",
primaryKey: false,
field: "correlation_project",
autoIncrement: false
},
lastInTime: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "last_in_time",
autoIncrement: false
},
inTimes: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: "0",
comment: null,
primaryKey: false,
field: "in_times",
autoIncrement: false
},
onlineDuration: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: "0",
comment: "在线时长 单位 s",
primaryKey: false,
field: "online_duration",
autoIncrement: false
},
lastInAddress: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "上次登录地点",
primaryKey: false,
field: "last_in_address",
autoIncrement: false
},
disabled: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "disabled",
autoIncrement: false
},
deleted: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "deleted",
autoIncrement: false
},
updateTime: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: sequelize.fn('now'),
comment: null,
primaryKey: false,
field: "update_time",
autoIncrement: false
}
}, {
tableName: "user",
comment: "",
indexes: []
});
dc.models.User = User;
return User;
};

20
api/app/lib/routes/alarm/index.js

@ -0,0 +1,20 @@
'use strict';
const application = require('../../controllers/alarm/app');
module.exports = function (app, router, opts) {
// 应用告警
app.fs.api.logAttr['POST/alarm/application/inspection'] = { content: '保存应用巡检信息', visible: true };
router.post('/alarm/application/inspection', application.inspection);
app.fs.api.logAttr['PUT/alarm/application/noted'] = { content: '保存检验状态', visible: true };
router.put('/alarm/application/noted', application.notedInspection);
app.fs.api.logAttr['POST/alarm/application/api'] = { content: '保存应用接口错误信息', visible: true };
router.post('/alarm/application/api', application.apiError);
app.fs.api.logAttr['POST/alarm/application/api_confirm'] = { content: '确认应用接口错误信息', visible: true };
router.post('/alarm/application/api_confirm', application.confirmApiError);
};

11
api/app/lib/routes/auth/index.js

@ -0,0 +1,11 @@
'use strict';
const auth = require('../../controllers/auth');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['POST/login'] = { content: '登录', visible: true };
router.post('/login', auth.login);
app.fs.api.logAttr['PUT/logout'] = { content: '登出', visible: false };
router.put('/logout', auth.logout);
};

17
api/app/lib/routes/organization/index.js

@ -0,0 +1,17 @@
'use strict';
const organization = require('../../controllers/organization');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/organization/deps'] = { content: '获取全部部门及其下用户', visible: true };
router.get('/organization/deps', organization.allDeps);
app.fs.api.logAttr['POST/organization/user'] = { content: '编辑成员', visible: true };
router.post('/organization/user', organization.editUser);
app.fs.api.logAttr['PUT/organization/user/:pomsUserId'] = { content: '修改成员状态', visible: true };
router.put('/organization/user/:pomsUserId', organization.putUser);
app.fs.api.logAttr['GET/organization/user'] = { content: '获取成员列表', visible: true };
router.get('/organization/user', organization.user);
};

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

@ -0,0 +1,8 @@
'use strict';
const project = require('../../controllers/project');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/project/app_list'] = { content: '获取应用列表', visible: true };
router.get('/project/app_list', project.appList);
};

36
api/app/lib/schedule/index.js

@ -0,0 +1,36 @@
'use strict';
const fs = require('fs');
const nodeSchedule = require('node-schedule');
// 将定时任务汇集未来可根据需要选取操作
module.exports = async function (app, opts) {
const scheduleInit = ({
interval, immediate, proRun,
}, callback) => {
if (proRun && opts.dev) {
return;
}
const j = nodeSchedule.scheduleJob(interval, callback);
if (immediate && (!proRun || (proRun && !opts.dev))) {
setTimeout(callback, 0)
}
return j;
}
app.fs.scheduleInit = scheduleInit
fs.readdirSync(__dirname).forEach((filename) => {
if (!['index.js'].some(f => filename == f)) {
const scheduleList = require(`./${filename}`)(app, opts)
for (let k of Object.keys(scheduleList)) {
console.info(`定时任务 ${k} 启动`);
}
app.fs.schedule = {
...app.fs.schedule,
...scheduleList,
}
}
});
};

45
api/app/lib/service/mqttVideoServer.js

@ -2,39 +2,22 @@
const mqtt = require('mqtt');
module.exports = async function factory (app, opts) {
console.info(`mqtt connecting ${opts.mqtt.mqttVideoServer}`);
// console.info(`mqtt connecting ${opts.mqtt.mqttVideoServer}`);
const client = mqtt.connect(opts.mqtt.mqttVideoServer);
// const client = mqtt.connect(opts.mqtt.mqttVideoServer);
client.on('connect', function () {
console.info(`mqtt connect success ${opts.mqtt.mqttVideoServer}`);
client.subscribe('topic/test', { qos: 0 });//订阅主题为test的消息
})
client.on('error', function (e) {
console.error(`mqtt connect failed ${opts.mqtt.mqttVideoServer}`);
app.fs.logger.error('info', '[FS-AUTH-MQTT]', `mqtt connect failed ${opts.mqtt.mqttVideoServer}`);
})
// client.on('connect', function () {
// console.info(`mqtt connect success ${opts.mqtt.mqttVideoServer}`);
// client.subscribe('topic/test', { qos: 0 });//订阅主题为test的消息
// })
// client.on('error', function (e) {
// console.error(`mqtt connect failed ${opts.mqtt.mqttVideoServer}`);
// app.fs.logger.error('info', '[FS-AUTH-MQTT]', `mqtt connect failed ${opts.mqtt.mqttVideoServer}`);
// })
client.on('message', async (top, message) => {
let msgStr = message.toString();
let msg = JSON.parse(msgStr.replace(/\\/g, ''));
if (msg.id && msg.online) {
const { cameraStatePush } = app.fs.utils
const { models } = app.fs.dc
const gbCameraRes = await models.GbCamera.findOne({
where: {
id: msg.id
}
})
if (gbCameraRes) {
cameraStatePush({
gbId: msg.id,
online: msg.online,
ipctype: gbCameraRes.ipctype,
})
}
}
});
// client.on('message', async (top, message) => {
// });
app.mqttVideoServer = client
// app.mqttVideoServer = client
}

51
api/app/lib/service/socket.js

@ -1,33 +1,40 @@
'use strict';
const moment = require('moment')
module.exports = async function factory (app, opts) {
app.socket.on('connection', async (socket) => {
console.info('WEB_SOCKET ' + socket.handshake.query.token + ' 已连接:' + socket.id);
console.info('WEB_SOCKET token:' + socket.handshake.query.token + ' 已连接:id ' + socket.id + ' 时间:' + moment(socket.handshake.time).format());
socket.on('disconnecting', async (reason) => {
console.info('WEB_SOCKET ' + socket.handshake.query.token + ' 已断开连接:' + reason);
const connectSeconds = moment().diff(moment(socket.handshake.time), 'seconds')
console.info('WEB_SOCKET token:' + socket.handshake.query.token + ' 已断开连接:' + reason + ' 连接时长:' + connectSeconds + 's');
const { models } = app.fs.dc
await models.User.increment({
onlineDuration: connectSeconds
}, {
where: {
id: socket.handshake.query.pomsUserId
}
})
})
})
// 使用测试 保持链接
setInterval(async () => {
const { connected } = app.socket.sockets
const roomId = 'ROOM_' + Math.random()
// if (connected) {
// for (let c in connected) {
// connected[c].join(roomId)
// }
// app.socket.to(roomId).emit('TEST', { someProperty: `【星域 ROOM:${roomId}】呼叫自然选择号!!!`, })
// }
app.socket.emit('TEST', { someProperty: '【广播】呼叫青铜时代号!!!', })
// app.socket.emit('CAMERA_ONLINE', {
// ipctype: 'yingshi',
// online: Math.random() > 0.5 ? 'ON' : 'OFF',
// gbId: Math.floor(Math.random() * 100),
// name: 'cameraName'
// })
}, 3000)
// setInterval(async () => {
// const { connected } = app.socket.sockets
// const roomId = 'ROOM_' + Math.random()
// // if (connected) {
// // for (let c in connected) {
// // connected[c].join(roomId)
// // }
// // app.socket.to(roomId).emit('TEST', { someProperty: `【星域 ROOM:${roomId}】呼叫自然选择号!!!`, })
// // }
// app.socket.emit('TEST', { someProperty: '【广播】呼叫青铜时代号!!!', })
// }, 3000)
}

116
api/config.js

@ -17,22 +17,22 @@ args.option('redisPort', 'redisPort');
args.option('redisPswd', 'redisPassword');
args.option('axyApiUrl', '安心云 api');
args.option('iotAuthApi', 'IOT 鉴权 api');
args.option('apiEmisUrl', '企业管理 api');
args.option('godUrl', '高德地图API请求地址');
args.option('godKey', '高德地图API key');
args.option('iotVideoServerUrl', '视频后端服务地址');
args.option('mqttVideoServer', '视频后台 mqtt 服务 URL');
args.option('cameraPlayWsHost', '视频播放地址 ws://xxx:xxx');
args.option('cameraPlayHttpFlvHost', '视频播放地址 httpFlv');
args.option('cameraPlayHlsHost', '视频播放地址 hls');
args.option('cameraPlayRtmpHost', '视频播放地址 rtmp');
args.option('cameraPlayRtspHost', '视频播放地址 rtsp');
// 七牛云存储参数
args.option('qnak', 'qiniuAccessKey');
args.option('qnsk', 'qiniuSecretKey');
args.option('qnbkt', 'qiniuBucket');
args.option('qndmn', 'qiniuDomain');
const flags = args.parse(process.argv);
const IOT_VIDEO_ACCESS_DB = process.env.IOT_VIDEO_ACCESS_DB || flags.pg;
const POMS_DB = process.env.POMS_DB || flags.pg;
const IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN = process.env.IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN || flags.fileHost;
// Redis 参数
@ -40,33 +40,32 @@ const IOTA_REDIS_SERVER_HOST = process.env.IOTA_REDIS_SERVER_HOST || flags.redis
const IOTA_REDIS_SERVER_PORT = process.env.IOTA_REDIS_SERVER_PORT || flags.redisPort || "6379";//redis 端口
const IOTA_REDIS_SERVER_PWD = process.env.IOTA_REDIS_SERVER_PWD || flags.redisPswd || "";//redis 密码
// 鉴权 api
const IOT_AUTH_API = process.env.IOT_AUTH_API || flags.iotAuthApi;
// 安心云api
const AXY_API_URL = process.env.AXY_API_URL || flags.axyApiUrl;
const API_ANXINYUN_URL = process.env.API_ANXINYUN_URL || flags.axyApiUrl;
// 企业管理 api
const API_EMIS_URL = process.env.API_EMIS_URL || flags.apiEmisUrl;
// 高德地图的参数
const GOD_URL = process.env.GOD_URL || flags.godUrl || 'https://restapi.amap.com/v3';
const GOD_KEY = process.env.GOD_KEY || flags.godKey;
// 萤石服务的地址
const YINGSHI_URL = process.env.YINGSHI_URL || flags.yingshiUrl || 'https://open.ys7.com/api';
// 视频后台服务的地址
const IOT_VIDEO_SERVER_URL = process.env.IOT_VIDEO_SERVER_URL || flags.iotVideoServerUrl
// 视频后台 mqtt 信息推送地址
const MQTT_VIDEO_SERVER = process.env.MQTT_VIDEO_SERVER || flags.mqttVideoServer;
// 几种国标摄像头播放地址前缀
const CAMERA_PLAY_WS_HOST = process.env.CAMERA_PLAY_WS_HOST || flags.cameraPlayWsHost;
const CAMERA_PLAY_HTTP_FLV_HOST = process.env.CAMERA_PLAY_HTTP_FLV_HOST || flags.cameraPlayHttpFlvHost;
const CAMERA_PLAY_HLS_HOST = process.env.CAMERA_PLAY_HLS_HOST || flags.cameraPlayHlsHost;
const CAMERA_PLAY_RTMP_HOST = process.env.CAMERA_PLAY_RTMP_HOST || flags.cameraPlayRtmpHost;
const CAMERA_PLAY_RTSP_HOST = process.env.CAMERA_PLAY_RTSP_HOST || flags.cameraPlayRtspHost;
// 七牛云存储参数
const QINIU_DOMAIN_QNDMN_RESOURCE = process.env.ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE || flags.qndmn;
const QINIU_BUCKET_RESOURCE = process.env.ANXINCLOUD_QINIU_BUCKET_RESOURCE || flags.qnbkt;
const QINIU_AK = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak;
const QINIU_SK = process.env.ANXINCLOUD_QINIU_SECRETKEY || flags.qnsk;
if (!IOT_VIDEO_ACCESS_DB || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT || !GOD_KEY || !MQTT_VIDEO_SERVER ||
!AXY_API_URL || !IOT_VIDEO_SERVER_URL || !IOT_AUTH_API ||
!CAMERA_PLAY_WS_HOST || !CAMERA_PLAY_HTTP_FLV_HOST || !CAMERA_PLAY_HLS_HOST || !CAMERA_PLAY_RTMP_HOST || !CAMERA_PLAY_RTSP_HOST
if (
!POMS_DB ||
!IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT || !GOD_KEY ||
// !MQTT_VIDEO_SERVER ||
!API_ANXINYUN_URL ||
!API_EMIS_URL ||
!QINIU_DOMAIN_QNDMN_RESOURCE || !QINIU_BUCKET_RESOURCE || !QINIU_AK || !QINIU_SK
) {
console.log('缺少启动参数,异常退出');
args.showHelp();
@ -80,10 +79,11 @@ const product = {
{
entry: require('@fs/attachment').entry,
opts: {
local: {
origin: IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN || `http://localhost:${flags.port || 8080}`,
rootPath: 'static',
childPath: 'upload',
qiniu: {
domain: QINIU_DOMAIN_QNDMN_RESOURCE,
bucket: QINIU_BUCKET_RESOURCE,
accessKey: QINIU_AK,
secretKey: QINIU_SK
},
maxSize: 104857600, // 100M
}
@ -92,8 +92,9 @@ const product = {
opts: {
dev,
exclude: [
{ p: '/camera', o: 'GET' }, // 暂时滴
{ p: '/application/check', o: 'GET' }, // 暂时滴
{ p: '/attachments/:p', o: 'POST' },
{ p: '/alarm/application/inspection', o: 'POST' },
{ p: '/project/app_list', o: 'GET' }
], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
redis: {
host: IOTA_REDIS_SERVER_HOST,
@ -103,13 +104,6 @@ const product = {
mqtt: {
mqttVideoServer: MQTT_VIDEO_SERVER,
},
cameraPlayHost: {
ws: CAMERA_PLAY_WS_HOST,
httpFlv: CAMERA_PLAY_HTTP_FLV_HOST,
hls: CAMERA_PLAY_HLS_HOST,
rtmp: CAMERA_PLAY_RTMP_HOST,
rtsp: CAMERA_PLAY_RTSP_HOST,
},
sms: {
///阿里云-安心云
accessKey: 'LTAI5tAFdjz7j38aNF2C9Qe8',
@ -120,45 +114,31 @@ const product = {
host: 'smtp.exmail.qq.com',
port: 465,
sender: {
name: '尚视',
name: '运维服务',
address: 'fsiot@free-sun.com.cn',
password: 'Fs2689'
}
},
pssaRequest: [
{// name 会作为一个 request 出现在 ctx.app.fs
name: 'authRequest',
root: IOT_AUTH_API
}, {
name: 'axyRequest',
root: AXY_API_URL
}, {
name: 'godRequest',
root: GOD_URL,
params: {
query: {
key: GOD_KEY
}
}
}, {
name: 'yingshiRequest',
root: YINGSHI_URL,
params: {
query: {
key: GOD_KEY
}
pssaRequest: [{// name 会作为一个 request 出现在 ctx.app.fs
name: 'axyRequest',
root: API_ANXINYUN_URL
}, {
name: 'emisRequest',
root: API_EMIS_URL
}, {
name: 'godRequest',
root: GOD_URL,
params: {
query: {
key: GOD_KEY
}
}, {
name: 'videoServerRequest',
root: IOT_VIDEO_SERVER_URL + '/api',
dataWord: 'text'
},
]
}
},]
}
}
],
dc: {
url: IOT_VIDEO_ACCESS_DB,
url: POMS_DB,
opts: {
pool: {
max: 80,

4
api/sequelize-automate.config.js

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

3
script/.vscode/settings.json

@ -1,3 +0,0 @@
{
"editor.wordWrap": "on"
}

543
script/0.0.1/0_init_db.sql

@ -1,543 +0,0 @@
/*
Navicat Premium Data Transfer
Source Server : 10.8.30.32
Source Server Type : PostgreSQL
Source Server Version : 90515
Source Host : 10.8.30.32:5432
Source Catalog : video_access
Source Schema : public
Target Server Type : PostgreSQL
Target Server Version : 90515
File Encoding : 65001
Date: 06/07/2022 11:45:25
*/
-- ----------------------------
-- Sequence structure for camera_ability_bind_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."camera_ability_bind_id_seq";
CREATE SEQUENCE "public"."camera_ability_bind_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- ----------------------------
-- Sequence structure for camera_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."camera_id_seq";
CREATE SEQUENCE "public"."camera_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- ----------------------------
-- Sequence structure for camera_remark_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."camera_remark_id_seq";
CREATE SEQUENCE "public"."camera_remark_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- ----------------------------
-- Sequence structure for gbCamera_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."gbCamera_id_seq";
CREATE SEQUENCE "public"."gbCamera_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- ----------------------------
-- Sequence structure for gb_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."gb_id_seq";
CREATE SEQUENCE "public"."gb_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- ----------------------------
-- Sequence structure for nvr_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."nvr_id_seq";
CREATE SEQUENCE "public"."nvr_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- ----------------------------
-- Sequence structure for secret_yingshi_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."secret_yingshi_id_seq";
CREATE SEQUENCE "public"."secret_yingshi_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- ----------------------------
-- Sequence structure for t_upload_comm_http_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."t_upload_comm_http_id_seq";
CREATE SEQUENCE "public"."t_upload_comm_http_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- ----------------------------
-- Sequence structure for vender_id_seq
-- ----------------------------
DROP SEQUENCE IF EXISTS "public"."vender_id_seq";
CREATE SEQUENCE "public"."vender_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- ----------------------------
-- Table structure for ax_project
-- ----------------------------
DROP TABLE IF EXISTS "public"."ax_project";
CREATE TABLE "public"."ax_project" (
"id" int4 NOT NULL,
"name" varchar(64) COLLATE "pg_catalog"."default" NOT NULL
)
;
COMMENT ON TABLE "public"."ax_project" IS '安心云项目副表,主要用于和camera等进行联合查询
';
-- ----------------------------
-- Records of ax_project
-- ----------------------------
INSERT INTO "public"."ax_project" VALUES (1, '安心云项目');
-- ----------------------------
-- Table structure for camera
-- ----------------------------
DROP TABLE IF EXISTS "public"."camera";
CREATE TABLE "public"."camera" (
"id" int4 NOT NULL DEFAULT nextval('camera_id_seq'::regclass),
"type" varchar(16) COLLATE "pg_catalog"."default" NOT NULL,
"name" varchar(128) COLLATE "pg_catalog"."default",
"channel_name" varchar(64) COLLATE "pg_catalog"."default",
"external_domain" varchar(64) COLLATE "pg_catalog"."default",
"rtmp" varchar(1024) COLLATE "pg_catalog"."default",
"serial_no" varchar(128) COLLATE "pg_catalog"."default",
"cloud_control" bool,
"high_definition" bool,
"voice" bool,
"memory_card" varchar(32) COLLATE "pg_catalog"."default",
"vender_id" int4,
"cascade_type" varchar(32) COLLATE "pg_catalog"."default",
"sip" varchar(1024) COLLATE "pg_catalog"."default",
"longitude" numeric(16,12),
"latitude" numeric(16,12),
"forbidden" bool NOT NULL DEFAULT false,
"create_time" timestamptz(6) NOT NULL,
"recycle_time" timestamptz(6),
"delete" bool NOT NULL DEFAULT false,
"create_user_id" int4 NOT NULL,
"nvr_id" int4,
"model" varchar(128) COLLATE "pg_catalog"."default",
"kind_id" int4,
"yingshi_secret_id" int4,
"gb_id" int4,
"top_serial_no" varchar(128) COLLATE "pg_catalog"."default"
)
;
COMMENT ON COLUMN "public"."camera"."type" IS '设备类型:yingshi - 萤石;nvr - NVR摄像头;ipc - IPC 网络摄像头;cascade - 级联摄像头';
COMMENT ON COLUMN "public"."camera"."name" IS '设备名称/安装位置';
COMMENT ON COLUMN "public"."camera"."channel_name" IS '通道名称';
COMMENT ON COLUMN "public"."camera"."external_domain" IS '外域名称';
COMMENT ON COLUMN "public"."camera"."serial_no" IS '设备编号';
COMMENT ON COLUMN "public"."camera"."cloud_control" IS '云台控制';
COMMENT ON COLUMN "public"."camera"."high_definition" IS '高清支持';
COMMENT ON COLUMN "public"."camera"."voice" IS '语音对讲支持';
COMMENT ON COLUMN "public"."camera"."memory_card" IS '内存卡容量';
COMMENT ON COLUMN "public"."camera"."vender_id" IS '设备厂商id';
COMMENT ON COLUMN "public"."camera"."cascade_type" IS '级联方式:up - 上级联;down - 下级联';
COMMENT ON COLUMN "public"."camera"."longitude" IS '经度';
COMMENT ON COLUMN "public"."camera"."latitude" IS '维度';
COMMENT ON COLUMN "public"."camera"."forbidden" IS '是否禁用';
COMMENT ON COLUMN "public"."camera"."recycle_time" IS '放入回收站时间';
COMMENT ON COLUMN "public"."camera"."delete" IS '是否彻底删除';
COMMENT ON COLUMN "public"."camera"."model" IS '型号';
COMMENT ON COLUMN "public"."camera"."top_serial_no" IS 'gb设备 level=0 的 steamid ';
-- ----------------------------
-- Table structure for camera_ability
-- ----------------------------
DROP TABLE IF EXISTS "public"."camera_ability";
CREATE TABLE "public"."camera_ability" (
"id" int4 NOT NULL,
"ability" varchar(64) COLLATE "pg_catalog"."default" NOT NULL
)
;
COMMENT ON TABLE "public"."camera_ability" IS '摄像机能力';
-- ----------------------------
-- Records of camera_ability
-- ----------------------------
INSERT INTO "public"."camera_ability" VALUES (1, '普通摄像头');
INSERT INTO "public"."camera_ability" VALUES (2, '人流量计数');
INSERT INTO "public"."camera_ability" VALUES (3, '热成像');
INSERT INTO "public"."camera_ability" VALUES (4, 'AI摄像头');
INSERT INTO "public"."camera_ability" VALUES (1314, '其他');
-- ----------------------------
-- Table structure for camera_ability_bind
-- ----------------------------
DROP TABLE IF EXISTS "public"."camera_ability_bind";
CREATE TABLE "public"."camera_ability_bind" (
"id" int4 NOT NULL DEFAULT nextval('camera_ability_bind_id_seq'::regclass),
"camera_id" int4 NOT NULL,
"ability_id" int4 NOT NULL
)
;
-- ----------------------------
-- Table structure for camera_kind
-- ----------------------------
DROP TABLE IF EXISTS "public"."camera_kind";
CREATE TABLE "public"."camera_kind" (
"id" int4 NOT NULL,
"kind" varchar(64) COLLATE "pg_catalog"."default" NOT NULL
)
;
COMMENT ON TABLE "public"."camera_kind" IS '摄像机类型';
-- ----------------------------
-- Records of camera_kind
-- ----------------------------
INSERT INTO "public"."camera_kind" VALUES (1, '枪机');
INSERT INTO "public"."camera_kind" VALUES (2, '球机');
INSERT INTO "public"."camera_kind" VALUES (1314, '其他');
-- ----------------------------
-- Table structure for camera_remark
-- ----------------------------
DROP TABLE IF EXISTS "public"."camera_remark";
CREATE TABLE "public"."camera_remark" (
"id" int4 NOT NULL DEFAULT nextval('camera_remark_id_seq'::regclass),
"camera_id" int4 NOT NULL,
"remark" varchar(256) COLLATE "pg_catalog"."default" NOT NULL
)
;
-- ----------------------------
-- Table structure for gbCamera
-- ----------------------------
DROP TABLE IF EXISTS "public"."gbCamera";
CREATE TABLE "public"."gbCamera" (
"id" int4 NOT NULL DEFAULT nextval('gb_id_seq'::regclass),
"level" int4,
"parent" varchar(32) COLLATE "pg_catalog"."default",
"streamid" varchar(255) COLLATE "pg_catalog"."default",
"online" varchar(255) COLLATE "pg_catalog"."default",
"manufactuer" varchar(255) COLLATE "pg_catalog"."default",
"model" varchar(255) COLLATE "pg_catalog"."default",
"civilCode" varchar(255) COLLATE "pg_catalog"."default",
"adddress" varchar(255) COLLATE "pg_catalog"."default",
"name" varchar(255) COLLATE "pg_catalog"."default",
"Addr" varchar(255) COLLATE "pg_catalog"."default",
"Sipip" varchar(255) COLLATE "pg_catalog"."default",
"ipctype" varchar(255) COLLATE "pg_catalog"."default",
"registerTime" varchar(255) COLLATE "pg_catalog"."default" DEFAULT ''::character varying,
"updateTime" varchar(255) COLLATE "pg_catalog"."default",
"playUrl" jsonb
)
;
COMMENT ON COLUMN "public"."gbCamera"."playUrl" IS '播放地址集合';
-- ----------------------------
-- Table structure for nvr
-- ----------------------------
DROP TABLE IF EXISTS "public"."nvr";
CREATE TABLE "public"."nvr" (
"id" int4 NOT NULL DEFAULT nextval('nvr_id_seq'::regclass),
"name" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
"vender_id" int4,
"serial_no" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
"region_code" varchar(64) COLLATE "pg_catalog"."default",
"longitude" numeric(16,12) NOT NULL,
"latitude" numeric(16,12) NOT NULL,
"create_time" timestamptz(6) NOT NULL DEFAULT now(),
"channel_count" int4,
"port" varchar(32) COLLATE "pg_catalog"."default",
"delete" bool NOT NULL DEFAULT false,
"create_user_id" int4 NOT NULL
)
;
COMMENT ON COLUMN "public"."nvr"."vender_id" IS '设备厂家id';
COMMENT ON COLUMN "public"."nvr"."serial_no" IS '设备编号';
COMMENT ON COLUMN "public"."nvr"."region_code" IS '行政区码';
COMMENT ON COLUMN "public"."nvr"."create_time" IS '创建时间';
COMMENT ON COLUMN "public"."nvr"."channel_count" IS '通道数';
COMMENT ON COLUMN "public"."nvr"."port" IS '端口';
-- ----------------------------
-- Table structure for secret_yingshi
-- ----------------------------
DROP TABLE IF EXISTS "public"."secret_yingshi";
CREATE TABLE "public"."secret_yingshi" (
"id" int4 NOT NULL DEFAULT nextval('secret_yingshi_id_seq'::regclass),
"key" varchar(512) COLLATE "pg_catalog"."default" NOT NULL,
"secret" varchar(512) COLLATE "pg_catalog"."default" NOT NULL,
"token" varchar(512) COLLATE "pg_catalog"."default",
"expire" varchar(512) COLLATE "pg_catalog"."default"
)
;
-- ----------------------------
-- Records of secret_yingshi
-- ----------------------------
INSERT INTO "public"."secret_yingshi" VALUES (2, '3ea2b502f6804d64b43e4cb3d135665c', '331c85c5b7ce76179f6eb7dccb8aeb27', 'at.culo424j9drz8atx9uikeq5e2mzkhnhx-490csubqrh-1o3hcaz-szuvfax6f', '1657681218109');
INSERT INTO "public"."secret_yingshi" VALUES (3, 'd0704fb9d5d14a6682c1c1d592c12512', '93d023269495b86be62cdfdcf34a6cd1', 'at.1y2mk5fh2myswbkp017pmvdi0v1zbdrw-5j39z78wsp-0x4vi2j-a3mxjlt32', '1657683606281');
INSERT INTO "public"."secret_yingshi" VALUES (1, '5d16a667e1c2423d9d0d634f781810b4', '0cc4e1ec4e6a53ea3dabeb09cd5f468b', 'at.9gq3xwwbazkk33y177ucqtk936bmd0sz-41x4xf24sh-1cn7x82-4lf7davve', '1657681803201');
-- ----------------------------
-- Table structure for t_upload_comm_http
-- ----------------------------
DROP TABLE IF EXISTS "public"."t_upload_comm_http";
CREATE TABLE "public"."t_upload_comm_http" (
"id" int4 NOT NULL DEFAULT nextval('t_upload_comm_http_id_seq'::regclass),
"content" json NOT NULL,
"enable" bool NOT NULL DEFAULT true,
"description" varchar(255) COLLATE "pg_catalog"."default"
)
;
-- ----------------------------
-- Table structure for vender
-- ----------------------------
DROP TABLE IF EXISTS "public"."vender";
CREATE TABLE "public"."vender" (
"id" int4 NOT NULL DEFAULT nextval('vender_id_seq'::regclass),
"name" varchar(64) COLLATE "pg_catalog"."default" NOT NULL
)
;
COMMENT ON TABLE "public"."vender" IS '设备厂商';
-- ----------------------------
-- Records of vender
-- ----------------------------
INSERT INTO "public"."vender" VALUES (2, '海康威视');
INSERT INTO "public"."vender" VALUES (3, '大华');
INSERT INTO "public"."vender" VALUES (1, '飞尚科技');
INSERT INTO "public"."vender" VALUES (1314, '其他');
-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."camera_ability_bind_id_seq"
OWNED BY "public"."camera_ability_bind"."id";
-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."camera_id_seq"
OWNED BY "public"."camera"."id";
-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."camera_remark_id_seq"
OWNED BY "public"."camera_remark"."id";
-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
SELECT setval('"public"."gbCamera_id_seq"', 2, false);
-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."nvr_id_seq"
OWNED BY "public"."nvr"."id";
-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."secret_yingshi_id_seq"
OWNED BY "public"."secret_yingshi"."id";
SELECT setval('"public"."secret_yingshi_id_seq"', 4, true);
-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
SELECT setval('"public"."t_upload_comm_http_id_seq"', 2, false);
-- ----------------------------
-- Alter sequences owned by
-- ----------------------------
ALTER SEQUENCE "public"."vender_id_seq"
OWNED BY "public"."vender"."id";
SELECT setval('"public"."vender_id_seq"', 4, true);
-- ----------------------------
-- Indexes structure for table ax_project
-- ----------------------------
CREATE UNIQUE INDEX "ax_project_id_uindex" ON "public"."ax_project" USING btree (
"id" "pg_catalog"."int4_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table ax_project
-- ----------------------------
ALTER TABLE "public"."ax_project" ADD CONSTRAINT "ax_project_pk" PRIMARY KEY ("id");
-- ----------------------------
-- Indexes structure for table camera
-- ----------------------------
CREATE UNIQUE INDEX "camera_id_uindex" ON "public"."camera" USING btree (
"id" "pg_catalog"."int4_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table camera
-- ----------------------------
ALTER TABLE "public"."camera" ADD CONSTRAINT "camera_pk" PRIMARY KEY ("id");
-- ----------------------------
-- Indexes structure for table camera_ability
-- ----------------------------
CREATE UNIQUE INDEX "camera_ability_id_uindex" ON "public"."camera_ability" USING btree (
"id" "pg_catalog"."int4_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table camera_ability
-- ----------------------------
ALTER TABLE "public"."camera_ability" ADD CONSTRAINT "camera_ability_pk" PRIMARY KEY ("id");
-- ----------------------------
-- Indexes structure for table camera_ability_bind
-- ----------------------------
CREATE UNIQUE INDEX "camera_ability_bind_id_uindex" ON "public"."camera_ability_bind" USING btree (
"id" "pg_catalog"."int4_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table camera_ability_bind
-- ----------------------------
ALTER TABLE "public"."camera_ability_bind" ADD CONSTRAINT "camera_ability_bind_pk" PRIMARY KEY ("id");
-- ----------------------------
-- Indexes structure for table camera_kind
-- ----------------------------
CREATE UNIQUE INDEX "camera_kind_id_uindex" ON "public"."camera_kind" USING btree (
"id" "pg_catalog"."int4_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table camera_kind
-- ----------------------------
ALTER TABLE "public"."camera_kind" ADD CONSTRAINT "camera_kind_pk" PRIMARY KEY ("id");
-- ----------------------------
-- Indexes structure for table camera_remark
-- ----------------------------
CREATE UNIQUE INDEX "camera_remark_id_uindex" ON "public"."camera_remark" USING btree (
"id" "pg_catalog"."int4_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table camera_remark
-- ----------------------------
ALTER TABLE "public"."camera_remark" ADD CONSTRAINT "camera_remark_pk" PRIMARY KEY ("id");
-- ----------------------------
-- Primary Key structure for table gbCamera
-- ----------------------------
ALTER TABLE "public"."gbCamera" ADD CONSTRAINT "gbCamera1_copy1_pkey" PRIMARY KEY ("id");
-- ----------------------------
-- Indexes structure for table nvr
-- ----------------------------
CREATE UNIQUE INDEX "nvr_id_uindex" ON "public"."nvr" USING btree (
"id" "pg_catalog"."int4_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table nvr
-- ----------------------------
ALTER TABLE "public"."nvr" ADD CONSTRAINT "nvr_pk" PRIMARY KEY ("id");
-- ----------------------------
-- Indexes structure for table secret_yingshi
-- ----------------------------
CREATE UNIQUE INDEX "secret_yingshi_id_uindex" ON "public"."secret_yingshi" USING btree (
"id" "pg_catalog"."int4_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table secret_yingshi
-- ----------------------------
ALTER TABLE "public"."secret_yingshi" ADD CONSTRAINT "secret_yingshi_pk" PRIMARY KEY ("id");
-- ----------------------------
-- Primary Key structure for table t_upload_comm_http
-- ----------------------------
ALTER TABLE "public"."t_upload_comm_http" ADD CONSTRAINT "t_upload_comm_http_pkey" PRIMARY KEY ("id");
-- ----------------------------
-- Indexes structure for table vender
-- ----------------------------
CREATE UNIQUE INDEX "vender_id_uindex" ON "public"."vender" USING btree (
"id" "pg_catalog"."int4_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table vender
-- ----------------------------
ALTER TABLE "public"."vender" ADD CONSTRAINT "vender_pk" PRIMARY KEY ("id");
-- ----------------------------
-- Foreign Keys structure for table camera
-- ----------------------------
ALTER TABLE "public"."camera" ADD CONSTRAINT "camera_camera_kind_id_fk" FOREIGN KEY ("kind_id") REFERENCES "public"."camera_kind" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE "public"."camera" ADD CONSTRAINT "camera_nvr_id_fk" FOREIGN KEY ("nvr_id") REFERENCES "public"."nvr" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE "public"."camera" ADD CONSTRAINT "camera_secret_yingshi_id_fk" FOREIGN KEY ("yingshi_secret_id") REFERENCES "public"."secret_yingshi" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE "public"."camera" ADD CONSTRAINT "camera_vender_id_fk" FOREIGN KEY ("vender_id") REFERENCES "public"."vender" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ----------------------------
-- Foreign Keys structure for table camera_ability_bind
-- ----------------------------
ALTER TABLE "public"."camera_ability_bind" ADD CONSTRAINT "camera_ability_bind_camera_ability_id_fk" FOREIGN KEY ("ability_id") REFERENCES "public"."camera_ability" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE "public"."camera_ability_bind" ADD CONSTRAINT "camera_ability_bind_camera_id_fk" FOREIGN KEY ("camera_id") REFERENCES "public"."camera" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ----------------------------
-- Foreign Keys structure for table camera_remark
-- ----------------------------
ALTER TABLE "public"."camera_remark" ADD CONSTRAINT "camera_remark_camera_id_fk" FOREIGN KEY ("camera_id") REFERENCES "public"."camera" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ----------------------------
-- Foreign Keys structure for table nvr
-- ----------------------------
ALTER TABLE "public"."nvr" ADD CONSTRAINT "nvr_vender_id_fk" FOREIGN KEY ("vender_id") REFERENCES "public"."vender" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;

186
script/1.0.0/1.init_tables.sql

@ -0,0 +1,186 @@
create table if not exists "user"
(
id serial not null,
pep_user_id integer not null,
role character varying[],
correlation_project integer[],
last_in_time timestamp with time zone,
in_times integer default 0 not null,
online_duration integer default 0,
last_in_address varchar(256),
disabled boolean default false not null,
deleted boolean default false not null,
update_time timestamp with time zone default now(),
constraint user_pk
primary key (id)
);
comment on column "user".pep_user_id is '项企对应用户id';
comment on column "user".role is '角色 也对应权限 admin 管理员 / all 全部角色 / data_analyst 数据分析 / after_sale 售后运维 / resource_manage 资源管理 / customer_service 客户服务';
comment on column "user".correlation_project is '关联的项目管理的项目id';
comment on column "user".online_duration is '在线时长 单位 s';
comment on column "user".last_in_address is '上次登录地点';
create unique index if not exists user_id_uindex
on "user" (id);
create table if not exists project_correlation
(
id serial not null,
anxin_project_id integer[] not null,
pep_project_id integer[] not null,
create_time timestamp with time zone not null,
create_user integer not null,
name varchar(64),
constraint project_correlation_pk
primary key (id)
);
comment on table project_correlation is '安心云项目和项目管理项目关系映射绑定';
comment on column project_correlation.pep_project_id is '项目管理的项目id';
create unique index if not exists project_correlation_id_uindex
on project_correlation (id);
create table if not exists action_log
(
id serial not null,
user_id integer,
time timestamp with time zone not null,
action varchar(256),
expand_params jsonb,
constraint action_log_pk
primary key (id),
constraint action_log_user_id_fk
foreign key (user_id) references "user"
);
comment on table action_log is '动态日志';
comment on column action_log.action is '动态内容';
create unique index if not exists action_log_id_uindex
on action_log (id);
create table if not exists quick_link
(
id serial not null,
user_id integer not null,
link varchar(256) not null,
constraint quick_link_pk
primary key (id),
constraint quick_link_user_id_fk
foreign key (user_id) references "user"
);
comment on table quick_link is '对应我的常用工具功能';
create unique index if not exists quick_link_id_uindex
on quick_link (id);
create table if not exists project_app
(
id serial not null,
name varchar(32) not null,
url varchar(1024) not null,
project_id integer not null,
constraint project_app_pk
primary key (id),
constraint project_app_project_correlation_id_fk
foreign key (project_id) references project_correlation
);
create table if not exists app_alarm
(
id serial not null,
serial_number varchar(32),
project_app_id integer,
app_domain varchar(512),
alarm_content varchar(1024),
create_time timestamp with time zone not null,
update_time timestamp with time zone,
confirm varchar(1024),
router varchar(1024),
status_code varchar(8),
constraint app_alarm_pk
primary key (id),
constraint app_alarm_project_app_id_fk
foreign key (project_app_id) references project_app
);
comment on table app_alarm is '应用异常';
comment on column app_alarm.serial_number is '自定义编号';
comment on column app_alarm.project_app_id is '对应的项目id';
comment on column app_alarm.app_domain is '应用域名';
comment on column app_alarm.alarm_content is '告警信息';
comment on column app_alarm.confirm is '确认信息';
comment on column app_alarm.router is '路由';
comment on column app_alarm.status_code is '状态码';
create unique index if not exists app_alarm_id_uindex
on app_alarm (id);
create table if not exists app_inspection
(
id serial not null,
project_app_id integer,
create_time timestamp with time zone not null,
screenshot character varying[],
noted_pep_user_id integer,
noted_time timestamp with time zone,
constraint app_inspection_pk
primary key (id),
constraint app_inspection_project_app_id_fk
foreign key (project_app_id) references project_app
);
comment on table app_inspection is '应用巡检';
comment on column app_inspection.screenshot is '截图存储路径';
comment on column app_inspection.noted_pep_user_id is '核验人员';
create unique index if not exists app_inspection_id_uindex
on app_inspection (id);
create unique index if not exists project_app_id_uindex
on project_app (id);
create table if not exists alarm_push_config
(
id serial not null,
name varchar(32) not null,
pep_project_id integer[] not null,
alarm_type character varying[],
receiver_pep_user_id integer[],
time_type character varying[],
create_time timestamp with time zone not null,
create_user_id integer not null,
disable boolean default false not null,
constraint alarm_push_config_pk
primary key (id)
);
comment on table alarm_push_config is '告警推送配置';
comment on column alarm_push_config.alarm_type is '监听的告警类型';
comment on column alarm_push_config.receiver_pep_user_id is '接收人id 项企';
comment on column alarm_push_config.time_type is '通知时效';
create unique index if not exists alarm_push_config_id_uindex
on alarm_push_config (id);

1
script/1.0.0/2.init_superadmin.sql

@ -0,0 +1 @@
INSERT INTO "user" ("pep_user_id", "role") VALUES (1, ARRAY['SuperAdmin']);

14
script/1.1.0/1.camera_remark.sql

@ -1,14 +0,0 @@
create table if not exists camera_remark
(
id serial not null,
camera_id integer not null,
remark varchar(256) not null,
constraint camera_remark_pk
primary key (id),
constraint camera_remark_camera_id_fk
foreign key (camera_id) references camera
);
create unique index if not exists camera_remark_id_uindex
on camera_remark (id);

17
script/1.1.1/data/1_update_status_code_data/.vscode/launch.json

@ -1,17 +0,0 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\index.js"
}
]
}

69
script/1.1.1/data/1_update_status_code_data/index.js

@ -1,69 +0,0 @@
try {
const { Pool, Client } = require('pg')
const XLSX = require('xlsx')
const path = require('path')
// 连接数据库
const pool = new Pool({
user: 'postgres',
host: '10.8.30.32',
database: 'video_access',
password: '123',
port: 5432,
})
const fun = async () => {
// note: we don't try/catch this because if connecting throws an exception
// we don't need to dispose of the client (it will be undefined)
const client = await pool.connect()
try {
await client.query('BEGIN')
// 读取数据文件
let workbook = XLSX.readFile(path.join(__dirname, '云录制错误码.xlsx'))
let firstSheetName = workbook.SheetNames[0];
let worksheet = workbook.Sheets[firstSheetName];
let res = XLSX.utils.sheet_to_json(worksheet);
// console.log(res);
for (let d of res) {
let statusRes = await client.query(`SELECT * FROM camera_status WHERE status=$1`, [d['错误码']]);
let statusRows = statusRes.rows
if (statusRows.length) {
} else {
console.log(`增加${d['错误码']}`);
const statusInQuery = `INSERT INTO "camera_status" (platform, status, describe, paraphrase) VALUES($1, $2, $3, $4) RETURNING id;`
const statusRows = (await client.query(statusInQuery, ['yingshi', d['错误码'], d['错误描述'], d['释义']])).rows
// console.log(statusRows);
if (d['解决方案']) {
let resolveArr = d['解决方案'].split(';');
// await client.query(`DELETE FROM "camera_status_solution" WHERE status_id=$1`, [statusRows[0].id]);
for (let r of resolveArr) {
await client.query(
`INSERT INTO "camera_status_resolve" (status_id, resolve) VALUES($1, $2) RETURNING id;`,
[statusRows[0].id, r]
)
}
}
}
}
// await client.query('ROLLBACK')
await client.query('COMMIT')
console.log('执行完毕~')
} catch (e) {
await client.query('ROLLBACK')
console.log('执行错误~')
throw e
} finally {
client.release();
}
}
fun()
} catch (error) {
console.error(error)
}

16
script/1.1.1/data/1_update_status_code_data/package.json

@ -1,16 +0,0 @@
{
"name": "appkey-generator",
"version": "1.0.0",
"description": "tool",
"main": "index.js",
"scripts": {
"test": "mocha",
"start": "set NODE_ENV=development&&node index"
},
"author": "liu",
"license": "ISC",
"dependencies": {
"pg": "^7.18.2",
"xlsx": "^0.17.1"
}
}

BIN
script/1.1.1/data/1_update_status_code_data/云录制错误码.xlsx

Binary file not shown.

141
script/1.1.1/data/2.insert_camera_status.sql

@ -1,141 +0,0 @@
-- ----------------------------
-- Records of camera_status
-- ----------------------------
INSERT INTO "public"."camera_status" VALUES (2, 'yingshi', '5000', '服务端内部处理异常', '服务端内部错误码', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (3, 'yingshi', '5400', '私有化协议vtm检测私有化协议中码流类型小于0或者设备序列号为空等非法参数场景返回(app不重试取流)', '客户端参数出错', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (4, 'yingshi', '5402', '回放找不到录像文件', '设备回放找不到录像文件', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (5, 'yingshi', '5403', '操作码或信令密钥与设备不匹配', '操作码或信令密钥与设备不匹配', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (6, 'yingshi', '5404', '设备不在线', '设备不在线', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (7, 'yingshi', '5405', '流媒体向设备发送或接受信令超时/cas响应超时', '设备回应信令10秒超时', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (8, 'yingshi', '5406', 'token失效', 'token失效', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (9, 'yingshi', '5407', '客户端的URL格式错误', '客户端的URL格式错误', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (10, 'yingshi', '5409', '预览开启隐私保护', '预览开启隐私保护', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (11, 'yingshi', '5411', 'token无权限', 'token无权限、用户无权限', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (12, 'yingshi', '5412', 'session不存在', 'session不存在', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (13, 'yingshi', '5413', '验证token的他异常(不具体)', 'token验证失败', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (14, 'yingshi', '5415', '设备通道错', '设备判断请求通道不存在', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (15, 'yingshi', '5416', '设备资源受限', '设备资源受限', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (16, 'yingshi', '5451', '设备不支持的码流类型', '设备不支持的码流类型', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (17, 'yingshi', '5452', '设备链接流媒体服务器失败', '设备链接流媒体服务器失败', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (18, 'yingshi', '5454', '流媒体中关于设备取流会话不存在', '流媒体中关于设备取流会话不存在', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (19, 'yingshi', '5455', '设备通道未关联', '设备通道未关联', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (20, 'yingshi', '5456', '设备通道关联设备不在线', '设备通道关联设备不在线', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (21, 'yingshi', '5457', '客户端不支持端到端加密', '客户端不支持端到端加密', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (22, 'yingshi', '5458', '设备不支持当前并发ECDH密', '设备不支持当前并发ECDH密', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (23, 'yingshi', '5459', 'VTDU 处理ECDH 加密失败', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (24, 'yingshi', '5492', '设备不支持的命令', '设备不支持的命令', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (25, 'yingshi', '5500', '服务器处理失败', '服务器处理失败', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (26, 'yingshi', '5503', 'vtm返回分配vtdu失败', 'vtm返回分配vtdu失败', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (27, 'yingshi', '5504', '流媒体vtdu达到最大负载', '流媒体vtdu达到最大负载', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (28, 'yingshi', '5544', '设备返回无视频源', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (29, 'yingshi', '5545', '视频分享时间已经结束', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (30, 'yingshi', '5546', 'vtdu返回达到取流并发路数限制', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (31, 'yingshi', '5547', 'vtdu返回开放平台用户并发限制', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (32, 'yingshi', '5556', 'ticket校验失败', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (33, 'yingshi', '5557', '回放服务器等待流头超时', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (34, 'yingshi', '5558', '查找录像开始时间错误', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (35, 'yingshi', '5560', '群组分享取流二次验证失败', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (36, 'yingshi', '5561', '分享群组用户被锁住', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (37, 'yingshi', '5562', '群组分享用户权限变更', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (38, 'yingshi', '5563', '认证服务连接失败', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (39, 'yingshi', '5564', '认证超时', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (40, 'yingshi', '5565', '缓存无效', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (41, 'yingshi', '5566', '不在分享时间内预览', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (42, 'yingshi', '5567', '分享通道被锁定', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (43, 'yingshi', '5568', '未找到认证类型', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (44, 'yingshi', '5569', '认证返回的参数异常', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (45, 'yingshi', '5600', '分享设备不在分享时间内', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (46, 'yingshi', '5601', '群组分享用户没权限', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (47, 'yingshi', '5602', '群组分享权限变更', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (48, 'yingshi', '5530', '机房故障不可用', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (49, 'yingshi', '5701', 'cas信令返回格式错误', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (50, 'yingshi', '5702', 'SPGW请求Cas、Status透传超时', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (51, 'yingshi', '5703', 'SPGW请求http不通', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (52, 'yingshi', '6001', '客户端参数出错', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (53, 'yingshi', '6099', '客户端默认错误', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (54, 'yingshi', '6101', '不支持的命令', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (55, 'yingshi', '6102', '设备流头发送失败', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (56, 'yingshi', '6103', 'cas/设备返回错误1', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (57, 'yingshi', '6104', 'cas/设备返回错误-1', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (58, 'yingshi', '6105', '设备返回错误码3', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (59, 'yingshi', '6106', '设备返回错误码4', '一般常见于多通道设 备预览 1、通道不存在 2、通道子码流不存在 3、通道不在线', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (60, 'yingshi', '6107', '设备返回错误码5', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (61, 'yingshi', '6108', 'cas信令回应重复', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (62, 'yingshi', '6109', '视频广场取消分享', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (63, 'yingshi', '6110', '设备信令默认错误', '设备错误返回的错误码,不具体', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (64, 'yingshi', '6501', '设备数据链路和实际链路不匹配', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (65, 'yingshi', '6502', '设备数据链路重复建立连接', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (66, 'yingshi', '6503', '设备数据链路端口不匹配', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (67, 'yingshi', '6504', '缓存设备数据链路失败(内存块不足)', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (68, 'yingshi', '6505', '设备发送确认头消息重复', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (69, 'yingshi', '6506', '设备数据先于确定头部到达', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (70, 'yingshi', '6508', '设备数据头部长度非法', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (71, 'yingshi', '6509', '索引找不到设备数据管理块', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (72, 'yingshi', '6510', '设备数据链路vtdu内存块协议状态不匹配', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (73, 'yingshi', '6511', '设备数据头部没有streamkey错误', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (74, 'yingshi', '6512', '设备数据头部非法(较笼统)', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (75, 'yingshi', '6513', '设备数据长度过小', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (76, 'yingshi', '6514', '设备老协议推流头部没有streamkey错误', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (77, 'yingshi', '6515', '设备老协议推流数据非法', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (78, 'yingshi', '6516', '设备老协议索引找不到内存管理块', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (79, 'yingshi', '6517', '设备老协议推流数据非法', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (80, 'yingshi', '6518', '设备数据包过大', NULL, 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (81, 'yingshi', '6519', '设备推流链路网络不稳定', '设备长时间未推流超时', 'f', NULL);
INSERT INTO "public"."camera_status" VALUES (82, 'yingshi', '6520', '设备推流链路网络不稳定(默认)', '设备网络异常', 'f', NULL);
-- ----------------------------
-- Records of camera_status_resolve
-- ----------------------------
INSERT INTO "public"."camera_status_resolve" VALUES (2, 2, '检测服务端是否正常');
INSERT INTO "public"."camera_status_resolve" VALUES (3, 3, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (4, 4, '检查是否有存储卡并且接触良好');
INSERT INTO "public"."camera_status_resolve" VALUES (5, 6, '检查设备网络');
INSERT INTO "public"."camera_status_resolve" VALUES (6, 6, '重启设备接入萤石云');
INSERT INTO "public"."camera_status_resolve" VALUES (7, 7, '检查设备网络');
INSERT INTO "public"."camera_status_resolve" VALUES (8, 7, '重启设备');
INSERT INTO "public"."camera_status_resolve" VALUES (9, 8, '刷新重试或者重启设备');
INSERT INTO "public"."camera_status_resolve" VALUES (10, 9, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (11, 13, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (12, 14, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (13, 15, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (14, 16, '刷新重试或者切换到高清模式');
INSERT INTO "public"."camera_status_resolve" VALUES (15, 17, '检查设备网络,重启设备,刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (16, 19, '检查设备通道是否关联');
INSERT INTO "public"."camera_status_resolve" VALUES (17, 20, '检查设备通道是否上线');
INSERT INTO "public"."camera_status_resolve" VALUES (18, 25, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (19, 26, 'vtdu服务异常,请请稍后重试');
INSERT INTO "public"."camera_status_resolve" VALUES (20, 27, '服务器负载达到上限,请稍后重试');
INSERT INTO "public"."camera_status_resolve" VALUES (21, 28, '设备是否接触良好');
INSERT INTO "public"."camera_status_resolve" VALUES (22, 28, '如果一直无法解决,请联系技术支持');
INSERT INTO "public"."camera_status_resolve" VALUES (23, 30, '请升级为企业版,放开并发限制');
INSERT INTO "public"."camera_status_resolve" VALUES (24, 31, '请确定开放平台用户预览是否超过用户并发数量限制');
INSERT INTO "public"."camera_status_resolve" VALUES (25, 33, '刷新重试,检测设备网络,重启设备');
INSERT INTO "public"."camera_status_resolve" VALUES (26, 48, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (27, 49, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (28, 50, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (29, 51, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (30, 52, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (31, 54, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (32, 55, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (33, 56, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (34, 57, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (35, 58, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (36, 59, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (37, 60, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (38, 61, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (39, 63, '刷新重试,或者重启设备');
INSERT INTO "public"."camera_status_resolve" VALUES (40, 64, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (41, 65, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (42, 66, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (43, 67, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (44, 68, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (45, 69, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (46, 70, '刷新重试,或者重启设备');
INSERT INTO "public"."camera_status_resolve" VALUES (47, 71, '刷新重试');
INSERT INTO "public"."camera_status_resolve" VALUES (48, 80, '刷新重试,或者重启设备');

140
script/1.1.1/schema/1.create_camera_status_table.sql

@ -1,140 +0,0 @@
create table if not exists camera_status
(
id serial not null,
platform varchar(32) not null,
status varchar(32) not null,
describe varchar(1024),
paraphrase varchar(1024),
forbidden boolean default false not null,
paraphrase_custom varchar(1024),
constraint camera_status_pk
primary key (id)
);
comment on column camera_status.platform is '平台分类 yingshi gb';
comment on column camera_status.describe is '错误描述';
comment on column camera_status.paraphrase is '释义';
comment on column camera_status.forbidden is '是否禁用';
comment on column camera_status.paraphrase_custom is '自定义释义';
create unique index if not exists camera_status_id_uindex
on camera_status (id);
create table if not exists camera_status_log
(
id serial not null,
status_id integer not null,
time timestamp not null,
constraint camera_status_log_pk
primary key (id),
constraint camera_status_log_camera_status_id_fk
foreign key (status_id) references camera_status
);
create unique index if not exists camera_status_log_id_uindex
on camera_status_log (id);
create unique index if not exists camera_status_log_id_uindex_2
on camera_status_log (id);
create table if not exists camera_status_push_config
(
id serial not null,
name varchar(64) not null,
push_way varchar(32) not null,
notice_way varchar(32) not null,
create_user integer not null,
forbidden boolean default false not null,
timing varchar(32),
constraint camera_online_status_push_config_pk
primary key (id)
);
comment on column camera_status_push_config.push_way is '推送方式 email / phone';
comment on column camera_status_push_config.notice_way is '通知方式 offline / online / timing';
comment on column camera_status_push_config.timing is '定时推送时间';
create unique index if not exists camera_online_status_push_config_id_uindex
on camera_status_push_config (id);
create table if not exists camera_status_push_log
(
id serial not null,
push_config_id integer,
receiver jsonb not null,
time timestamp,
push_way varchar(128) not null,
constraint camera_status_push_log_pk
primary key (id)
);
comment on table camera_status_push_log is '上下线推送日志';
create unique index if not exists camera_status_push_log_id_uindex
on camera_status_push_log (id);
create table if not exists camera_status_push_monitor
(
id serial not null,
config_id integer not null,
camera_id integer not null,
constraint camera_status_push_monitor_pk
primary key (id),
constraint camera_status_push_monitor_camera_id_fk
foreign key (camera_id) references camera,
constraint camera_status_push_monitor_camera_status_push_config_id_fk
foreign key (config_id) references camera_status_push_config
);
create unique index if not exists camera_status_push_monitor_id_uindex
on camera_status_push_monitor (id);
create table if not exists camera_status_push_receiver
(
id serial not null,
config_id integer not null,
receiver varchar(64) not null,
constraint camera_status_push_receiver_pk
primary key (id),
constraint camera_status_push_receiver_camera_status_push_config_id_fk
foreign key (config_id) references camera_status_push_config
);
comment on column camera_status_push_receiver.receiver is '接受者信息 邮箱或者电话号码';
create unique index if not exists camera_status_push_receiver_id_uindex
on camera_status_push_receiver (id);
create table if not exists camera_status_resolve
(
id serial not null,
status_id integer not null,
resolve varchar(1024) not null,
constraint camera_status_resolve_pk
primary key (id),
constraint camera_status_resolve_camera_status_id_fk
foreign key (status_id) references camera_status
);
comment on table camera_status_resolve is '错误码解决方案';
create unique index if not exists camera_status_resolve_id_uindex
on camera_status_resolve (id);

21
script/1.1.2/schema/1.update_camera_status_config.sql

@ -1,21 +0,0 @@
alter table camera_status_push_config alter column notice_way type varchar[32] using notice_way::varchar[32];
DROP TABLE if exists camera_status_push_log;
create table if not exists camera_status_push_log
(
id serial not null,
push_config_id integer,
time timestamp,
push_way varchar(128) not null,
camera integer[] not null,
receiver character varying[] not null,
constraint camera_status_push_log_pk
primary key (id)
);
comment on table camera_status_push_log is '上下线推送日志';
create unique index if not exists camera_status_push_log_id_uindex
on camera_status_push_log (id);

5
script/1.1.2/schema/2.update_push_log.sql

@ -1,5 +0,0 @@
alter table camera_status_push_log
add timing varchar(64);
alter table camera_status_push_log
add notice_way varchar[];

4
script/1.1.2/schema/3.update_camera_channel_no.sql

@ -1,4 +0,0 @@
alter table camera
add channel_no varchar(128);
comment on column camera.channel_no is '通道号';

13
script/1.1.2/schema/4.create_camera_status_offline_log.sql

@ -1,13 +0,0 @@
create table if not exists camera_status_offline_log
(
id serial not null,
camera_id integer not null,
status varchar(32) not null,
time timestamp not null,
constraint camera_status_offline_log_pk
primary key (id)
);
create unique index if not exists camera_status_offline_log_id_uindex
on camera_status_offline_log (id);

19
script/1.2.1/schema/1.create_application.sql

@ -1,19 +0,0 @@
create table if not exists application
(
id serial not null,
name varchar(32) not null,
type character varying[],
app_key varchar(64) not null,
app_secret varchar(64) not null,
create_user_id integer not null,
create_time timestamp not null,
forbidden boolean default false not null,
constraint application_pk
primary key (id)
);
comment on column application.type is 'web / app / wxapp / other';
create unique index if not exists application_id_uindex
on application (id);

4
script/1.3.0/schema/1.alert_gbcamera_did.sql

@ -1,4 +0,0 @@
alter table "gbCamera"
add did varchar(128);
comment on column "gbCamera".did is '设备自定义id';

90
script/1.3.0/schema/2.create_mirror_table.sql

@ -1,90 +0,0 @@
create table if not exists mirror
(
id serial not null,
template varchar(63) not null,
create_user integer not null,
create_time timestamp with time zone not null,
update_time timestamp with time zone,
title varchar(128),
show_header boolean not null,
publish boolean default false not null,
mid varchar(32) not null,
publish_time timestamp with time zone,
constraint mirror_pk
primary key (id)
);
comment on column mirror.template is '模板标识';
create unique index if not exists mirror_id_uindex
on mirror (id);
create table if not exists mirror_tree
(
id serial not null,
name varchar(64) not null,
level integer not null,
dependence integer,
mirror_id integer not null,
constraint mirror_tree_pk
primary key (id),
constraint mirror_tree_mirror_id_fk
foreign key (mirror_id) references mirror
);
comment on table mirror_tree is '镜像服务的树节点';
comment on column mirror_tree.level is '层级标注';
create unique index if not exists mirror_tree_id_uindex
on mirror_tree (id);
create table if not exists mirror_filter_group
(
id serial not null,
name varchar(64) not null,
forbidden boolean default false not null,
mirror_id integer not null,
constraint mirror_filter_group_pk
primary key (id),
constraint mirror_filter_group_mirror_id_fk
foreign key (mirror_id) references mirror
);
comment on table mirror_filter_group is '筛选分组';
create unique index if not exists mirror_filter_group_id_uindex
on mirror_filter_group (id);
create table if not exists mirror_filter
(
id serial not null,
name varchar(64) not null,
group_id integer not null,
constraint mirror_filter_pk
primary key (id),
constraint mirror_filter_mirror_filter_group_id_fk
foreign key (group_id) references mirror_filter_group
);
create unique index if not exists mirror_filter_id_uindex
on mirror_filter (id);
create table if not exists mirror_camera
(
id serial not null,
camera_id integer not null,
tree_ids integer[] not null,
filter_ids integer[],
mirror_id integer not null,
constraint mirror_camera_pk
primary key (id),
constraint mirror_camera_camera_id_fk
foreign key (camera_id) references camera,
constraint mirror_camera_mirror_id_fk
foreign key (mirror_id) references mirror
);
create unique index if not exists mirror_camera_id_uindex
on mirror_camera (id);

17
script/1.3.1/data/1_sync_camera_data/.vscode/launch.json

@ -1,17 +0,0 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\index.js"
}
]
}

8
script/1.3.1/data/1_sync_camera_data/Dockerfile

@ -1,8 +0,0 @@
FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2
COPY . /var/app
WORKDIR /var/app
RUN npm cache clean -f
RUN rm -rf package-lock.json
RUN npm install --registry http://10.8.30.22:7000
CMD ["node", "index.js"]

156
script/1.3.1/data/1_sync_camera_data/index.js

@ -1,156 +0,0 @@
try {
const { Pool, Client } = require('pg')
const XLSX = require('xlsx')
const path = require('path')
const request = require('superagent')
const moment = require('moment')
// 连接数据库
const vcmpPool = new Pool({
user: 'FashionAdmin',
host: '10.8.40.223',
database: 'video-access',
password: 'Fas123_',
port: 5432,
})
const anxinPool = new Pool({
user: 'FashionAdmin',
host: '10.8.40.223',
database: 'AnxinCloud',
password: 'Fas123_',
port: 5432,
})
const fun = async () => {
// note: we don't try/catch this because if connecting throws an exception
// we don't need to dispose of the client (it will be undefined)
const vcmpClient = await vcmpPool.connect()
const anxinClient = await anxinPool.connect()
try {
await vcmpClient.query('BEGIN')
const yingshiHost = 'https://open.ys7.com/api/'
// 获取全部萤石摄像头信息
const anxinYSRes = await anxinClient.query(`SELECT * FROM t_video_ipc WHERE type=$1`, ['yingshi']);
// console.log(anxinYSRes);
// 获取所有萤石账号信息
const secretYSRes = await vcmpClient.query(`SELECT * FROM secret_yingshi`)
const secretRes = secretYSRes.rows
// console.log('secretRes', secretRes);
let addSuccessCount = 0
let addRepeatCount = 0
let addUnlegalCount = 0
let addNoAuthCount = 0
for (let c of anxinYSRes.rows) {
console.log(`处理序列号:${c.serial_no}`)
if (!c.serial_no) {
addUnlegalCount += 1
continue
}
let vcmpExistRes = await vcmpClient.query(
`SELECT * FROM "gbCamera" WHERE streamid=$1`, [c.serial_no]
)
let vcmpExistRows = vcmpExistRes.rows
let cameraExistRes = await vcmpClient.query(`SELECT * FROM camera WHERE serial_no=$1`, [c.serial_no])
let cameraRes = cameraExistRes.rows || []
let repeatCamera = cameraRes.find(cr => {
return (
!cr.channel_no && c.channel_no == 1
) || (cr.channel_no == c.channel_no)
})
if (repeatCamera) {
console.log(`当前设备序列号 ${c.serial_no} 通道号 ${c.channel_no} 已存在`);
console.log(`已存在 id=${repeatCamera.id} 通道号 ${repeatCamera.channel_no}`);
addRepeatCount++
continue
}
if (vcmpExistRows.length) {
let beloneSecretId = null
let serialUnlegal = false
for (let s of secretRes) {
let start = (new Date()).getTime();
console.log(`当前查询账号key ${s.key}`);
if (!s.newToken) {
const tokenRes = await request.post(`${yingshiHost}lapp/token/get`).query({
appKey: s.key,
appSecret: s.secret
})
console.log(`查询 token 结果`, tokenRes.body);
if (tokenRes.body.code == 200 && tokenRes.body.data) {
const { accessToken, expireTime } = tokenRes.body.data
s.newToken = accessToken
} else {
throw `未能获取萤石token ${s.key} ${tokenRes.body.code}`
}
}
while (((new Date()).getTime() - start) < 3000) {//限制频率
continue;
}
// 检测设备所属
const cameraState = await request.post(`${yingshiHost}lapp/device/info`).query({
accessToken: s.newToken,
deviceSerial: c.serial_no
})
console.log(`查询设备所属结果`, cameraState.body.code, cameraState.body.msg);
if (cameraState.body.code == 200) {
console.log(`所属萤石账号 ${s.key}`);
beloneSecretId = s.id
break
} else if (cameraState.body.code == 20018) {
} else if (cameraState.body.code == 20014) {
serialUnlegal = true
break
}
}
if (serialUnlegal) {
addUnlegalCount++
continue
}
if (!beloneSecretId) {
console.error('没有查询到所属账号')
console.log(vcmpExistRows);
addNoAuthCount++
continue
}
const cameraInQuery = `INSERT INTO "camera" (type, name, serial_no, cloud_control,longitude,latitude,create_time,create_user_id,yingshi_secret_id,gb_id,channel_no) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id;`
await vcmpClient.query(cameraInQuery, ['yingshi', c.name, c.serial_no, c.has_ptz, c.longitude, c.latitude, moment().format(), 1, beloneSecretId, vcmpExistRows[0].id, c.channel_no])
addSuccessCount++
} else {
console.error('当前序列号不存在,应在 vcmp 添加相应萤石账号');
addNoAuthCount++
}
}
console.log(`
${anxinYSRes.rows.length}
添加 ${addSuccessCount}
已有 ${addRepeatCount}
序列号不合法 ${addUnlegalCount}
无所属 ${addNoAuthCount}
`);
// await client.query('ROLLBACK')
await vcmpClient.query('COMMIT')
console.log('执行完毕~')
} catch (e) {
await vcmpClient.query('ROLLBACK')
console.log('执行错误~')
throw e
} finally {
vcmpClient.release();
}
}
fun()
} catch (error) {
console.error(error)
}

18
script/1.3.1/data/1_sync_camera_data/package.json

@ -1,18 +0,0 @@
{
"name": "appkey-generator",
"version": "1.0.0",
"description": "tool",
"main": "index.js",
"scripts": {
"test": "mocha",
"start": "set NODE_ENV=development&&node index"
},
"author": "liu",
"license": "ISC",
"dependencies": {
"moment": "^2.29.4",
"pg": "^7.18.2",
"superagent": "3.5.2",
"xlsx": "^0.17.1"
}
}

BIN
web/client/assets/fonts/YouSheBiaoTiHei-2.ttf

Binary file not shown.

BIN
web/client/assets/images/problem/setup.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

3
web/client/index.ejs

@ -6,6 +6,9 @@
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
<meta content="upgrade-insecure-requests" http-equiv="Content-Security-Policy">
<link rel="shortcut icon" href="/assets/images/favicon.ico">
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
<script>LA.init({ id: "Jo4eTlZVqgx3uwqm", ck: "Jo4eTlZVqgx3uwqm" })</script>
<script src="/assets/js/jessibuca/jessibuca.js"></script>
<script
src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_14797_10.b43556420cacd0119dedf94deff663bb.js"></script>

10
web/client/index.html

@ -5,9 +5,11 @@
<meta charset="UTF-8">
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
<!-- <meta content="upgrade-insecure-requests" http-equiv="Content-Security-Policy"> 会将http转化成https,导致jessibuca视频播放不成功 -->
<link rel="shortcut icon" href="/assets/images/favicon.ico">
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
<script>LA.init({id: "Jo4eTlZVqgx3uwqm",ck: "Jo4eTlZVqgx3uwqm"})</script>
<script src="/assets/js/jessibuca/jessibuca.js"></script>
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_14797_10.b43556420cacd0119dedf94deff663bb.js"></script>
</head>
@ -16,11 +18,11 @@
<div id='PomsApp' style="height: 100%;"></div>
<!-- Webpack -->
<script type="text/javascript" src="http://localhost:5001/client/build/app.js"></script>
<script type="text/javascript" src="http://localhost:5601/client/build/app.js"></script>
<!-- Vite -->
<!-- <script type="module">
import RefreshRuntime from "http://localhost:5002/@react-refresh"
import RefreshRuntime from "http://localhost:5602/@react-refresh"
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => { }
window.$RefreshSig$ = () => (type) => type

19
web/client/src/app.jsx

@ -5,21 +5,36 @@ import Layout from './layout';
import Auth from './sections/auth';
import Example from './sections/example';
import Example1 from './sections/example1';
import Problem from './sections/problem';
import NoMatch from './sections/noMatch';
import Console from './sections/console';
const App = props => {
const { projectName } = props
useEffect(() => {
document.title = projectName;
console.log(`
_ _
         
      |  _  _ l
      \` ミ_xノ
     /      |
    /    
      | | |
 |   | | |
 | (ヽ__ヽ_)__)
 二つ
`);
}, [])
return (
<Layout
title={projectName}
sections={[
Example,Example1,
Auth, NoMatch
Example,Example1,Problem,
Auth, NoMatch,Console
]}
/>
)

5
web/client/src/components/setup.jsx

@ -11,6 +11,9 @@ function Setup(props) {
tableType,
tableList
} = props;
console.log(tableType,
tableList);
const [check, setCheck] = useState([]);
const checkboxcss = { width: "25%", height: 16, margin: "0 0 20px 0" };
@ -98,7 +101,7 @@ function Setup(props) {
{item.title}
</div>
<div style={{ padding: "15px 12px", width: 530 }}>
{item.list.map((itm) => {
{item.list?.map((itm) => {
return (
<Checkbox
key={itm.value}

2
web/client/src/index.jsx

@ -6,4 +6,4 @@ import { render } from 'react-dom';
import App from './app';
import './index.less';
render((<App projectName="飞尚物联" />), document.getElementById('PomsApp'));
render((<App projectName="运维服务" />), document.getElementById('PomsApp'));

5
web/client/src/index.less

@ -66,4 +66,9 @@ body {
.semi-portal-inner {
position: fixed;
}
@font-face {
font-family: 'YouSheBiaoTiHei'; //这个可以任意取,但是应与后面相对应eg:yxingguang
src: url('/assets/fonts/YouSheBiaoTiHei-2.ttf');
}

2
web/client/src/layout/actions/global.js

@ -18,7 +18,7 @@ export function initLayout (title, copyright, sections, actions) {
export const RESIZE = 'RESIZE';
export function resize (clientHeight, clientWidth) {
const headerHeight = 60
const headerHeight = 50
const footerHeight = 0
return {
type: RESIZE,

13
web/client/src/layout/actions/webSocket.js

@ -2,18 +2,20 @@
import io from 'socket.io-client';
export const INIT_WEB_SOCKET = 'INIT_WEB_SOCKET'
export function initWebSocket ({ ioUrl, token }) {
export function initWebSocket ({ ioUrl, token, pomsUserId }) {
if (!ioUrl) {
ioUrl = localStorage.getItem('apiRoot')
ioUrl = JSON.parse(ioUrl).root
}
if (!token) {
const user = sessionStorage.getItem('vcmpUser')
let user = sessionStorage.getItem('pomsUser')
if (user) {
token = JSON.parse(user).token
user = JSON.parse(user)
token = user.token
pomsUserId = user.pomsUserInfo.id
}
}
if (!ioUrl || !token) {
if (!ioUrl || !token || !pomsUserId) {
return {
type: '',
}
@ -25,7 +27,8 @@ export function initWebSocket ({ ioUrl, token }) {
// 'http://10.8.30.7:4000'
, {
query: {
token: token
token: token,
pomsUserId: pomsUserId
},
});
dispatch({

162
web/client/src/layout/components/header/index.jsx

@ -2,94 +2,106 @@
import React from "react";
import { connect } from "react-redux";
import { Nav, Avatar, Dropdown } from "@douyinfe/semi-ui";
import "./index.less";
const Header = (props) => {
const { dispatch, history, user, actions, socket } = props;
const { dispatch, history, user, actions, socket, headerItems, tochange } = props;
return (
<>
return (
<>
<div id="top-slider">
<Nav
mode={"horizontal"}
onClick={({ itemKey }) => {
if (itemKey == "logout") {
dispatch(actions.auth.logout(user));
const iotAuth = document.getElementById('iotAuth').contentWindow;
iotAuth.postMessage({ action: 'logout' }, '*');
if (socket) {
socket.disconnect();
mode={"horizontal"}
onClick={({ itemKey }) => {
if (itemKey == "logout") {
dispatch(actions.auth.logout(user));
if (socket) {
socket.disconnect();
}
history.push(`/signin`);
}
}}
style={{
height: 48,
minWidth: 520,
background: '#1D2343',
backgroundSize: "100% 100%",
color: "white",
}}
header={{
logo: (
<img
src="/assets/images/background/logo.png"
style={{ display: "inline-block", width: 280, height: 40 }}
/>
),
text: (
<div style={{ fontFamily: "YouSheBiaoTiHei", color: '#FFFFFF' }}>运维管理系统</div>
),
}}
footer={
<>
{headerItems.map((item, index) => {
if (item.hasOwnProperty('items')) {
return (
<Nav.Sub
key={index + 'a'}
itemKey={item.itemKey}
text={item.text}
dropdownStyle={{ color: '#F2F3F5' }}
>
{item.hasOwnProperty('items') && item.items.map((ite, idx) => (
<Nav.Item key={idx + 'd'} itemKey={ite.itemKey} text={ite.text} onClick={() => { tochange(ite); }} />
))}
</Nav.Sub>
)
}
history.push(`/signin`);
}
}}
style={{
height: 60,
minWidth: 520,
background: '#1D2343',
backgroundSize: "100% 100%",
color: "white",
}}
header={{
logo: (
<img
src="/assets/images/background/logo.png"
style={{ display: "inline-block", width: 280, height: 52 }}
/>
),
text: "",
}}
footer={
<Nav.Sub
else {
return (
<Nav.Item key={index + 'a'} itemKey={item.itemKey} text={item.text} onClick={() => { tochange(item) }} />
)
}
})}
<Nav.Sub
itemKey={"user"}
text={
<div
style={{
marginLeft: 20,
display: "inline-block",
color: "white",
}}
>
<img
src="/assets/images/background/notice.png"
style={{
display: "inline-block",
width: 18,
height: 18,
position: "relative",
top: 6,
left: -10,
}}
/>
<Avatar size="small" color="light-blue" style={{marginRight:4}}>
<img src="/assets/images/avatar/6.png" />
</Avatar>
<div style={{
display: "inline-block", position: "relative",
top: 10,
left: 4,
marginRight: 4,
}}>
<div>你好</div>
<div>{user && user.displayName}</div>
</div>
</div>
<div
style={{
marginLeft: 20,
display: "inline-block",
color: "white",
}}
>
<Avatar size="small" color="light-blue" style={{ marginRight: 4 }}>
<img src="/assets/images/avatar/6.png" />
</Avatar>
<div style={{
display: "inline-block", position: "relative",
top: 10,
left: 4,
marginRight: 4,
}}>
</div>
</div>
}
>
>
<Nav.Item itemKey={"logout"} text={"退出"} />
</Nav.Sub>
}
</Nav.Sub>
</>
}
/>
</>
);
</div>
</>
);
};
function mapStateToProps (state) {
const { global, auth, webSocket } = state;
return {
actions: global.actions,
user: auth.user,
socket: webSocket.socket,
};
const { global, auth, webSocket } = state;
return {
actions: global.actions,
user: auth.user,
socket: webSocket.socket,
};
}
export default connect(mapStateToProps)(Header);

14
web/client/src/layout/components/header/index.less

@ -0,0 +1,14 @@
#top-slider{
.semi-navigation-item-text{
color: #F2F3F5;
}
.semi-navigation-item-icon{
color: #F2F3F5;
}
.semi-navigation-item-selected{
background: none;
}
.semi-navigation-item-text{
overflow: inherit;
}
}

202
web/client/src/layout/components/sider/index.jsx

@ -4,145 +4,83 @@ import { connect } from 'react-redux';
import { Nav } from '@douyinfe/semi-ui';
import { push } from 'react-router-redux';
import { useLocation } from 'react-router';
import "./index.less";
let scrollbar = null
const homePath = '/equipmentWarehouse/nvr'
const Sider = props => {
const { collapsed, clientHeight, dispatch, pathname } = props
const [items, setItems] = useState([])
const [selectedKeys, setSelectedKeys] = useState([])
const [openKeys, setOpenKeys] = useState([])
useEffect(() => {
const { sections, dispatch, user } = props;
let nextItems = []
let pathname_ = pathname == '/' ? homePath : pathname
const initKeys = (items, lastKeys) => {
for (let it of items) {
if (it.to && it.to == pathname_) {
lastKeys.selectedKeys.push(it.itemKey)
return lastKeys
} else if (it.items && it.items.length) {
const preLastKeys = JSON.parse(JSON.stringify(lastKeys))
preLastKeys.openKeys.push(it.itemKey)
const nextKeys = initKeys(it.items, JSON.parse(JSON.stringify(preLastKeys)))
if (nextKeys.selectedKeys.length > preLastKeys.selectedKeys.length || nextKeys.openKeys.length > preLastKeys.openKeys.length) {
return nextKeys
}
}
}
return lastKeys
return {
selectedKeys: [],
openKeys: []
}
}
for (let c of sections) {
if (typeof c.getNavItem == 'function') {
let item = c.getNavItem(user, dispatch);
if (item) {
nextItems.push.apply(nextItems, item)
}
}
}
setItems(nextItems)
const { selectedKeys, openKeys } = initKeys(
nextItems,
{
selectedKeys: [],
openKeys: []
}
)
if (selectedKeys.length || openKeys.length) {
setSelectedKeys(selectedKeys)
setOpenKeys(openKeys)
} else {
const lastSelectedKeys = localStorage.getItem('vcmp_selected_sider')
if (lastSelectedKeys) {
setSelectedKeys(JSON.parse(lastSelectedKeys))
}
const lastOpenKeys = localStorage.getItem('vcmp_open_sider')
if (lastOpenKeys) {
const homePath = '/example/e1/c1'
const Sider = (props) => {
const { collapsed, clientHeight, dispatch, pathname, leftItems, leftChange } = props
const [items, setItems] = useState([])
const [selectedKeys, setSelectedKeys] = useState([])
const [openKeys, setOpenKeys] = useState([])
useEffect(() => {
const { sections, dispatch, user } = props;
let nextItems = leftItems
setItems(nextItems)
scrollbar = new PerfectScrollbar('#page-slider', { suppressScrollX: true });
if (pathname == '/') {
dispatch(push(homePath))
}
}, [leftItems])
let routeSelectedKey = [useLocation().pathname.split('/')[1]]//
let routeSelectedKeys = [useLocation().pathname.split('/')[2]]//
let routeSelectedKeyss = [useLocation().pathname.split('/')[3]]//
useEffect(() => {
if (routeSelectedKeyss[0]) {
setSelectedKeys(routeSelectedKeyss)
}
else if (routeSelectedKeys[0]) {
setSelectedKeys(routeSelectedKeys)
}
else {
setSelectedKeys(routeSelectedKey)
}
const lastOpenKeys = localStorage.getItem('poms_open_sider')
if (lastOpenKeys) {
setOpenKeys(JSON.parse(lastOpenKeys))
}
}
scrollbar = new PerfectScrollbar('#page-slider', { suppressScrollX: true });
if (pathname == '/') {
dispatch(push(homePath))
}
}, [])
let routeSelectedKey = [useLocation().pathname.split('/')[1]]//
let routeSelectedKeys = [useLocation().pathname.split('/')[2]]//
useEffect(()=>{
let pathname_ = pathname == '/' ? homePath : pathname
const initKeys = (items, lastKeys) => {
for (let it of items) {
if (it.to && it.to == pathname_) {
lastKeys.selectedKeys.push(it.itemKey)
return lastKeys
} else if (it.items && it.items.length) {
const preLastKeys = JSON.parse(JSON.stringify(lastKeys))
preLastKeys.openKeys.push(it.itemKey)
const nextKeys = initKeys(it.items, JSON.parse(JSON.stringify(preLastKeys)))
if (nextKeys.selectedKeys.length > preLastKeys.selectedKeys.length || nextKeys.openKeys.length > preLastKeys.openKeys.length) {
return nextKeys
}
}
}
return lastKeys
}
if(routeSelectedKeys[0]){
setSelectedKeys(routeSelectedKeys)
}
else{
setSelectedKeys(routeSelectedKey)
}
const lastOpenKeys = localStorage.getItem('vcmp_open_sider')
if (lastOpenKeys) {
setOpenKeys(JSON.parse(lastOpenKeys))
}
},[window.localStorage.vcmp_open_sider,window.localStorage.vcmp_selected_sider])
}
}, [window.localStorage.poms_open_sider, window.localStorage.poms_selected_sider, leftChange])
useEffect(() => {
if (scrollbar) {
scrollbar.update();
}
})
return (
<div id={'page-slider'} style={{ height: clientHeight, position: 'relative' }}>
<Nav
style={{}}
// defaultOpenKeys={openKeys}
// defaultSelectedKeys ={selectedKeys}
selectedKeys={selectedKeys}
openKeys={openKeys}
onSelect={({ selectedItems, selectedKeys, }) => {
const selectItem = selectedItems[0]
if (selectItem.to) {
dispatch(push(selectItem.to))
}
setSelectedKeys(selectedKeys)
localStorage.setItem('vcmp_selected_sider', JSON.stringify(selectedKeys))
}}
onOpenChange={({ openKeys }) => {
setOpenKeys(openKeys)
localStorage.setItem('vcmp_open_sider', JSON.stringify(openKeys))
}}
items={items}
/>
</div>
)
useEffect(() => {
if (scrollbar) {
scrollbar.update();
}
})
return (
<div id={'page-slider'} style={{ height: clientHeight, position: 'relative', background: '#101531', width: 180 }}>
<Nav
style={{ background: '#101531', width: 180, padding: 0 }}
selectedKeys={selectedKeys}
// bodyStyle={{}}
// mode = "vertical"
// footer={{
// collapseButton: true,
// }}
openKeys={openKeys}
onSelect={({ selectedItems, selectedKeys, }) => {
const selectItem = selectedItems[0]
if (selectItem.to) {
dispatch(push(selectItem.to))
}
let myfatherKey = selectItem.to
localStorage.setItem('poms_selected_sider', JSON.stringify([myfatherKey.split('/')[1]]))
setSelectedKeys(selectedKeys)
}}
onOpenChange={({ openKeys }) => {
setOpenKeys(openKeys)
localStorage.setItem('poms_open_sider', JSON.stringify(openKeys))
}}
items={items}
/>
</div>
)
}
function mapStateToProps (state) {
const { global } = state;
return {
clientHeight: global.clientHeight,
};
const { global } = state;
return {
clientHeight: global.clientHeight,
};
}
export default connect(mapStateToProps)(Sider);

15
web/client/src/layout/components/sider/index.less

@ -0,0 +1,15 @@
#page-slider{
.semi-navigation-sub .semi-navigation-item-selected{
background: rgba(0, 90, 189, 0.5);
color: #F2F3F5;
}
.semi-navigation-item-text{
color: #F2F3F5;
}
.semi-navigation-item-icon{
color:#F2F3F5
}
.semi-navigation-sub-title-selected{
background: #1D2343;
}
}

432
web/client/src/layout/containers/layout/index.jsx

@ -11,7 +11,7 @@ import * as NProgress from 'nprogress';
import PerfectScrollbar from 'perfect-scrollbar';
NProgress.configure({
template: `
template: `
<div class="bar" style="height:2px" role="bar">
<div class="peg"></div>
</div>
@ -24,200 +24,274 @@ NProgress.configure({
let scrollbar
const LayoutContainer = props => {
const {
dispatch, msg, user, copyright, children, sections, clientWidth, clientHeight,
location, match, routes, history, authCrossLoading, socket,
microAppWaiting
} = props
const [collapsed, setCollapsed] = useState(false)
const {
dispatch, msg, user, copyright, children, sections, clientWidth, clientHeight,
location, match, routes, history, socket,
} = props
const [collapsed, setCollapsed] = useState(false)
NProgress.start();
NProgress.start();
const resize_ = () => {
dispatch(resize(
document.getElementById('PomsApp').clientHeight,
document.getElementById('PomsApp').clientWidth - (collapsed ? 120 : 240)
));
}
const resize_ = () => {
dispatch(resize(
document.getElementById('PomsApp').clientHeight,
document.getElementById('PomsApp').clientWidth - (collapsed ? 120 : 240)
));
}
function deepCopy (data) {//
//string,number,bool,null,undefined,symbol
//object,array,date
if (data && typeof data === "object") {
//
if (typeof data === "function") {
let tempFunc = data.bind(null);
tempFunc.prototype = deepCopy(data.prototype);
return tempFunc;
}
switch (Object.prototype.toString.call(data)) {
case "[object String]":
return data.toString();
case "[object Number]":
return Number(data.toString());
case "[object Boolean]":
return new Boolean(data.toString());
case "[object Date]":
return new Date(data.getTime());
case "[object Array]":
var arr = [];
for (let i = 0; i < data.length; i++) {
arr[i] = deepCopy(data[i]);
}
return arr;
useEffect(() => {
let topItems=[]//
let siderItems=[]//
for (let c of sections) {
if (typeof c.getNavItem == 'function') {
let item = c.getNavItem(user, dispatch);
if (item) {
if(item.length>0){
let itm = JSON.parse(JSON.stringify(item[0]));
let itms = JSON.parse(JSON.stringify(item[0]));
if(itm.hasOwnProperty('items')){
siderItems.push.apply(siderItems,itms.items)
for (let i = 0; i < itm.items.length; i++) {
delete itm.items[i].items
}
topItems.push(itm)
//js
case "[object Object]":
var obj = {};
for (let key in data) {
//hasOwnProperty obj.hasOwnProperty(prop)
obj[key] = deepCopy(data[key]);
}
return obj;
}
} else {
//string,number,bool,null,undefined,symbol
return data;
}
}
const [allItems, setAllItems] = useState([])
const [headerItems, setHeaderItems] = useState([])
const [leftItems, setLeftItems] = useState([])
const [leftChange, setLeftChange] = useState(true)
const [leftShow, setLeftShow] = useState(false)
useEffect(() => {
let topItems = []//
const lastSelectedKeys = localStorage.getItem('poms_selected_sider')
let nextItems = []//
for (let c of sections) {
if (typeof c.getNavItem == 'function') {
let item = c.getNavItem(user, dispatch);
if (item) {
nextItems.push.apply(nextItems, item)
if (item.length > 0) {
for (let j = 0; j < item.length; j++) {
let itm = deepCopy(item[j]);
if (itm.hasOwnProperty('items')) {
for (let i = 0; i < itm.items.length; i++) {
itm.items[i].fatherKey = itm.itemKey
delete itm.items[i].items
}
topItems.push(itm)
}
else {
topItems.push.apply(topItems, item)
}
}
}
}
else{
topItems.push.apply(topItems, item)
}
}
setAllItems(nextItems)
setHeaderItems(topItems)
if (lastSelectedKeys) {//
for (let i = 0; i < nextItems.length; i++) {
if (JSON.parse(lastSelectedKeys)[0] == nextItems[i].itemKey) {
let openArr = []
for (let j = 0; j < nextItems[i].items.length; j++) {
openArr.push(nextItems[i].items[j].itemKey)
}
localStorage.setItem('poms_open_sider', JSON.stringify(openArr))
setLeftItems(nextItems[i].items)
}
}
}
}
setLeftShow(true)
}
else {
setLeftShow(false)
}
}
console.log('siderItems',siderItems);
console.log('topItems',topItems);
window.addEventListener('resize', resize_);
return () => {
window.removeEventListener('resize', resize_);
}
}, [])
window.addEventListener('resize', resize_);
return () => {
window.removeEventListener('resize', resize_);
}
}, [])
useEffect(() => {
NProgress.done();
if ((!user || !user.authorized) && !authCrossLoading) {
history.push('/signin');
}
if (msg) {
if (msg.done) {
Notification.success({
// title: msg.done,
content: msg.done,
duration: 2,
})
}
if (msg.error) {
Notification.error({
// title: msg.error,
content: msg.error,
duration: 2,
})
}
}
const dom = document.getElementById('page-content');
if (dom) {
if (!scrollbar) {
scrollbar = new PerfectScrollbar('#page-content', { suppressScrollX: true });
scrollbar.update();
} else {
scrollbar.update();
dom.scrollTop = 0;
}
}
})
useEffect(() => {
NProgress.done();
if ((!user || !user.authorized)) {
history.push('/signin');
}
if (msg) {
if (msg.done) {
Notification.success({
// title: msg.done,
content: msg.done,
duration: 2,
})
}
if (msg.error) {
Notification.error({
// title: msg.error,
content: msg.error,
duration: 2,
})
}
}
const dom = document.getElementById('page-content');
if (dom) {
if (!scrollbar) {
scrollbar = new PerfectScrollbar('#page-content', { suppressScrollX: true });
scrollbar.update();
} else {
scrollbar.update();
dom.scrollTop = 0;
}
}
})
// websocket 使
useEffect(() => {
console.log(socket)
if (socket) {
socket.on('CAMERA_ONLINE', function (msg) {
console.info(msg);
if (msg.online == 'ON') {
Notification.success({
title: 'Hi',
content: (<div><div>{msg.name}</div><div style={{ marginTop: 5 }}>已上线</div></div>),
duration: 2,
})
}
if (msg.online == 'OFF') {
Notification.error({
title: 'Hi',
content: (<div><div>{msg.name}</div><div style={{ marginTop: 5 }}>发生离线</div></div>),
duration: 2,
})
// websocket 使
useEffect(() => {
console.log(socket)
if (socket) {
socket.on('CAMERA_ONLINE', function (msg) {
console.info(msg);
if (msg.online == 'ON') {
Notification.success({
title: 'Hi',
content: (<div><div>{msg.name}</div><div style={{ marginTop: 5 }}>已上线</div></div>),
duration: 2,
})
}
if (msg.online == 'OFF') {
Notification.error({
title: 'Hi',
content: (<div><div>{msg.name}</div><div style={{ marginTop: 5 }}>发生离线</div></div>),
duration: 2,
})
}
});
return () => {
socket.off("CAMERA_ONLINE");
}
});
return () => {
socket.off("CAMERA_ONLINE");
}
}
}, [socket])
}
}, [socket])
return (
<Layout id="layout" style={{ height: '100%' }}>
{
authCrossLoading || microAppWaiting ?
<div style={{
// position: 'absolute',
height: '100%', width: '100%',
display: 'flex', alignItems: 'center', placeContent: 'center',
backgroundColor: "#000"
}}>
<video
autoPlay loop muted
style={{}}
src={`${__webpack_public_path__}assets/video/cross_loading.mp4`}
type="video/mp4"
/>
</div>
:
<>
<Layout.Header>
<Header
user={user}
pathname={location.pathname}
toggleCollapsed={() => {
setCollapsed(!collapsed);
}}
collapsed={collapsed}
history={history}
/>
</Layout.Header>
<Layout style={{ height: 'calc(100% - 60px)' }}>
<Layout.Sider>
<Sider
sections={sections}
dispatch={dispatch}
user={user}
pathname={location.pathname}
collapsed={collapsed}
return (
<Layout id="layout" style={{ height: '100%' }}>
{
<>
<Layout.Header>
<Header
headerItems={headerItems}
user={user}
pathname={location.pathname}
toggleCollapsed={() => {
setCollapsed(!collapsed);
}}
collapsed={collapsed}
history={history}
tochange={(val) => {
setLeftChange(!leftChange)
if (val.fatherKey) {
for (let i = 0; i < allItems.length; i++) {
if (val.fatherKey == allItems[i].itemKey) {
let openArr = []
for (let j = 0; j < allItems[i].items.length; j++) {
openArr.push(allItems[i].items[j].itemKey)
}
localStorage.setItem('poms_selected_sider', JSON.stringify([val.fatherKey]))
localStorage.setItem('poms_open_sider', JSON.stringify(openArr))
setLeftItems(allItems[i].items)
}
}
setLeftShow(true)
}
else {
localStorage.setItem('poms_open_sider', JSON.stringify([]))
localStorage.removeItem('poms_selected_sider')
setLeftShow(false)
}
history.push(val.to);
}}
/>
</Layout.Sider>
<Layout.Content>
<div style={{
margin: '12px 12px 0px',
background: "#F6FAFF",
}}>
<div id="page-content" style={{
height: clientHeight - 12,
minWidth: 520,
position: 'relative',
}}>
<div style={{
minHeight: clientHeight - 32 - 12,
position: 'relative',
padding: '12px 8px',
}}>
{children}
</div>
<Layout.Footer>
<Footer />
</Layout.Footer>
</div>
</div>
</Layout.Content>
</Layout>
</>
}
</Layout >
)
</Layout.Header>
<Layout style={{ height: 'calc(100% - 50px)' }}>
{leftShow ? (<Layout.Sider>
<Sider
sections={sections}
leftItems={leftItems}
dispatch={dispatch}
user={user}
leftChange={leftChange}
pathname={location.pathname}
collapsed={collapsed}
/>
</Layout.Sider>) : ('')}
<Layout.Content>
<div style={{
margin: '12px 12px 0px',
background: "#F6FAFF",
}}>
<div id="page-content" style={{
height: clientHeight - 12,
minWidth: 520,
position: 'relative',
}}>
<div style={{
minHeight: clientHeight - 32 - 12,
position: 'relative',
padding: '12px 8px',
}}>
{children}
</div>
<Layout.Footer>
<Footer />
</Layout.Footer>
</div>
</div>
</Layout.Content>
</Layout>
</>
}
</Layout >
)
}
function mapStateToProps (state) {
const { global, auth, ajaxResponse, webSocket } = state;
return {
title: global.title,
copyright: global.copyright,
sections: global.sections,
actions: global.actions,
clientWidth: global.clientWidth,
clientHeight: global.clientHeight,
msg: ajaxResponse.msg,
user: auth.user,
socket: webSocket.socket
};
const { global, auth, ajaxResponse, webSocket } = state;
return {
title: global.title,
copyright: global.copyright,
sections: global.sections,
actions: global.actions,
clientWidth: global.clientWidth,
clientHeight: global.clientHeight,
msg: ajaxResponse.msg,
user: auth.user,
socket: webSocket.socket
};
}
export default connect(mapStateToProps)(LayoutContainer);

53
web/client/src/layout/index.jsx

@ -25,9 +25,6 @@ const Root = props => {
const [outerRoutes, setOuterRoutes] = useState([])
const [combineRoutes, setCombineRoutes] = useState([])
const [innnerRoutes, setInnerRoutes] = useState([])
const [authCrossLoading, setAuthCrossLoading] = useState(true)
const [microAppWaiting, setMicroAppWaiting] = useState(true)
const [resourceRoot, setResourceRoot] = useState({})
const flatRoutes = (routes) => {
const combineRoutes = [];
@ -130,7 +127,6 @@ const Root = props => {
store.dispatch(resize(document.getElementById('PomsApp').clientHeight, document.getElementById('PomsApp').clientWidth));
store.dispatch(actions.auth.initAuth());
const resourceRoot = await store.dispatch(initApiRoot())
setResourceRoot(resourceRoot.payload)
store.dispatch(initWebSocket({}))
const combineRoutes = flatRoutes(innerRoutes);
@ -153,46 +149,6 @@ const Root = props => {
component={route.component}
/>
)))
// IOT system cross
window.addEventListener('message', async function (e) { // message
const { data, origin } = e
if (origin == 'https://open.ys7.com') return
if (data && data.action) {
if (data.action == 'initUser') {
await store.dispatch(actions.auth.initAuth(data.user))
} else if (data.action == 'logout') {
await store.dispatch(actions.auth.logout())
}
}
setAuthCrossLoading(false)
});
// setAuthCrossLoading(false)
// MicroApp
const microAppListen = async (data) => {
if (data.action == 'initMicro') {
await store.dispatch(actions.auth.initAuth({
authorized: true,
// token: data.data.token,
// mirrorId: data.data.mirrorId
...(data.data || {})
}))
// await store.dispatch(push('/noMatch'));
setMicroAppWaiting(false)
}
}
if (window.__MICRO_APP_ENVIRONMENT__) {
console.info('MicroApp')
setAuthCrossLoading(false)
window.microApp.addDataListener(microAppListen, true)
} else {
console.info('NOT MicroApp')
setMicroAppWaiting(false)
}
// setMicroAppWaiting(false)
}, [])
return (
@ -209,8 +165,6 @@ const Root = props => {
<Layout
history={history}
routes={innnerRoutes}
authCrossLoading={authCrossLoading}
microAppWaiting={microAppWaiting}
>
{combineRoutes}
</Layout>
@ -226,13 +180,6 @@ const Root = props => {
</ConfigProvider>
: ''
}
{
resourceRoot.iotAuthWeb ?
<iframe id="iotAuth" src={`${resourceRoot.iotAuthWeb}/cross`} style={{ position: 'absolute', top: 0, height: 1, visibility: 'hidden' }} frameBorder={0} >
<p>你的浏览器不支持 iframe</p>
</iframe>
: ''
}
</>
)

34
web/client/src/sections/auth/actions/auth.js

@ -1,13 +1,14 @@
'use strict';
import { ApiTable, AxyRequest } from '$utils'
import { ApiTable, AxyRequest, EmisRequest } from '$utils'
import { Request } from '@peace/utils';
export const INIT_AUTH = 'INIT_AUTH';
export function initAuth (userData) {
const sessionUser = JSON.parse(sessionStorage.getItem('vcmpUser'))
const sessionUser = JSON.parse(sessionStorage.getItem('pomsUser'))
const user = userData || sessionUser || {};
if (user.authorized && !sessionUser) {
sessionStorage.setItem('vcmpUser', JSON.stringify(user))
sessionStorage.setItem('pomsUser', JSON.stringify(user))
}
return {
type: INIT_AUTH,
@ -33,18 +34,18 @@ export function login (username, password) {
}
// return dispatch({
// type: LOGIN_SUCCESS,
// payload: {
// user: {
// authorized: true,
// namePresent: 'TEST'
// }
// },
// type: LOGIN_SUCCESS,
// payload: {
// user: {
// authorized: true,
// namePresent: 'TEST'
// }
// },
// });
return AxyRequest.post(ApiTable.login, { username, password, domain: 'anxinyun' })
return Request.post(ApiTable.login, { username, password, code: 'POMS' })
.then(user => {
sessionStorage.setItem('vcmpUser', JSON.stringify(user));
sessionStorage.setItem('pomsUser', JSON.stringify(user));
return dispatch({
type: LOGIN_SUCCESS,
payload: { user: user },
@ -63,12 +64,13 @@ export function login (username, password) {
export const LOGOUT = 'LOGOUT';
export function logout () {
const user = JSON.parse(sessionStorage.getItem('vcmpUser'))
const user = JSON.parse(sessionStorage.getItem('pomsUser'))
user && user.token ?
AxyRequest.post(ApiTable.logout, {
token: user.token
Request.put(ApiTable.logout, {
token: user.token,
code: 'POMS'
}) : null;
sessionStorage.removeItem('vcmpUser');
sessionStorage.removeItem('pomsUser');
return {
type: LOGOUT
};

12
web/client/src/sections/auth/containers/login.jsx

@ -20,11 +20,9 @@ const Login = props => {
useEffect(() => {
if (user && user.authorized) {
const iotAuth = document.getElementById('iotAuth').contentWindow;
iotAuth.postMessage({ action: 'login', user: user }, '*');
dispatch(push('/equipmentWarehouse/nvr'));
localStorage.setItem('vcmp_selected_sider', JSON.stringify(['nvr']))
localStorage.setItem('vcmp_open_sider', JSON.stringify(['equipmentWarehouse']))
dispatch(push('/console'));
localStorage.setItem('poms_open_sider', JSON.stringify([]))
localStorage.removeItem('poms_selected_sider')
}
}, [user])
@ -72,7 +70,7 @@ const Login = props => {
onSubmit={values => {
dispatch(login(values.username, values.password)).then(res => {
const data = res.payload.user
dispatch(actions.layout.initWebSocket({ ioUrl: apiRoot, token: data.token }))
dispatch(actions.layout.initWebSocket({ ioUrl: apiRoot, token: data.token ,pomsUserId:data.pomsUserInfo.id}))
})
}}
getFormApi={formApi => form.current = formApi}
@ -96,7 +94,7 @@ const Login = props => {
prefix={<IconLock style={{ color: '#1859C1', marginRight: 14, marginLeft: 8 }} />}
style={{ background: 'rgba(24, 89, 193, 0.08)', height: 40 }}
/>
<Button htmlType='submit' block theme="solid" style={{ marginTop: 17, height: 40, backgroundColor: '#1859C1' }}>立即登录</Button>
<Button htmlType='submit' block theme="solid" loading={isRequesting} style={{ marginTop: 17, height: 40, backgroundColor: '#1859C1' }}>立即登录</Button>
</Form>
</div>
</div>

14
web/client/src/sections/console/actions/console.js

@ -0,0 +1,14 @@
'use strict';
import { ApiTable ,basicAction} from '$utils'
// export function getMembers (orgId) {
// return dispatch => basicAction({
// type: 'get',
// dispatch: dispatch,
// actionType: 'GET_MEMBERS',
// url: `${ApiTable.getEnterprisesMembers.replace('{enterpriseId}', orgId)}`,
// msg: { error: '获取用户列表失败' },
// reducer: { name: 'members' }
// });
// }

7
web/client/src/sections/console/actions/index.js

@ -0,0 +1,7 @@
'use strict';
import * as console from './console'
export default {
...console
}

49
web/client/src/sections/console/containers/console.jsx

@ -0,0 +1,49 @@
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { Spin, Card } from '@douyinfe/semi-ui';
import '../style.less'
const { Meta } = Card;
const Console = (props) => {
const { dispatch, actions, user, loading, socket } = props
useEffect(() => {
// ACTION
// dispatch(actions.example.getMembers(user.orgId))
}, [])
// websocket 使
// useEffect(() => {
// console.log(socket)
// if (socket) {
// socket.on('TEST', function (msg) {
// console.info(msg);
// });
// return () => {
// socket.off("TEST");
// }
// }
// }, [socket])
return (
<>
<div>
texttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttext
</div>
</>
)
}
function mapStateToProps (state) {
const { auth, global, members, webSocket } = state;
return {
// loading: members.isRequesting,
// user: auth.user,
// actions: global.actions,
// members: members.data,
// socket: webSocket.socket
};
}
export default connect(mapStateToProps)(Console);

5
web/client/src/sections/console/containers/index.js

@ -0,0 +1,5 @@
'use strict';
import Console from './console';
export { Console };

15
web/client/src/sections/console/index.js

@ -0,0 +1,15 @@
'use strict';
import reducers from './reducers';
import routes from './routes';
import actions from './actions';
import { getNavItem } from './nav-item';
export default {
key: 'console',
name: '控制台',
reducers: reducers,
routes: routes,
actions: actions,
getNavItem: getNavItem
};

15
web/client/src/sections/console/nav-item.jsx

@ -0,0 +1,15 @@
import React from 'react';
import { IconCode } from '@douyinfe/semi-icons';
export function getNavItem (user, dispatch) {
return (
[
{
itemKey: 'console',
text: '控制台',
to: '/console',
// icon: <IconCode />,
},
]
);
}

5
web/client/src/sections/console/reducers/index.js

@ -0,0 +1,5 @@
'use strict';
export default {
}

13
web/client/src/sections/console/routes.js

@ -0,0 +1,13 @@
'use strict';
import { Console, } from './containers';
export default [{
type: 'inner',
route: {
path: '/console',
key: 'console',
breadcrumb: '控制台',
component: Console,
// 不设置 component 则面包屑禁止跳转
}
}];

7
web/client/src/sections/console/style.less

@ -0,0 +1,7 @@
#example {
box-shadow: 3px 3px 2px black;
}
#example:hover {
color: yellowgreen;
}

17
web/client/src/sections/example/nav-item.jsx

@ -1,18 +1,20 @@
import React from 'react';
import { IconCode } from '@douyinfe/semi-icons';
import { Badge } from "@douyinfe/semi-ui";
export function getNavItem (user, dispatch) {
return (
[
{
itemKey: 'example',
text: '举个栗子',
text: <Badge count={5} type='danger' >举个栗子</Badge>,
icon: <IconCode />,
items: [
{
itemKey: 'e1',
text: '举个棒子1',
text:'举个棒子1',
icon: <IconCode />,
to: '/example/e1/c1',
items: [{
itemKey: 'c1', to: '/example/e1/c1', text: '举个锤子1'
}]
@ -20,6 +22,7 @@ export function getNavItem (user, dispatch) {
itemKey: 'e2',
text: '举个棒子2',
icon: <IconCode />,
to: '/example/e2/c2',
items: [{
itemKey: 'c2', to: '/example/e2/c2', text: '举个锤子2'
}]
@ -27,15 +30,5 @@ export function getNavItem (user, dispatch) {
]
},
]
// ,[
// {
// itemKey: 'equipmentWarehouse', text: '', icon:<iconpark-icon style={{width:20,height:20}} name="she-1"></iconpark-icon>,
// items: [
// { itemKey: 'nvr', to: '/equipmentWarehouse/nvr', text: 'NVR' },
// { itemKey: 'camera', to: '/equipmentWarehouse/camera', text: '' },
// { itemKey: 'recycle', to: '/equipmentWarehouse/recycle', text: '' },
// ]
// },
// ]
);
}

2
web/client/src/sections/example/routes.js

@ -11,7 +11,7 @@ export default [{
childRoutes: [{
path: '/e1',
key: 'e1',
// component: Example,
component: Example,
breadcrumb: '棒子1',
childRoutes:[{
path: '/c1',

12
web/client/src/sections/example1/nav-item.jsx

@ -13,6 +13,7 @@ export function getNavItem (user, dispatch) {
itemKey: 'e3',
text: '举个棒子3',
icon: <IconCode />,
to: '/example/e3/c3',
items: [{
itemKey: 'c3', to: '/example/e3/c3', text: '举个锤子3'
}]
@ -20,6 +21,7 @@ export function getNavItem (user, dispatch) {
itemKey: 'e4',
text: '举个棒子4',
icon: <IconCode />,
to: '/example/e4/c4',
items: [{
itemKey: 'c4', to: '/example/e4/c4', text: '举个锤子4'
}]
@ -27,15 +29,5 @@ export function getNavItem (user, dispatch) {
]
},
]
// ,[
// {
// itemKey: 'equipmentWarehouse', text: '', icon:<iconpark-icon style={{width:20,height:20}} name="she-1"></iconpark-icon>,
// items: [
// { itemKey: 'nvr', to: '/equipmentWarehouse/nvr', text: 'NVR' },
// { itemKey: 'camera', to: '/equipmentWarehouse/camera', text: '' },
// { itemKey: 'recycle', to: '/equipmentWarehouse/recycle', text: '' },
// ]
// },
// ]
);
}

3
web/client/src/sections/noMatch/containers/noMatch.jsx

@ -12,7 +12,7 @@ const NoMatch = (props) => {
<p style={{ fontSize: 32, lineHeight: 2 }}>PAGE NOT FOUND</p>
<p>很遗憾您暂时无法访问该页面</p>
<p>请检查您访问的链接地址是否正确</p>
<p style={{ marginTop: 80 }}>Copyright © {moment().year()} 飞尚尚视</p>
<p style={{ marginTop: 80 }}>Copyright © {moment().year()} 飞尚运维服务</p>
</div>
</div>
)
@ -21,6 +21,7 @@ const NoMatch = (props) => {
function mapStateToProps (state) {
const { } = state;
return {
};
}

14
web/client/src/sections/problem/actions/dataAlarm.jsx

@ -0,0 +1,14 @@
'use strict';
import { ApiTable ,basicAction} from '$utils'
// export function getMembers (orgId) {
// return dispatch => basicAction({
// type: 'get',
// dispatch: dispatch,
// actionType: 'GET_MEMBERS',
// url: `${ApiTable.getEnterprisesMembers.replace('{enterpriseId}', orgId)}`,
// msg: { error: '' },
// reducer: { name: 'members' }
// });
// }

7
web/client/src/sections/problem/actions/index.js

@ -0,0 +1,7 @@
'use strict';
import * as dataAlarm from './dataAlarm'
export default {
...dataAlarm
}

42
web/client/src/sections/problem/components/statistics.jsx

@ -0,0 +1,42 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import { DatePicker } from "@douyinfe/semi-ui";
const Statistics = ({ dispatch, actions, close, modalName, visible, appData }) => {
return (
<div style={{ width: '100%', height: 180, border: '1px solid #00000012', backgroundColor: 'white', marginBottom: 20 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>数据异常统计</div>
<DatePicker
type="dateTimeRange"
style={{ width: 405 }}
// defaultPickerValue={[new Date('2022-08-08 00:00'), new Date('2022-08-09 12:00')]}
value={[new Date('2022-08-08 00:00'), new Date('2022-08-09 00:00')]}
prefix='统计时段:'
onChange={console.log}
/>
</div>
<div style={{ width: '100%', display: 'flex', justifyContent: 'space-around', marginTop: 8 }}>
<div style={{ width: 'calc(25% - 20px)', height: 126, backgroundColor: '#F2F3F5', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>暂未开放敬请期待</div>
<div style={{ width: 'calc(25% - 20px)', height: 126, backgroundColor: '#F2F3F5', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>暂未开放敬请期待</div>
<div style={{ width: 'calc(25% - 20px)', height: 126, backgroundColor: '#F2F3F5', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>暂未开放敬请期待</div>
<div style={{ width: 'calc(25% - 20px)', height: 126, backgroundColor: '#F2F3F5', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>暂未开放敬请期待</div>
</div>
</div>
)
}
function mapStateToProps (state) {
const { auth, global, members } = state;
return {
// user: auth.user,
// actions: global.actions,
// global: global,
// members: members.data,
};
}
export default connect(mapStateToProps)(Statistics);

167
web/client/src/sections/problem/components/tableData.jsx

@ -0,0 +1,167 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import { Button, Form, Modal, Skeleton, Pagination, Table } from "@douyinfe/semi-ui";
import { SkeletonScreen, } from "$components";
const TableData = ({ dispatch, actions, route, collectData, setSetup }) => {
const api = useRef();
useEffect(() => {
}, [route])
return (
<>
<div style={{ backgroundColor: '#FFFFFF' }}>
<div style={{}}>
<div style={{ display: "flex", marginBottom: 16 }}>
<Form
onSubmit={(values) => console.log(values)}
// onValueChange={values=>console.log(values)}
getFormApi={(formApi) => (api.current = formApi)}
layout="horizontal"
style={{ position: "relative", width: "100%", flex: 1 }}
>
{(() => {
let frame = []
collectData[route]?.map((v, index) => {
if (index == 0) {
frame.push(<Form.Input
label={v.name + ':'}
field={v.field}
key={v.field}
maxLength="16"
placeholder="搜索项目名称、结构物名称、或告警源名称"
labelPosition="left"
style={{ width: 292, marginRight: 10, }}
/>)
} else {
frame.push(<Form.Select
label={v.name + ':'}
labelPosition="left"
field={v.field}
key={v.field}
style={{ width: 116, marginRight: 10, color: "#F9F9F9", }}
placeholder="全部"
showClear
>
{v.data?.map((item) => {
return (
<Form.Select.Option key={item.value} value={item.value}>
{item.name}
</Form.Select.Option>
);
})}
</Form.Select>)
}
})
frame.push(<Form.DatePicker
type="dateTimeRange"
labelPosition="left"
style={{ width: 352 }}
label={collectData.common.name + ':'}
field={collectData.common.field}
key={collectData.common.field}
// defaultPickerValue={[new Date('2022-08-08 00:00'), new Date('2022-08-09 12:00')]}
values={[new Date('2022-08-08 00:00'), new Date()]}
onChange={(v) => console.log(v)}
/>)
return frame
})()}
</Form>
<div
style={{
width: 100,
display: "flex",
justifyContent: "space-between",
alignItems: "flex-end",
marginRight: 10
}}
>
<img src="/assets/images/problem/setup.png"
style={{ width: 24, height: 24 }}
onClick={() => setSetup(true)} />
<Button
theme="solid"
type="primary"
style={{
width: 65,
height: 30,
borderRadius: 3,
}}
onClick={() => {
api.current.validate().then((v) => {
console.log(v);
// setSearch(v);
// setQuery({ limit: 10, page: 0 })
});
}}
>
搜索
</Button>
</div>
</div>
</div>
<Skeleton
loading={false}
active={true}
placeholder={SkeletonScreen()}
>
<Table
columns={[{ title: '叫', dataIndex: 'name', }]}
dataSource={[{ name: '1', }]}
bordered={false}
empty="暂无数据"
style={{
padding: "0px 20px",
}}
pagination={false}
onRow={(record, index) => {
if (index % 1 === 0) {
return { style: { background: '' } }
}
}}
/>
</Skeleton>
<div
style={{
display: "flex",
justifyContent: "flex-end",
padding: "20px 20px",
}}
>
<span style={{ lineHeight: "30px" }}>
{100}个设备
</span>
<Pagination
className="22"
total={100}
showSizeChanger
currentPage={1}
pageSizeOpts={[10, 20, 30, 40]}
onChange={(currentPage, pageSize) => {
// setQuery({ limit: pageSize, page: currentPage - 1 });
// page.current = currentPage - 1
}}
/>
</div>
</div >
</>
)
}
function mapStateToProps (state) {
const { auth, global, members } = state;
return {
// user: auth.user,
// actions: global.actions,
// global: global,
// members: members.data,
};
}
export default connect(mapStateToProps)(TableData);

261
web/client/src/sections/problem/containers/dataAlarm.jsx

@ -0,0 +1,261 @@
import React, { useEffect, useState } from 'react'
import { connect } from 'react-redux'
import { IconAlertCircle } from '@douyinfe/semi-icons'
import Statistics from '../components/statistics'
import TableData from '../components/tableData'
import { Setup } from "$components";
import '../style.less'
const DataAlarm = ({ match, dispatch, actions, user, loading, socket }) => {
const [route, setRoute] = useState('') //
const [abnormalLenght, setAbnormalLenght] = useState(0) //
const [collect, setCollect] = useState([]) //
const [setup, setSetup] = useState(false); //
const [tableSetup, setTableSetup] = useState([]); //
const tableType = { dataLnterrupt: 'dataLnterrupt', dataAbnormal: 'dataAbnormal', strategyHit: 'strategyHit', videoAbnormal: 'videoAbnormal', useAbnormal: 'useAbnormal', deviceAbnormal: 'deviceAbnormal' }
useEffect(() => {
setRoute(match.url.substring(match.url.lastIndexOf("/") + 1, match.url.length))
console.log(match.url)
console.log(tableType);
}, [])
useEffect(() => {
attribute(tableType[route], route);
}, [route])
//
const collectData = {
dataLnterrupt: [ //(dataLnterrupt)
{ name: '搜索', field: '1' },
{
name: '中断类型', field: '2',
data: [
{ name: '服务异常', value: '11' },
{ name: '链接中断', value: '22' },
{ name: '设备异常', value: '33' }]
},
{
name: '中断状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
dataAbnormal: [ //(dataAbnormal)
{ name: '搜索', field: '1' },
{
name: '异常类型', field: '2',
data: [
{ name: '超远程', value: '11' },
{ name: '超限幅', value: '22' }]
},
{
name: '异常状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
strategyHit: [ // (strategyHit)
{ name: '搜索', field: '1' },
{
name: '策略类型', field: '2',
data: [
{ name: '超阀值', value: '11' },
{ name: '防驶入告警', value: '22' },
{ name: '设备关闭', value: '33' },
{ name: '超围栏', value: '44' }]
},
{
name: '命中状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
videoAbnormal: [ // (videoAbnormal)
{ name: '搜索', field: '1' },
{
name: '设备类型', field: '2',
data: [
{ name: '枪机', value: '11' },
{ name: '球机', value: '22' },
{ name: '其他', value: '33' }]
},
{
name: '异常状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
useAbnormal: [ // (useAbnormal)
{ name: '搜索', field: '1' },
{
name: '异常类型', field: '2',
data: [
{ name: '接口报错', value: '11' },
{ name: '加载超时', value: '22' },
{ name: '元素异常', value: '33' }]
},
{
name: '异常状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
}],
deviceAbnormal: [ // (deviceAbnormal)
{ name: '搜索', field: '1' },
{
name: '设备类型', field: '2',
data: [
{ name: '传感器', value: '11' },
{ name: 'DTU', value: '22' },
{ name: '服务器', value: '33' }]
},
{
name: '异常状态', field: '3',
data: [
{ name: '当前', value: '11' },
{ name: '历史', value: '22' }]
},
{
name: '异常类型', field: '4',
data: [
{ name: '离线', value: '11' }]
}],
common: {
name: '持续时间',
field: '5'
}
}
//
const tableList = {
dataLnterrupt: ['1', '2', '3', '4', '6', '9', '7', '5', '10', '8', '11', '12', '13', '14'],
dataAbnormal: ['1', '2', '3', '4', '15', '16', '9', '7', '5', '10', '8', '11', '12', '13'],
strategyHit: ['1', '2', '3', '17', '18', '7', '5', '10', '8', '11', '12', '13'],
videoAbnormal: ['1', '2', '3', '19', '20', '21', '22', '5', '6', '7', '8', '12', '13'],
useAbnormal: ['1', '23', '24', '15', '25', '7', '8', '12', '13'],
deviceAbnormal: ['1', '2', '3', '19', '15', '21', '5', '6', '7', '8', '12', '13'],
}
//
const columns = {
dataLnterrupt: ['0', '1', '2', '3', '4', '5', '6', '7', '8',],
dataAbnormal: ['0', '1', '2', '3', '15', '5', '6', '7', '8'],
strategyHit: ['0', '1', '2', '3', '17', '5', '10', '11', '7', '8'],
videoAbnormal: ['0', '1', '2', '3', '21', '20', '5', '7', '8'],
useAbnormal: ['0', '1', '23', '24', '15', '25', '7', '8'],
deviceAbnormal: ['0', '1', '2', '3', '15', '19', '5', '7', '8'],
}
//
const columnAll = [
{ name: '问题编号', value: '0' },
{ name: '项目名称', value: '1' },
{ name: '结构物名称', value: '2' },
{ name: '告警源', value: '3' },
{ name: '中断类型', value: '4' },
{ name: '告警信息', value: '5' },
{ name: '常见原因', value: '6' },
{ name: '产生时间', value: '7' },
{ name: '更新时间', value: '8' },
{ name: '服务器地址', value: '9' },
{ name: '告警等级', value: '10' },
{ name: '产生次数', value: '11' },
{ name: '确认信息', value: '12' },
{ name: '确认/恢复时间', value: '13' },
{ name: '项目阶段', value: '14' },
{ name: '异常类型', value: '15' },
{ name: '异常原因', value: '16' },
{ name: '策略类型', value: '17' },
{ name: '命中状态', value: '18' },
{ name: '位置信息', value: '19' },
{ name: '设备类型', value: '20' },
{ name: '设备厂家', value: '21' },
{ name: '接入方式', value: '22' },
{ name: '应用名称', value: '23' },
{ name: 'URL地址', value: '24' },
{ name: '异常信息', value: '25' },
]
const attribute = (tableType, route) => {
let arr = localStorage.getItem(tableType)
? JSON.parse(localStorage.getItem(tableType))
: [];
// if (route) {
// console.log(tableList[route]);
// console.log([...[1,2,3],...[2,3]]);
// let setup = tableList[route].map(v => columnAll.find(vv => v == vv.value))
// console.log(setup);
// setTableSetup([{ list: setup }])
// }
// for (let i = 0; i < arr.length; i++) {
// let colum = column.filter((item) => {
// return item.key === arr[i];
// });
// columns.splice(i + 2, 0, colum[0]);
// }
// setSetupp(columns);
}
const distinguish = (route) => {
switch (route) {
case value:
break;
default:
break;
}
}
return (
<div>
{/* 滞留提醒 */}
<div>
{abnormalLenght > 0 ? <div style={{ height: 30, fontSize: 12, display: 'flex' }}><IconAlertCircle /><div>当前滞留5个工单即将超时请尽快处理</div></div> : ""}
</div>
<Statistics />
<TableData
route={route}
collectData={collectData}
setSetup={setSetup}
/>
{setup ? (
<Setup
tableType={tableType[route] || []}
tableList={tableSetup}
close={() => {
setSetup(false);
attribute(tableType[route]);
// setcameraSetup(false);
}}
/>
) : (
""
)}
</div>
)
}
function mapStateToProps (state) {
const { auth, global, members, webSocket } = state
return {
// loading: members.isRequesting,
user: auth.user,
actions: global.actions,
// members: members.data,
// socket: webSocket.socket
}
}
export default connect(mapStateToProps)(DataAlarm)

5
web/client/src/sections/problem/containers/index.js

@ -0,0 +1,5 @@
'use strict';
import DataAlarm from './dataAlarm';
export { DataAlarm };

15
web/client/src/sections/problem/index.js

@ -0,0 +1,15 @@
'use strict';
import reducers from './reducers';
import routes from './routes';
import actions from './actions';
import { getNavItem } from './nav-item';
export default {
key: 'problem',
name: '书写示例',
reducers: reducers,
routes: routes,
actions: actions,
getNavItem: getNavItem
};

33
web/client/src/sections/problem/nav-item.jsx

@ -0,0 +1,33 @@
import React from 'react';
import { IconCode } from '@douyinfe/semi-icons';
export function getNavItem (user, dispatch) {
return (
[{
itemKey: 'problem', text: '问题',
items: [
{
itemKey: 'dataAlarm', text: '数据告警', to: '/problem/dataAlarm/dataLnterrupt', icon: <iconpark-icon style={{ width: 20, height: 20 }} name="she-1"></iconpark-icon>,
items: [
{ itemKey: 'dataLnterrupt', to: '/problem/dataAlarm/dataLnterrupt', text: '数据中断' },
{ itemKey: 'dataAbnormal', to: '/problem/dataAlarm/dataAbnormal', text: '数据异常' },
{ itemKey: 'strategyHit', to: '/problem/dataAlarm/strategyHit', text: '策略命中' },
{ itemKey: 'videoAbnormal', to: '/problem/dataAlarm/videoAbnormal', text: '视频异常' },
]
}, {
itemKey: 'useAlarm', text: '应用告警', to: '/problem/useAlarm/useAbnormal', icon: <iconpark-icon style={{ width: 20, height: 20 }} name="she-1"></iconpark-icon>,
items: [
{ itemKey: 'useAbnormal', to: '/problem/useAlarm/useAbnormal', text: '应用异常' },
]
}, {
itemKey: 'deviceAlarm', text: '设备告警', to: '/problem/deviceAlarm/deviceAbnormal', icon: <iconpark-icon style={{ width: 20, height: 20 }} name="she-1"></iconpark-icon>,
items: [
{ itemKey: 'deviceAbnormal', to: '/problem/deviceAlarm/deviceAbnormal', text: '设备异常' },
]
},
]
},
]
);
}

5
web/client/src/sections/problem/reducers/index.js

@ -0,0 +1,5 @@
'use strict';
export default {
}

61
web/client/src/sections/problem/routes.js

@ -0,0 +1,61 @@
'use strict';
import { DataAlarm, } from './containers';
export default [{
type: 'inner',
route: {
path: '/problem',
key: 'problem',
breadcrumb: '问题',
// 不设置 component 则面包屑禁止跳转
childRoutes: [{
path: '/dataAlarm',
key: 'dataAlarm',
breadcrumb: '数据告警',
// 不设置 component 则面包屑禁止跳转
childRoutes: [{
path: '/dataLnterrupt',
key: 'dataLnterrupt',
breadcrumb: '数据中断',
component: DataAlarm,
}, {
path: '/dataAbnormal',
key: 'dataAbnormal',
breadcrumb: '数据异常',
component: DataAlarm,
}, {
path: '/strategyHit',
key: 'strategyHit',
breadcrumb: '策略命中',
component: DataAlarm,
}, {
path: '/videoAbnormal',
key: 'videoAbnormal',
breadcrumb: '视频异常',
component: DataAlarm,
}]
}, {
path: '/useAlarm',
key: 'useAlarm',
breadcrumb: '应用告警',
// 不设置 component 则面包屑禁止跳转
childRoutes: [{
path: '/useAbnormal',
key: 'useAbnormal',
breadcrumb: '应用异常',
component: DataAlarm,
},]
}, {
path: '/deviceAlarm',
key: 'deviceAlarm',
breadcrumb: '设备告警',
// 不设置 component 则面包屑禁止跳转
childRoutes: [{
path: '/deviceAbnormal',
key: 'deviceAbnormal',
breadcrumb: '设备异常',
component: DataAlarm,
},]
}]
}
}];

7
web/client/src/sections/problem/style.less

@ -0,0 +1,7 @@
#example {
box-shadow: 3px 3px 2px black;
}
#example:hover {
color: yellowgreen;
}

25
web/client/src/utils/func.js

@ -2,32 +2,9 @@
export const isAuthorized = (authcode) => {
if (JSON.parse(sessionStorage.getItem('user'))) {
const { resources } = JSON.parse(sessionStorage.getItem('vcmpUser'));
const { resources } = JSON.parse(sessionStorage.getItem('pomsUser'));
return resources.includes(authcode);
} else {
return false;
}
}
export const checkAudioVideo = (constraintsData) => {
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先,如果有getUserMedia的话,就获得它
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
// 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
if (!getUserMedia) {
return Promise.reject({ code: 404 });
}
// 否则,为老的navigator.getUserMedia方法包裹一个Promise
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
}
return navigator.mediaDevices.getUserMedia(constraintsData)
}

13
web/client/src/utils/index.js

@ -1,21 +1,20 @@
'use strict';
import { isAuthorized, checkAudioVideo } from './func';
import { isAuthorized } from './func';
import { AuthorizationCode } from './authCode';
import {
ApiTable,RouteTable,
AuthRequest, AxyRequest,
basicAction,RouteRequest
ApiTable, RouteTable,
AxyRequest, EmisRequest,
basicAction, RouteRequest
} from './webapi'
export {
isAuthorized,
checkAudioVideo,
AuthorizationCode,
RouteTable,
ApiTable,
AuthRequest,
AxyRequest,
EmisRequest,
basicAction,
RouteRequest,
}

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

@ -1,11 +1,11 @@
"use strict";
import { ProxyRequest, customWebUtils } from "@peace/utils";
export const AuthRequest = new ProxyRequest("_auth");
export const AxyRequest = new ProxyRequest("_axy");
export const EmisRequest = new ProxyRequest("_emis")
export const webUtils = new customWebUtils({
userKey: 'vcmpUser'
userKey: 'pomsUser'
});
const { basicAction, RouteRequest } = webUtils
export {
@ -15,11 +15,9 @@ export {
export const ApiTable = {
login: "login",
logout: "logout",
};
export const RouteTable = {
apiRoot: "/api/root",
fileUpload: "/_upload/new",
cleanUpUploadTrash: "/_upload/cleanup",
};
apiRoot: "/api/root",
fileUpload: "/_upload/new",
cleanUpUploadTrash: "/_upload/cleanup",
};

36
web/config.js

@ -12,22 +12,19 @@ dev && console.log('\x1B[33m%s\x1b[0m', '请遵循并及时更新 readme.md,
// // 启动参数
args.option(['p', 'port'], '启动端口');
args.option(['u', 'api-url'], 'webapi的URL');
args.option('apiVcmpUrl', 'webapi的URL 外网可访问');
args.option('apiAuthUrl', 'IOT 鉴权 api');
args.option('apiPomsUrl', 'webapi的URL 外网可访问');
args.option('apiAnxinyunUrl', '安心云 api');
args.option('iotAuthWeb', 'IOT 鉴权 web');
args.option('iotVideoServer', 'IOT 后端视频服务鉴权');
args.option('apiEmisUrl', '企业管理 api');
const flags = args.parse(process.argv);
const API_URL = process.env.API_URL || flags.apiUrl;
const API_VCMP_URL = process.env.API_VCMP_URL || flags.apiVcmpUrl;
const API_AUTH_URL = process.env.API_AUTH_URL || flags.apiAuthUrl;
const IOT_AUTH_WEB = process.env.IOT_AUTH_WEB || flags.iotAuthWeb;
const API_POMS_URL = process.env.API_POMS_URL || flags.apiPomsUrl;
const API_EMIS_URL = process.env.API_EMIS_URL || flags.apiEmisUrl;
const API_ANXINYUN_URL = process.env.API_ANXINYUN_URL || flags.apiAnxinyunUrl;
const IOT_VIDEO_SERVER = process.env.IOT_VIDEO_SERVER || flags.iotVideoServer;
if (!API_URL || !API_ANXINYUN_URL || !API_AUTH_URL || !IOT_AUTH_WEB || !IOT_VIDEO_SERVER) {
if (!API_URL || !API_ANXINYUN_URL) {
console.log('缺少启动参数,异常退出');
args.showHelp();
process.exit(-1);
@ -42,12 +39,6 @@ const product = {
host: API_URL,
match: /^\/_api\//,
}
}, {
entry: require('./middlewares/proxy').entry,
opts: {
host: API_AUTH_URL,
match: /^\/_auth\//,
}
}, {
entry: require('./middlewares/proxy').entry,
opts: {
@ -57,22 +48,13 @@ const product = {
}, {
entry: require('./middlewares/proxy').entry,
opts: {
host: IOT_VIDEO_SERVER,
match: /^\/_vs\//,
}
},{
// TODO 还不能用
entry: require('./middlewares/proxy').entry,
opts: {
host: 'https://open.ys7.com',
match: /^\/_yingshi\//,
host: API_EMIS_URL,
match: /^\/_emis\//,
}
}, {
entry: require('./routes').entry,
opts: {
apiUrl: API_VCMP_URL,
iotAuthWeb: IOT_AUTH_WEB,
iotVideoServer: IOT_VIDEO_SERVER,
apiUrl: API_POMS_URL,
staticRoot: './client',
}
}, {

4
web/middlewares/vite-dev.js

@ -19,7 +19,7 @@ module.exports = {
// 因为 Vite 自会接管
})
server.listen(5002)
console.info('vite server.listen 5002');
server.listen(5602)
console.info('vite server.listen 5602');
}
};

8
web/middlewares/webpack-dev.js

@ -10,7 +10,7 @@ const compiler = webpack(devConfig);
module.exports = {
entry: function (app, router, opts) {
app.use(proxy('http://localhost:5001', {
app.use(proxy('http://localhost:5601', {
filter: function (ctx) {
return /\/build/.test(url.parse(ctx.url).path);
},
@ -19,7 +19,7 @@ module.exports = {
}
}));
app.use(proxy('http://localhost:5001', {
app.use(proxy('http://localhost:5601', {
filter: function (ctx) {
return /\/$/.test(url.parse(ctx.url).path);
},
@ -46,11 +46,11 @@ module.exports = {
});
server.use(middleware(compiler));
server.listen('5001', function (err) {
server.listen('5601', function (err) {
if (err) {
console.error(err);
} else {
console.info(`webpack-dev listen 5001`);
console.info(`webpack-dev listen 5601`);
}
})
}

4
web/package.json

@ -7,7 +7,7 @@
"test": "mocha",
"start-vite": "cross-env NODE_ENV=developmentVite npm run start-params",
"start": "cross-env NODE_ENV=development npm run start-params",
"start-params": "node server -p 5000 -u http://localhost:4000 --apiVcmpUrl http://localhost:4000 --apiAuthUrl http://localhost:4200 --apiAnxinyunUrl http://localhost:4100 --iotAuthWeb http://localhost:5200 --iotVideoServer http://221.230.55.27:8081",
"start-params": "node server -p 5600 -u http://localhost:4600 --apiPomsUrl http://localhost:4600 --apiAnxinyunUrl http://10.8.30.112:4100 --apiEmisUrl http://10.8.30.112:14000",
"deploy": "export NODE_ENV=production&& npm run build && node server",
"build-dev": "cross-env NODE_ENV=development&&webpack --config webpack.config.js",
"build": "cross-env NODE_ENV=production&&webpack --config webpack.config.prod.js"
@ -79,4 +79,4 @@
"webpack-dev-server": "^3.11.2",
"webpack-hot-middleware": "^2.25.0"
}
}
}

2
web/webpack.config.js

@ -36,7 +36,7 @@ module.exports = {
plugins: [
new webpack.HotModuleReplacementPlugin(),
new BundleAnalyzerPlugin({
analyzerPort: 8000,
analyzerPort: 8600,
}),
new SemiWebpackPlugin({
theme: {

Loading…
Cancel
Save