yuan_yi
3 years ago
16 changed files with 924 additions and 0 deletions
@ -0,0 +1,41 @@ |
|||||
|
{ |
||||
|
// 使用 IntelliSense 了解相关属性。 |
||||
|
// 悬停以查看现有属性的描述。 |
||||
|
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 |
||||
|
"version": "0.2.0", |
||||
|
"configurations": [ |
||||
|
{ |
||||
|
"type": "node", |
||||
|
"request": "launch", |
||||
|
"name": "启动API", |
||||
|
"program": "${workspaceRoot}/server.js", |
||||
|
"env": { |
||||
|
"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" |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
"type": "node", |
||||
|
"request": "launch", |
||||
|
"name": "run mocha", |
||||
|
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", |
||||
|
"stopOnEntry": false, |
||||
|
"args": [ |
||||
|
"app/test/*.test.js", |
||||
|
"--no-timeouts" |
||||
|
], |
||||
|
"cwd": "${workspaceRoot}", |
||||
|
"runtimeExecutable": null, |
||||
|
"env": { |
||||
|
"NODE_ENV": "development" |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
|
||||
|
FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2 |
||||
|
|
||||
|
MAINTAINER liuxinyi "liu.xinyi@free-sun.com.cn" |
||||
|
|
||||
|
COPY . /var/app |
||||
|
|
||||
|
WORKDIR /var/app |
||||
|
|
||||
|
EXPOSE 8080 |
||||
|
|
||||
|
CMD ["-g", "postgres://FashionAdmin:123456@iota-m1:5433/SmartRiver", "--qnak", "5XrM4wEB9YU6RQwT64sPzzE6cYFKZgssdP5Kj3uu", "--qnsk", "w6j2ixR_i-aelc6I7S3HotKIX-ukMzcKmDfH6-M5", "--qnbkt", "anxinyun-test", "--qndmn", "http://test.resources.anxinyun.cn"] |
||||
|
|
||||
|
ENTRYPOINT [ "node", "server.js" ] |
@ -0,0 +1,3 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
module.exports = require('./lib'); |
@ -0,0 +1,189 @@ |
|||||
|
'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 |
||||
|
}; |
@ -0,0 +1,36 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
const routes = require('./routes'); |
||||
|
const authenticator = require('./middlewares/authenticator'); |
||||
|
// const apiLog = require('./middlewares/api-log');
|
||||
|
const businessRest = require('./middlewares/business-rest'); |
||||
|
|
||||
|
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.api.authAttr = app.fs.api.authAttr || {}; |
||||
|
app.fs.api.logAttr = app.fs.api.logAttr || {}; |
||||
|
|
||||
|
router.use(authenticator(app, opts)); |
||||
|
router.use(businessRest(app, router, opts)); |
||||
|
// router.use(apiLog(app, opts));
|
||||
|
|
||||
|
router = routes(app, router, opts); |
||||
|
}; |
||||
|
|
||||
|
module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Sequelize, models: {} }
|
||||
|
require('./models/user')(dc); |
||||
|
require('./models/user_token')(dc); |
||||
|
require('./models/department')(dc); |
||||
|
require('./models/resource')(dc); |
||||
|
require('./models/user_resource')(dc); |
||||
|
require('./models/places')(dc); |
||||
|
require('./models/user_placeSecurityRecord')(dc); |
||||
|
require('./models/report_type')(dc); |
||||
|
require('./models/report_downManage')(dc); |
||||
|
require('./models/department')(dc); |
||||
|
require('./models/report_configition')(dc); |
||||
|
require('./models/report_collection')(dc); |
||||
|
require('./models/report_rectify')(dc); |
||||
|
}; |
@ -0,0 +1,83 @@ |
|||||
|
/** |
||||
|
* Created by PengPeng on 2017/4/26. |
||||
|
*/ |
||||
|
'use strict'; |
||||
|
|
||||
|
const moment = require('moment'); |
||||
|
const pathToRegexp = require('path-to-regexp'); |
||||
|
|
||||
|
function factory(app, opts) { |
||||
|
async function sendToEsAsync(producer, payloads) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
producer.send(payloads, function (err) { |
||||
|
if (err) { |
||||
|
reject(err); |
||||
|
} else { |
||||
|
resolve(); |
||||
|
} |
||||
|
}); |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
async function logger(ctx, next) { |
||||
|
const { path, method } = ctx; |
||||
|
const start = Date.now(); |
||||
|
|
||||
|
// 等待路由处理
|
||||
|
await next(); |
||||
|
|
||||
|
try { |
||||
|
let logAttr = null; |
||||
|
for (let prop in app.fs.api.logAttr) { |
||||
|
let keys = []; |
||||
|
let re = pathToRegexp(prop.replace(/\:[A-Za-z_\-]+\b/g, '(\\d+)'), keys); |
||||
|
if (re.test(`${method}${path}`)) { |
||||
|
logAttr = app.fs.api.logAttr[prop]; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
let parameter = null, parameterShow = null, user_id, _token, app_key; |
||||
|
if (ctx.fs.api) { |
||||
|
const { actionParameter, actionParameterShow, userId, token, appKey } = ctx.fs.api; |
||||
|
parameter = actionParameter; |
||||
|
parameterShow = actionParameterShow; |
||||
|
user_id = userId; |
||||
|
_token = token; |
||||
|
app_key = appKey; |
||||
|
} |
||||
|
const producer = ctx.fs.kafka.producer; |
||||
|
|
||||
|
const message = { |
||||
|
log_time: moment().toISOString(), |
||||
|
method: method, |
||||
|
content: logAttr ? logAttr.content : '', |
||||
|
parameter: JSON.stringify(parameter) || JSON.stringify(ctx.request.body), |
||||
|
parameter_show: parameterShow, |
||||
|
visible: logAttr ? logAttr.visible : true, |
||||
|
cost: Date.now() - start, |
||||
|
status_code: ctx.status, |
||||
|
url: ctx.request.url, |
||||
|
user_agent: ctx.request.headers["user-agent"], |
||||
|
user_id: user_id, |
||||
|
session: _token, |
||||
|
app_key: app_key, |
||||
|
header: JSON.stringify(ctx.request.headers), |
||||
|
ip: ctx.request.headers["x-real-ip"] || ctx.ip |
||||
|
}; |
||||
|
|
||||
|
const payloads = [{ |
||||
|
topic: `${opts.kafka.topicPrefix}`, |
||||
|
messages: [JSON.stringify(message)], |
||||
|
partition: 0 |
||||
|
}]; |
||||
|
|
||||
|
await sendToEsAsync(producer, payloads); |
||||
|
|
||||
|
} catch (e) { |
||||
|
ctx.fs.logger.error(`日志记录失败: ${e}`); |
||||
|
} |
||||
|
} |
||||
|
return logger; |
||||
|
} |
||||
|
|
||||
|
module.exports = factory; |
@ -0,0 +1,150 @@ |
|||||
|
/** |
||||
|
* Created by PengLing on 2017/3/27. |
||||
|
*/ |
||||
|
'use strict'; |
||||
|
|
||||
|
const pathToRegexp = require('path-to-regexp'); |
||||
|
const util = require('util'); |
||||
|
const moment = require('moment'); |
||||
|
|
||||
|
class ExcludesUrls { |
||||
|
constructor(opts) { |
||||
|
this.allUrls = undefined; |
||||
|
this.reload(opts); |
||||
|
} |
||||
|
|
||||
|
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); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
isExcluded(path, method) { |
||||
|
return this.allUrls.some(function (url) { |
||||
|
return !url.auth |
||||
|
&& url.pregexp.test(path) |
||||
|
&& (url.o === '*' || url.o.indexOf(method) !== -1); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断Url是否不鉴权 |
||||
|
* @param {*} opts {exclude: [*] or []},'*'或['*']:跳过所有路由; []:所有路由都要验证 |
||||
|
* @param {*} path 当前request的path |
||||
|
* @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: '/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); |
||||
|
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 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()) { |
||||
|
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; |
||||
|
console.log(resources, 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; |
||||
|
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(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = factory; |
@ -0,0 +1,50 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
const request = require('superagent'); |
||||
|
const buildUrl = (url,token) => { |
||||
|
let connector = url.indexOf('?') === -1 ? '?' : '&'; |
||||
|
return `${url}${connector}token=${token}`; |
||||
|
}; |
||||
|
|
||||
|
function factory(app, router, opts) { |
||||
|
return async function (ctx, next) { |
||||
|
|
||||
|
const token = ctx.fs.api.token; |
||||
|
|
||||
|
//console.log(username,password)
|
||||
|
const req = { |
||||
|
get: (url, query) => { |
||||
|
return request |
||||
|
.get(buildUrl(url,token)) |
||||
|
.query(query) |
||||
|
}, |
||||
|
post: (url, data, query) => { |
||||
|
return request |
||||
|
.post(buildUrl(url,token)) |
||||
|
.query(query) |
||||
|
//.set('Content-Type', 'application/json')
|
||||
|
.send(data); |
||||
|
}, |
||||
|
|
||||
|
put: (url, data) => { |
||||
|
return request |
||||
|
.put(buildUrl(url,token)) |
||||
|
//.set('Content-Type', 'application/json')
|
||||
|
.send(data); |
||||
|
}, |
||||
|
|
||||
|
delete: (url) => { |
||||
|
return request |
||||
|
.del(buildUrl(url,token)) |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
app.business = app.business || {}; |
||||
|
app.business.request = req; |
||||
|
|
||||
|
await next(); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
module.exports = factory; |
||||
|
|
@ -0,0 +1,108 @@ |
|||||
|
/* 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" |
||||
|
}, |
||||
|
name: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "name", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
username: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: "用户名 账号", |
||||
|
primaryKey: false, |
||||
|
field: "username", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
password: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "password", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
departmentId: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: "部门id", |
||||
|
primaryKey: false, |
||||
|
field: "department_id", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
email: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "email", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
enable: { |
||||
|
type: DataTypes.BOOLEAN, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: "启用状态", |
||||
|
primaryKey: false, |
||||
|
field: "enable", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
delete: { |
||||
|
type: DataTypes.BOOLEAN, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "delete", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
phone: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: "手机号(小程序使用手机号登录)", |
||||
|
primaryKey: false, |
||||
|
field: "phone", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
post: { |
||||
|
type: DataTypes.STRING, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: "职位", |
||||
|
primaryKey: false, |
||||
|
field: "post", |
||||
|
autoIncrement: false |
||||
|
} |
||||
|
}, { |
||||
|
tableName: "user", |
||||
|
comment: "", |
||||
|
indexes: [] |
||||
|
}); |
||||
|
dc.models.User = User; |
||||
|
|
||||
|
|
||||
|
return User; |
||||
|
}; |
@ -0,0 +1,32 @@ |
|||||
|
'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); |
||||
|
}; |
@ -0,0 +1,17 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
const path = require('path'); |
||||
|
const fs = require('fs'); |
||||
|
|
||||
|
module.exports = function (app, router, opts) { |
||||
|
fs.readdirSync(__dirname).forEach((filename) => { |
||||
|
if (filename.indexOf('.') !== 0 &&fs.lstatSync(path.join(__dirname, filename)).isDirectory()) { |
||||
|
fs.readdirSync(path.join(__dirname, filename)).forEach((api) => { |
||||
|
if (api.indexOf('.') == 0 || api.indexOf('.js') == -1) return; |
||||
|
require(`./${filename}/${api}`)(app, router, opts); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return router; |
||||
|
}; |
@ -0,0 +1,103 @@ |
|||||
|
'use strict'; |
||||
|
/*jslint node:true*/ |
||||
|
const path = require('path'); |
||||
|
const os = require('os'); |
||||
|
const moment = require('moment'); |
||||
|
const args = require('args'); |
||||
|
|
||||
|
const dev = process.env.NODE_ENV == 'development'; |
||||
|
|
||||
|
// 启动参数
|
||||
|
args.option(['p', 'port'], '启动端口'); |
||||
|
args.option(['g', 'pg'], 'postgre服务URL'); |
||||
|
args.option(['f', 'fileHost'], '文件中心本地化存储: WebApi 服务器地址(必填), 该服务器提供文件上传Web服务'); |
||||
|
|
||||
|
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) { |
||||
|
console.log('缺少启动参数,异常退出'); |
||||
|
args.showHelp(); |
||||
|
process.exit(-1); |
||||
|
} |
||||
|
|
||||
|
const product = { |
||||
|
port: flags.port || 8080, |
||||
|
staticDirs: ['static'], |
||||
|
mws: [ |
||||
|
{ |
||||
|
entry: require('@fs/attachment').entry, |
||||
|
opts: { |
||||
|
local: { |
||||
|
origin: IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN || `http://localhost:${flags.port || 8080}`, |
||||
|
rootPath: 'static', |
||||
|
childPath: 'upload', |
||||
|
}, |
||||
|
maxSize: 104857600, // 100M
|
||||
|
} |
||||
|
}, { |
||||
|
entry: require('./app').entry, |
||||
|
opts: { |
||||
|
exclude: [], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
|
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
dc: { |
||||
|
url: IOT_VIDEO_ACCESS_DB, |
||||
|
opts: { |
||||
|
pool: { |
||||
|
max: 80, |
||||
|
min: 10, |
||||
|
idle: 10000 |
||||
|
}, |
||||
|
define: { |
||||
|
freezeTableName: true, // 固定表名
|
||||
|
timestamps: false // 不含列 "createAt"/"updateAt"/"DeleteAt"
|
||||
|
}, |
||||
|
timezone: '+08:00', |
||||
|
logging: false |
||||
|
}, |
||||
|
models: [require('./app').models] |
||||
|
}, |
||||
|
logger: { |
||||
|
level: 'info', |
||||
|
json: false, |
||||
|
filename: path.join(__dirname, 'log', 'runtime.log'), |
||||
|
colorize: false, |
||||
|
maxsize: 1024 * 1024 * 5, |
||||
|
rotationFormat: false, |
||||
|
zippedArchive: true, |
||||
|
maxFiles: 10, |
||||
|
prettyPrint: true, |
||||
|
label: '', |
||||
|
timestamp: () => moment().format('YYYY-MM-DD HH:mm:ss.SSS'), |
||||
|
eol: os.EOL, |
||||
|
tailable: true, |
||||
|
depth: null, |
||||
|
showLevel: true, |
||||
|
maxRetries: 1 |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const development = { |
||||
|
port: product.port, |
||||
|
staticDirs: product.staticDirs, |
||||
|
mws: product.mws, |
||||
|
dc: product.dc, |
||||
|
logger: product.logger |
||||
|
}; |
||||
|
|
||||
|
if (dev) { |
||||
|
// mws
|
||||
|
for (let mw of development.mws) { |
||||
|
// if (mw.opts.exclude) mw.opts.exclude = ['*']; // 使用 ['*'] 跳过所有路由
|
||||
|
} |
||||
|
// logger
|
||||
|
development.logger.filename = path.join(__dirname, 'log', 'development.log'); |
||||
|
development.logger.level = 'debug'; |
||||
|
development.dc.opts.logging = console.log; |
||||
|
} |
||||
|
|
||||
|
module.exports = dev ? development : product; |
@ -0,0 +1,36 @@ |
|||||
|
{ |
||||
|
"name": "smart-emergency", |
||||
|
"version": "1.0.0", |
||||
|
"description": "fs smart emergency api", |
||||
|
"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: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" |
||||
|
}, |
||||
|
"author": "", |
||||
|
"license": "MIT", |
||||
|
"repository": {}, |
||||
|
"dependencies": { |
||||
|
"@fs/attachment": "^1.0.0", |
||||
|
"args": "^3.0.7", |
||||
|
"crypto-js": "^4.0.0", |
||||
|
"file-saver": "^2.0.2", |
||||
|
"fs-web-server-scaffold": "^2.0.2", |
||||
|
"ioredis": "^4.19.4", |
||||
|
"koa-convert": "^1.2.0", |
||||
|
"koa-proxy": "^0.9.0", |
||||
|
"moment": "^2.24.0", |
||||
|
"path": "^0.12.7", |
||||
|
"path-to-regexp": "^3.0.0", |
||||
|
"pg": "^7.9.0", |
||||
|
"redis": "^3.1.2", |
||||
|
"request": "^2.88.2", |
||||
|
"superagent": "^3.5.2", |
||||
|
"uuid": "^3.3.2" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"mocha": "^6.0.2" |
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
module.exports = { |
||||
|
// 数据库配置 与 sequelize 相同
|
||||
|
dbOptions: { |
||||
|
database: 'yinjiguanli', |
||||
|
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: ['user_placeSecurityRecord', 'places'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
|
||||
|
skipTables: ['user'], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性
|
||||
|
tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中
|
||||
|
ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面
|
||||
|
attrLength: false, // 在生成模型的字段中 是否生成 如 var(128)这种格式,公司一般使用 String ,则配置为 false
|
||||
|
}, |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
/** |
||||
|
* Created by rain on 2016/1/25. |
||||
|
*/ |
||||
|
|
||||
|
'use strict'; |
||||
|
/*jslint node:true*/ |
||||
|
//from koa
|
||||
|
|
||||
|
const scaffold = require('fs-web-server-scaffold'); |
||||
|
const config = require('./config'); |
||||
|
|
||||
|
module.exports = scaffold(config); |
@ -0,0 +1,15 @@ |
|||||
|
'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, ''); |
||||
|
} |
||||
|
}))); |
||||
|
} |
||||
|
}; |
Loading…
Reference in new issue