diff --git a/code/VideoAccess-VCMP/api/.vscode/launch.json b/code/VideoAccess-VCMP/api/.vscode/launch.json index 18a127d..9568af8 100644 --- a/code/VideoAccess-VCMP/api/.vscode/launch.json +++ b/code/VideoAccess-VCMP/api/.vscode/launch.json @@ -19,6 +19,7 @@ "--redisHost 127.0.0.1", "--redisPort 6379", "--axyApiUrl http://127.0.0.1:4100", + "--iotAuthApi http://127.0.0.1:4200", "--godUrl https://restapi.amap.com/v3", "--godKey 21c2d970e1646bb9a795900dd00093ce" ] diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js index 589f324..52ac91f 100644 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js @@ -50,12 +50,20 @@ async function getCameraProject (ctx, next) { where: findOption.where }) + let cameraIds = [] + let createUserIds = new Set() + + for (let c of cameraRes) { + cameraIds.push(c.dataValues.id) + createUserIds.add(c.dataValues.createUserId) + } + // 查在安心云绑定的数据 - const cameraIds = cameraRes.map(c => { - return c.dataValues.id - }) const axbindCameraRes = await ctx.app.fs.axyRequest.get('vcmp/camera/project', { query: { token, cameraId: cameraIds.join(',') } }) + // 查对应创建者信息 + const corUsers = await ctx.app.fs.authRequest.get(`user/${[...createUserIds].join(',')}/message`, { query: { token } }) + for (let { dataValues: camera } of cameraRes) { const corBindCamera = axbindCameraRes.find(b => b.cameraId == camera.id) if (corBindCamera) { @@ -63,6 +71,14 @@ async function getCameraProject (ctx, next) { } else { camera.station = [] } + const corUser = corUsers.find(u => u.id == camera.createUserId) + if (corUser) { + camera.createUser = { + namePresent: corUser.namePresent + } + } else { + camera.createUser = {} + } } ctx.status = 200; @@ -105,6 +121,70 @@ async function getCamera (ctx) { } } +async function detail (ctx) { + let errMsg = `获取摄像头详情失败` + try { + const { models } = ctx.fs.dc; + const { cameraId } = ctx.params + const { userId, token } = ctx.fs.api + const { utils: { rtmp2others } } = ctx.app.fs + + const cameraRes = await models.Camera.findOne({ + where: { + id: cameraId + } + }) + + if (!cameraRes) { + throw errMsg + } + + const cameraProject = await ctx.app.fs.axyRequest.get('vcmp/camera/project', { query: { token, cameraId: cameraRes.id } }) + const bindStations = [] + for (let c of cameraProject) { + for (let s of c.stations) { + bindStations.push(s) + } + } + + const otherUrls = await rtmp2others(cameraRes.rtmp) + + ctx.status = 200; + ctx.body = { + ...cameraRes.dataValues, + station: bindStations, + videoUrl: otherUrls + } + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: errMsg + } + } +} + +async function getCameraListAll (ctx) { + try { + const { models } = ctx.fs.dc; + + const cameraRes = await models.Camera.findAll({ + attributes: ['id', 'name', 'type'], + where: { + delete: false, + recycleTime: null, + } + }) + + ctx.status = 200; + ctx.body = cameraRes + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = {} + } +} + async function banned (ctx) { try { const { models } = ctx.fs.dc; @@ -154,6 +234,8 @@ async function del (ctx) { module.exports = { getCameraProject, getCamera, + getCameraListAll, + detail, banned, del, }; \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js index d216756..edc9da9 100644 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js @@ -71,15 +71,40 @@ async function get (ctx) { findOption.where.venderId = venderId } - const res = await models.Nvr.findAll(findOption) + const nvrRes = await models.Nvr.findAll(findOption) const total = await models.Nvr.count({ where: findOption.where }) + const nvrIds = nvrRes.map(r => r.id) + const cameraRes = await models.Camera.findAll({ + where: { + nvrId: { $in: nvrIds } + } + }) + const cameraIds = cameraRes.map(c => c.id) + + // 查在安心云绑定的数据 + const axbindCameraRes = await ctx.app.fs.axyRequest.get('vcmp/camera/project', { query: { token, cameraId: cameraIds.join(',') } }) + + for (let { dataValues: n } of nvrRes) { + const corCameras = cameraRes.filter(c => c.nvrId == n.id) + const corBind = axbindCameraRes.filter(b => corCameras.some(c => c.id == b.cameraId)) + + if (corBind.length) { + n.station = [] + for (let c of corBind) { + n.station = n.station.concat(c.stations) + } + } else { + n.station = [] + } + } + ctx.status = 200; ctx.body = { total: total, - data: res + data: nvrRes } } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); @@ -128,8 +153,73 @@ async function del (ctx, next) { } } +async function detail (ctx) { + let errMsg = '获取 NVR 详情失败' + try { + const models = ctx.fs.dc.models; + const { userId, token } = ctx.fs.api + const { nvrId } = ctx.params + + const nvrRes = await models.Nvr.findOne({ + attributes: { exclude: ['delete'] }, + where: { + id: nvrId + }, + include: [{ + model: models.Vender + }] + }) + + if (!nvrRes) { + throw errMsg + } + + // 查询对应的后端服务相关信息 + + const cameraRes = await models.Camera.findAll({ + attributes: ['id', 'name', 'channelName', 'serialNo', 'rtmp'], + where: { + nvrId + } + }) + let cameras = [] + let cameraIds = [] + for (let c of cameraRes) { + cameraIds.push(c.id) + cameras.push(c.dataValues) + } + + const cameraProject = await ctx.app.fs.axyRequest.get('vcmp/camera/project', { query: { token, cameraId: cameraIds.join(',') } }) + const bindStations = [] + for (let c of cameraProject) { + for (let s of c.stations) { + bindStations.push(s) + } + } + + const corUser = await ctx.app.fs.authRequest.get(`user/${nvrRes.createUserId}/message`, { query: { token } }) + + let nvrDetail = { + ...nvrRes.dataValues, + station: bindStations, + camera: cameras, + createUser: { + namePresent: corUser[0].namePresent + } + } + + ctx.status = 200; + ctx.body = nvrDetail + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { message: errMsg } + } +} + module.exports = { edit, get, del, + detail, }; \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/app/lib/index.js b/code/VideoAccess-VCMP/api/app/lib/index.js index 0ad68f8..681e536 100644 --- a/code/VideoAccess-VCMP/api/app/lib/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/index.js @@ -1,6 +1,7 @@ 'use strict'; const routes = require('./routes'); +const utils = require('./utils') const redisConnect = require('./service/redis') const socketConect = require('./service/socket') const paasRequest = require('./service/paasRequest'); @@ -11,9 +12,13 @@ module.exports.entry = function (app, router, opts) { app.fs.logger.log('info', '[FS-AUTH]', 'Inject auth and api mv into router.'); app.fs.api = app.fs.api || {}; + app.fs.utils = app.fs.utils || {}; app.fs.api.authAttr = app.fs.api.authAttr || {}; app.fs.api.logAttr = app.fs.api.logAttr || {}; + // 工具类函数 + utils(app, opts) + // 顺序固定 ↓ redisConnect(app, opts) socketConect(app, opts) @@ -21,6 +26,7 @@ module.exports.entry = function (app, router, opts) { // 实例其他平台请求方法 paasRequest(app, opts) + // 鉴权中间件 router.use(authenticator(app, opts)); // 日志记录 diff --git a/code/VideoAccess-VCMP/api/app/lib/models/vender.js b/code/VideoAccess-VCMP/api/app/lib/models/vender.js index f74bc23..d81d97c 100644 --- a/code/VideoAccess-VCMP/api/app/lib/models/vender.js +++ b/code/VideoAccess-VCMP/api/app/lib/models/vender.js @@ -34,6 +34,10 @@ module.exports = dc => { Camera.belongsTo(Vender, { foreignKey: 'venderId', targetKey: 'id' }); Vender.hasMany(Camera, { foreignKey: 'venderId', sourceKey: 'id' }); + const Nvr = dc.models.Nvr; + Nvr.belongsTo(Vender, { foreignKey: 'venderId', targetKey: 'id' }); + Vender.hasMany(Nvr, { foreignKey: 'venderId', sourceKey: 'id' }); + dc.models.Vender = Vender; return Vender; diff --git a/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js b/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js index 931e683..218e924 100644 --- a/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js @@ -9,6 +9,12 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/camera'] = { content: '获取摄像头信息', visible: false }; router.get('/camera', camera.getCamera); + app.fs.api.logAttr['GET/camera/:cameraId/detail'] = { content: '获取摄像头详情', visible: false }; + router.get('/camera/:cameraId/detail', camera.detail); + + app.fs.api.logAttr['GET/camera/listAll'] = { content: '获取摄像头详情', visible: false }; + router.get('/camera/listAll', camera.getCameraListAll); + app.fs.api.logAttr['PUT/camera/banned'] = { content: '禁用摄像头', visible: false }; router.put('/camera/banned', camera.banned); diff --git a/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js b/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js index 6e4d6a9..0ddef8f 100644 --- a/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js @@ -3,12 +3,15 @@ const nvr = require('../../controllers/nvr'); module.exports = function (app, router, opts) { - app.fs.api.logAttr['POST/nvr'] = { content: '添加/修改nvr', visible: false }; - router.post('/nvr', nvr.edit); + app.fs.api.logAttr['POST/nvr'] = { content: '添加/修改nvr', visible: false }; + router.post('/nvr', nvr.edit); - app.fs.api.logAttr['GET/nvr'] = { content: '获取nvr', visible: false }; - router.get('/nvr', nvr.get); + app.fs.api.logAttr['GET/nvr'] = { content: '获取nvr', visible: false }; + router.get('/nvr', nvr.get); - app.fs.api.logAttr['DEL/nvr'] = { content: '删除nvr', visible: false }; - router.del('/nvr/:nvrId', nvr.del); + app.fs.api.logAttr['GET/nvr/detail/:nvrId'] = { content: '获取nvr详情', visible: false }; + router.get('/nvr/detail/:nvrId', nvr.detail); + + app.fs.api.logAttr['DEL/nvr'] = { content: '删除nvr', visible: false }; + router.del('/nvr/:nvrId', nvr.del); }; diff --git a/code/VideoAccess-VCMP/api/app/lib/utils/index.js b/code/VideoAccess-VCMP/api/app/lib/utils/index.js new file mode 100644 index 0000000..c8e95e9 --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/utils/index.js @@ -0,0 +1,16 @@ +'use strict'; + +const path = require('path'); +const fs = require('fs'); + +module.exports = async function (app, opts) { + fs.readdirSync(__dirname).forEach((filename) => { + if (filename != 'index.js') { + const utils = require(`./${filename}`) + app.fs.utils = { + ...app.fs.utils, + ...utils, + } + } + }); +}; diff --git a/code/VideoAccess-VCMP/api/app/lib/utils/rtmp2others.js b/code/VideoAccess-VCMP/api/app/lib/utils/rtmp2others.js new file mode 100644 index 0000000..157d06c --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/utils/rtmp2others.js @@ -0,0 +1,22 @@ +'use strict'; + +async function rtmp2others (rtmp) { + + return { + liveUrl: { + rtmp: 'xx', + hls: 'xx', + flv: 'xx', + ezopen: 'xx', + onvif: 'xx', + }, + replayUrl: { + cloud: 'xx', + local: 'xx', + } + } +} + +module.exports = { + rtmp2others, +} \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/config.js b/code/VideoAccess-VCMP/api/config.js index 149666d..9465604 100644 --- a/code/VideoAccess-VCMP/api/config.js +++ b/code/VideoAccess-VCMP/api/config.js @@ -14,7 +14,8 @@ args.option(['f', 'fileHost'], '文件中心本地化存储: WebApi 服务器地 args.option('redisHost', 'redisHost'); args.option('redisPort', 'redisPort'); args.option('redisPswd', 'redisPassword'); -args.option('axyApiUrl', 'axyApiUrl'); // 安心云 api +args.option('iotAuthApi', 'IOT 鉴权 api'); +args.option('axyApiUrl', '安心云 api'); args.option('godUrl', '高德地图API请求地址'); args.option('godKey', '高德地图API key'); @@ -27,6 +28,7 @@ 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 密码 +const IOT_AUTH_API = process.env.IOT_AUTH_API || flags.iotAuthApi; const AXY_API_URL = process.env.AXY_API_URL || flags.axyApiUrl; const GOD_URL = process.env.GOD_URL || flags.godUrl || 'https://restapi.amap.com/v3'; const GOD_KEY = process.env.GOD_KEY || flags.godKey; @@ -60,7 +62,10 @@ const product = { port: IOTA_REDIS_SERVER_PORT, pwd: IOTA_REDIS_SERVER_PWD }, - pssaRequest: [{ // name 会作为一个 request 出现在 ctx.app.fs + pssaRequest: [{// name 会作为一个 request 出现在 ctx.app.fs + name: 'authRequest', + root: IOT_AUTH_API + }, { name: 'axyRequest', root: AXY_API_URL }, { diff --git a/code/VideoAccess-VCMP/api/utils/forward-api.js b/code/VideoAccess-VCMP/api/utils/forward-api.js deleted file mode 100644 index 6b48e3e..0000000 --- a/code/VideoAccess-VCMP/api/utils/forward-api.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; -const proxy = require('koa-proxy'); -const convert = require('koa-convert'); - -module.exports = { - entry: function (app, router, opts) { - app.use(convert(proxy({ - host: opts.host, - match: opts.match, - map: function (path) { - return path.replace(opts.match, ''); - } - }))); - } -}; \ No newline at end of file