diff --git a/code/VideoAccess-VCMP/api/.vscode/launch.json b/code/VideoAccess-VCMP/api/.vscode/launch.json index e8d5ae1..5bef1ea 100644 --- a/code/VideoAccess-VCMP/api/.vscode/launch.json +++ b/code/VideoAccess-VCMP/api/.vscode/launch.json @@ -13,12 +13,11 @@ "NODE_ENV": "development" }, "args": [ - "-p 14000", - "-f http://localhost:14000", - // "-g postgres://postgres:123@10.8.30.32:5432/yinjiguanli", - // "-g postgres://postgres:123456@221.230.55.27:5432/yinjiguanli", - // "-g postgres://FashionAdmin:123456@10.8.30.156:5432/SmartEmergency", - "-g postgres://postgres:Mantis1921@116.63.50.139:54327/smartYingji" + "-p 4000", + "-f http://localhost:4000", + "-g postgres://postgres:123@10.8.30.32:5432/video_access", + "--redisHost 127.0.0.1", + "--redisPort 6379" ] }, { diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/auth/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/auth/index.js deleted file mode 100644 index 00040ce..0000000 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/auth/index.js +++ /dev/null @@ -1,189 +0,0 @@ -'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; - let password = Hex.stringify(MD5(params.password)); - - const userRes = await models.User.findOne({ - where: { - username: params.username, - password: password, - delete: false, - }, - attributes: { exclude: ['password'] }, - include: [{ - attributes: ["resourceId"], - model: models.UserResource - }] - }); - - if (!userRes) { - ctx.status = 400; - ctx.body = { - "message": "账号或密码错误" - } - } else if (!userRes.enable) { - ctx.status = 400; - ctx.body = { message: "该用户已被禁用" } - } else { - const token = uuid.v4(); - - let userRslt = Object.assign(userRes.dataValues, { - authorized: true, - token: token, - userResources: userRes.userResources.map(r => r.resourceId), - }); - - await models.UserToken.create({ - token: token, - userInfo: userRslt, - expired: moment().add(30, 'days').format() - }); - - ctx.status = 200; - ctx.body = userRslt; - } - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); - ctx.status = 400; - ctx.body = { - "message": "登录失败" - } - } -} - -/** - * 微信小程序登录 - * @@requires.body {phone-手机号, password-密码} ctx - */ -async function wxLogin(ctx, next) { - const transaction = await ctx.fs.dc.orm.transaction(); - try { - const models = ctx.fs.dc.models; - const params = ctx.request.body; - let password = Hex.stringify(MD5(params.password)); - const userRes = await models.User.findOne({ - where: { - phone: params.phone, - password: password, - delete: false, - }, - attributes: { exclude: ['password'] } - }); - if (!userRes) { - ctx.status = 400; - ctx.body = { message: "手机号或密码错误" } - } else if (!userRes.enable) { - ctx.status = 400; - ctx.body = { message: "该用户已被禁用" } - } else { - const token = uuid.v4(); - //获取用户关注区域信息 - const departmentRes = await models.Department.findOne({ where: { id: userRes.departmentId } }); - let attentionRegion = departmentRes; - while (attentionRegion.dependence && attentionRegion.type != 1) { - const departmentParent = await models.Department.findOne({ where: { id: attentionRegion.dependence } }); - attentionRegion = { - ...departmentParent.dataValues, - nextRegin: attentionRegion - } - } - //获取用户权限信息 - const resourceRes = await models.UserResource.findAll({ - where: { - userId: userRes.id - }, - include: [{ - model: models.Resource, - attributes: ['code', 'name'], - }], - attributes: [] - }); - let userRslt = Object.assign({ - authorized: true, - token: token, - ...userRes.dataValues - }); - await models.UserToken.create({ - token: token, - userInfo: userRslt, - expired: moment().add(30, 'day').format('YYYY-MM-DD HH:mm:ss') - }, { transaction: transaction }); - ctx.status = 200; - ctx.body = Object.assign({ - ...userRslt, - userRegionType: departmentRes.type,//1-市级,2-区县级,3-乡镇级,4-村级 - attentionRegion: attentionRegion, - resources: resourceRes.map(r => r.resource) - }); - } - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); - ctx.status = 400; - ctx.body = { - "message": "登录失败" - } - } -} - -async function logout(ctx) { - try { - const { token, code } = ctx.request.body; - const models = ctx.fs.dc.models; - - await models.UserToken.destroy({ - where: { - token: token, - }, - }); - - ctx.status = 204; - } catch (error) { - ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); - ctx.status = 400; - ctx.body = { - "message": "登出失败" - } - } -} - -/** - * 微信小程序登出 - * @request.body {token-用户登录Token} ctx - */ -async function wxLogout(ctx) { - try { - const { token } = ctx.request.body; - const models = ctx.fs.dc.models; - await models.UserToken.destroy({ - where: { - token: token, - }, - }); - ctx.status = 204; - } catch (error) { - ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); - ctx.status = 400; - ctx.body = { - "message": "登出失败" - } - } -} - -module.exports = { - login, - wxLogin, - logout, - wxLogout -}; \ 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 new file mode 100644 index 0000000..5bb4373 --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js @@ -0,0 +1,68 @@ +'use strict'; +const moment = require('moment') + +async function edit (ctx, next) { + const transaction = await ctx.fs.dc.orm.transaction(); + try { + const models = ctx.fs.dc.models; + const { userId } = ctx.fs.api + const data = ctx.request.body; + + // 或取其他服务信息 + const nvrData = { + channelCount: 8, + port: 8080, + } + + if (data.id) { + // 修改 + const storageData = Object.assign({}, data, nvrData) + await models.Nvr.update(storageData, { + where: { + id: data.id + }, + transaction + }) + } else { + // 添加 + const storageData = Object.assign({}, data, nvrData, { + createTime: moment().format(), + createUserId: userId, + delete: false, + }) + await models.Nvr.create(storageData, { transaction }) + } + + await transaction.commit(); + ctx.status = 204; + } catch (error) { + await transaction.rollback(); + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = {} + } +} + +async function del (ctx, next) { + try { + const models = ctx.fs.dc.models; + const { nvrId } = ctx.params + + await models.Nvr.destroy({ + where: { + id: nvrId + } + }) + + ctx.status = 204; + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = {} + } +} + +module.exports = { + edit, + del, +}; \ 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 0fd17b8..d5668f6 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 redisConnect = require('./service/redis') const authenticator = require('./middlewares/authenticator'); // const apiLog = require('./middlewares/api-log'); const businessRest = require('./middlewares/business-rest'); @@ -12,8 +13,10 @@ module.exports.entry = function (app, router, opts) { app.fs.api.authAttr = app.fs.api.authAttr || {}; app.fs.api.logAttr = app.fs.api.logAttr || {}; + redisConnect(app, opts) + router.use(authenticator(app, opts)); - router.use(businessRest(app, router, opts)); + // router.use(businessRest(app, router, opts)); // router.use(apiLog(app, opts)); router = routes(app, router, opts); diff --git a/code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js b/code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js index cdc5caf..8399456 100644 --- a/code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js +++ b/code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js @@ -13,13 +13,13 @@ class ExcludesUrls { this.reload(opts); } - sanitizePath(path) { + sanitizePath (path) { if (!path) return '/'; const p = '/' + path.replace(/^\/+/i, '').replace(/\/+$/, '').replace(/\/{2,}/, '/'); return p; } - reload(opts) { + reload (opts) { // load all url if (!this.allUrls) { this.allUrls = opts; @@ -37,7 +37,7 @@ class ExcludesUrls { } } - isExcluded(path, method) { + isExcluded (path, method) { return this.allUrls.some(function (url) { return !url.auth && url.pregexp.test(path) @@ -58,9 +58,7 @@ let isPathExcluded = function (opts, path, method) { if (!excludeAll) { let excludeOpts = opts.exclude || []; excludeOpts.push({ p: '/login', o: 'POST' }); - excludeOpts.push({ p: '/wxLogin', o: 'POST' }); excludeOpts.push({ p: '/logout', o: 'PUT' }); - excludeOpts.push({ p: '/wxLogout', o: 'PUT' }); excludes = new ExcludesUrls(excludeOpts); } let excluded = excludeAll || excludes.isExcluded(path, method); @@ -72,14 +70,10 @@ let authorizeToken = async function (ctx, token) { 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 axyRes = await ctx.fs.dc.models.UserToken.findOne({ - where: { - token: token, - expired: { $gte: moment().format('YYYY-MM-DD HH:mm:ss') } - } - }); - const { userInfo, expired } = axyRes; - if (!expired || moment().valueOf() <= moment(expired).valueOf()) { + const expired = await ctx.redis.hget(token, 'expired'); + + if (expired && moment().valueOf() <= moment(expired).valueOf()) { + const userInfo = JSON.parse(await ctx.redis.hget(token, 'userInfo')); rslt = { 'authorized': userInfo.authorized, 'resources': (userInfo || {}).resources || [], @@ -112,13 +106,14 @@ let isResourceAvailable = function (resources, options) { return !authCode || (resources || []).some(code => code === authCode); }; -function factory(app, opts) { - return async function auth(ctx, next) { +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)) { diff --git a/code/VideoAccess-VCMP/api/app/lib/routes/auth/index.js b/code/VideoAccess-VCMP/api/app/lib/routes/auth/index.js deleted file mode 100644 index b0de650..0000000 --- a/code/VideoAccess-VCMP/api/app/lib/routes/auth/index.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -const auth = require('../../controllers/auth'); - -module.exports = function (app, router, opts) { - /** - * @api {Post} login 登录. - * @apiVersion 1.0.0 - * @apiGroup Auth - */ - app.fs.api.logAttr['POST/login'] = { content: '登录', visible: true }; - router.post('/login', auth.login); - - /** - * @api {POST} wxLogin 微信小程序登录.(使用手机号、密码登录) - * @apiVersion 1.0.0 - * @apiGroup Auth - */ - app.fs.api.logAttr['POST/wxLogin'] = { content: '微信小程序登录', visible: true }; - router.post('/wxLogin', auth.wxLogin); - - app.fs.api.logAttr['PUT/logout'] = { content: '登出', visible: false }; - router.put('/logout', auth.logout); - - /** - * @api {PUT} wxLogout 微信小程序登出 - * @apiVersion 1.0.0 - * @apiGroup Auth - */ - app.fs.api.logAttr['PUT/wxLogout'] = { content: '登出', visible: false }; - router.put('/wxLogout', auth.wxLogout); -}; diff --git a/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js b/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js new file mode 100644 index 0000000..ed6654c --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js @@ -0,0 +1,11 @@ +'use strict'; + +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['DEL/nvr'] = { content: '删除nvr', visible: false }; + router.del('/nvr/:nvrId', nvr.del); +}; diff --git a/code/VideoAccess-VCMP/api/app/lib/service/redis.js b/code/VideoAccess-VCMP/api/app/lib/service/redis.js new file mode 100644 index 0000000..5e38a29 --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/service/redis.js @@ -0,0 +1,29 @@ +'use strict'; +const redis = require("ioredis") +const moment = require('moment') + +module.exports = async function factory (app, opts) { + let client = new redis(opts.redis.port, opts.redis.host); + + client.on("error", function (err) { + app.fs.logger.error('info', '[FS-AUTH-REDIS]', 'redis connect error.'); + console.error("Error :", err); + process.exit(-1); + }); + + client.on('connect', function () { + console.log(`redis connect success ${opts.redis.host + ':' + opts.redis.port}`); + }) + + // 自定义方法 + async function hdelall (key) { + const obj = await client.hgetall(key); + const hkeys = Object.keys(obj) + await client.hdel(key, hkeys) + } + + app.redis = client + app.redisTools = { + hdelall, + } +} diff --git a/code/VideoAccess-VCMP/api/config.js b/code/VideoAccess-VCMP/api/config.js index 5537608..084a131 100644 --- a/code/VideoAccess-VCMP/api/config.js +++ b/code/VideoAccess-VCMP/api/config.js @@ -11,13 +11,20 @@ const dev = process.env.NODE_ENV == 'development'; args.option(['p', 'port'], '启动端口'); args.option(['g', 'pg'], 'postgre服务URL'); args.option(['f', 'fileHost'], '文件中心本地化存储: WebApi 服务器地址(必填), 该服务器提供文件上传Web服务'); +args.option('redisHost', 'redisHost'); +args.option('redisPort', 'redisPort'); +args.option('redisPswd', 'redisPassword'); const flags = args.parse(process.argv); const IOT_VIDEO_ACCESS_DB = process.env.IOT_VIDEO_ACCESS_DB || flags.pg; const IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN = process.env.IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN || flags.fileHost; -if (!IOT_VIDEO_ACCESS_DB) { +const IOTA_REDIS_SERVER_HOST = process.env.IOTA_REDIS_SERVER_HOST || flags.redisHost || "localhost";//redis IP +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 密码 + +if (!IOT_VIDEO_ACCESS_DB || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT) { console.log('缺少启动参数,异常退出'); args.showHelp(); process.exit(-1); @@ -41,6 +48,11 @@ const product = { entry: require('./app').entry, opts: { exclude: [], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由 + redis: { + host: IOTA_REDIS_SERVER_HOST, + port: IOTA_REDIS_SERVER_PORT, + pwd: IOTA_REDIS_SERVER_PWD + }, } } ], diff --git a/code/VideoAccess-VCMP/api/package.json b/code/VideoAccess-VCMP/api/package.json index 43c97b7..c4d0945 100644 --- a/code/VideoAccess-VCMP/api/package.json +++ b/code/VideoAccess-VCMP/api/package.json @@ -5,7 +5,7 @@ "main": "server.js", "scripts": { "test": "set DEBUG=true&&\"node_modules/.bin/mocha\" --harmony --reporter spec app/test/*.test.js", - "start": "set NODE_ENV=development&&node server -p 14000 -g postgres://postgres:123@10.8.30.32:5432/yinjiguanli -f http://localhost:14000", + "start": "set NODE_ENV=development&&node server -p 4000 -g postgres://postgres:123@10.8.30.32:5432/video_access -f http://localhost:4000", "start:linux": "export NODE_ENV=development&&node server -p 4000 -g postgres://FashionAdmin:123456@10.8.30.39:5432/pm1", "automate": "sequelize-automate -c sequelize-automate.config.js" }, @@ -18,7 +18,7 @@ "crypto-js": "^4.0.0", "file-saver": "^2.0.2", "fs-web-server-scaffold": "^2.0.2", - "ioredis": "^4.19.4", + "ioredis": "^5.0.4", "koa-convert": "^1.2.0", "koa-proxy": "^0.9.0", "moment": "^2.24.0",