Browse Source

deleted: api/.vscode/launch.json

deleted:    api/Dockerfile
	deleted:    api/app/index.js
	deleted:    api/app/lib/index.js
	deleted:    api/app/lib/middlewares/api-log.js
	deleted:    api/app/lib/middlewares/authenticator.js
	deleted:    api/app/lib/routes/index.js
	deleted:    api/app/lib/schedule/index.js
	deleted:    api/app/lib/service/redis.js
	deleted:    api/config.js
	deleted:    api/package.json
	deleted:    api/server.js
	deleted:    api/utils/forward-api.js
	modified:   web/client/assets/color.less
	modified:   web/client/src/themes/dark.json
	modified:   web/client/src/utils/webapi.js
	modified:   web/package.json
master
yinweiwen 2 years ago
parent
commit
b36d0c8f5e
  1. 40
      code/api/.vscode/launch.json
  2. 21
      code/api/Dockerfile
  3. 3
      code/api/app/index.js
  4. 27
      code/api/app/lib/index.js
  5. 83
      code/api/app/lib/middlewares/api-log.js
  6. 143
      code/api/app/lib/middlewares/authenticator.js
  7. 17
      code/api/app/lib/routes/index.js
  8. 18
      code/api/app/lib/schedule/index.js
  9. 44
      code/api/app/lib/service/redis.js
  10. 117
      code/api/config.js
  11. 35
      code/api/package.json
  12. 12
      code/api/server.js
  13. 15
      code/api/utils/forward-api.js
  14. 1119
      code/web/client/assets/color.less
  15. 2
      code/web/client/src/themes/dark.json
  16. 2
      code/web/client/src/utils/webapi.js
  17. 1
      code/web/package.json

40
code/api/.vscode/launch.json

@ -1,40 +0,0 @@
{
// 使 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 4200",
"-f http://localhost:4200",
"-g postgres://postgres:123@10.8.30.32:5432/xxxx",
"--redisHost 127.0.0.1",
"--redisPort 6379"
]
},
{
"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"
}
}
]
}

21
code/api/Dockerfile

@ -1,21 +0,0 @@
FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12-dev as builder
COPY . /var/app
WORKDIR /var/app
EXPOSE 8080
RUN npm config set registry=http://10.8.30.22:7000
RUN echo "{\"time\":\"$BUILD_TIMESTAMP\",\"build\": \"$BUILD_NUMBER\",\"revision\": \"$SVN_REVISION_1\",\"URL\":\"$SVN_URL_1\"}" > version.json
RUN npm cache clean -f
RUN rm -rf package-lock.json
RUN npm install --registry http://10.8.30.22:7000
FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12
COPY --from=builder --chown=node /var/app /home/node/app
WORKDIR /home/node/app
CMD ["node", "server.js"]

3
code/api/app/index.js

@ -1,3 +0,0 @@
'use strict';
module.exports = require('./lib');

27
code/api/app/lib/index.js

@ -1,27 +0,0 @@
'use strict';
const routes = require('./routes');
const authenticator = require('./middlewares/authenticator');
// const apiLog = require('./middlewares/api-log');
const redisConnect = require('./service/redis')
const schedule = require('./schedule')
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 || {};
redisConnect(app, opts)
schedule(app, opts)
router.use(authenticator(app, 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);
};

83
code/api/app/lib/middlewares/api-log.js

@ -1,83 +0,0 @@
/**
* 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;

143
code/api/app/lib/middlewares/authenticator.js

@ -1,143 +0,0 @@
/**
* 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: '/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');
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;
};
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);
};
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' }
}
}
} 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;

17
code/api/app/lib/routes/index.js

@ -1,17 +0,0 @@
'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;
};

18
code/api/app/lib/schedule/index.js

@ -1,18 +0,0 @@
'use strict';
const fs = require('fs');
// 将定时任务汇集未来可根据需要选取操作
module.exports = async function (app, opts) {
fs.readdirSync(__dirname).forEach((filename) => {
if (!['index.js'].some(f => filename == f)) {
const schedule = require(`./${filename}`)(app, opts)
for (let k of Object.keys(schedule)) {
console.info(`定时任务 ${k} 启动`);
}
app.fs.schedule = {
...app.fs.schedule,
...schedule,
}
}
});
};

44
code/api/app/lib/service/redis.js

@ -1,44 +0,0 @@
'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}`);
})
// 查询尚未过期token放入redis
// const tokenRes = await app.fs.dc.models.UserToken.findAll({
// where: {
// expired: { $gte: moment().format('YYYY-MM-DD HH:mm:ss') }
// }
// });
// for (let t of tokenRes) {
// const { token, dataValues } = t
// dataValues.userInfo = JSON.stringify(dataValues.userInfo)
// dataValues.expired = moment(dataValues.expired).format()
// await client.hmset(token, dataValues);
// }
// token 2 redis end
// 自定义方法
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,
}
}

117
code/api/config.js

@ -1,117 +0,0 @@
'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服务');
args.option('redisHost', 'redisHost');
args.option('redisPort', 'redisPort');
args.option('redisPswd', 'redisPassword');
const flags = args.parse(process.argv);
const IOT_AUTH_DB = process.env.IOT_AUTH_DB || flags.pg;
const IOT_AUTH_LOCAL_SVR_ORIGIN = process.env.IOT_AUTH_LOCAL_SVR_ORIGIN || flags.fileHost;
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_AUTH_DB || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT) {
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_AUTH_LOCAL_SVR_ORIGIN || `http://localhost:${flags.port || 8080}`,
rootPath: 'static',
childPath: 'upload',
},
maxSize: 104857600, // 100M
}
}, {
entry: require('./app').entry,
opts: {
exclude: [// 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
// { p: '/cross_token/check', o: 'POST' }
],
redis: {
host: IOTA_REDIS_SERVER_HOST,
port: IOTA_REDIS_SERVER_PORT,
pwd: IOTA_REDIS_SERVER_PWD
},
}
}
],
dc: {
url: IOT_AUTH_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;

35
code/api/package.json

@ -1,35 +0,0 @@
{
"name": "iot-auth",
"version": "1.0.0",
"description": "fs iot-auth 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 4200 -g postgres://postgres:123@10.8.30.32:5432/iot_auth -f http://localhost:4200",
"start:linux": "export NODE_ENV=development&&node server -p 4200 -g postgres://postgres:123@10.8.30.32:5432/iot_auth"
},
"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": "^5.0.4",
"koa-convert": "^1.2.0",
"koa-proxy": "^0.9.0",
"moment": "^2.24.0",
"node-schedule": "^2.1.0",
"path": "^0.12.7",
"path-to-regexp": "^3.0.0",
"pg": "^7.9.0",
"request": "^2.88.2",
"superagent": "^3.5.2",
"uuid": "^3.3.2"
},
"devDependencies": {
"mocha": "^6.0.2"
}
}

12
code/api/server.js

@ -1,12 +0,0 @@
/**
* 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);

15
code/api/utils/forward-api.js

@ -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, '');
}
})));
}
};

1119
code/web/client/assets/color.less

File diff suppressed because it is too large

2
code/web/client/src/themes/dark.json

File diff suppressed because one or more lines are too long

2
code/web/client/src/utils/webapi.js

@ -2,7 +2,7 @@
import request from 'superagent'; import request from 'superagent';
export const ApiTable = { export const ApiTable = {
login: 'project/login', login: 'login',
logout: 'logout', logout: 'logout',
getEnterprisesMembers: 'enterprises/{enterpriseId}/members', getEnterprisesMembers: 'enterprises/{enterpriseId}/members',

1
code/web/package.json

@ -46,7 +46,6 @@
"react-dom": "^17.0.0", "react-dom": "^17.0.0",
"react-if": "^2.2.1", "react-if": "^2.2.1",
"react-jsonschema-form": "^1.8.1", "react-jsonschema-form": "^1.8.1",
"react-quill": "^1.3.5",
"react-redux": "^7.2.1", "react-redux": "^7.2.1",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-router-redux": "^4.0.8", "react-router-redux": "^4.0.8",

Loading…
Cancel
Save