diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js index 5f3c581..2f0ee29 100644 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js @@ -1,12 +1,37 @@ 'use strict'; async function createYingshi (ctx) { + let errMsg = '添加萤石摄像头失败' try { const { models } = ctx.fs.dc const { userId, token } = ctx.fs.api + const { utils: { token4yingshi } } = ctx.app.fs const { id, name, cloudControl, highDefinition, memoryCard, - voice, kindId, abilityId, rtmp, } = ctx.request.body + voice, kindId, abilityId, rtmp, serialNo } = ctx.request.body; + + const serialNo_ = String(serialNo).toUpperCase() + const secretRes = await models.SecretYingshi.findAll() + let cameraBeloneSecret = null + for (let s of secretRes) { + const tokenYingshi = await token4yingshi(s.dataValues) + // 检测设备所属 + const cameraState = await ctx.app.fs.yingshiRequest.post('lapp/device/info', { + query: { + accessToken: tokenYingshi, + deviceSerial: serialNo_ + } + }) + if (cameraState.code == 200) { + cameraBeloneSecretId = s.dataValues.id + break + } + } + + if (!cameraBeloneSecret) { + errMsg = '请联系管理员核验或新增萤石云权限' + throw errMsg + } let storageData = { type: 'yingshi', name, cloudControl, highDefinition, memoryCard, @@ -29,10 +54,12 @@ async function createYingshi (ctx) { } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; - ctx.body = {} + ctx.body = { + message: errMsg + } } } module.exports = { - + createYingshi }; \ 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 681e536..ff102aa 100644 --- a/code/VideoAccess-VCMP/api/app/lib/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/index.js @@ -1,7 +1,7 @@ 'use strict'; -const routes = require('./routes'); const utils = require('./utils') +const routes = require('./routes'); const redisConnect = require('./service/redis') const socketConect = require('./service/socket') const paasRequest = require('./service/paasRequest'); @@ -16,9 +16,6 @@ module.exports.entry = function (app, router, opts) { 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) @@ -26,6 +23,9 @@ module.exports.entry = function (app, router, opts) { // 实例其他平台请求方法 paasRequest(app, opts) + // 工具类函数 + utils(app, opts) + // 鉴权中间件 router.use(authenticator(app, opts)); @@ -41,5 +41,6 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq require('./models/camera')(dc); require('./models/nvr')(dc); require('./models/vender')(dc); + require('./models/secret_yingshi')(dc); require('./models/ax_project')(dc); }; diff --git a/code/VideoAccess-VCMP/api/app/lib/models/secret_yingshi.js b/code/VideoAccess-VCMP/api/app/lib/models/secret_yingshi.js new file mode 100644 index 0000000..dd2e419 --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/models/secret_yingshi.js @@ -0,0 +1,61 @@ +/* eslint-disable*/ +'use strict'; + +module.exports = dc => { + const DataTypes = dc.ORM; + const sequelize = dc.orm; + const SecretYingshi = sequelize.define("secretYingshi", { + id: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: true, + field: "id", + autoIncrement: true, + unique: "secret_yingshi_id_uindex" + }, + key: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "key", + autoIncrement: false + }, + secret: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "secret", + autoIncrement: false + }, + token: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null, + comment: null, + primaryKey: false, + field: "token", + autoIncrement: false + }, + expire: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null, + comment: null, + primaryKey: false, + field: "expire", + autoIncrement: false + } + }, { + tableName: "secret_yingshi", + comment: "", + indexes: [] + }); + dc.models.SecretYingshi = SecretYingshi; + return SecretYingshi; +}; \ No newline at end of file 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 218e924..088ed86 100644 --- a/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js @@ -1,8 +1,16 @@ 'use strict'; const camera = require('../../controllers/camera'); +const cameraCreate = require('../../controllers/camera/create') module.exports = function (app, router, opts) { + + // 摄像头创建 + app.fs.api.logAttr['POST/camera/yingshi'] = { content: '创建萤石摄像头', visible: false }; + router.post('/camera/yingshi', cameraCreate.createYingshi); + + // 摄像头创建 END + app.fs.api.logAttr['GET/camera/project'] = { content: '获取摄像头列表及项目绑定信息', visible: false }; router.get('/camera/project', camera.getCameraProject); diff --git a/code/VideoAccess-VCMP/api/app/lib/utils/index.js b/code/VideoAccess-VCMP/api/app/lib/utils/index.js index c8e95e9..93dd58d 100644 --- a/code/VideoAccess-VCMP/api/app/lib/utils/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/utils/index.js @@ -6,7 +6,7 @@ const fs = require('fs'); module.exports = async function (app, opts) { fs.readdirSync(__dirname).forEach((filename) => { if (filename != 'index.js') { - const utils = require(`./${filename}`) + const utils = require(`./${filename}`)(app, opts) 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 index e17d112..a60069c 100644 --- a/code/VideoAccess-VCMP/api/app/lib/utils/rtmp2others.js +++ b/code/VideoAccess-VCMP/api/app/lib/utils/rtmp2others.js @@ -1,31 +1,34 @@ 'use strict'; -async function rtmp2others (rtmp) { - return { - liveUrl: { - hd: {// 高清 - rtmp: 'xx', - hls: 'xx', - flv: 'xx', - ezopen: 'xx', - onvif: 'xx', +module.exports = function (app, opts) { + async function rtmp2others (rtmp) { + + return { + liveUrl: {// 直播 + hd: {// 高清 + rtmp: 'xx', + hls: 'xx', + flv: 'xx', + ezopen: 'xx', + onvif: 'xx', + }, + sd: {// 标清 + rtmp: 'xx', + hls: 'xx', + flv: 'xx', + ezopen: 'xx', + onvif: 'xx', + } }, - sd: {// 标清 - rtmp: 'xx', - hls: 'xx', - flv: 'xx', - ezopen: 'xx', - onvif: 'xx', + replayUrl: {// 回放 + cloud: 'xx', + local: 'xx', } - }, - replayUrl: { - cloud: 'xx', - local: 'xx', } } -} -module.exports = { - rtmp2others, + return { + rtmp2others + } } \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js b/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js new file mode 100644 index 0000000..3bf6953 --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js @@ -0,0 +1,49 @@ +'use strict'; + +const moment = require('moment') + +module.exports = function (app, opts) { + async function token4yingshi ({ key, secret, token, expire } = {}) { + const { models } = app.fs.dc + if (!key || !secret) { + throw '参数 { key, secret } 缺一不可' + } + + if (token && expire && moment().isBefore(moment(expire))) { + return token + } else { + const secretRes = await models.SecretYingshi.findOne({ + where: { key, secret } + }) + if (secretRes && secretRes.expire && secretRes.token && moment().isBefore(moment(secretRes.expire))) { + return secretRes.token + } + } + + // 也可以做基于 redis 的缓存 + const tokenRes = await app.fs.yingshiRequest.post(`lapp/token/get`, { + query: { + appKey: key, + appSecret: secret + } + }) + if (tokenRes.code == 200) { + const { accessToken, expireTime } = tokenRes.data + await models.SecretYingshi.update({ + token: accessToken, + expire: expireTime + }, { + where: { + key, secret + } + }) + return accessToken + } else { + throw tokenRes + } + } + + return { + token4yingshi + } +} \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/config.js b/code/VideoAccess-VCMP/api/config.js index 9465604..dd70cf4 100644 --- a/code/VideoAccess-VCMP/api/config.js +++ b/code/VideoAccess-VCMP/api/config.js @@ -32,6 +32,7 @@ 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; +const YINGSHI_URL = process.env.YINGSHI_URL || flags.yingshiUrl || 'https://open.ys7.com/api'; if (!IOT_VIDEO_ACCESS_DB || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT || !GOD_KEY) { console.log('缺少启动参数,异常退出'); @@ -76,6 +77,14 @@ const product = { key: GOD_KEY } } + }, { + name: 'yingshiRequest', + root: YINGSHI_URL, + params: { + query: { + key: GOD_KEY + } + } },] } } diff --git a/code/VideoAccess-VCMP/api/sequelize-automate.config.js b/code/VideoAccess-VCMP/api/sequelize-automate.config.js index dde9092..ee1849f 100644 --- a/code/VideoAccess-VCMP/api/sequelize-automate.config.js +++ b/code/VideoAccess-VCMP/api/sequelize-automate.config.js @@ -1,35 +1,35 @@ module.exports = { - // 数据库配置 与 sequelize 相同 - dbOptions: { - database: 'video_access', - username: 'postgres', - password: '123', - dialect: 'postgres', - host: '10.8.30.32', - port: 5432, - define: { - underscored: false, - freezeTableName: false, - charset: 'utf8mb4', - timezone: '+00: 00', - dialectOptions: { - collate: 'utf8_general_ci', - }, - timestamps: false, - }, - }, - options: { - type: 'freesun', // 指定 models 代码风格 - camelCase: true, // Models 文件中代码是否使用驼峰命名 - modalNameSuffix: false, // 模型名称是否带 ‘Model’ 后缀 - fileNameCamelCase: false, // Model 文件名是否使用驼峰法命名,默认文件名会使用表名,如 `user_post.js`;如果为 true,则文件名为 `userPost.js` - dir: './app/lib/models', // 指定输出 models 文件的目录 - typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 - emptyDir: true, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` - tables: null, // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 - skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 - tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 - ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 - attrLength: false, // 在生成模型的字段中 是否生成 如 var(128)这种格式,公司一般使用 String ,则配置为 false - }, + // 数据库配置 与 sequelize 相同 + dbOptions: { + database: 'video_access', + username: 'postgres', + password: '123', + dialect: 'postgres', + host: '10.8.30.32', + port: 5432, + define: { + underscored: false, + freezeTableName: false, + charset: 'utf8mb4', + timezone: '+00: 00', + dialectOptions: { + collate: 'utf8_general_ci', + }, + timestamps: false, + }, + }, + options: { + type: 'freesun', // 指定 models 代码风格 + camelCase: true, // Models 文件中代码是否使用驼峰命名 + modalNameSuffix: false, // 模型名称是否带 ‘Model’ 后缀 + fileNameCamelCase: false, // Model 文件名是否使用驼峰法命名,默认文件名会使用表名,如 `user_post.js`;如果为 true,则文件名为 `userPost.js` + dir: './app/lib/models', // 指定输出 models 文件的目录 + typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 + emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` + tables: ['secret_yingshi'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 + skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 + tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 + ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 + attrLength: false, // 在生成模型的字段中 是否生成 如 var(128)这种格式,公司一般使用 String ,则配置为 false + }, } \ No newline at end of file