feat(backend): Added new and legacy password handling
This commit is contained in:
parent
34549d5c71
commit
d8134314c6
8 changed files with 227 additions and 62 deletions
|
@ -12,8 +12,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "4.5.0",
|
"@prisma/client": "4.5.0",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"express": "4.18.2"
|
"express": "4.18.2",
|
||||||
|
"pino": "^8.7.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^10.1.0",
|
||||||
|
|
|
@ -27,7 +27,7 @@ if (filter) {
|
||||||
// Dump filtered data from raw data
|
// Dump filtered data from raw data
|
||||||
const file = 'freesewing-dump.json'
|
const file = 'freesewing-dump.json'
|
||||||
data = filterData(JSON.parse(fs.readFileSync(path.resolve(dir, file), { encoding: 'utf-8' })))
|
data = filterData(JSON.parse(fs.readFileSync(path.resolve(dir, file), { encoding: 'utf-8' })))
|
||||||
console.log(JSON.stringify(data, null ,2))
|
console.log(JSON.stringify(data, null, 2))
|
||||||
} else {
|
} else {
|
||||||
// Load filtered data for migration
|
// Load filtered data for migration
|
||||||
const file = 'freesewing-filtered.json'
|
const file = 'freesewing-filtered.json'
|
||||||
|
@ -47,6 +47,8 @@ if (filter) {
|
||||||
console.log()
|
console.log()
|
||||||
await migratePatterns(data.patterns)
|
await migratePatterns(data.patterns)
|
||||||
console.log()
|
console.log()
|
||||||
|
await migrateSubscribers(data.subscribers)
|
||||||
|
console.log()
|
||||||
}
|
}
|
||||||
|
|
||||||
function progress(text) {
|
function progress(text) {
|
||||||
|
@ -55,6 +57,27 @@ function progress(text) {
|
||||||
process.stdout.write(text)
|
process.stdout.write(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function migrateSubscribers(subscribers) {
|
||||||
|
const total = subscribers.length
|
||||||
|
let i = 0
|
||||||
|
for (const sub of subscribers) {
|
||||||
|
i++
|
||||||
|
progress(` 📰 subscriber ${i}/${total}`)
|
||||||
|
await createSubscriber(sub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createSubscriber(sub) {
|
||||||
|
const record = await prisma.subscriber.create({
|
||||||
|
data: {
|
||||||
|
createdAt: sub.createdAt,
|
||||||
|
data: JSON.stringify({}),
|
||||||
|
ehash: hash(clean(sub.email)),
|
||||||
|
email: encrypt(clean(sub.email)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function migratePatterns(patterns) {
|
async function migratePatterns(patterns) {
|
||||||
const total = Object.keys(patterns).length
|
const total = Object.keys(patterns).length
|
||||||
let i = 0
|
let i = 0
|
||||||
|
@ -109,7 +132,7 @@ async function migrateUsers(users) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createUser(user) {
|
async function createUser(user) {
|
||||||
const ehash = hash(user.email)
|
const ehash = hash(clean(user.email))
|
||||||
const record = await prisma.user.create({
|
const record = await prisma.user.create({
|
||||||
data: {
|
data: {
|
||||||
consent: user.consent,
|
consent: user.consent,
|
||||||
|
@ -122,7 +145,7 @@ async function createUser(user) {
|
||||||
newsletter: user.newsletter,
|
newsletter: user.newsletter,
|
||||||
password: JSON.stringify({
|
password: JSON.stringify({
|
||||||
type: 'v2',
|
type: 'v2',
|
||||||
data: user.password
|
data: user.password,
|
||||||
}),
|
}),
|
||||||
patron: user.patron,
|
patron: user.patron,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
|
@ -133,13 +156,12 @@ async function createUser(user) {
|
||||||
data.userhandles[user.handle] = record.id
|
data.userhandles[user.handle] = record.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only migrate user data if the user was active in the last 6 months
|
* Only migrate user data if the user was active in the last 6 months
|
||||||
* Unless they are patrons. Keep patrons regardless coz patrons rock.
|
* Unless they are patrons. Keep patrons regardless coz patrons rock.
|
||||||
*/
|
*/
|
||||||
function filterData(data) {
|
function filterData(data) {
|
||||||
let i=0
|
let i = 0
|
||||||
const filtered = {
|
const filtered = {
|
||||||
users: {},
|
users: {},
|
||||||
people: {},
|
people: {},
|
||||||
|
@ -171,9 +193,9 @@ function filterData(data) {
|
||||||
* Migrates role field
|
* Migrates role field
|
||||||
*/
|
*/
|
||||||
function getRole(entry) {
|
function getRole(entry) {
|
||||||
if (entry.handle === 'joost') return "admin"
|
if (entry.handle === 'joost') return 'admin'
|
||||||
if (entry.patron > 0) return "patron"
|
if (entry.patron > 0) return 'patron'
|
||||||
return "user"
|
return 'user'
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -225,6 +247,6 @@ function migratePattern(entry) {
|
||||||
function migrateSubscriber(entry) {
|
function migrateSubscriber(entry) {
|
||||||
return {
|
return {
|
||||||
createdAt: entry.created,
|
createdAt: entry.created,
|
||||||
email: entry.email
|
email: entry.email,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import jwt from 'jsonwebtoken'
|
||||||
//import fs from 'fs'
|
//import fs from 'fs'
|
||||||
import Zip from 'jszip'
|
import Zip from 'jszip'
|
||||||
//import rimraf from 'rimraf'
|
//import rimraf from 'rimraf'
|
||||||
import { ehash } from '../utils/crypto.mjs'
|
import { clean, hash } from '../utils/crypto.mjs'
|
||||||
|
|
||||||
export function UserController() {}
|
export function UserController() {}
|
||||||
|
|
||||||
|
@ -16,15 +16,16 @@ UserController.prototype.signup = async (req, res, tools) => {
|
||||||
if (!req.body.language) return res.status(400).send('languageMissing')
|
if (!req.body.language) return res.status(400).send('languageMissing')
|
||||||
|
|
||||||
// Requests looks ok - does the user exist?
|
// Requests looks ok - does the user exist?
|
||||||
const hash = ehash(req.body.email)
|
const emailhash = hash(clean(req.body.email))
|
||||||
|
|
||||||
// Destructure what we need from tools
|
// Destructure what we need from tools
|
||||||
const { prisma, config, encrypt } = tools
|
const { prisma, config, encrypt } = tools
|
||||||
if ((await prisma.user.findUnique({ where: { ehash: hash } }))) return res.status(400).send('emailExists')
|
if (await prisma.user.findUnique({ where: { ehash: emailhash } }))
|
||||||
|
return res.status(400).send('emailExists')
|
||||||
|
|
||||||
// It does not. Creating user entry
|
// It does not. Creating user entry
|
||||||
|
|
||||||
const username = `user-${hash.slice(0,6)}-${time().slice(-6)}` // Temporary username
|
const username = `user-${hash.slice(0, 6)}-${time().slice(-6)}` // Temporary username
|
||||||
//const user = await.prisma.user.create({
|
//const user = await.prisma.user.create({
|
||||||
// ehash: hash, // Hash of the email to search on
|
// ehash: hash, // Hash of the email to search on
|
||||||
// ihash: hash, // Hash of the (initial) email to search on
|
// ihash: hash, // Hash of the (initial) email to search on
|
||||||
|
@ -37,7 +38,7 @@ UserController.prototype.signup = async (req, res, tools) => {
|
||||||
password String
|
password String
|
||||||
*/
|
*/
|
||||||
return res.status(200).send({})
|
return res.status(200).send({})
|
||||||
/*
|
/*
|
||||||
(err, user) => {
|
(err, user) => {
|
||||||
if (err) return res.sendStatus(500)
|
if (err) return res.sendStatus(500)
|
||||||
if (user !== null) return res.status(400).send('userExists')
|
if (user !== null) return res.status(400).send('userExists')
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { verifyConfig } from './config.mjs'
|
||||||
// Middleware
|
// Middleware
|
||||||
import { loadExpressMiddleware, loadPassportMiddleware } from './middleware.mjs'
|
import { loadExpressMiddleware, loadPassportMiddleware } from './middleware.mjs'
|
||||||
// Encryption
|
// Encryption
|
||||||
import { ehash, encryption } from './utils/crypto.mjs'
|
import { encryption } from './utils/crypto.mjs'
|
||||||
|
|
||||||
// Bootstrap
|
// Bootstrap
|
||||||
const config = verifyConfig()
|
const config = verifyConfig()
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
import bcrypt from 'bcryptjs' // Required for legacy password hashes
|
||||||
import { createHash, createCipheriv, createDecipheriv, scryptSync, randomBytes } from 'crypto'
|
import { createHash, createCipheriv, createDecipheriv, scryptSync, randomBytes } from 'crypto'
|
||||||
|
import { log } from './log.mjs'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cleans a string (typically email) for hashing
|
* Cleans a string (typically email) for hashing
|
||||||
|
@ -100,3 +102,82 @@ export const encryption = (stringKey, salt = 'FreeSewing') => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Salts and hashes a password
|
||||||
|
*/
|
||||||
|
function hashPassword(input, salt = false) {
|
||||||
|
if (salt === false) salt = Buffer.from(randomBytes(16))
|
||||||
|
else salt = Buffer.from(salt, 'hex')
|
||||||
|
const hash = scryptSync(input, salt, 64)
|
||||||
|
|
||||||
|
return {
|
||||||
|
hash: hash.toString('hex'),
|
||||||
|
salt: salt.toString('hex'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verifies a (user-provided) password against the stored hash + salt
|
||||||
|
*
|
||||||
|
* Note that:
|
||||||
|
* - For legacy password hashes, the password field will hold serialized
|
||||||
|
* JSON with a 'type' field set to 'v2' and a 'data' field holding the
|
||||||
|
* legacy hash info to pass to the verifyLegacyPassword() method below.
|
||||||
|
* - For new password hashes, the password field will hold serialized
|
||||||
|
* JSON with a 'type' field set to 'v3' and a 'hash' and 'salt' field.
|
||||||
|
* - When legacy passwords are confirmed, they will be re-hashed and
|
||||||
|
* updated in the database. The database update is not handled here but
|
||||||
|
* prepared, by returning the new value for the password field as the
|
||||||
|
* second element in the returned array.
|
||||||
|
*/
|
||||||
|
export function verifyPassword(input, passwordField) {
|
||||||
|
let data
|
||||||
|
try {
|
||||||
|
data = JSON.parse(passwordField)
|
||||||
|
} catch {
|
||||||
|
/*
|
||||||
|
* This should not happen. Let's just log a warning and return false
|
||||||
|
*/
|
||||||
|
log.warn(passwordField, 'Unable to parse JSON in password field')
|
||||||
|
return [false, false]
|
||||||
|
}
|
||||||
|
// Is this a legacy password field?
|
||||||
|
if (data.type === 'v2') {
|
||||||
|
const result = verifyLegacyPassword(input, data.data)
|
||||||
|
if (result) {
|
||||||
|
// Correct password for legacy password. Re-hash and return.
|
||||||
|
return [true, hashPassword(input)]
|
||||||
|
}
|
||||||
|
} else if (data.type === 'v3') {
|
||||||
|
if (data.hash && data.salt) {
|
||||||
|
const verify = hashPassword(input, data.salt)
|
||||||
|
if (data.hash === verify.hash && data.salt === verify.salt) {
|
||||||
|
// Son of a bitch, you're in
|
||||||
|
return [true, false]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [false, false]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verifies a legacy password hash
|
||||||
|
*
|
||||||
|
* Legacy means that an account was imported from the v2 FreeSewing backend
|
||||||
|
* which used MongoDB as a database with Mongoose as an ORM.
|
||||||
|
* Passwords were handles with the mongoose-bcrypt plugin and have been
|
||||||
|
* imported from a database dump.
|
||||||
|
*
|
||||||
|
* So to verify these passwords, we need to verify the original logic of
|
||||||
|
* the mongoose plugin which uses the bcryptjs library.
|
||||||
|
*
|
||||||
|
* Each time a user with a legacy password field logs in with the correct
|
||||||
|
* password, we re-hash the password field with the new (crypto) hasing method.
|
||||||
|
* This way, in a while all users will be migrated, and we can drop this method
|
||||||
|
* and the cryptojs dependency
|
||||||
|
*/
|
||||||
|
function verifyLegacyPassword(password, hash) {
|
||||||
|
return bcrypt.compareSync(password, hash)
|
||||||
|
}
|
||||||
|
|
3
sites/backend/src/utils/log.mjs
Normal file
3
sites/backend/src/utils/log.mjs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import logger from 'pino'
|
||||||
|
|
||||||
|
export const log = logger()
|
|
@ -1,45 +0,0 @@
|
||||||
import dateFormat from 'dateformat'
|
|
||||||
|
|
||||||
// FIXME: This needs work
|
|
||||||
|
|
||||||
const now = () => dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss')
|
|
||||||
|
|
||||||
const logWorthy = (msg, data) => {
|
|
||||||
let d = { at: now() }
|
|
||||||
switch (msg) {
|
|
||||||
case 'ping':
|
|
||||||
case 'login':
|
|
||||||
case 'wrongPassword':
|
|
||||||
case 'passwordSet':
|
|
||||||
case 'dataExport':
|
|
||||||
d.user = data.user.handle
|
|
||||||
d.from = data.req.ip
|
|
||||||
d.with = data.req.headers['user-agent']
|
|
||||||
break
|
|
||||||
case 'signupRequest':
|
|
||||||
d.email = data.email
|
|
||||||
d.confirmation = data.confirmation
|
|
||||||
break
|
|
||||||
case 'accountRemovalFailed':
|
|
||||||
d.err = data.err
|
|
||||||
d.user = data.user.handle
|
|
||||||
d.from = data.req.ip
|
|
||||||
d.with = data.req.headers['user-agent']
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
d.data = data
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
const log = (type, msg, data) => {
|
|
||||||
console.log(type, msg, logWorthy(msg, data))
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info = (msg, data) => log('info', msg, data)
|
|
||||||
log.warning = (msg, data) => log('warning', msg, data)
|
|
||||||
log.error = (msg, data) => log('error', msg, data)
|
|
||||||
|
|
||||||
export default log
|
|
103
yarn.lock
103
yarn.lock
|
@ -6104,6 +6104,13 @@ abbrev@1, abbrev@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||||
|
|
||||||
|
abort-controller@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
|
||||||
|
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
|
||||||
|
dependencies:
|
||||||
|
event-target-shim "^5.0.0"
|
||||||
|
|
||||||
accepts@~1.3.8:
|
accepts@~1.3.8:
|
||||||
version "1.3.8"
|
version "1.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
||||||
|
@ -6903,6 +6910,11 @@ atob@^2.1.2:
|
||||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||||
|
|
||||||
|
atomic-sleep@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b"
|
||||||
|
integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==
|
||||||
|
|
||||||
attr-accept@^1.1.0:
|
attr-accept@^1.1.0:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz#48230c79f93790ef2775fcec4f0db0f5db41ca52"
|
resolved "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz#48230c79f93790ef2775fcec4f0db0f5db41ca52"
|
||||||
|
@ -11367,12 +11379,17 @@ event-source-polyfill@1.0.25:
|
||||||
resolved "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-1.0.25.tgz#d8bb7f99cb6f8119c2baf086d9f6ee0514b6d9c8"
|
resolved "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-1.0.25.tgz#d8bb7f99cb6f8119c2baf086d9f6ee0514b6d9c8"
|
||||||
integrity sha512-hQxu6sN1Eq4JjoI7ITdQeGGUN193A2ra83qC0Ltm9I2UJVAten3OFVN6k5RX4YWeCS0BoC8xg/5czOCIHVosQg==
|
integrity sha512-hQxu6sN1Eq4JjoI7ITdQeGGUN193A2ra83qC0Ltm9I2UJVAten3OFVN6k5RX4YWeCS0BoC8xg/5czOCIHVosQg==
|
||||||
|
|
||||||
|
event-target-shim@^5.0.0:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||||
|
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||||
|
|
||||||
eventemitter3@^4.0.0, eventemitter3@^4.0.4:
|
eventemitter3@^4.0.0, eventemitter3@^4.0.4:
|
||||||
version "4.0.7"
|
version "4.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||||
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
||||||
|
|
||||||
events@^3.0.0:
|
events@^3.0.0, events@^3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||||
|
@ -11694,6 +11711,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
|
||||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||||
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
|
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
|
||||||
|
|
||||||
|
fast-redact@^3.1.1:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa"
|
||||||
|
integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==
|
||||||
|
|
||||||
fast-safe-stringify@^2.0.7:
|
fast-safe-stringify@^2.0.7:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
|
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
|
||||||
|
@ -18839,6 +18861,11 @@ omit.js@^2.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/omit.js/-/omit.js-2.0.2.tgz#dd9b8436fab947a5f3ff214cb2538631e313ec2f"
|
resolved "https://registry.yarnpkg.com/omit.js/-/omit.js-2.0.2.tgz#dd9b8436fab947a5f3ff214cb2538631e313ec2f"
|
||||||
integrity sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==
|
integrity sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==
|
||||||
|
|
||||||
|
on-exit-leak-free@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz#5c703c968f7e7f851885f6459bf8a8a57edc9cc4"
|
||||||
|
integrity sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==
|
||||||
|
|
||||||
on-finished@2.4.1:
|
on-finished@2.4.1:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
|
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
|
||||||
|
@ -19766,6 +19793,36 @@ pinkie@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
||||||
integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
|
integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
|
||||||
|
|
||||||
|
pino-abstract-transport@v1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3"
|
||||||
|
integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==
|
||||||
|
dependencies:
|
||||||
|
readable-stream "^4.0.0"
|
||||||
|
split2 "^4.0.0"
|
||||||
|
|
||||||
|
pino-std-serializers@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.0.0.tgz#4c20928a1bafca122fdc2a7a4a171ca1c5f9c526"
|
||||||
|
integrity sha512-mMMOwSKrmyl+Y12Ri2xhH1lbzQxwwpuru9VjyJpgFIH4asSj88F2csdMwN6+M5g1Ll4rmsYghHLQJw81tgZ7LQ==
|
||||||
|
|
||||||
|
pino@^8.7.0:
|
||||||
|
version "8.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pino/-/pino-8.7.0.tgz#58621608a3d8540ae643cdd9194cdd94130c78d9"
|
||||||
|
integrity sha512-l9sA5uPxmZzwydhMWUcm1gI0YxNnYl8MfSr2h8cwLvOAzQLBLewzF247h/vqHe3/tt6fgtXeG9wdjjoetdI/vA==
|
||||||
|
dependencies:
|
||||||
|
atomic-sleep "^1.0.0"
|
||||||
|
fast-redact "^3.1.1"
|
||||||
|
on-exit-leak-free "^2.1.0"
|
||||||
|
pino-abstract-transport v1.0.0
|
||||||
|
pino-std-serializers "^6.0.0"
|
||||||
|
process-warning "^2.0.0"
|
||||||
|
quick-format-unescaped "^4.0.3"
|
||||||
|
real-require "^0.2.0"
|
||||||
|
safe-stable-stringify "^2.3.1"
|
||||||
|
sonic-boom "^3.1.0"
|
||||||
|
thread-stream "^2.0.0"
|
||||||
|
|
||||||
pirates@^4.0.0, pirates@^4.0.5:
|
pirates@^4.0.0, pirates@^4.0.5:
|
||||||
version "4.0.5"
|
version "4.0.5"
|
||||||
resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
|
resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
|
||||||
|
@ -20747,6 +20804,11 @@ process-on-spawn@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
fromentries "^1.2.0"
|
fromentries "^1.2.0"
|
||||||
|
|
||||||
|
process-warning@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.0.0.tgz#341dbeaac985b90a04ebcd844d50097c7737b2ee"
|
||||||
|
integrity sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==
|
||||||
|
|
||||||
process@^0.11.10:
|
process@^0.11.10:
|
||||||
version "0.11.10"
|
version "0.11.10"
|
||||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||||
|
@ -21034,6 +21096,11 @@ queue-microtask@^1.2.2:
|
||||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||||
|
|
||||||
|
quick-format-unescaped@^4.0.3:
|
||||||
|
version "4.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7"
|
||||||
|
integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==
|
||||||
|
|
||||||
quick-lru@^4.0.1:
|
quick-lru@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
|
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
|
||||||
|
@ -21580,6 +21647,16 @@ readable-stream@1.1.x:
|
||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
util-deprecate "^1.0.1"
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
|
readable-stream@^4.0.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.2.0.tgz#a7ef523d3b39e4962b0db1a1af22777b10eeca46"
|
||||||
|
integrity sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==
|
||||||
|
dependencies:
|
||||||
|
abort-controller "^3.0.0"
|
||||||
|
buffer "^6.0.3"
|
||||||
|
events "^3.3.0"
|
||||||
|
process "^0.11.10"
|
||||||
|
|
||||||
readdir-glob@^1.0.0:
|
readdir-glob@^1.0.0:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.2.tgz#b185789b8e6a43491635b6953295c5c5e3fd224c"
|
resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.2.tgz#b185789b8e6a43491635b6953295c5c5e3fd224c"
|
||||||
|
@ -21613,6 +21690,11 @@ readdirp@^3.4.0, readdirp@~3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
|
real-require@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78"
|
||||||
|
integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==
|
||||||
|
|
||||||
recursive-readdir@^2.2.2:
|
recursive-readdir@^2.2.2:
|
||||||
version "2.2.2"
|
version "2.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"
|
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"
|
||||||
|
@ -23352,6 +23434,13 @@ socks@^2.7.1:
|
||||||
ip "^2.0.0"
|
ip "^2.0.0"
|
||||||
smart-buffer "^4.2.0"
|
smart-buffer "^4.2.0"
|
||||||
|
|
||||||
|
sonic-boom@^3.1.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.2.0.tgz#ce9f2de7557e68be2e52c8df6d9b052e7d348143"
|
||||||
|
integrity sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA==
|
||||||
|
dependencies:
|
||||||
|
atomic-sleep "^1.0.0"
|
||||||
|
|
||||||
sort-keys-length@^1.0.0:
|
sort-keys-length@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188"
|
resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188"
|
||||||
|
@ -23525,6 +23614,11 @@ split2@^3.0.0, split2@^3.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream "^3.0.0"
|
readable-stream "^3.0.0"
|
||||||
|
|
||||||
|
split2@^4.0.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809"
|
||||||
|
integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==
|
||||||
|
|
||||||
split@^1.0.0:
|
split@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
|
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
|
||||||
|
@ -24365,6 +24459,13 @@ thenify-all@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
any-promise "^1.0.0"
|
any-promise "^1.0.0"
|
||||||
|
|
||||||
|
thread-stream@^2.0.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.2.0.tgz#310c03a253f729094ce5d4638ef5186dfa80a9e8"
|
||||||
|
integrity sha512-rUkv4/fnb4rqy/gGy7VuqK6wE1+1DOCOWy4RMeaV69ZHMP11tQKZvZSip1yTgrKCMZzEMcCL/bKfHvSfDHx+iQ==
|
||||||
|
dependencies:
|
||||||
|
real-require "^0.2.0"
|
||||||
|
|
||||||
throttle-debounce@^3.0.1:
|
throttle-debounce@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
|
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue