Skip to content

Commit

Permalink
Merge pull request #475 from ourzora/iain/switch-ipfs-to-pinata
Browse files Browse the repository at this point in the history
Fix media upload server -> move to pinata.cloud backend
  • Loading branch information
iainnash authored Aug 22, 2024
2 parents 2511e50 + a83e8a3 commit 79ffc28
Show file tree
Hide file tree
Showing 6 changed files with 14,178 additions and 12,232 deletions.
3 changes: 1 addition & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"hono": "^4.0.9",
"html-react-parser": "^3.0.9",
"ioredis": "^5.2.3",
"ipfs-http-client": "^59.0.0",
"ipfs-service": "workspace:*",
"lanyard": "^1.1.2",
"lodash": "^4.17.21",
Expand Down Expand Up @@ -96,4 +95,4 @@
"vite-plugin-svgr": "^2.4.0",
"vitest": "^0.27.3"
}
}
}
48 changes: 48 additions & 0 deletions apps/web/src/app/api/upload-key/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { NextRequest, NextResponse } from 'next/server'

const PINATA_API_KEY = process.env.PINATA_API_KEY

const keyRestrictions = {
keyName: 'Signed Upload JWT',
maxUses: 1,
permissions: {
endpoints: {
data: {
pinList: false,
userPinnedDataTotal: false,
},
pinning: {
pinFileToIPFS: true,
pinJSONToIPFS: false,
pinJobs: false,
unpin: false,
userPinPolicy: false,
},
},
},
}

export async function POST(request: NextRequest): Promise<NextResponse> {
try {
const options = {
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
authorization: `Bearer ${PINATA_API_KEY}`,
},
body: JSON.stringify(keyRestrictions),
}

const jwtRepsonse = await fetch(
'https://api.pinata.cloud/users/generateApiKey',
options
)
const json = await jwtRepsonse.json()
const { JWT } = json
return NextResponse.json({ JWT }, { status: 200 })
} catch (e) {
console.log(e)
return NextResponse.json({ error: 'Server Error' }, { status: 500 })
}
}
1 change: 1 addition & 0 deletions apps/web/src/hooks/useArtworkUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ export const useArtworkUpload = ({
try {
onUploadStart()
const ipfs = await uploadToIPFS(files)
console.log({ ipfs })
onUploadSuccess(ipfs)
} catch (err) {
setIpfsUploadError(true)
Expand Down
123 changes: 88 additions & 35 deletions packages/ipfs-service/src/upload.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
import { hashFiles } from './hash'
import { create as createIpfsClient } from 'ipfs-http-client'
import last from 'it-last'

const IPFS_API_BASE =
process.env.NEXT_PUBLIC_IPFS_UPLOAD_API || 'https://upload.ipfs.zora.co'

const defaultIpfsOptions = {
cidVersion: 1,
} as const

const ipfs = createIpfsClient({
url: `${IPFS_API_BASE}/api/v0`,
})

const defaultOptions = {
onProgress: undefined,
Expand All @@ -25,6 +12,56 @@ export type IPFSUploadResponse = {

type ProgressCallback = (progress: number) => void

function uploadFileWithProgress(
data: FormData,
onProgress: (progress: number) => void
): Promise<void> {
return new Promise(async (resolve, reject) => {
const xhr = new XMLHttpRequest()

const uploadKey = await fetch('/api/upload-key', {
method: 'POST',
headers: {
accept: 'application/json',
},
})

if (!uploadKey.ok) {
reject('No KEY')
}
const { JWT: jwt } = await uploadKey.json()

xhr.open('POST', 'https://api.pinata.cloud/pinning/pinFileToIPFS', true)
xhr.setRequestHeader('Authorization', `Bearer ${jwt}`)

// Add event listener to track upload progress
xhr.upload.onprogress = (event: ProgressEvent) => {
if (event.lengthComputable) {
const progress = (event.loaded / event.total) * 100
onProgress(progress) // Call the progress callback
}
}

// Handle successful upload
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
const jsonResponse = JSON.parse(xhr.responseText)
resolve(jsonResponse)
} else {
reject(new Error(`Upload failed with status: ${xhr.status}`))
}
}

// Handle errors
xhr.onerror = () => {
reject(new Error('An error occurred during the upload.'))
}

// Send the FormData
xhr.send(data)
})
}

const uploadCache = {
prefix: 'ZORA/IPFSUploadCache',
get(files: File[]): IPFSUploadResponse | undefined {
Expand Down Expand Up @@ -62,14 +99,20 @@ export async function uploadFile(
if (cached) return cached
}

const root = await ipfs.add(file, {
...defaultIpfsOptions,
progress: (bytes: number) => {
if (typeof onProgress === 'function') onProgress((bytes / file.size) * 100)
},
})
const data = new FormData()
data.append('file', file)

const response = (await uploadFileWithProgress(data, (progress) => {
console.log(`Upload progress: ${progress}%`)
// You can also update the UI with the progress here
if (typeof onProgress === 'function') {
onProgress(progress)
}
})) as any

const cid = root.cid.toString()
console.log({ response })

const cid = response.IpfsHash.toString()
const uri = `ipfs://${cid}`

console.info('ipfs-service/upload', { cid, uri })
Expand Down Expand Up @@ -119,25 +162,35 @@ export async function uploadDirectory(
if (cached) return cached
}

const totalBytes = entries.reduce((total, entry) => total + entry.content.size, 0)
let completeBytes = 0

const root = await last(
ipfs.addAll(entries, {
...defaultIpfsOptions,
wrapWithDirectory: true,
progress: (bytes: number) => {
completeBytes += bytes
if (typeof onProgress === 'function') {
onProgress((completeBytes / totalBytes) * 100)
}
},
const data = new FormData()
entries.forEach((file) => {
console.log({ file })
data.append('file', file.content, `builder/${file.path}`)
})
data.append(
'pinataOptions',
JSON.stringify({
cidVersion: 1,
})
)
data.append(
'pinataMetadata',
JSON.stringify({
name: 'builder',
})
)

const response = (await uploadFileWithProgress(data, (progress) => {
console.log(`Upload progress: ${progress}%`)
// You can also update the UI with the progress here
if (typeof onProgress === 'function') {
onProgress(progress)
}
})) as any

if (!root) throw new Error('Directory upload failed')
console.log({ response })

const cid = root.cid.toString()
const cid = response.IpfsHash.toString()
const uri = `ipfs://${cid}`

console.info('ipfs-service/uploadDirectory', { cid })
Expand Down
Loading

0 comments on commit 79ffc28

Please sign in to comment.