增加分布式锁
This commit is contained in:
parent
1a4da3ca73
commit
37aca45c9d
@ -58,7 +58,7 @@
|
||||
#### 20240423
|
||||
1. 签到列表接口(11)增加返回字段: 是否已领取奖励
|
||||
1. 检查签到并领取奖励(23), 增加post参数, 可以领取指定天数的奖励
|
||||
1. 宝箱助力(18), 增加返回scoreBonus, 表示当前宝箱已得到的助力积分
|
||||
1. 宝箱助力(18), 宝箱助力状态查询(26) 增加返回scoreBonus, 表示当前宝箱已得到的助力积分
|
||||
|
||||
### 1. 钱包预登录
|
||||
|
||||
@ -803,6 +803,7 @@ body:
|
||||
chestMax: 10, // 宝箱最大可助力次数
|
||||
nickname: '11', // 宝箱所有者昵称
|
||||
avatar: '', // 宝箱所有者头像, 可能为空
|
||||
scoreBonus: 1 //当前箱子已助力积分
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -33,10 +33,12 @@
|
||||
"fastify": "^4.15.0",
|
||||
"fastify-plugin": "^3.0.0",
|
||||
"fastify-xml-body-parser": "^2.2.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"mongodb-extended-json": "^1.11.1",
|
||||
"mongoose": "8.2.3",
|
||||
"nanoid": "^3.1.23",
|
||||
"node-schedule": "^2.0.0",
|
||||
"redlock": "^5.0.0-beta.2",
|
||||
"siwe": "^2.1.4",
|
||||
"tracer": "^1.1.6",
|
||||
"zutils": "link:packages/zutils"
|
||||
|
@ -7,8 +7,8 @@ import { IncomingMessage, Server, ServerResponse } from 'http'
|
||||
import { mongoose } from '@typegoose/typegoose'
|
||||
import logger from 'logger/logger'
|
||||
import NonceRecordSchedule from 'schedule/noncerecord.schedule'
|
||||
import { RouterMap, SyncLocker, ZRedisClient } from 'zutils'
|
||||
import CacheSchedule from 'schedule/cache.schedule'
|
||||
import { RouterMap, ZRedisClient } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
|
||||
const zReqParserPlugin = require('plugins/zReqParser')
|
||||
|
||||
|
66
src/common/SyncLocker.ts
Normal file
66
src/common/SyncLocker.ts
Normal file
@ -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: 10,
|
||||
|
||||
// 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 unlock(req: IRequest) {
|
||||
if (req.lock) {
|
||||
req.lock.release()
|
||||
}
|
||||
}
|
||||
|
||||
public async checkLock(req: IRequest) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
|
||||
let lock = await redlock.acquire([key], 3000)
|
||||
req.lock = lock
|
||||
return true
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import { ActivityInfo } from 'models/ActivityInfo'
|
||||
import { ActivityUser } from 'models/ActivityUser'
|
||||
import { rankKey, rankLevel } from 'services/rank.svr'
|
||||
import { BaseController, ROLE_ANON, SyncLocker, ZError, ZRedisClient, role, router } from 'zutils'
|
||||
import { BaseController, ROLE_ANON, ZError, ZRedisClient, role, router } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
import { ScoreRecord } from 'models/ScoreRecord'
|
||||
import { formatAddress } from 'zutils/utils/chain.util'
|
||||
import { formatNumShow, isValidShareCode } from 'common/Utils'
|
||||
@ -32,7 +33,7 @@ export default class ActivityController extends BaseController {
|
||||
*/
|
||||
@router('post /api/activity/upload_invite_code')
|
||||
async uploadInviteCode(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('upload_invite_code', req)
|
||||
await checkReCaptcha(req, 'invite_user')
|
||||
let { code } = req.params
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ZError, SyncLocker, BaseController, router } from 'zutils'
|
||||
import { ZError, BaseController, router } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
import { FastifyRequest } from 'fastify'
|
||||
import { ActivityItem } from 'models/ActivityItem'
|
||||
import { TokenClaimHistory } from 'models/TokenClaimHistory'
|
||||
@ -16,7 +17,7 @@ export default class ChainController extends BaseController {
|
||||
|
||||
// @router('post /api/lottery/claim_usdt')
|
||||
async preClaimUsdt(req: FastifyRequest) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
let user = req.user
|
||||
if (user.address) {
|
||||
throw new ZError(11, 'no address')
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ZError, SyncLocker, ZRedisClient, BaseController, ROLE_ANON, role, router } from 'zutils'
|
||||
import { ZError, ZRedisClient, BaseController, ROLE_ANON, role, router } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
import { ActivityChest, ChestStatusEnum } from 'models/ActivityChest'
|
||||
import { ActivityUser } from 'models/ActivityUser'
|
||||
import { rankKey, rankLevel, updateRankScore } from 'services/rank.svr'
|
||||
@ -185,6 +186,7 @@ class BoxController extends BaseController {
|
||||
avatar: chestOwner?.twitterAvatar || '',
|
||||
chestCurrent: chest.bonusUsers.length,
|
||||
chestMax: chest.maxBounsCount,
|
||||
scoreBonus: chest.scoreBonus,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
@ -196,6 +198,7 @@ class BoxController extends BaseController {
|
||||
? formatAddress(chestOwner.address)
|
||||
: 'unknown',
|
||||
avatar: chestOwner?.twitterAvatar || '',
|
||||
scoreBonus: chest.scoreBonus,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,7 +207,7 @@ class BoxController extends BaseController {
|
||||
*/
|
||||
@router('post /api/chest/enhance')
|
||||
async enhance(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('enhance', req)
|
||||
await checkReCaptcha(req, 'chest_share')
|
||||
const { code } = req.params
|
||||
@ -307,7 +310,7 @@ class BoxController extends BaseController {
|
||||
*/
|
||||
@router('post /api/chest/open')
|
||||
async openChest(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('open_chest', req)
|
||||
const user = req.user
|
||||
const { chestId } = req.params
|
||||
|
@ -3,7 +3,8 @@ import { ActivityGame } from 'models/ActivityGame'
|
||||
import { DAILY_SIGN, SIGN_SEQ, SIGN_TOTAL, TicketRecord, USE_TICKET } from 'models/TicketRecord'
|
||||
import { queryCheckInList } from 'services/chain.svr'
|
||||
import { checkInToday, seqSignCfg, seqSignScore, totalSignCfg, totalSignScore } from 'services/sign.svr'
|
||||
import { ZError, SyncLocker, BaseController, router } from 'zutils'
|
||||
import { ZError, BaseController, router } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
import { formatDate } from 'utils/utcdate.util'
|
||||
import { generateStepReward } from 'services/game.svr'
|
||||
import { updateRankScore } from 'services/rank.svr'
|
||||
@ -23,7 +24,7 @@ class GameController extends BaseController {
|
||||
*/
|
||||
@router('post /api/user/checkin')
|
||||
async checkIn(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('checkin', req)
|
||||
const user = req.user
|
||||
const { address } = user
|
||||
@ -99,7 +100,7 @@ class GameController extends BaseController {
|
||||
*/
|
||||
@router('post /api/user/checkin/claim')
|
||||
async claimCheckResult(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('claim_checkin_total', req)
|
||||
const user = req.user
|
||||
let { days } = req.params
|
||||
@ -150,7 +151,7 @@ class GameController extends BaseController {
|
||||
*/
|
||||
@router('post /api/user/checkin/claim_seq')
|
||||
async claimCheckSeqResult(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('claim_checkin_seq', req)
|
||||
const user = req.user
|
||||
let { days } = req.params
|
||||
@ -302,7 +303,7 @@ class GameController extends BaseController {
|
||||
@router('post /api/game/pre_step')
|
||||
async gameChest(req) {
|
||||
const user = req.user
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
let { step } = req.params
|
||||
step = step || '1'
|
||||
if (isNaN(step)) {
|
||||
@ -335,7 +336,7 @@ class GameController extends BaseController {
|
||||
*/
|
||||
@router('post /api/game/step')
|
||||
async gameStep(req, res) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('step', req)
|
||||
const user = req.user
|
||||
const { id } = req.params
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { EMPTY_REWARD, SCORE_DRAW } from 'common/Constants'
|
||||
import { LotteryCache } from 'common/LotteryCache'
|
||||
import { ZError, SyncLocker, BaseController, router } from 'zutils'
|
||||
import { ZError, BaseController, router } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
import { FUSION_CFG } from 'configs/fusion'
|
||||
import { ALL_ITEMS } from 'configs/items'
|
||||
import { LOTTERY_CFG } from 'configs/lottery'
|
||||
@ -80,7 +81,7 @@ export default class LotteryController extends BaseController {
|
||||
|
||||
// @router('get /api/lottery/draw')
|
||||
async draw(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
let user = req.user
|
||||
const { start, end, rewards } = LOTTERY_CFG
|
||||
const startTime = new Date(start).getTime()
|
||||
@ -144,7 +145,7 @@ export default class LotteryController extends BaseController {
|
||||
|
||||
// @router('get /api/lottery/fusion')
|
||||
async fusion(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
let user = req.user
|
||||
let items = await ActivityItem.find({ user: user.id, activity: user.activity })
|
||||
let itemCountMap = new Map()
|
||||
|
@ -5,7 +5,8 @@ import { NFTHolderRecord } from 'models/NFTHodlerRecord'
|
||||
import { queryNftBalance } from 'services/chain.svr'
|
||||
import { generateChestLevel, generateNewChest } from 'services/game.svr'
|
||||
import { checkDiscordRole } from 'services/oauth.svr'
|
||||
import { SyncLocker, BaseController, router, role, ROLE_ANON, ZError } from 'zutils'
|
||||
import { BaseController, router, role, ROLE_ANON, ZError } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
|
||||
const sourceList = require('../../configs/partner_nft_list.json')
|
||||
const tierMap = {
|
||||
@ -103,7 +104,7 @@ class NftController extends BaseController {
|
||||
*/
|
||||
// @router('post /api/partner/claim')
|
||||
async claimNftHolderReward(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
const user = req.user
|
||||
let { contract } = req.params
|
||||
if (!contract) {
|
||||
@ -142,7 +143,7 @@ class NftController extends BaseController {
|
||||
|
||||
@router('post /api/partner/claim')
|
||||
async claimNftHolderRewardDC(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
// throw new ZError(20, 'maintance now, please try later')
|
||||
|
||||
logger.db('claim_partner', req)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ZError, SyncLocker, ZRedisClient, BaseController, ROLE_ANON, role, router } from 'zutils'
|
||||
import { ZError, ZRedisClient, BaseController, ROLE_ANON, role, router } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
import { BOOST_CFG } from 'configs/boost'
|
||||
import { ActivityUser } from 'models/ActivityUser'
|
||||
import { DEFAULT_EXPIRED, NonceRecord } from 'models/NonceRecord'
|
||||
@ -163,7 +164,7 @@ class SignController extends BaseController {
|
||||
|
||||
// @router('get /api/user/state/boost')
|
||||
async boost(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
const user = req.user
|
||||
if (user.boost > 1 && user.boostExpire && user.boostExpire > Date.now()) {
|
||||
throw new ZError(11, 'already boosted')
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ZError, SyncLocker, BaseController, router } from 'zutils'
|
||||
import { ZError, BaseController, router } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
import { TaskCfg, TaskTypeEnum } from 'models/ActivityInfo'
|
||||
import { TaskStatus, TaskStatusEnum } from 'models/ActivityUser'
|
||||
import { join } from 'path'
|
||||
@ -77,7 +78,7 @@ export default class TasksController extends BaseController {
|
||||
|
||||
@router('post /api/tasks/begin_task')
|
||||
async beginTask(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('begin_task', req)
|
||||
let user = req.user
|
||||
let activity = req.activity
|
||||
@ -175,7 +176,7 @@ export default class TasksController extends BaseController {
|
||||
|
||||
@router('post /api/tasks/claim')
|
||||
async claimTask(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('claim_task', req)
|
||||
const user = req.user
|
||||
const activity = req.activity
|
||||
|
@ -3,7 +3,8 @@ import logger from 'logger/logger'
|
||||
import { ChestStatusEnum } from 'models/ActivityChest'
|
||||
import { SourceEnum, VoucherRecord, VoucherStatusEnum } from 'models/VoucherRecord'
|
||||
import { generateNewChest } from 'services/game.svr'
|
||||
import { SyncLocker, BaseController, router, ZError } from 'zutils'
|
||||
import { BaseController, router, ZError } from 'zutils'
|
||||
import { SyncLocker } from 'common/SyncLocker'
|
||||
import { customAlphabet } from 'nanoid'
|
||||
import { BASE52_ALPHABET } from 'common/Constants'
|
||||
/**
|
||||
@ -90,7 +91,7 @@ class VoucherController extends BaseController {
|
||||
|
||||
@router('post /api/voucher/claim')
|
||||
async claimVoucherReward(req) {
|
||||
new SyncLocker().checkLock(req)
|
||||
await new SyncLocker().checkLock(req)
|
||||
logger.db('claim_voucher', req)
|
||||
const user = req.user
|
||||
const { id } = req.params
|
||||
|
57
yarn.lock
57
yarn.lock
@ -815,6 +815,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
|
||||
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
|
||||
|
||||
"@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==
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||
@ -2231,6 +2236,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==
|
||||
|
||||
co@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||
@ -2507,6 +2517,11 @@ denque@^1.5.0:
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf"
|
||||
integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==
|
||||
|
||||
denque@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
|
||||
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
|
||||
|
||||
depd@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||
@ -3801,6 +3816,21 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4:
|
||||
resolved "https://registry.npmjs.org/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"
|
||||
|
||||
ipaddr.js@1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
|
||||
@ -4508,6 +4538,16 @@ locate-path@^6.0.0:
|
||||
dependencies:
|
||||
p-locate "^5.0.0"
|
||||
|
||||
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.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.isfunction@^3.0.6:
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
|
||||
@ -4908,6 +4948,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"
|
||||
@ -5427,6 +5472,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.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.1.tgz#8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74"
|
||||
@ -5868,6 +5920,11 @@ stack-utils@^2.0.3:
|
||||
dependencies:
|
||||
escape-string-regexp "^2.0.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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user