├── .babelrc ├── .gitignore ├── README.md ├── auth ├── index.js └── wechatAuth.js ├── config.json ├── config ├── redis.json └── requireLogin.json ├── constant └── index.js ├── endpoint.js ├── package.json ├── rabbitMQ ├── Client.js ├── RabbitSend.js └── index.js ├── router └── index.js ├── server.js ├── src ├── controller │ ├── BaseHandler.js │ ├── UserHandler.js │ └── index.js ├── routers │ ├── BaseRouter.js │ ├── index.js │ └── userRouter.js └── session │ ├── index.js │ └── store.js └── util ├── common ├── Common.js ├── fetch.js ├── index.js ├── json2form.js └── json2param.js └── logger ├── index.js └── logConfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015-node5", "stage-3"], 3 | "plugins": [ 4 | "transform-decorators-legacy", 5 | "transform-class-properties" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | logs 3 | security.json 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # api-rest -------------------------------------------------------------------------------- /auth/index.js: -------------------------------------------------------------------------------- 1 | export { default as wechatAuth } from './wechatAuth'; 2 | -------------------------------------------------------------------------------- /auth/wechatAuth.js: -------------------------------------------------------------------------------- 1 | import { token } from '../security.json'; 2 | import crypto from 'crypto'; 3 | function wechatAuth(ctx) { 4 | const signature = ctx.query.signature; 5 | const timestamp = ctx.query.timestamp; 6 | const nonce = ctx.query.nonce; 7 | return checkSignature(signature, timestamp, nonce, token); 8 | } 9 | 10 | function checkSignature(signature,timestamp,nonce,token){ 11 | const tmpArr = [token, timestamp, nonce]; 12 | tmpArr.sort(); 13 | const tmpStr = tmpArr.join(''); 14 | const shasum = crypto.createHash('sha1'); 15 | shasum.update(tmpStr); 16 | const shaResult = shasum.digest('hex'); 17 | if(shaResult === signature){ 18 | return true; 19 | } 20 | return false; 21 | } 22 | 23 | export default wechatAuth; 24 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 8888, 3 | "rabbitMq_host": "192.168.41.144", 4 | "rabbitMq_port": "5672", 5 | "rabbitMq_user": "admin", 6 | "rabbitMq_password": "wangrui1994", 7 | "MQ_QUEUE_COMMON": "jslight-service-common", 8 | "MQ_QUEUE_ORDER": "jslight-service-order", 9 | "MQ_QUEUE_COMMON_TEST": "jslight-service-common-test", 10 | "maxAge": 1800000, 11 | "redis_maxAge": 1800 12 | } 13 | -------------------------------------------------------------------------------- /config/redis.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "192.168.41.138", 3 | "port": "6379", 4 | "password": "wangrui1994" 5 | } 6 | -------------------------------------------------------------------------------- /config/requireLogin.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /constant/index.js: -------------------------------------------------------------------------------- 1 | export const POLL_HOUR = 1; 2 | export const POLL_HOUR_ORDER = 0.5; 3 | -------------------------------------------------------------------------------- /endpoint.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register') 2 | 3 | require('./server'); 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api-rest", 3 | "version": "1.0.0", 4 | "description": "the api of store project", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "better-npm-run start-online", 8 | "dev": "better-npm-run start-dev" 9 | }, 10 | "betterScripts": { 11 | "start-dev": { 12 | "command": "supervisor ./endpoint.js", 13 | "env": { 14 | "NODE_ENV": "development" 15 | } 16 | }, 17 | "start-online": { 18 | "command": "node ./endpoint.js", 19 | "env": { 20 | "NODE_ENV": "production" 21 | } 22 | } 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/burning0xb/api-rest.git" 27 | }, 28 | "keywords": [ 29 | "koa2", 30 | "wechat" 31 | ], 32 | "author": "wangrui", 33 | "license": "ISC", 34 | "dependencies": { 35 | "ali-oss": "^4.8.0", 36 | "amqplib": "^0.5.1", 37 | "autobind-decorator": "^1.4.3", 38 | "babel": "^6.23.0", 39 | "babel-core": "^6.24.1", 40 | "babel-plugin-transform-class-properties": "^6.24.1", 41 | "babel-preset-es2015-node5": "^1.2.0", 42 | "babel-preset-stage-3": "^6.24.1", 43 | "babel-register": "^6.24.1", 44 | "better-npm-run": "0.0.15", 45 | "co": "^4.6.0", 46 | "cross-env": "^5.0.1", 47 | "crypto": "0.0.3", 48 | "form-data": "^2.1.4", 49 | "ioredis": "^2.5.0", 50 | "kafka-node": "^1.6.2", 51 | "koa-bodyparser": "^3.2.0", 52 | "koa-convert": "^1.2.0", 53 | "koa-cors": "0.0.16", 54 | "koa-logger": "^2.0.1", 55 | "koa-multer": "^1.0.1", 56 | "koa-router": "^7.1.1", 57 | "koa-session2": "^1.0.9", 58 | "koa2": "^2.0.0-alpha.7", 59 | "log4js": "^1.1.1", 60 | "moment": "^2.18.1", 61 | "node-aliyun-sms": "^1.0.1", 62 | "node-fetch": "^1.6.3", 63 | "node-schedule": "^1.2.4", 64 | "node-uuid": "^1.4.8", 65 | "urlencode": "^1.1.0", 66 | "uuid": "^3.1.0", 67 | "xml2js": "^0.4.17" 68 | }, 69 | "devDependencies": { 70 | "babel-cli": "^6.24.1", 71 | "babel-plugin-transform-decorators-legacy": "^1.3.4" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /rabbitMQ/Client.js: -------------------------------------------------------------------------------- 1 | import amqp from 'amqplib/callback_api'; 2 | import RabbitSend from './RabbitSend'; 3 | import config from '../config.json'; 4 | 5 | export default class Client { 6 | 7 | constructor() { 8 | return new Promise((resolve, reject) => { 9 | amqp.connect('amqp://' + config.rabbitMq_user + ':' + config.rabbitMq_password + '@' + config.rabbitMq_host + ':' + config.rabbitMq_port, this.on_connect.bind(this, resolve)); 10 | }).catch((err) => { 11 | console.log(err); 12 | }); 13 | } 14 | 15 | init(ch, ok) { 16 | const server = new RabbitSend(ch, ok) 17 | return server; 18 | } 19 | 20 | bail(err) { 21 | console.error(err); 22 | } 23 | 24 | init_client(resolve, RabbitSend) { 25 | resolve({ 26 | RabbitSend: RabbitSend 27 | }); 28 | } 29 | 30 | on_connect(resolve, err, conn) { 31 | if (err !== null) return this.bail(err); 32 | conn.createChannel((err, ch) => { 33 | if (err !== null) return this.bail(err); 34 | 35 | ch.assertQueue('', { exclusive: true }, (err, ok) => { 36 | if (err !== null) return this.bail(err); 37 | global.ch = ch; 38 | global.ok = ok; 39 | this.init_client(resolve, (ch, ok) => { return this.init(ch, ok); }); 40 | }); 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rabbitMQ/RabbitSend.js: -------------------------------------------------------------------------------- 1 | import config from '../config.json'; 2 | import uuid from 'node-uuid'; 3 | 4 | export default class RabbitSend { 5 | constructor(ch, ok) { 6 | this.ch = ch; 7 | this.ok = ok; 8 | this.ramdom = Date.now(); 9 | } 10 | 11 | mabeAnswer(msg) { 12 | if (global.msgQueue.includes(msg.properties.correlationId)) { 13 | console.log(msg.content.toString()); 14 | const index = global.msgQueue.indexOf(msg.properties.correlationId); 15 | global.msgQueue.splice(index, 1); 16 | global.resolveRabbit[msg.properties.correlationId].resolve({ 17 | finalRes: JSON.parse(msg.content.toString()) 18 | }); 19 | delete global.resolveRabbit[msg.properties.correlationId]; 20 | } else { 21 | if (global.resolveRabbit[msg.properties.correlationId]) { 22 | global.resolveRabbit[msg.properties.correlationId].reject({ 23 | err: 'Unexpected message' 24 | }); 25 | delete global.resolveRabbit[msg.properties.correlationId]; 26 | } else { 27 | console.log('未找到对应的MQ'); 28 | } 29 | } 30 | } 31 | 32 | send(content, type) { 33 | console.log(' [x] Requesting is ', content); 34 | let queue = config.MQ_QUEUE_COMMON; 35 | // let queue = config.MQ_QUEUE_COMMON_TEST; 36 | switch (type) { 37 | case 'order': 38 | queue = config.MQ_QUEUE_ORDER; 39 | break; 40 | case 'pay': 41 | queue = config.MQ_QUEUE_PAY; 42 | break; 43 | default: 44 | queue = config.MQ_QUEUE_COMMON; 45 | // queue = config.MQ_QUEUE_COMMON_TEST; 46 | break; 47 | } 48 | return new Promise((resolve, reject) => { 49 | const correlationId = uuid(); 50 | global.msgQueue.push(correlationId); 51 | global.resolveRabbit[correlationId] = { 52 | resolve: resolve, 53 | reject: reject 54 | }; 55 | if (!global.readyListener.includes(queue)) { 56 | global.readyListener.push(queue); 57 | this.ch.consume(this.ok.queue, (msg) => { 58 | this.mabeAnswer(msg); 59 | }, { noAck: true }); 60 | } 61 | this.ch.sendToQueue(queue, new Buffer(JSON.stringify(content)), { 62 | replyTo: this.ok.queue, 63 | correlationId: correlationId 64 | }); 65 | }).catch((err) => { 66 | console.log(err); 67 | }); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /rabbitMQ/index.js: -------------------------------------------------------------------------------- 1 | export { default as Client } from './Client'; 2 | -------------------------------------------------------------------------------- /router/index.js: -------------------------------------------------------------------------------- 1 | import koaRouter from 'koa-router'; 2 | import multer from 'koa-multer'; 3 | import { Client } from '../rabbitMQ'; 4 | import { logger } from '../util/logger'; 5 | import { Common } from '../util/common'; 6 | import * as routers from '../src/routers'; 7 | import schedule from 'node-schedule'; 8 | 9 | const util = new Common(); 10 | 11 | let path = ''; 12 | if (process.env.NODE_ENV === 'development') { 13 | // 自己修改dev路径 14 | path = '/Users/burning/uploads/api-rest'; 15 | } else { 16 | // 服务器路径 17 | path = '/root/uploads/api-rest'; 18 | } 19 | 20 | const storage = multer.diskStorage({ 21 | destination: (req, file, cb) => { 22 | cb(null, path); 23 | }, 24 | filename: (req, file, cb) => { 25 | cb(null, `${Date.now()}-${file.originalname}`); 26 | } 27 | }) 28 | 29 | const upload = multer({ storage }); 30 | 31 | const router = koaRouter({ 32 | prefix: '/api-rest/api' 33 | }); 34 | 35 | router.get('/', (ctx, next) => { 36 | ctx.body = "this is the home page, we only support api"; 37 | }) 38 | 39 | new Client().then((res) => { 40 | logger.info('rabbitMQ is ready'); 41 | global.MQ = res.RabbitSend; 42 | }).then(() => { 43 | for (let _router in routers) { 44 | if (_router !== '') { 45 | routers[_router](router, upload); 46 | console.log(`${_router} 加载成功 👌`); 47 | } 48 | } 49 | }); 50 | 51 | export default router; 52 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | import Koa from 'koa2'; 2 | import config from './config.json'; 3 | import router from './router'; 4 | import { RedisStore } from './src/session'; 5 | import session from 'koa-session2'; 6 | import Logger from 'koa-logger'; 7 | import bodyParser from 'koa-bodyparser'; 8 | import { logger, logger_date } from './util/logger'; 9 | import BaseRouter from './src/routers/BaseRouter'; 10 | import convert from "koa-convert" 11 | import cors from 'koa-cors'; 12 | import schedule from 'node-schedule'; 13 | 14 | global.msgQueue = []; 15 | global.resolveRabbit = {}; 16 | global.readyListener = []; 17 | 18 | logger.info('api server start'); 19 | logger.warn('do not kill this'); 20 | 21 | const app = new Koa(); 22 | const baseRouter = new BaseRouter(); 23 | 24 | app.use(session({ 25 | key: 'jslight:session', 26 | store: new RedisStore(), 27 | maxAge: config.maxAge 28 | })); 29 | 30 | app.use(bodyParser()); 31 | app.use(Logger()); 32 | app.use(convert(cors())); 33 | 34 | // 中间件 35 | app.use(async (ctx, next) => { 36 | if (baseRouter.requireLogin(ctx)) { 37 | await next(); 38 | } 39 | }); 40 | 41 | app 42 | .use(router.routes()) 43 | .use(router.allowedMethods()); 44 | 45 | app.listen(config.port, () => { 46 | logger.info(`server is running port ${config.port}`); 47 | }); 48 | -------------------------------------------------------------------------------- /src/controller/BaseHandler.js: -------------------------------------------------------------------------------- 1 | import { logger } from '../../util/logger'; 2 | 3 | export default class BaseHandler { 4 | 5 | /** 6 | * [info description] 7 | * @method info 8 | * @param {[type]} msg [description] 9 | * @return {[type]} [description] 10 | */ 11 | info(msg) { 12 | logger.info(msg); 13 | } 14 | 15 | /** 16 | * [err description] 17 | * @method err 18 | * @param {[type]} msg [description] 19 | * @return {[type]} [description] 20 | */ 21 | err(msg) { 22 | logger.err(msg); 23 | } 24 | 25 | /** 26 | * [warn description] 27 | * @method warn 28 | * @param {[type]} msg [description] 29 | * @return {[type]} [description] 30 | */ 31 | warn(msg) { 32 | logger.warn(msg); 33 | } 34 | 35 | initServer(server) { 36 | return server(global.ch, global.ok); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/controller/UserHandler.js: -------------------------------------------------------------------------------- 1 | import BaseHandler from './BaseHandler'; 2 | 3 | /** 4 | * CartHandler create by burning0xb 5 | */ 6 | export default class UserHandler extends BaseHandler { 7 | constructor(server) { 8 | super(); 9 | this.server = server; 10 | } 11 | 12 | /** 13 | * [getUserList description] 14 | * @param {[type]} ctx [description] 15 | * @return {Promise} [description] 16 | */ 17 | async getUserList(ctx) { 18 | const body = ctx.request.body; 19 | const content = { 20 | class: 'user', 21 | func: 'getUserList', 22 | content: {} 23 | }; 24 | const server = this.initServer(this.server); 25 | const res1 = await server.send(content); 26 | // return res1; 27 | 28 | 29 | const content2 = { 30 | class: 'common', 31 | func: 'getOrderList', 32 | content: {} 33 | }; 34 | const server2 = this.initServer(this.server); 35 | const res2 = await server2.send(content2, 'order'); 36 | 37 | return { res1, res2 }; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/controller/index.js: -------------------------------------------------------------------------------- 1 | export { default as UserHandler } from './UserHandler'; 2 | -------------------------------------------------------------------------------- /src/routers/BaseRouter.js: -------------------------------------------------------------------------------- 1 | import requireLogin from '../../config/requireLogin.json'; 2 | 3 | export default class BaseRouter { 4 | 5 | constructor() { 6 | this.whiteList = []; 7 | requireLogin.map((key) => { 8 | this.whiteList.push(`/api${key}`); 9 | }); 10 | } 11 | 12 | requireLogin(ctx) { 13 | console.log(ctx.request.body); 14 | if (ctx.request.body.device === 'IOS') { 15 | return true; 16 | } 17 | if (!ctx.session.user && !ctx.session.admin_user && this.whiteList.includes(ctx.url)) { 18 | console.log(`${ctx.url} is require login`); 19 | ctx.body = { 20 | code: '10000', 21 | err: 'requireLogin' 22 | } 23 | return false; 24 | } 25 | return true; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/routers/index.js: -------------------------------------------------------------------------------- 1 | export { default as userRouter } from './userRouter'; 2 | -------------------------------------------------------------------------------- /src/routers/userRouter.js: -------------------------------------------------------------------------------- 1 | import { UserHandler } from '../controller'; 2 | 3 | function userRouter(router, upload) { 4 | 5 | const userHandler = new UserHandler(global.MQ); 6 | 7 | router.get('/user/getUserList', async (ctx, next) => { 8 | const user = await userHandler.getUserList(ctx); 9 | ctx.body = user; 10 | }) 11 | } 12 | 13 | export default userRouter; 14 | -------------------------------------------------------------------------------- /src/session/index.js: -------------------------------------------------------------------------------- 1 | export { default as RedisStore } from './store'; 2 | -------------------------------------------------------------------------------- /src/session/store.js: -------------------------------------------------------------------------------- 1 | import Redis from 'ioredis'; 2 | import { Store } from 'koa-session2'; 3 | import config from '../../config.json'; 4 | import { port, host, password } from '../../config/redis.json'; 5 | 6 | export default class RedisStore extends Store { 7 | constructor() { 8 | super(); 9 | this.redis = new Redis({ 10 | port, host, family: 4, password 11 | }); 12 | } 13 | 14 | async get(sid) { 15 | let data = await this.redis.get(`SESSION:${sid}`); 16 | return JSON.parse(data); 17 | } 18 | 19 | async set(session, opts) { 20 | if(!opts.sid) { 21 | opts.sid = this.getID(24); 22 | } 23 | await this.redis.set(`SESSION:${opts.sid}`, JSON.stringify(session), 'EX', config.redis_maxAge); 24 | return opts.sid; 25 | } 26 | 27 | async destroy(sid) { 28 | return await this.redis.del(`SESSION:${sid}`); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /util/common/Common.js: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | 3 | export default class Common { 4 | 5 | constructor() { 6 | 7 | } 8 | 9 | getCookie(cookie, key) { 10 | const cookies = cookie.split(';'); 11 | let cookie_value = ''; 12 | cookies.map((_key) => { 13 | if (_key.includes(key)) { 14 | cookie_value = _key.replace(`${key}=`, ''); 15 | } 16 | }); 17 | return cookie_value; 18 | } 19 | 20 | ramdomStr() { 21 | const str = Date.now(); 22 | const shasum = crypto.createHash('md5'); 23 | shasum.update(str.toString()); 24 | return shasum.digest('hex'); 25 | } 26 | 27 | json2xml(alias, body, CDATA=[]) { 28 | let str = `<${alias}>`; 29 | for (let key in body) { 30 | str += CDATA.includes(key) ? `<${key}>` : `<${key}>${body[key]}`; 31 | } 32 | str += ``; 33 | return str; 34 | } 35 | 36 | sleep(time) { 37 | return new Promise((resolve, reject) => { 38 | setTimeout(() => { 39 | resolve(); 40 | }, time * 1000); 41 | }); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /util/common/fetch.js: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch'; 2 | import { json2form, json2param } from '../common'; 3 | 4 | export function jsonPost(url, jsonBody, headers) { 5 | const _headers = { 6 | 'Accept': 'application/json', 7 | 'Content-Type': 'application/json' 8 | }; 9 | if (headers) { 10 | for (let prop in headers) { 11 | _headers[prop] = headers[prop]; 12 | } 13 | } 14 | return fetch(url, { 15 | method: 'POST', 16 | headers: _headers, 17 | body: JSON.stringify(jsonBody) 18 | }).then((res) => { 19 | return res.json(); 20 | }) 21 | } 22 | 23 | export function formPost(url, jsonBody) { 24 | const form = json2form(jsonBody); 25 | return fetch(url, { 26 | method: 'POST', 27 | headers: form.getHeaders(), 28 | body: form 29 | }).then((res) => { 30 | return res.json(); 31 | }).catch((err) => { 32 | console.log(err); 33 | }); 34 | } 35 | 36 | export function paramGet(url, jsonBody, headers) { 37 | if (jsonBody) { 38 | url += '?' + json2param(jsonBody); 39 | } 40 | const param = { 41 | method: 'GET' 42 | }; 43 | if (headers) { 44 | param.headers = headers; 45 | } 46 | return fetch(url, param).then((res) => { 47 | return res.json(); 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /util/common/index.js: -------------------------------------------------------------------------------- 1 | export { default as json2param } from './json2param' 2 | export { default as json2form } from './json2form'; 3 | export { default as Common } from './Common'; 4 | -------------------------------------------------------------------------------- /util/common/json2form.js: -------------------------------------------------------------------------------- 1 | import FormData from 'form-data'; 2 | 3 | function json2form(jsonBody) { 4 | const form = new FormData(); 5 | for (let prop in jsonBody) { 6 | if (prop !== '') { 7 | form.append(prop, jsonBody[prop]); 8 | } 9 | } 10 | return form; 11 | } 12 | 13 | export default json2form; 14 | -------------------------------------------------------------------------------- /util/common/json2param.js: -------------------------------------------------------------------------------- 1 | function json2param(json) { 2 | if (json.length = 0) { 3 | console.log('please put right jsonData and the size must > 0'); 4 | return ''; 5 | } 6 | let res = ''; 7 | for (let prop in json) { 8 | if (prop !== '') { 9 | res += `${prop}=${json[prop]}&` 10 | } 11 | } 12 | res = res.substr(0, res.length - 1); 13 | return res; 14 | } 15 | 16 | export default json2param; 17 | -------------------------------------------------------------------------------- /util/logger/index.js: -------------------------------------------------------------------------------- 1 | import log4js from 'log4js'; 2 | import log4js_config from './logConfig.json'; 3 | log4js.configure(log4js_config); 4 | 5 | 6 | export const logger = log4js.getLogger('log_file'); 7 | export const logger_date = log4js.getLogger('log_date'); 8 | 9 | logger.info('log4j started'); 10 | logger_date.info('log4j_date started'); 11 | -------------------------------------------------------------------------------- /util/logger/logConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "appenders": [{ 3 | "type": "console", 4 | "category": "console" 5 | }, 6 | { 7 | "category": "log_file", 8 | "type": "console", 9 | "filename": "./logs/log_file/file.log", 10 | "maxLogSize": 104800, 11 | "backups": 100 12 | }, 13 | { 14 | "category": "log_date", 15 | "type": "dateFile", 16 | "filename": "./logs/log_date/date", 17 | "alwaysIncludePattern": true, 18 | "pattern": "-yyyy-MM-dd-hh.log" 19 | } 20 | ], 21 | "replaceConsole": true, 22 | "levels": { 23 | "log_file": "ALL", 24 | "console": "ALL", 25 | "log_date": "ALL" 26 | } 27 | } 28 | --------------------------------------------------------------------------------