Merge pull request #308 from shocknet/merged-coordinates
Merged coordinates
This commit is contained in:
commit
11992beb7c
8 changed files with 253 additions and 72 deletions
|
|
@ -4,4 +4,6 @@ MS_TO_TOKEN_EXPIRATION=4500000
|
||||||
DISABLE_SHOCK_ENCRYPTION=false
|
DISABLE_SHOCK_ENCRYPTION=false
|
||||||
CACHE_HEADERS_MANDATORY=true
|
CACHE_HEADERS_MANDATORY=true
|
||||||
SHOCK_CACHE=true
|
SHOCK_CACHE=true
|
||||||
TRUSTED_KEYS=true
|
TRUSTED_KEYS=true
|
||||||
|
TORRENT_SEED_URL=https://webtorrent.shock.network
|
||||||
|
TORRENT_SEED_TOKEN=jibberish
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@
|
||||||
"localtunnel": "^1.9.0",
|
"localtunnel": "^1.9.0",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"method-override": "^2.3.7",
|
"method-override": "^2.3.7",
|
||||||
|
"node-fetch": "^2.6.1",
|
||||||
"node-persist": "^3.1.0",
|
"node-persist": "^3.1.0",
|
||||||
"promise": "^8.1.0",
|
"promise": "^8.1.0",
|
||||||
"ramda": "^0.27.1",
|
"ramda": "^0.27.1",
|
||||||
|
|
@ -68,6 +69,7 @@
|
||||||
"@types/jest": "^24.0.18",
|
"@types/jest": "^24.0.18",
|
||||||
"@types/jsonwebtoken": "^8.3.7",
|
"@types/jsonwebtoken": "^8.3.7",
|
||||||
"@types/lodash": "^4.14.141",
|
"@types/lodash": "^4.14.141",
|
||||||
|
"@types/node-fetch": "^2.5.8",
|
||||||
"@types/ramda": "types/npm-ramda#dist",
|
"@types/ramda": "types/npm-ramda#dist",
|
||||||
"@types/react": "16.x.x",
|
"@types/react": "16.x.x",
|
||||||
"@types/socket.io": "^2.1.11",
|
"@types/socket.io": "^2.1.11",
|
||||||
|
|
|
||||||
|
|
@ -924,6 +924,7 @@ const sendHRWithInitialMsg = async (
|
||||||
* @typedef {object} SpontPaymentOptions
|
* @typedef {object} SpontPaymentOptions
|
||||||
* @prop {Common.Schema.OrderTargetType} type
|
* @prop {Common.Schema.OrderTargetType} type
|
||||||
* @prop {string=} postID
|
* @prop {string=} postID
|
||||||
|
* @prop {string=} ackInfo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1296,6 +1297,7 @@ const setLastSeenApp = () =>
|
||||||
* @returns {Promise<[string, Common.Schema.RawPost]>}
|
* @returns {Promise<[string, Common.Schema.RawPost]>}
|
||||||
*/
|
*/
|
||||||
const createPostNew = async (tags, title, content) => {
|
const createPostNew = async (tags, title, content) => {
|
||||||
|
const SEA = require('../Mediator').mySEA
|
||||||
/** @type {Common.Schema.RawPost} */
|
/** @type {Common.Schema.RawPost} */
|
||||||
const newPost = {
|
const newPost = {
|
||||||
date: Date.now(),
|
date: Date.now(),
|
||||||
|
|
@ -1311,6 +1313,23 @@ const createPostNew = async (tags, title, content) => {
|
||||||
newPost.contentItems[uuid] = c
|
newPost.contentItems[uuid] = c
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const mySecret = require('../Mediator').getMySecret()
|
||||||
|
|
||||||
|
await Common.Utils.asyncForEach(content, async c => {
|
||||||
|
// @ts-expect-error
|
||||||
|
const uuid = Gun.text.random()
|
||||||
|
newPost.contentItems[uuid] = c
|
||||||
|
if (
|
||||||
|
(c.type === 'image/embedded' || c.type === 'video/embedded') &&
|
||||||
|
c.isPrivate
|
||||||
|
) {
|
||||||
|
const encryptedMagnet = await SEA.encrypt(c.magnetURI, mySecret)
|
||||||
|
newPost.contentItems[uuid] = { ...c, magnetURI: encryptedMagnet }
|
||||||
|
} else {
|
||||||
|
newPost.contentItems[uuid] = c
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
const postID = await Common.makePromise((res, rej) => {
|
const postID = await Common.makePromise((res, rej) => {
|
||||||
const _n = require('../Mediator')
|
const _n = require('../Mediator')
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ const {
|
||||||
Constants: { ErrorCode },
|
Constants: { ErrorCode },
|
||||||
Schema
|
Schema
|
||||||
} = Common
|
} = Common
|
||||||
|
const { assertNever } = require('assert-never')
|
||||||
|
const crypto = require('crypto')
|
||||||
|
const fetch = require('node-fetch')
|
||||||
|
|
||||||
const LightningServices = require('../../../../utils/lightningServices')
|
const LightningServices = require('../../../../utils/lightningServices')
|
||||||
const {
|
const {
|
||||||
|
|
@ -20,6 +23,7 @@ const {
|
||||||
const { writeCoordinate } = require('../../../coordinates')
|
const { writeCoordinate } = require('../../../coordinates')
|
||||||
const Key = require('../key')
|
const Key = require('../key')
|
||||||
const Utils = require('../utils')
|
const Utils = require('../utils')
|
||||||
|
const { gunUUID } = require('../../../../utils')
|
||||||
|
|
||||||
const getUser = () => require('../../Mediator').getUser()
|
const getUser = () => require('../../Mediator').getUser()
|
||||||
|
|
||||||
|
|
@ -219,17 +223,178 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
||||||
/**
|
/**
|
||||||
* @param {Common.InvoiceWhenListed} invoice
|
* @param {Common.InvoiceWhenListed} invoice
|
||||||
*/
|
*/
|
||||||
const onData = invoice => {
|
const onData = async invoice => {
|
||||||
if (invoice.settled) {
|
if (invoice.settled) {
|
||||||
|
writeCoordinate(invoice.r_hash.toString(), coord)
|
||||||
|
|
||||||
if (order.targetType === 'tip') {
|
if (order.targetType === 'tip') {
|
||||||
getUser()
|
getUser()
|
||||||
.get('postToTipCount')
|
.get('postToTipCount')
|
||||||
// CAST: Checked above.
|
// CAST: Checked above.
|
||||||
.get(/** @type {string} */ (order.ackInfo))
|
.get(/** @type {string} */ (order.ackInfo))
|
||||||
.set(null) // each item in the set is a tip
|
.set(null) // each item in the set is a tip
|
||||||
|
} else if (order.targetType === 'contentReveal') {
|
||||||
|
// -----------------------------------------
|
||||||
|
logger.debug('Content Reveal')
|
||||||
|
|
||||||
|
//assuming digital product that only requires to be unlocked
|
||||||
|
const postID = order.ackInfo
|
||||||
|
|
||||||
|
if (!Common.isPopulatedString(postID)) {
|
||||||
|
logger.error(`Invalid post ID`)
|
||||||
|
logger.error(postID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: do this reactively
|
||||||
|
const selectedPost = await new Promise(res => {
|
||||||
|
getUser()
|
||||||
|
.get(Key.POSTS_NEW)
|
||||||
|
.get(postID)
|
||||||
|
.load(res)
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.debug(selectedPost)
|
||||||
|
|
||||||
|
if (Common.isPost(selectedPost)) {
|
||||||
|
logger.error('Post id provided does not correspond to a valid post')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Record<string,string>} <contentID,decryptedRef>
|
||||||
|
*/
|
||||||
|
const contentsToSend = {}
|
||||||
|
const mySecret = require('../../Mediator').getMySecret()
|
||||||
|
logger.debug('SECRET OK')
|
||||||
|
let privateFound = false
|
||||||
|
await Common.Utils.asyncForEach(
|
||||||
|
Object.entries(selectedPost.contentItems),
|
||||||
|
async ([contentID, item]) => {
|
||||||
|
if (
|
||||||
|
item.type !== 'image/embedded' &&
|
||||||
|
item.type !== 'video/embedded'
|
||||||
|
) {
|
||||||
|
return //only visual content can be private
|
||||||
|
}
|
||||||
|
if (!item.isPrivate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
privateFound = true
|
||||||
|
const decrypted = await SEA.decrypt(item.magnetURI, mySecret)
|
||||||
|
contentsToSend[contentID] = decrypted
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (!privateFound) {
|
||||||
|
logger.error(`Post provided does not contain private content`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const ackData = { unlockedContents: contentsToSend }
|
||||||
|
const toSend = JSON.stringify(ackData)
|
||||||
|
const encrypted = await SEA.encrypt(toSend, secret)
|
||||||
|
const ordResponse = {
|
||||||
|
type: 'orderAck',
|
||||||
|
response: encrypted
|
||||||
|
}
|
||||||
|
logger.debug('RES READY')
|
||||||
|
|
||||||
|
const uuid = gunUUID()
|
||||||
|
orderResponse.ackNode = uuid
|
||||||
|
|
||||||
|
await /** @type {Promise<void>} */ (new Promise((res, rej) => {
|
||||||
|
getUser()
|
||||||
|
.get(Key.ORDER_TO_RESPONSE)
|
||||||
|
.get(uuid)
|
||||||
|
.put(ordResponse, ack => {
|
||||||
|
if (ack.err && typeof ack.err !== 'number') {
|
||||||
|
rej(
|
||||||
|
new Error(
|
||||||
|
`Error saving encrypted orderAck to order to response usergraph: ${ack}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
logger.debug('RES SENT CONTENT')
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------
|
||||||
|
} else if (order.targetType === 'spontaneousPayment') {
|
||||||
|
// no action required
|
||||||
|
} else if (order.targetType === 'torrentSeed') {
|
||||||
|
logger.debug('TORRENT')
|
||||||
|
const numberOfTokens = Number(order.ackInfo)
|
||||||
|
if (isNaN(numberOfTokens)) {
|
||||||
|
logger.error('ackInfo provided is not a valid number')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const seedUrl = process.env.TORRENT_SEED_URL
|
||||||
|
const seedToken = process.env.TORRENT_SEED_TOKEN
|
||||||
|
if (!seedUrl || !seedToken) {
|
||||||
|
logger.error('torrentSeed service not available')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.debug('SEED URL OK')
|
||||||
|
const tokens = Array(numberOfTokens)
|
||||||
|
for (let i = 0; i < numberOfTokens; i++) {
|
||||||
|
tokens[i] = crypto.randomBytes(32).toString('hex')
|
||||||
|
}
|
||||||
|
/**@param {string} token */
|
||||||
|
const enrollToken = async token => {
|
||||||
|
const reqData = {
|
||||||
|
seed_token: seedToken,
|
||||||
|
wallet_token: token
|
||||||
|
}
|
||||||
|
// @ts-expect-error TODO
|
||||||
|
const res = await fetch(`${seedUrl}/api/enroll_token`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(reqData)
|
||||||
|
})
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw new Error('torrentSeed service currently not available')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Promise.all(tokens.map(enrollToken))
|
||||||
|
logger.debug('RES SEED OK')
|
||||||
|
const ackData = { seedUrl, tokens }
|
||||||
|
const toSend = JSON.stringify(ackData)
|
||||||
|
const encrypted = await SEA.encrypt(toSend, secret)
|
||||||
|
const serviceResponse = {
|
||||||
|
type: 'orderAck',
|
||||||
|
response: encrypted
|
||||||
|
}
|
||||||
|
console.log('RES SEED SENT')
|
||||||
|
|
||||||
|
const uuid = gunUUID()
|
||||||
|
orderResponse.ackNode = uuid
|
||||||
|
|
||||||
|
await /** @type {Promise<void>} */ (new Promise((res, rej) => {
|
||||||
|
getUser()
|
||||||
|
.get(Key.ORDER_TO_RESPONSE)
|
||||||
|
.get(uuid)
|
||||||
|
.put(serviceResponse, ack => {
|
||||||
|
if (ack.err && typeof ack.err !== 'number') {
|
||||||
|
rej(
|
||||||
|
new Error(
|
||||||
|
`Error saving encrypted orderAck to order to response usergraph: ${ack}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
logger.debug('RES SENT SEED')
|
||||||
|
} else if (order.targetType === 'other') {
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
assertNever(order.targetType)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeCoordinate(invoice.r_hash.toString(), coord)
|
|
||||||
stream.off()
|
stream.off()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1192,12 +1192,35 @@ module.exports = async (
|
||||||
|
|
||||||
app.post('/api/lnd/unifiedTrx', async (req, res) => {
|
app.post('/api/lnd/unifiedTrx', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { type, amt, to, memo, feeLimit, postID } = req.body
|
const { type, amt, to, memo, feeLimit, postID, ackInfo } = req.body
|
||||||
|
|
||||||
if (type !== 'spont' && type !== 'post') {
|
if (
|
||||||
|
type !== 'spont' &&
|
||||||
|
type !== 'post' &&
|
||||||
|
type !== 'spontaneousPayment' &&
|
||||||
|
type !== 'tip' &&
|
||||||
|
type !== 'torrentSeed' &&
|
||||||
|
type !== 'contentReveal' &&
|
||||||
|
type !== 'other'
|
||||||
|
) {
|
||||||
return res.status(415).json({
|
return res.status(415).json({
|
||||||
field: 'type',
|
field: 'type',
|
||||||
errorMessage: `Only 'spont' and 'post' payments supported via this endpoint for now.`
|
errorMessage: `Only 'spontaneousPayment'| 'tip' | 'torrentSeed' | 'contentReveal' | 'other' payments supported via this endpoint for now.`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const typesThatShouldContainAckInfo = [
|
||||||
|
'tip',
|
||||||
|
'torrentSeed',
|
||||||
|
'contentReveal'
|
||||||
|
]
|
||||||
|
|
||||||
|
const shouldContainAckInfo = typesThatShouldContainAckInfo.includes(type)
|
||||||
|
|
||||||
|
if (shouldContainAckInfo && !Common.isPopulatedString(ackInfo)) {
|
||||||
|
return res.status(400).json({
|
||||||
|
field: 'ackInfo',
|
||||||
|
errorMessage: `Transactions of type ${typesThatShouldContainAckInfo} should contain an ackInfo field.`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1241,7 +1264,8 @@ module.exports = async (
|
||||||
return res.status(200).json(
|
return res.status(200).json(
|
||||||
await GunActions.sendSpontaneousPayment(to, amt, memo, feeLimit, {
|
await GunActions.sendSpontaneousPayment(to, amt, memo, feeLimit, {
|
||||||
type,
|
type,
|
||||||
postID
|
postID,
|
||||||
|
ackInfo
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -66,66 +66,6 @@ module.exports = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseJSON = data => {
|
|
||||||
try {
|
|
||||||
if (typeof data === 'string') {
|
|
||||||
return JSON.parse(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
} catch (err) {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const decryptEvent = ({ eventName, data, socket }) => {
|
|
||||||
try {
|
|
||||||
const deviceId = socket.handshake.query['x-shockwallet-device-id']
|
|
||||||
if (Encryption.isNonEncrypted(eventName)) {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedData = parseJSON(data)
|
|
||||||
|
|
||||||
if (!deviceId) {
|
|
||||||
throw {
|
|
||||||
field: 'deviceId',
|
|
||||||
message: 'Please specify a device ID'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Encryption.isAuthorizedDevice({ deviceId })) {
|
|
||||||
throw {
|
|
||||||
field: 'deviceId',
|
|
||||||
message: 'Please exchange keys with the API before using the socket'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const decryptedKey = Encryption.decryptKey({
|
|
||||||
deviceId,
|
|
||||||
message: parsedData.encryptedKey
|
|
||||||
})
|
|
||||||
const decryptedMessage = Encryption.decryptMessage({
|
|
||||||
message: parsedData.encryptedData,
|
|
||||||
key: decryptedKey,
|
|
||||||
iv: parsedData.iv
|
|
||||||
})
|
|
||||||
const decryptedData = JSON.parse(decryptedMessage)
|
|
||||||
return decryptedData
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(
|
|
||||||
`[SOCKET] An error has occurred while decrypting an event (${eventName}):`,
|
|
||||||
err
|
|
||||||
)
|
|
||||||
|
|
||||||
return socket.emit('encryption:error', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onNewInvoice = (socket, subID) => {
|
const onNewInvoice = (socket, subID) => {
|
||||||
const { lightning } = LightningServices.services
|
const { lightning } = LightningServices.services
|
||||||
logger.warn('Subscribing to invoices socket...' + subID)
|
logger.warn('Subscribing to invoices socket...' + subID)
|
||||||
|
|
@ -677,7 +617,7 @@ module.exports = (
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Common.Schema.SimpleReceivedRequest[]} receivedReqs
|
* @param {ReadonlyArray<Common.SimpleReceivedRequest>} receivedReqs
|
||||||
*/
|
*/
|
||||||
const onReceivedReqs = receivedReqs => {
|
const onReceivedReqs = receivedReqs => {
|
||||||
const processed = receivedReqs.map(({ id, requestorPK, timestamp }) => {
|
const processed = receivedReqs.map(({ id, requestorPK, timestamp }) => {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,21 @@
|
||||||
/**
|
/**
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
const Gun = require('gun')
|
||||||
|
|
||||||
const { asyncFilter } = require('./helpers')
|
const { asyncFilter } = require('./helpers')
|
||||||
|
|
||||||
module.exports = {
|
/**
|
||||||
asyncFilter
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
const gunUUID = () => {
|
||||||
|
// @ts-expect-error Not typed
|
||||||
|
const uuid = Gun.Text.random()
|
||||||
|
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
asyncFilter,
|
||||||
|
gunUUID
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
yarn.lock
21
yarn.lock
|
|
@ -722,6 +722,14 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
|
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
|
||||||
integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==
|
integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==
|
||||||
|
|
||||||
|
"@types/node-fetch@^2.5.8":
|
||||||
|
version "2.5.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
|
||||||
|
integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
form-data "^3.0.0"
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "12.7.4"
|
version "12.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.4.tgz#64db61e0359eb5a8d99b55e05c729f130a678b04"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.4.tgz#64db61e0359eb5a8d99b55e05c729f130a678b04"
|
||||||
|
|
@ -1838,7 +1846,7 @@ colour@~0.7.1:
|
||||||
resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778"
|
resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778"
|
||||||
integrity sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=
|
integrity sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=
|
||||||
|
|
||||||
combined-stream@^1.0.6, combined-stream@~1.0.6:
|
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
|
||||||
version "1.0.8"
|
version "1.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||||
|
|
@ -2946,6 +2954,15 @@ forever-agent@~0.6.1:
|
||||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||||
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
|
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
|
||||||
|
|
||||||
|
form-data@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
|
||||||
|
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
|
||||||
|
dependencies:
|
||||||
|
asynckit "^0.4.0"
|
||||||
|
combined-stream "^1.0.8"
|
||||||
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
form-data@~2.3.2:
|
form-data@~2.3.2:
|
||||||
version "2.3.3"
|
version "2.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
|
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
|
||||||
|
|
@ -4991,7 +5008,7 @@ nice-try@^1.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||||
|
|
||||||
node-fetch@^2.3.0:
|
node-fetch@^2.3.0, node-fetch@^2.6.1:
|
||||||
version "2.6.1"
|
version "2.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue