From 8f9c5e3f649407d32c3fb2f58eb9bd1bb58320cb Mon Sep 17 00:00:00 2001 From: zhl Date: Thu, 7 Jan 2021 12:53:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=BA=9B=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api.md | 18 +++++---- package-lock.json | 43 ++++++++++---------- package.json | 8 ++-- src/api.server.ts | 35 +++++++++++----- src/common/ZError.ts | 14 +++++++ src/controllers/AccountController.ts | 18 +++++++++ src/models/Account.ts | 35 ++++++++++++++++ src/plugins/apiauth.ts | 60 ++++++++++++++++++++++++++++ src/plugins/zReqParser.ts | 41 +++++++++++++++++++ tsconfig.json | 3 +- 10 files changed, 232 insertions(+), 43 deletions(-) create mode 100644 src/common/ZError.ts create mode 100644 src/controllers/AccountController.ts create mode 100644 src/models/Account.ts create mode 100644 src/plugins/apiauth.ts create mode 100644 src/plugins/zReqParser.ts diff --git a/docs/api.md b/docs/api.md index 1139425..e23bdf0 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,6 +1,8 @@ # 卡牌游戏接口说明 ## 一. 说明 +所有接口均需上传sessionid + 通用返回JSON结构, 接口Response的数据结构说明只包含data部分 ``` JSON { @@ -14,7 +16,7 @@ ## 二. 客户端接口列表 ### 1. 玩家资料 -1. Method: GET +1. Method: POST 2. URI: /api/:accountid/uinfo | 字段 | 说明 | @@ -28,7 +30,7 @@ ``` ### 2. 可用卡牌信息 -1. Method: GET +1. Method: POST 2. URI: /api/:accountid/cards | 字段 | 说明 | @@ -52,7 +54,7 @@ ``` ### 3. 可用英雄列表 -1. Method: GET +1. Method: POST 2. URI: /api/:accountid | 字段 | 说明 | @@ -80,7 +82,7 @@ ``` ### 4. 自定义卡组列表 -1. Method: GET +1. Method: POST 2. URI: /api/:accountid/card_group/:heroid | 字段 | 说明 | @@ -128,7 +130,7 @@ 根据errcode判断成功or失败 ### 6. 删除卡组 -1. Method: GET +1. Method: POST 2. URI: /api/:accountid/card_group/delete/:gid @@ -165,7 +167,7 @@ 根据errcode判断成功or失败 ### 8. 抽卡 -1. Method: GET +1. Method: POST 2. URI: /api/:accountid/card/draw/:count | 字段 | 说明 | @@ -186,7 +188,7 @@ ``` ### 9. 对战记录列表 -1. Method: GET +1. Method: POST 2. URI: /api/:accountid/records | 字段 | 说明 | @@ -221,7 +223,7 @@ ```json { exp: 1022, // 当前经验值 - exp_get: 100, // 本次获得的经验 + exp_POST: 100, // 本次获得的经验 level: 1 // 当前等级 } ``` diff --git a/package-lock.json b/package-lock.json index dbce424..31fbe2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", + "dev": true, "requires": { "@types/node": "*" } @@ -51,6 +52,7 @@ "version": "3.6.3", "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.3.tgz", "integrity": "sha512-6YNqGP1hk5bjUFaim+QoFFuI61WjHiHE1BNeB41TA00Xd2K7zG4lcWyLLq/XtIp36uMavvS5hoAUJ+1u/GcX2Q==", + "dev": true, "requires": { "@types/bson": "*", "@types/node": "*" @@ -590,9 +592,9 @@ } }, "kareem": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", - "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", + "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" }, "light-my-request": { "version": "4.4.1", @@ -700,11 +702,11 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "mongodb": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", - "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.1.tgz", + "integrity": "sha512-uH76Zzr5wPptnjEKJRQnwTsomtFOU/kQEU8a9hKHr2M7y9qVk7Q4Pkv0EQVp88742z9+RwvsdTw6dRjDZCNu1g==", "requires": { - "bl": "^2.2.1", + "bl": "^2.2.0", "bson": "^1.1.4", "denque": "^1.4.1", "require_optional": "^1.0.1", @@ -720,17 +722,16 @@ } }, "mongoose": { - "version": "5.11.10", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.11.10.tgz", - "integrity": "sha512-daE2L6VW7WNywv7tL2KUkBViWvODbzr50Of1kJpIbzW3w3N5/TYcgSmhCsEDWfYGQXbun2rdd7+sOdsEC8zQSQ==", + "version": "5.10.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.3.tgz", + "integrity": "sha512-FLemltuzcsCHlFpEZ3bYOiNhJfHful+GoS+3uRgdEWGlY0HKfOjm9xsISM/tql8vRvhjr7qveuRfoBBGO3xNtw==", "requires": { - "@types/mongodb": "^3.5.27", "bson": "^1.1.4", - "kareem": "2.3.2", - "mongodb": "3.6.3", + "kareem": "2.3.1", + "mongodb": "3.6.1", "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.8.3", - "mquery": "3.2.3", + "mpath": "0.7.0", + "mquery": "3.2.2", "ms": "2.1.2", "regexp-clone": "1.0.0", "safe-buffer": "5.2.1", @@ -756,14 +757,14 @@ "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" }, "mpath": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", - "integrity": "sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz", + "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==" }, "mquery": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.3.tgz", - "integrity": "sha512-cIfbP4TyMYX+SkaQ2MntD+F2XbqaBHUYWk3j+kqdDztPWok3tgyssOZxMHMtzbV1w9DaSlvEea0Iocuro41A4g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", + "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", "requires": { "bluebird": "3.5.1", "debug": "3.1.0", diff --git a/package.json b/package.json index bf4878f..7079071 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "dev:api": "DEBUG=colyseus:*,jc:* node --require ts-node/register --inspect src/api.ts" }, "author": "", "license": "ISC", @@ -22,14 +22,14 @@ "fastify-jwt": "^2.2.0", "fastify-plugin": "^3.0.0", "mime-types": "^2.1.28", - "mongoose": "^5.11.10", + "mongoose": "5.10.3", "mongoose-findorcreate": "^3.0.0", "tracer": "^1.1.4", "urlencode": "^1.1.0" }, "devDependencies": { - "@types/debug": "^4.1.5", - "@types/mongoose": "^5.10.3", + "@types/debug": "4.1.5", + "@types/mongoose": "5.10.3", "@types/node": "^14.14.20", "ts-node": "^9.1.1", "typescript": "^4.1.3" diff --git a/src/api.server.ts b/src/api.server.ts index c91df2b..7f8253c 100644 --- a/src/api.server.ts +++ b/src/api.server.ts @@ -1,4 +1,9 @@ -import fastify, {FastifyError, FastifyInstance} from 'fastify' +import fastify, { + FastifyError, + FastifyInstance, + FastifyReply, + FastifyRequest +} from 'fastify' import helmet from 'fastify-helmet' import { Server, IncomingMessage, ServerResponse } from 'http' import {RouterMap} from './decorators/router'; @@ -7,7 +12,6 @@ import logger from './logger/logger'; import {Config} from "./cfg/Config"; const zReqParserPlugin = require('./plugins/zReqParser'); - const apiAuthPlugin = require('./plugins/apiauth'); const fs = require('fs'); @@ -25,10 +29,10 @@ export class ApiServer { private registerPlugins() { this.server.register(require('fastify-formbody')); this.server.register(zReqParserPlugin); - // @ts-ignore + this.server.register(apiAuthPlugin); this.server.register( helmet, - { hidePoweredBy: { setTo: 'PHP 4.2.0' } } + { hidePoweredBy: false } ) this.server.register(require('fastify-cors'), {}); @@ -57,7 +61,7 @@ export class ApiServer { */ initControllers() { logger.info('Bootstrap controllers...'); - const controllers = join(__dirname, 'api/controllers'); + const controllers = join(__dirname, 'controllers'); fs.readdirSync(controllers) .filter((file: string) => ~file.search(/^[^.].*\.(ts|js)$/)) .forEach((file: any) => { @@ -81,10 +85,10 @@ export class ApiServer { } } private setErrHandler() { - this.server.setNotFoundHandler(function(request: any, reply: { send: (arg0: { code: number; msg: string; }) => void; }){ - reply.send({code: 404, msg: 'page not found'}) + this.server.setNotFoundHandler(function(request: any, reply: { send: (arg0: { errcode: number; errmsg: string; }) => void; }){ + reply.send({errcode: 404, errmsg: 'page not found'}) }); - this.server.setErrorHandler(function (error: FastifyError, request: any, reply: { send: (arg0: { code: any; msg: any; }) => void; }) { + this.server.setErrorHandler(function (error: FastifyError, request: any, reply: { send: (arg0: { errcode: any; errmsg: any; }) => void; }) { let statusCode = error && error.statusCode || 100; if (statusCode >= 500) { logger.error(error) @@ -93,7 +97,19 @@ export class ApiServer { } else { logger.error(error) } - reply.send({code: statusCode, msg: error ? error.message : 'unknown error'}) + reply.send({errcode: statusCode, errmsg: error ? error.message : 'unknown error'}) + }) + } + private setFormatSend() { + this.server.addHook('preSerialization', async (request: FastifyRequest, reply: FastifyReply, payload) => { + // @ts-ignore + if (!payload.errcode) { + payload = { + errcode: 0, + data: payload + } + } + return payload }) } public async start() { @@ -103,6 +119,7 @@ export class ApiServer { self.initControllers(); self.registerRouter(); self.setErrHandler(); + self.setFormatSend(); this.server.listen({port: config.port}, (err: any, address: any) => { if (err) { logger.log(err) diff --git a/src/common/ZError.ts b/src/common/ZError.ts new file mode 100644 index 0000000..8fb12ee --- /dev/null +++ b/src/common/ZError.ts @@ -0,0 +1,14 @@ +import {FastifyError} from "fastify"; + +export class ZError implements FastifyError { + code: string; + statusCode?: number; + message: string; + name: string; + + + constructor(statusCode: number, message: string) { + this.statusCode = statusCode; + this.message = message; + } +} diff --git a/src/controllers/AccountController.ts b/src/controllers/AccountController.ts new file mode 100644 index 0000000..e64785b --- /dev/null +++ b/src/controllers/AccountController.ts @@ -0,0 +1,18 @@ +import BaseController from "../common/base.controller"; +import {role, router} from "../decorators/router"; +import {Account} from "../models/Account"; +import {ZError} from "../common/ZError"; + +export default class AccountController extends BaseController { + @role('anon') + @router('post /api/:accountid/uinfo') + async info(req: any, res: any) { + let {accountid} = req.params; + if (true) { + throw new ZError(101, 'no acc'); + } + let account = (await Account.findOrCreate({accountid})).doc; + + return account.toJson(); + } +} diff --git a/src/models/Account.ts b/src/models/Account.ts new file mode 100644 index 0000000..8a67796 --- /dev/null +++ b/src/models/Account.ts @@ -0,0 +1,35 @@ +import {prop, getModelForClass, modelOptions, index, plugin} from '@typegoose/typegoose'; +import {dbconn} from '../decorators/dbconn'; +// @ts-ignore +import findOrCreate from 'mongoose-findorcreate'; +import {Base, FindOrCreate, TimeStamps} from "@typegoose/typegoose/lib/defaultClasses"; + +interface AccountClass extends Base, TimeStamps {} +@dbconn() +@index({ accountid: 1}, { unique: true }) +@plugin(findOrCreate) +@modelOptions({schemaOptions: {collection: "account", timestamps: true}}) + +class AccountClass extends FindOrCreate{ + @prop() + public accountid: string; + @prop() + public city?: string; + @prop({default: false}) + public locked: boolean; + @prop() + public lockedTime?: Date; + @prop() + public comment?: string; + @prop() + public lastLogin?: Date; + + public toJson() { + return { + accountid: this.accountid, + } + } +} + +// @ts-ignore +export const Account = getModelForClass(AccountClass, {existingConnection: AccountClass['db']}); diff --git a/src/plugins/apiauth.ts b/src/plugins/apiauth.ts new file mode 100644 index 0000000..7118bd6 --- /dev/null +++ b/src/plugins/apiauth.ts @@ -0,0 +1,60 @@ +import { + FastifyInstance, + FastifyPluginAsync, FastifyReply, + FastifyRequest, +} from 'fastify'; +import fastifyPlugin from 'fastify-plugin'; +import {Account} from '../models/Account'; + +declare module 'fastify' { + interface FastifyInstance { + apiAuth: (request: FastifyRequest, reply: FastifyReply) => {}; + } + + interface FastifyRequest { + session?: {[key: string]: any}; + roles?: any, + user?: any + } +} + +const apiAuthPlugin: FastifyPluginAsync = async function( + fastify: FastifyInstance, + options?: any +) { + + // 只有路由配置的role为anon才不需要过滤 + fastify.decorate("apiAuth", async function(request: FastifyRequest, reply: FastifyReply) { + if (!request.roles || request.roles.indexOf('anon') == -1) { + try { + // @ts-ignore + let { accountid, sessionid } = request.params; + //TODO: 增加sessionid的校验 + // if (!accountid || !sessionid) { + // return reply.send({code: 11, msg: 'need accountid and sessionid'}); + // } + if (!accountid) { + return reply.send({code: 11, msg: 'need accountid and sessionid'}); + } + // const data = this.jwt.verify(request.token); + // if (!data || !data.id) { + // return reply.send({code: 10, msg: 'need login'}); + // } + // let account = await Account.findById(data.id); + // if (!account) { + // return reply.send({code: 10, msg: 'need login'}); + // } + // request.user = account; + + } catch (err) { + return reply.send({code: 401, msg: 'need auth'}) + } + } + + }) +}; + +export = fastifyPlugin(apiAuthPlugin, { + fastify: '>=3.0.0', + name: 'apiauth', +}); diff --git a/src/plugins/zReqParser.ts b/src/plugins/zReqParser.ts new file mode 100644 index 0000000..cfd5463 --- /dev/null +++ b/src/plugins/zReqParser.ts @@ -0,0 +1,41 @@ +import { + FastifyInstance, + FastifyPluginAsync, + FastifyReply, + FastifyRequest +} from "fastify"; +import fastifyPlugin from "fastify-plugin"; + +/** + * 将post 和 get 的参数统一到 req.params + */ +declare module 'fastify' { + interface FastifyInstance { + zReqParser: (request: FastifyRequest, reply: FastifyReply) => {}; + } + + interface FastifyRequest { + session?: {[key: string]: any}; + } +} +const zReqParserPlugin: FastifyPluginAsync = async function( + fastify: FastifyInstance, + options?: any, +) { + fastify.addHook('preValidation', async (request: FastifyRequest, reply: FastifyReply) => { + let params = request.params || {}; + if (request.query) { + Object.assign(params, request.query); + } + if (request.body) { + Object.assign(params, request.body); + } + request.params = params; + }) + return; +} + +export = fastifyPlugin(zReqParserPlugin, { + fastify: '>=3.0.0', + name: 'zReqParser', +}); diff --git a/tsconfig.json b/tsconfig.json index 1ac48f9..07f851e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,7 @@ "strictNullChecks": false, "esModuleInterop": true, "moduleResolution": "node", - "experimentalDecorators": true + "experimentalDecorators": true, + "emitDecoratorMetadata": true, } }