diff --git a/.env.development b/.env.development index 7eda8d1..edc3b75 100644 --- a/.env.development +++ b/.env.development @@ -1,7 +1,8 @@ -API_PORT=3006 -API_HOST=127.0.0.1 +API_PORT=3007 +API_HOST=10.0.1.3 API_TOKEN_SECRET=sdf(**&*&xx2213 API_TOKEN_EXPIRESIN=1d +GOOGLE_OAUTH_CLIENT="165555585193-glmtnb94s3kkq906hal72ppiuoqpjjc5.apps.googleusercontent.com" DB_MAIN=mongodb://localhost/wallet-development \ No newline at end of file diff --git a/package.json b/package.json index f8d6c36..904096f 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,10 @@ "dotenv": "^16.0.3", "fastify": "^4.8.1", "fastify-plugin": "^4.2.1", + "google-auth-library": "^8.5.2", "mongoose": "^6.6.5", "mongoose-findorcreate": "^3.0.0", + "nanoid": "^3.1.23", "tracer": "^1.1.6" }, "devDependencies": { diff --git a/src/api.server.ts b/src/api.server.ts index 7c23a87..9e3dab0 100644 --- a/src/api.server.ts +++ b/src/api.server.ts @@ -1,73 +1,66 @@ -import fastify, { - FastifyError, - FastifyInstance, - FastifyReply, - FastifyRequest, -} from "fastify"; -import helmet from "@fastify/helmet"; -import { IncomingMessage, Server, ServerResponse } from "http"; -import { RouterMap } from "decorators/router"; -import { mongoose } from "@typegoose/typegoose"; -import logger from "logger/logger"; -import config from "config/config"; +import fastify, { FastifyError, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify' +import helmet from '@fastify/helmet' +import { IncomingMessage, Server, ServerResponse } from 'http' +import { RouterMap } from 'decorators/router' +import { mongoose } from '@typegoose/typegoose' +import logger from 'logger/logger' +import config from 'config/config' +import { ConnectOptions } from 'mongoose' -const zReqParserPlugin = require("plugins/zReqParser"); +const zReqParserPlugin = require('plugins/zReqParser') -const zTokenParserPlugin = require("plugins/zTokenParser"); +const zTokenParserPlugin = require('plugins/zTokenParser') -const apiAuthPlugin = require("plugins/apiauth"); +const apiAuthPlugin = require('plugins/apiauth') -const fs = require("fs"); -const join = require("path").join; +const fs = require('fs') +const join = require('path').join -require("./common/Extend"); +require('./common/Extend') export class ApiServer { - server: FastifyInstance; + server: FastifyInstance public constructor() { - this.server = fastify({ logger: true, trustProxy: true }); - this.registerPlugins(); + this.server = fastify({ logger: true, trustProxy: true }) + this.registerPlugins() } private registerPlugins() { - this.server.register(require("@fastify/formbody")); - this.server.register(zReqParserPlugin); - this.server.register(helmet, { hidePoweredBy: false }); - this.server.register(zTokenParserPlugin); + this.server.register(require('@fastify/formbody')) + this.server.register(zReqParserPlugin) + this.server.register(helmet, { hidePoweredBy: false }) + this.server.register(zTokenParserPlugin) this.server.register(apiAuthPlugin, { secret: config.api.token_secret, expiresIn: config.api.token_expiresIn, - }); + }) - this.server.register(require("@fastify/cors"), {}); + this.server.register(require('@fastify/cors'), {}) } private registerRouter() { - logger.log("register api routers"); - let self = this; + logger.log('register api routers') + let self = this for (let [controller, config] of RouterMap.decoratedRouters) { for (let data of config.data) { logger.info( - "add router", - data.method || "all", + 'add router', + data.method || 'all', data.path, - `${data.target.constructor.name}.${controller.name}()` - ); + `${data.target.constructor.name}.${controller.name}()`, + ) // @ts-ignore - self.server[data.method || "all"]( + self.server[data.method || 'all']( data.path, { - preValidation: async function ( - request: FastifyRequest, - reply: FastifyReply - ) { - request.roles = config.roles; - await this.apiAuth(request, reply); + preValidation: async function (request: FastifyRequest, reply: FastifyReply) { + request.roles = config.roles + await this.apiAuth(request, reply) }, }, - controller - ); + controller, + ) } } } @@ -75,58 +68,53 @@ export class ApiServer { * 加载所有的controller */ initControllers() { - logger.info("Bootstrap controllers..."); - const controllers = join(__dirname, "./controllers"); + logger.info('Bootstrap controllers...') + const controllers = join(__dirname, './controllers') fs.readdirSync(controllers) .filter((file: string) => ~file.search(/^[^.].*\.(ts|js)$/)) .forEach((file: any) => { // logger.log(file); - return require(join(controllers, file)); - }); + return require(join(controllers, file)) + }) } async connectDB() { - const options = { - useNewUrlParser: true, - poolSize: 5, + const options: ConnectOptions = { + minPoolSize: 5, + maxPoolSize: 10, keepAlive: true, keepAliveInitialDelay: 300000, - useUnifiedTopology: true, - }; - const uri = config.db_main; - logger.info(`connect to ${uri} ...`); + } + const uri = config.db_main + logger.info(`connect to ${uri} ...`) try { // await mongoose.createConnection(uri, options) - await mongoose.connect(uri, options); - logger.log("DB Connected"); + await mongoose.connect(uri, options) + logger.log('DB Connected') } catch (err) { - logger.log(`DB Connection Error: ${err.message}`); + logger.log(`DB Connection Error: ${err.message}`) } } private setErrHandler() { this.server.setNotFoundHandler(function ( request: any, - reply: { send: (arg0: { errcode: number; errmsg: string }) => void } + reply: { send: (arg0: { errcode: number; errmsg: string }) => void }, ) { - reply.send({ errcode: 404, errmsg: "page not found" }); - }); - this.server.setErrorHandler(function ( - error: FastifyError, - request: FastifyRequest, - reply: FastifyReply - ) { - let statusCode = (error && error.statusCode) || 100; + reply.send({ errcode: 404, errmsg: 'page not found' }) + }) + this.server.setErrorHandler(function (error: FastifyError, request: FastifyRequest, reply: FastifyReply) { + let statusCode = (error && error.statusCode) || 100 if (statusCode >= 500) { - logger.error(error); + logger.error(error) } else if (statusCode >= 400) { - logger.info(error); + logger.info(error) } else { - logger.error(error); + logger.error(error) } reply.code(200).send({ errcode: statusCode, - errmsg: error ? error.message : "unknown error", - }); - }); + errmsg: error ? error.message : 'unknown error', + }) + }) } /** * 格式化接口返回数据, 统一封装成如下格式 @@ -138,40 +126,34 @@ export class ApiServer { * @private */ private setFormatSend() { - this.server.addHook( - "preSerialization", - async (request: FastifyRequest, reply: FastifyReply, payload) => { - reply.header("X-Powered-By", "PHP/5.4.16"); - // @ts-ignore - if (!payload.errcode) { - payload = { - errcode: 0, - data: payload, - }; + this.server.addHook('preSerialization', async (request: FastifyRequest, reply: FastifyReply, payload) => { + reply.header('X-Powered-By', 'PHP/5.4.16') + // @ts-ignore + if (!payload.errcode) { + payload = { + errcode: 0, + data: payload, } - return payload; } - ); + return payload + }) } public async start() { - let self = this; + let self = this return new Promise(async (resolve, reject) => { - await self.connectDB(); - self.initControllers(); - self.registerRouter(); - self.setErrHandler(); - self.setFormatSend(); + await self.connectDB() + self.initControllers() + self.registerRouter() + self.setErrHandler() + self.setFormatSend() // new Schedule().start(); - this.server.listen( - { port: config.api.port }, - (err: any, address: any) => { - if (err) { - logger.log(err); - process.exit(0); - } - resolve && resolve(address); + this.server.listen({ port: config.api.port, host: config.api.host }, (err: any, address: any) => { + if (err) { + logger.log(err) + process.exit(0) } - ); - }); + resolve && resolve(address) + }) + }) } } diff --git a/src/controllers/main.controllers.ts b/src/controllers/main.controllers.ts index 8bb445c..d478431 100644 --- a/src/controllers/main.controllers.ts +++ b/src/controllers/main.controllers.ts @@ -1,11 +1,97 @@ -import BaseController from "common/base.controller"; -import { role, router } from "decorators/router"; +import BaseController from 'common/base.controller' +import { ZError } from 'common/ZError' +import { role, router } from 'decorators/router' + +import { OAuth2Client } from 'google-auth-library' +import { Account, PlatEnum } from 'modules/Account' +import { Wallet } from 'modules/Wallet' +import { WalletExt } from 'modules/WalletExt' +import { customAlphabet } from 'nanoid' + +const nanoid = customAlphabet('1234567890abcdef', 10) +const GOOGLE_OAUTH_ISS = 'https://accounts.google.com' +const GOOGLE_OAUTH_ISS1 = 'accounts.google.com' class MainController extends BaseController { - @role("anon") - @router("post /client/login") + @role('anon') + @router('post /client/login') async clientLogin(req, res) { - const { code, token } = req.params; - return {}; + const { code, token } = req.params + return {} + } + + @role('anon') + @router('post /wallet/login/google') + async checkGoogleJwt(req, res) { + const { token } = req.params + const CLIENT_ID = process.env.GOOGLE_OAUTH_CLIENT + const client = new OAuth2Client(CLIENT_ID) + const ticket = await client.verifyIdToken({ + idToken: token, + audience: CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend + // Or, if multiple clients access the backend: + //[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3] + }) + const payload = ticket.getPayload() + if (!(payload.iss === GOOGLE_OAUTH_ISS || payload.iss === GOOGLE_OAUTH_ISS1)) { + throw new ZError(10, 'id token error') + } + if (payload.aud !== CLIENT_ID) { + throw new ZError(11, 'client id mismatch') + } + const openId = payload.sub + let data: any = { + email: payload.email, + emailVerified: payload.email_verified, + } + if (payload.locale) data.locale = payload.locale + if (payload.name) data.nickname = payload.name + if (payload.picture) data.avatar = payload.picture + let user = await Account.insertOrUpdate({ plat: PlatEnum.GOOGLE, openId }, data) + const ztoken = await res.jwtSign({ id: user.id }) + return { token: ztoken } + } + + @router('get /wallet/info') + async getWalletInfo(req, res) { + let user = req.user + let record = await Wallet.insertOrUpdate({ account: user.id }, {}) + let data: any = { oid: user.id } + if (record.nweRecord) { + record.salt = nanoid() + record.is = nanoid(12) + record.nweRecord = false + await record.save() + } + Object.assign(data, record.toJson()) + return data + } + + @router('post /wallet/info') + async uploadWalletInfo(req, res) { + let user = req.user + let { key } = req.params + if (!key) { + throw new ZError(10, 'no data to save') + } + let record = await Wallet.insertOrUpdate({ account: user.id }, {}) + record.key = key + await record.save() + return {} + } + + @router('get /wallet/collection') + async getUserCollection(req, res) { + let user = req.user + let { data } = req.params + await WalletExt.insertOrUpdate({ account: user.id }, { data }) + return {} + } + + @router('post /wallet/collection') + async uploadUserCollection(req, res) { + let user = req.user + let record = await WalletExt.insertOrUpdate({ account: user.id }, {}) + return record.data } } diff --git a/src/decorators/dbconn.ts b/src/decorators/dbconn.ts index a170f6c..9bdf96b 100644 --- a/src/decorators/dbconn.ts +++ b/src/decorators/dbconn.ts @@ -8,11 +8,6 @@ export function dbconn(name?: string) { return target => { name = name || 'main' const url = config['db_' + name] - target['db'] = mongoose.createConnection(url, { - useNewUrlParser: true, - useCreateIndex: true, - useFindAndModify: false, - useUnifiedTopology: true, - }) + target['db'] = mongoose.createConnection(url, {}) } } diff --git a/src/modules/Account.ts b/src/modules/Account.ts new file mode 100644 index 0000000..734a127 --- /dev/null +++ b/src/modules/Account.ts @@ -0,0 +1,41 @@ +import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose' +import { dbconn } from 'decorators/dbconn' +import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses' +import { BaseModule } from './Base' + +export enum PlatEnum { + GOOGLE = 0, +} + +interface AccountClass extends Base, TimeStamps {} +@dbconn() +@index({ plat: 1, openId: 1 }, { unique: true }) +@modelOptions({ schemaOptions: { collection: 'account', timestamps: true } }) +class AccountClass extends BaseModule { + @prop({ enum: PlatEnum, default: PlatEnum.GOOGLE }) + public plat!: PlatEnum + @prop({ required: true }) + public openId!: string + @prop() + public nickname?: string + @prop() + public avatar?: string + @prop() + public email?: string + @prop({ required: true, default: false }) + public emailVerified: boolean + @prop({ default: 0 }) + public sex?: string + @prop() + public locale?: string + @prop({ default: false }) + public locked: boolean + @prop() + public lockedTime?: Date + @prop() + public comment?: string + @prop() + public lastLogin?: Date +} + +export const Account = getModelForClass(AccountClass, { existingConnection: AccountClass.db }) diff --git a/src/modules/Wallet.ts b/src/modules/Wallet.ts new file mode 100644 index 0000000..17c837e --- /dev/null +++ b/src/modules/Wallet.ts @@ -0,0 +1,43 @@ +import { getModelForClass, index, modelOptions, mongoose, prop, Severity } from '@typegoose/typegoose' +import { dbconn } from 'decorators/dbconn' +import { BaseModule } from './Base' + +@dbconn() +@index({ account: 1 }, { unique: true }) +@modelOptions({ + schemaOptions: { collection: 'wallet', timestamps: true }, +}) +class WalletClass extends BaseModule { + @prop({ required: true }) + public account!: string + + /** + * 钱包的master key + */ + @prop() + public key: string + + /** + * 钱包客户端存储的密码 + */ + @prop() + public is: string + /** + * 用于客户端生成密钥时的加盐 + */ + @prop() + public salt: string + + @prop({ required: true, default: true }) + public nweRecord: boolean + + public toJson() { + return { + key: this.key, + is: this.is, + salt: this.salt, + } + } +} + +export const Wallet = getModelForClass(WalletClass, { existingConnection: WalletClass.db }) diff --git a/src/modules/WalletExt.ts b/src/modules/WalletExt.ts new file mode 100644 index 0000000..b1d9dcd --- /dev/null +++ b/src/modules/WalletExt.ts @@ -0,0 +1,22 @@ +import { getModelForClass, index, modelOptions, mongoose, prop, Severity } from '@typegoose/typegoose' +import { dbconn } from 'decorators/dbconn' +import { BaseModule } from './Base' + +@dbconn() +@index({ account: 1 }, { unique: true }) +@modelOptions({ + schemaOptions: { collection: 'wallet_ext', timestamps: true }, + options: { allowMixed: Severity.ALLOW }, +}) +class WalletExtClass extends BaseModule { + @prop({ required: true }) + public account!: string + + @prop({ type: mongoose.Schema.Types.Mixed }) + public data: any + + @prop({ required: true, default: true }) + public nweRecord: boolean +} + +export const WalletExt = getModelForClass(WalletExtClass, { existingConnection: WalletExtClass.db }) diff --git a/src/plugins/apiauth.ts b/src/plugins/apiauth.ts index abdc01a..50bf6da 100644 --- a/src/plugins/apiauth.ts +++ b/src/plugins/apiauth.ts @@ -1,55 +1,50 @@ -import { FastifyPluginAsync, FastifyReply, FastifyRequest } from "fastify"; -import fastifyPlugin from "fastify-plugin"; +import { FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify' +import fastifyPlugin from 'fastify-plugin' +import { Account } from 'modules/Account' -declare module "fastify" { +declare module 'fastify' { interface FastifyRequest { - roles?: string[]; - user?: any; - token?: string; + roles?: string[] + user?: any + token?: string } interface FastifyInstance { - apiAuth: (request: FastifyRequest, reply: FastifyReply) => {}; + apiAuth: (request: FastifyRequest, reply: FastifyReply) => {} } } export interface ApiAuthOptions { - secret: string; - expiresIn: string; + secret: string + expiresIn: string } -const apiAuthPlugin: FastifyPluginAsync = async function ( - fastify, - opts -) { - fastify.register(require("@fastify/jwt"), { +const apiAuthPlugin: FastifyPluginAsync = async function (fastify, opts) { + fastify.register(require('@fastify/jwt'), { secret: opts.secret, sign: { expiresIn: opts.expiresIn }, - }); + }) // 只有路由配置的role为anon才不需要过滤 - fastify.decorate( - "apiAuth", - async function (request: FastifyRequest, reply: FastifyReply) { - if (!request.roles || request.roles.indexOf("anon") == -1) { - try { - if (!request.token) { - return reply.send({ code: 11, msg: "need login" }); - } - //@ts-ignore - 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" }); + fastify.decorate('apiAuth', async function (request: FastifyRequest, reply: FastifyReply) { + if (!request.roles || request.roles.indexOf('anon') == -1) { + try { + if (!request.token) { + return reply.send({ code: 11, msg: 'need login' }) } + //@ts-ignore + 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 default fastifyPlugin(apiAuthPlugin, "4.x"); +export default fastifyPlugin(apiAuthPlugin, '4.x') diff --git a/yarn.lock b/yarn.lock index a065259..b595e54 100644 --- a/yarn.lock +++ b/yarn.lock @@ -148,40 +148,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@redis/bloom@1.0.2": - version "1.0.2" - resolved "https://registry.npmmirror.com/@redis/bloom/-/bloom-1.0.2.tgz#42b82ec399a92db05e29fffcdfd9235a5fc15cdf" - integrity sha512-EBw7Ag1hPgFzdznK2PBblc1kdlj5B5Cw3XwI9/oG7tSn85/HKy3X9xHy/8tm/eNXJYHLXHJL/pkwBpFMVVefkw== - -"@redis/client@1.3.0": - version "1.3.0" - resolved "https://registry.npmmirror.com/@redis/client/-/client-1.3.0.tgz#c62ccd707f16370a2dc2f9e158a28b7da049fa77" - integrity sha512-XCFV60nloXAefDsPnYMjHGtvbtHR8fV5Om8cQ0JYqTNbWcQo/4AryzJ2luRj4blveWazRK/j40gES8M7Cp6cfQ== - dependencies: - cluster-key-slot "1.1.0" - generic-pool "3.8.2" - yallist "4.0.0" - -"@redis/graph@1.0.1": - version "1.0.1" - resolved "https://registry.npmmirror.com/@redis/graph/-/graph-1.0.1.tgz#eabc58ba99cd70d0c907169c02b55497e4ec8a99" - integrity sha512-oDE4myMCJOCVKYMygEMWuriBgqlS5FqdWerikMoJxzmmTUErnTRRgmIDa2VcgytACZMFqpAOWDzops4DOlnkfQ== - -"@redis/json@1.0.4": - version "1.0.4" - resolved "https://registry.npmmirror.com/@redis/json/-/json-1.0.4.tgz#f372b5f93324e6ffb7f16aadcbcb4e5c3d39bda1" - integrity sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw== - -"@redis/search@1.1.0": - version "1.1.0" - resolved "https://registry.npmmirror.com/@redis/search/-/search-1.1.0.tgz#7abb18d431f27ceafe6bcb4dd83a3fa67e9ab4df" - integrity sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ== - -"@redis/time-series@1.0.3": - version "1.0.3" - resolved "https://registry.npmmirror.com/@redis/time-series/-/time-series-1.0.3.tgz#4cfca8e564228c0bddcdf4418cba60c20b224ac4" - integrity sha512-OFp0q4SGrTH0Mruf6oFsHGea58u8vS/iI5+NpYdicaM+7BgqBZH8FFvNZ8rYYLrUO/QRqMq72NpXmxLVNcdmjA== - "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -367,6 +333,13 @@ acorn@^8.4.1, acorn@^8.8.0: resolved "https://registry.npmmirror.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +agent-base@6: + version "6.0.2" + resolved "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" @@ -434,6 +407,11 @@ array-union@^2.1.0: resolved "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +arrify@^2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + asn1.js@^5.3.0: version "5.4.1" resolved "https://registry.npmmirror.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" @@ -477,11 +455,16 @@ balanced-match@^1.0.0: resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1: +base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bignumber.js@^9.0.0: + version "9.1.0" + resolved "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62" + integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -514,6 +497,11 @@ bson@^4.6.5, bson@^4.7.0: dependencies: buffer "^5.6.0" +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -563,11 +551,6 @@ chokidar@^3.5.1: optionalDependencies: fsevents "~2.3.2" -cluster-key-slot@1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" - integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== - color-convert@^2.0.1: version "2.0.1" resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -621,7 +604,7 @@ dateformat@4.5.1: resolved "https://registry.npmmirror.com/dateformat/-/dateformat-4.5.1.tgz#c20e7a9ca77d147906b6dc2261a8be0a5bd2173c" integrity sha512-OD0TZ+B7yP7ZgpJf5K2DIbj3FZvFvxgFUuaqA/V5zTjAtAAXZ1E8bktHxmAGs4x5b7PflqA9LeQ84Og7wYtF7Q== -debug@4.x, debug@^4.0.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@4.x, debug@^4.0.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -674,7 +657,7 @@ dynamic-dedupe@^0.3.0: dependencies: xtend "^4.0.0" -ecdsa-sig-formatter@^1.0.11: +ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: version "1.0.11" resolved "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== @@ -823,6 +806,11 @@ events@^3.3.0: resolved "https://registry.npmmirror.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +extend@^3.0.2: + version "3.0.2" + resolved "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + fast-decode-uri-component@^1.0.1: version "1.0.1" resolved "https://registry.npmmirror.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" @@ -892,6 +880,11 @@ fast-redact@^3.1.1: resolved "https://registry.npmmirror.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== +fast-text-encoding@^1.0.0: + version "1.0.6" + resolved "https://registry.npmmirror.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" + integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== + fast-uri@^2.0.0, fast-uri@^2.1.0: version "2.1.0" resolved "https://registry.npmmirror.com/fast-uri/-/fast-uri-2.1.0.tgz#9279432d6b53675c90116b947ed2bbba582d6fb5" @@ -1030,10 +1023,23 @@ function-bind@^1.1.1: resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -generic-pool@3.8.2: - version "3.8.2" - resolved "https://registry.npmmirror.com/generic-pool/-/generic-pool-3.8.2.tgz#aab4f280adb522fdfbdc5e5b64d718d3683f04e9" - integrity sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg== +gaxios@^5.0.0, gaxios@^5.0.1: + version "5.0.2" + resolved "https://registry.npmmirror.com/gaxios/-/gaxios-5.0.2.tgz#ca3a40e851c728d31d7001c2357062d46bf966d1" + integrity sha512-TjtV2AJOZoMQqRYoy5eM8cCQogYwazWNYLQ72QB0kwa6vHHruYkGmhhyrlzbmgNHK1dNnuP2WSH81urfzyN2Og== + dependencies: + extend "^3.0.2" + https-proxy-agent "^5.0.0" + is-stream "^2.0.0" + node-fetch "^2.6.7" + +gcp-metadata@^5.0.0: + version "5.0.1" + resolved "https://registry.npmmirror.com/gcp-metadata/-/gcp-metadata-5.0.1.tgz#8d1e785ee7fad554bc2a80c1f930c9a9518d2b00" + integrity sha512-jiRJ+Fk7e8FH68Z6TLaqwea307OktJpDjmYnU7/li6ziwvVvU2RlrCyQo5vkdeP94chm0kcSCOOszvmuaioq3g== + dependencies: + gaxios "^5.0.0" + json-bigint "^1.0.0" glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" @@ -1080,11 +1086,42 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +google-auth-library@^8.5.2: + version "8.5.2" + resolved "https://registry.npmmirror.com/google-auth-library/-/google-auth-library-8.5.2.tgz#bcdced8f7b475b80bf0e9c109c7c7e930866947b" + integrity sha512-FPfOSaI8n2TVXFHTP8/vAVFCXhyALj7w9/Rgefux3oeKZ/nQDNmfNTJ+lIKcoYT1cKkvMllp1Eood7Y5L+TP+A== + dependencies: + arrify "^2.0.0" + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + fast-text-encoding "^1.0.0" + gaxios "^5.0.0" + gcp-metadata "^5.0.0" + gtoken "^6.1.0" + jws "^4.0.0" + lru-cache "^6.0.0" + +google-p12-pem@^4.0.0: + version "4.0.1" + resolved "https://registry.npmmirror.com/google-p12-pem/-/google-p12-pem-4.0.1.tgz#82841798253c65b7dc2a4e5fe9df141db670172a" + integrity sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ== + dependencies: + node-forge "^1.3.1" + grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.npmmirror.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +gtoken@^6.1.0: + version "6.1.2" + resolved "https://registry.npmmirror.com/gtoken/-/gtoken-6.1.2.tgz#aeb7bdb019ff4c3ba3ac100bbe7b6e74dce0e8bc" + integrity sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ== + dependencies: + gaxios "^5.0.1" + google-p12-pem "^4.0.0" + jws "^4.0.0" + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" @@ -1102,6 +1139,14 @@ helmet@^6.0.0: resolved "https://registry.npmmirror.com/helmet/-/helmet-6.0.0.tgz#8e183820ddccd7729a206ad73c577b264f495595" integrity sha512-FO9RpR1wNJepH/GbLPQVtkE2eESglXL641p7SdyoT4LngHFJcZheHMoyUcjCZF4qpuMMO1u5q6RK0l9Ux8JBcg== +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -1179,6 +1224,11 @@ is-number@^7.0.0: resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1196,6 +1246,13 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +json-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== + dependencies: + bignumber.js "^9.0.0" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -1216,6 +1273,23 @@ json5@^2.2.1: resolved "https://registry.npmmirror.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + dependencies: + jwa "^2.0.0" + safe-buffer "^5.0.1" + kareem@2.4.1: version "2.4.1" resolved "https://registry.npmmirror.com/kareem/-/kareem-2.4.1.tgz#7d81ec518204a48c1cb16554af126806c3cd82b0" @@ -1398,11 +1472,28 @@ ms@2.1.3: resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nanoid@^3.1.23: + version "3.3.4" + resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +node-fetch@^2.6.7: + version "2.6.7" + resolved "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1.3.1: + version "1.3.1" + resolved "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -1595,18 +1686,6 @@ real-require@^0.2.0: resolved "https://registry.npmmirror.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== -redis@^4.3.1: - version "4.3.1" - resolved "https://registry.npmmirror.com/redis/-/redis-4.3.1.tgz#290532a0c22221e05e991162ac4dca1e1b2ff6da" - integrity sha512-cM7yFU5CA6zyCF7N/+SSTcSJQSRMEKN0k0Whhu6J7n9mmXRoXugfWDBo5iOzGwABmsWKSwGPTU5J4Bxbl+0mrA== - dependencies: - "@redis/bloom" "1.0.2" - "@redis/client" "1.3.0" - "@redis/graph" "1.0.1" - "@redis/json" "1.0.4" - "@redis/search" "1.1.0" - "@redis/time-series" "1.0.3" - reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -1866,6 +1945,11 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + tracer@^1.1.6: version "1.1.6" resolved "https://registry.npmmirror.com/tracer/-/tracer-1.1.6.tgz#5afc5ea0d7c6026dbafb36bed86e88cafe9bebad" @@ -1981,6 +2065,11 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" @@ -1994,6 +2083,14 @@ whatwg-url@^11.0.0: tr46 "^3.0.0" webidl-conversions "^7.0.0" +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -2016,7 +2113,7 @@ xtend@^4.0.0, xtend@^4.0.2: resolved "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -yallist@4.0.0, yallist@^4.0.0: +yallist@^4.0.0: version "4.0.0" resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==