diff --git a/.env.development b/.env.development index 50d2480..14f01b4 100644 --- a/.env.development +++ b/.env.development @@ -59,4 +59,6 @@ OKX_SECRET_KEY='AF7F4CEE2A10715F9709D38452CE0BFD' DISCORD_CLIENT_ID='1199290913155981345' DISCORD_CLIENT_SECRET='0-iIPG1waeQ7GpFV3e_dGH6kfjv1SVNS' -DISCORD_REDIRECT_URI='https://oauth-svr.cebggame.com/oauth/redirect' \ No newline at end of file +DISCORD_REDIRECT_URI='https://oauth-svr.cebggame.com/oauth/redirect' + +REDIS=redis://192.168.100.22:6379/13 \ No newline at end of file diff --git a/package.json b/package.json index b02c332..8dde61d 100644 --- a/package.json +++ b/package.json @@ -29,10 +29,12 @@ "fastify": "^4.8.1", "fastify-plugin": "^4.2.1", "google-auth-library": "^8.5.2", + "ioredis": "^5.4.1", "mongoose": "^6.6.5", "mongoose-findorcreate": "^3.0.0", "nanoid": "^3.1.23", "node-schedule": "^2.1.1", + "redlock": "^5.0.0-beta.2", "rustwallet": "file:./rustwallet", "siwe": "^2.1.4", "tracer": "^1.1.6", diff --git a/publish.sh b/publish.sh new file mode 100755 index 0000000..ea43468 --- /dev/null +++ b/publish.sh @@ -0,0 +1,4 @@ +cd .. +tar zcvf wallet-svr.tar.gz ./wallet-svr +scp -P27256 ./wallet-svr.tar.gz root@45.78.31.162:./upload +cd wallet-svr \ No newline at end of file diff --git a/src/api.server.ts b/src/api.server.ts index 8dfcb37..1159527 100644 --- a/src/api.server.ts +++ b/src/api.server.ts @@ -136,7 +136,13 @@ export class ApiServer { }) this.server.setErrorHandler(function (error: FastifyError, request: FastifyRequest, reply: FastifyReply) { let statusCode = (error && error.statusCode) || 100 - logger.info(error) + if (statusCode >= 500) { + logger.error(error) + } else if (statusCode >= 400) { + logger.error(error) + } else { + logger.info(error?.message || error || 'unknown error') + } reply.code(200).send({ errcode: statusCode, errmsg: error ? error.message : 'unknown error', diff --git a/src/common/SyncLocker.ts b/src/common/SyncLocker.ts new file mode 100644 index 0000000..ec2fa1d --- /dev/null +++ b/src/common/SyncLocker.ts @@ -0,0 +1,66 @@ +import { singleton } from 'zutils' +import Client from 'ioredis' +import Redlock, { Lock, ResourceLockedError } from 'redlock' +import logger from 'logger/logger' + +interface IRequest { + method: string + url: string + user?: { + id: string + } + lock?: Lock +} + +const redisA = new Client(process.env.REDIS) +const redlock = new Redlock( + // You should have one client for each independent redis node + // or cluster. + [redisA], + { + // The expected clock drift; for more details see: + // http://redis.io/topics/distlock + driftFactor: 0.01, // multiplied by lock ttl to determine drift time + + // The max number of times Redlock will attempt to lock a resource + // before erroring. + retryCount: 0, + + // the time in ms between attempts + retryDelay: 200, // time in ms + + // the max time in ms randomly added to retries + // to improve performance under high contention + // see https://www.awsarchitectureblog.com/2015/03/backoff.html + retryJitter: 200, // time in ms + + // The minimum remaining time on a lock before an extension is automatically + // attempted with the `using` API. + automaticExtensionThreshold: 500, // time in ms + }, +) + +redlock.on('error', error => { + // Ignore cases where a resource is explicitly marked as locked on a client. + if (error instanceof ResourceLockedError) { + return + } + // Log all other errors. + logger.error(error) +}) + +@singleton +export class SyncLocker { + public async unlock(req: IRequest) { + if (req.lock) { + await req.lock.release() + } + } + + public async checkLock(req: IRequest, key?: string, lockTime: number = 60000) { + key = key || `${req.method}:${req.url}:${req.user?.id || ''}` + let lock = await redlock.acquire([key], lockTime) + req.lock = lock + return true + } +} diff --git a/src/controllers/mail.controller.ts b/src/controllers/mail.controller.ts index ebbc5eb..6a45903 100644 --- a/src/controllers/mail.controller.ts +++ b/src/controllers/mail.controller.ts @@ -13,8 +13,14 @@ import { DEFAULT_VERIFY_MAIL_SUBJECT, EmailSvr, } from 'service/email.svr' -import { uuid } from 'zutils/utils/security.util' +import { sha1, uuid } from 'zutils/utils/security.util' import { BaseController, role, ROLE_ANON, router, ZError } from 'zutils' +import { SyncLocker } from 'common/SyncLocker' + +export const isEmail = (email: string) => { + const reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/ + return reg.test(email) +} class MailController extends BaseController { /** @@ -131,7 +137,15 @@ class MailController extends BaseController { if (!email || !type) { throw new ZError(10, 'params mismatch') } + if (!isEmail(email)) { + throw new ZError(12, 'email error') + } type = parseInt(type) + if (type !== CodeType.REGIST && type !== CodeType.RESET && type !== CodeType.VERIFY && type !== CodeType.LOGIN) { + throw new ZError(13, 'type error') + } + const lockKey = sha1(`${email}_${type}`) + await new SyncLocker().checkLock(req, lockKey, 55000) if (type === CodeType.REGIST) { let account = await Account.findByEmail(email) if (account) { diff --git a/src/net/NetClient.ts b/src/net/NetClient.ts index 75229dd..ab36141 100644 --- a/src/net/NetClient.ts +++ b/src/net/NetClient.ts @@ -29,7 +29,7 @@ export class NetClient { headers: { 'Content-Type': 'application/json' }, } Object.assign(defaultCfg, data) - console.log(defaultCfg) + // console.log(defaultCfg) const res = await axios(defaultCfg) return res.data } diff --git a/src/service/email.svr.ts b/src/service/email.svr.ts index f0e6a6d..f4c8cfd 100644 --- a/src/service/email.svr.ts +++ b/src/service/email.svr.ts @@ -52,7 +52,7 @@ export interface IMailData { } const DEFAULT_MSG_DATA: IMailData = { - from: 'CEBG ', + from: 'Counter Fire ', to: '', subject: 'Please verify your email address', } diff --git a/yarn.lock b/yarn.lock index 480b2e3..4c5da74 100644 --- a/yarn.lock +++ b/yarn.lock @@ -493,6 +493,11 @@ resolved "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@ioredis/commands@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" + integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== + "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" @@ -1467,6 +1472,11 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" +cluster-key-slot@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz" @@ -2866,6 +2876,21 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ioredis@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.4.1.tgz#1c56b70b759f01465913887375ed809134296f40" + integrity sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA== + dependencies: + "@ioredis/commands" "^1.1.1" + cluster-key-slot "^1.1.0" + debug "^4.3.4" + denque "^2.1.0" + lodash.defaults "^4.2.0" + lodash.isarguments "^3.1.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + ip@^2.0.0: version "2.0.0" resolved "https://registry.npmmirror.com/ip/-/ip-2.0.0.tgz" @@ -3184,11 +3209,21 @@ lodash.clonedeep@^4.5.0: resolved "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz" integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== +lodash.isarguments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== + lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" @@ -3580,6 +3615,11 @@ next-tick@^1.1.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== +node-abort-controller@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + node-addon-api@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" @@ -4015,6 +4055,13 @@ redis@^3.1.2: redis-errors "^1.2.0" redis-parser "^3.0.0" +redlock@^5.0.0-beta.2: + version "5.0.0-beta.2" + resolved "https://registry.yarnpkg.com/redlock/-/redlock-5.0.0-beta.2.tgz#a629c07e07d001c0fdd9f2efa614144c4416fe44" + integrity sha512-2RDWXg5jgRptDrB1w9O/JgSZC0j7y4SlaXnor93H/UJm/QyDiFgBKNtrh0TI6oCXqYSaSoXxFh6Sd3VtYfhRXw== + dependencies: + node-abort-controller "^3.0.1" + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz" @@ -4395,6 +4442,11 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"