diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json index c06ba73..689d295 100644 --- a/api/.vscode/launch.json +++ b/api/.vscode/launch.json @@ -22,7 +22,12 @@ "--apiEmisUrl http://10.8.30.112:14000", "--godUrl https://restapi.amap.com/v3", "--godKey 21c2d970e1646bb9a795900dd00093ce", - "--mqttVideoServer mqtt://10.8.30.71:30883" + "--mqttVideoServer mqtt://10.8.30.71:30883", + "--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5", + "--qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa", + "--qnbkt dev-operational-service", + // "--qndmn http://resources.anxinyun.cn", + "--qndmn http://rhvqdivo5.hn-bkt.clouddn.com", ] }, { diff --git a/api/app/lib/controllers/alarm/app.js b/api/app/lib/controllers/alarm/app.js new file mode 100644 index 0000000..0a13f29 --- /dev/null +++ b/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, +}; \ No newline at end of file diff --git a/api/app/lib/controllers/auth/index.js b/api/app/lib/controllers/auth/index.js index 2f4823d..4a751d5 100644 --- a/api/app/lib/controllers/auth/index.js +++ b/api/app/lib/controllers/auth/index.js @@ -19,18 +19,48 @@ async function login (ctx, next) { } else { const pomsRegisterRes = await models.User.findOne({ where: { - pepUserId: emisLoginRes.id + 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 - await models.User.update({ + let userUpdateData = { lastInTime: moment().format(), - inTimes: pomsRegisterRes.inTimes + 1 - }, { + inTimes: pomsRegisterRes.inTimes + 1, + lastInAddress: '' + } + try { + // 获取ip转为地点并记录 + let ip = + // '117.90.39.49' || + ctx.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 } diff --git a/api/app/lib/controllers/organization/index.js b/api/app/lib/controllers/organization/index.js new file mode 100644 index 0000000..36e2e70 --- /dev/null +++ b/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, +}; \ No newline at end of file diff --git a/api/app/lib/controllers/project/index.js b/api/app/lib/controllers/project/index.js new file mode 100644 index 0000000..744b3b7 --- /dev/null +++ b/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 +}; \ No newline at end of file diff --git a/api/app/lib/middlewares/authenticator.js b/api/app/lib/middlewares/authenticator.js index 53e8c5b..ac08b3c 100644 --- a/api/app/lib/middlewares/authenticator.js +++ b/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; diff --git a/api/app/lib/models/app_alarm.js b/api/app/lib/models/app_alarm.js index f179e2b..6d6bdbb 100644 --- a/api/app/lib/models/app_alarm.js +++ b/api/app/lib/models/app_alarm.js @@ -24,18 +24,18 @@ module.exports = dc => { field: "serial_number", autoIncrement: false }, - pepProjectId: { + projectAppId: { type: DataTypes.INTEGER, allowNull: true, defaultValue: null, comment: "对应的项目id", primaryKey: false, - field: "pep_project_id", + field: "project_app_id", autoIncrement: false }, appDomain: { type: DataTypes.STRING, - allowNull: false, + allowNull: true, defaultValue: null, comment: "应用域名", primaryKey: false, @@ -77,6 +77,24 @@ module.exports = dc => { 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", diff --git a/api/app/lib/models/app_inspection.js b/api/app/lib/models/app_inspection.js index 550d1b4..84d6235 100644 --- a/api/app/lib/models/app_inspection.js +++ b/api/app/lib/models/app_inspection.js @@ -15,14 +15,18 @@ module.exports = dc => { autoIncrement: true, unique: "app_inspection_id_uindex" }, - pepProjectId: { + projectAppId: { type: DataTypes.INTEGER, allowNull: true, defaultValue: null, comment: null, primaryKey: false, - field: "pep_project_id", - autoIncrement: false + field: "project_app_id", + autoIncrement: false, + references: { + key: "id", + model: "projectApp" + } }, createTime: { type: DataTypes.DATE, @@ -41,6 +45,24 @@ module.exports = dc => { 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", diff --git a/api/app/lib/models/user.js b/api/app/lib/models/user.js index 67c002d..9f1461e 100644 --- a/api/app/lib/models/user.js +++ b/api/app/lib/models/user.js @@ -2,87 +2,114 @@ '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: null, - 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 - } - }, { - tableName: "user", - comment: "", - indexes: [] - }); - dc.models.User = User; - return User; + 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; }; \ No newline at end of file diff --git a/api/app/lib/routes/alarm/index.js b/api/app/lib/routes/alarm/index.js new file mode 100644 index 0000000..50dc0cb --- /dev/null +++ b/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); +}; diff --git a/api/app/lib/routes/auth/index.js b/api/app/lib/routes/auth/index.js index 6ed595d..c4df619 100644 --- a/api/app/lib/routes/auth/index.js +++ b/api/app/lib/routes/auth/index.js @@ -3,15 +3,9 @@ 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); app.fs.api.logAttr['PUT/logout'] = { content: '登出', visible: false }; router.put('/logout', auth.logout); - -}; +}; \ No newline at end of file diff --git a/api/app/lib/routes/organization/index.js b/api/app/lib/routes/organization/index.js new file mode 100644 index 0000000..d7010d9 --- /dev/null +++ b/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); +}; diff --git a/api/app/lib/routes/project/index.js b/api/app/lib/routes/project/index.js new file mode 100644 index 0000000..a3dbe3e --- /dev/null +++ b/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); +}; \ No newline at end of file diff --git a/api/config.js b/api/config.js index 93ccaa6..500b4af 100644 --- a/api/config.js +++ b/api/config.js @@ -24,6 +24,12 @@ args.option('godKey', '高德地图API key'); args.option('mqttVideoServer', '视频后台 mqtt 服务 URL'); +// 七牛云存储参数 +args.option('qnak', 'qiniuAccessKey'); +args.option('qnsk', 'qiniuSecretKey'); +args.option('qnbkt', 'qiniuBucket'); +args.option('qndmn', 'qiniuDomain'); + const flags = args.parse(process.argv); const POMS_DB = process.env.POMS_DB || flags.pg; @@ -46,11 +52,19 @@ const GOD_KEY = process.env.GOD_KEY || flags.godKey; // 视频后台 mqtt 信息推送地址 const MQTT_VIDEO_SERVER = process.env.MQTT_VIDEO_SERVER || flags.mqttVideoServer; +// 七牛云存储参数 +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 ( !POMS_DB || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT || !GOD_KEY || !MQTT_VIDEO_SERVER || !AXY_API_URL || - !API_EMIS_URL + !API_EMIS_URL || + !QINIU_DOMAIN_QNDMN_RESOURCE || !QINIU_BUCKET_RESOURCE || !QINIU_AK || !QINIU_SK ) { console.log('缺少启动参数,异常退出'); args.showHelp(); @@ -64,10 +78,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 } @@ -76,8 +91,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, diff --git a/api/sequelize-automate.config.js b/api/sequelize-automate.config.js index 4631443..e34d53b 100644 --- a/api/sequelize-automate.config.js +++ b/api/sequelize-automate.config.js @@ -26,7 +26,7 @@ module.exports = { dir: './app/lib/models', // 指定输出 models 文件的目录 typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` - tables: null, // 指定生成哪些表的 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_',] ,长度较长的 前缀放前面 diff --git a/web/client/assets/images/problem/setup.png b/web/client/assets/images/problem/setup.png new file mode 100644 index 0000000..58aa294 Binary files /dev/null and b/web/client/assets/images/problem/setup.png differ diff --git a/web/client/src/app.jsx b/web/client/src/app.jsx index 064eeb2..e1a94b4 100644 --- a/web/client/src/app.jsx +++ b/web/client/src/app.jsx @@ -14,6 +14,19 @@ const App = props => { useEffect(() => { document.title = projectName; + + console.log(` + _ _ + /> フ + | _ _ l + /\` ミ_xノ + / | + / ヽ ノ + │ | | | + / ̄| | | | + | ( ̄ヽ__ヽ_)__) + \二つ + `); }, []) return ( diff --git a/web/client/src/components/setup.jsx b/web/client/src/components/setup.jsx index 9ff3a55..c4cd070 100644 --- a/web/client/src/components/setup.jsx +++ b/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}