filecoin-pin
Version:
Bridge IPFS content to Filecoin Onchain Cloud using familiar tools
164 lines (147 loc) • 4.89 kB
text/typescript
/**
* Shared Synapse upload functionality
*
* This module provides a reusable upload pattern for CAR files to Filecoin
* via Synapse SDK, used by both the import command and pinning server.
*/
import type { PieceCID } from '@filoz/synapse-sdk'
import { METADATA_KEYS, type ProviderInfo, type UploadCallbacks } from '@filoz/synapse-sdk'
import type { CID } from 'multiformats/cid'
import type { Logger } from 'pino'
import type { SynapseService } from '../synapse/index.js'
import type { ProgressEvent, ProgressEventHandler } from '../utils/types.js'
export type UploadProgressEvents =
| ProgressEvent<'onUploadComplete', { pieceCid: PieceCID }>
| ProgressEvent<'onPieceAdded', { txHash: `0x${string}` | undefined }>
| ProgressEvent<'onPieceConfirmed', { pieceIds: number[] }>
export interface SynapseUploadOptions {
/**
* Optional callbacks for monitoring upload progress
*/
onProgress?: ProgressEventHandler<UploadProgressEvents>
/**
* Context identifier for logging (e.g., pinId, import job ID)
*/
contextId?: string
/**
* Optional metadata to associate with the upload
*/
pieceMetadata?: Record<string, string>
}
export interface SynapseUploadResult {
pieceCid: string
pieceId?: number | undefined
dataSetId: string
providerInfo: ProviderInfo
}
/**
* Get the direct download URL for a piece from a provider
*/
export function getDownloadURL(providerInfo: ProviderInfo, pieceCid: string): string {
const serviceURL = providerInfo.products?.PDP?.data?.serviceURL
return serviceURL ? `${serviceURL.replace(/\/$/, '')}/piece/${pieceCid}` : ''
}
/**
* Get the service URL from provider info
*/
export function getServiceURL(providerInfo: ProviderInfo): string {
return providerInfo.products?.PDP?.data?.serviceURL ?? ''
}
/**
* Upload a CAR file to Filecoin via Synapse.
*
* This function encapsulates the common upload pattern:
* 1. Submit CAR data to Synapse storage
* 2. Track upload progress via callbacks
* 3. Return piece information
*
* @param synapseService - Initialized Synapse service
* @param carData - CAR file data as Uint8Array
* @param rootCid - The IPFS root CID to associate with this piece
* @param logger - Logger instance for tracking
* @param options - Optional callbacks and context
* @returns Upload result with piece information
*/
export async function uploadToSynapse(
synapseService: SynapseService,
carData: Uint8Array,
rootCid: CID,
logger: Logger,
options: SynapseUploadOptions = {}
): Promise<SynapseUploadResult> {
const { onProgress, contextId = 'upload' } = options
// Merge provided callbacks with logging callbacks
const uploadCallbacks: UploadCallbacks = {
onUploadComplete: (pieceCid) => {
logger.info(
{
event: 'synapse.upload.piece_uploaded',
contextId,
pieceCid: pieceCid.toString(),
},
'Upload to PDP server complete'
)
onProgress?.({ type: 'onUploadComplete', data: { pieceCid } })
},
onPieceAdded: (txHash) => {
if (txHash != null) {
logger.info(
{
event: 'synapse.upload.piece_added',
contextId,
txHash: txHash,
},
'Piece addition transaction submitted'
)
} else {
logger.info(
{
event: 'synapse.upload.piece_added',
contextId,
},
'Piece added to data set'
)
}
onProgress?.({ type: 'onPieceAdded', data: { txHash } })
},
onPieceConfirmed: (pieceIds) => {
logger.info(
{
event: 'synapse.upload.piece_confirmed',
contextId,
pieceIds,
},
'Piece addition confirmed on-chain'
)
onProgress?.({ type: 'onPieceConfirmed', data: { pieceIds } })
},
}
// Upload using Synapse with IPFS root CID metadata
const uploadOptions: Parameters<typeof synapseService.synapse.storage.upload>[1] = {
...uploadCallbacks,
metadata: {
...(options.pieceMetadata ?? {}),
[METADATA_KEYS.IPFS_ROOT_CID]: rootCid.toString(), // Associate piece with IPFS root CID
},
context: synapseService.storage,
}
const synapseResult = await synapseService.synapse.storage.upload(carData, uploadOptions)
// Log success
logger.info(
{
event: 'synapse.upload.success',
contextId,
pieceCid: synapseResult.pieceCid,
pieceId: synapseResult.pieceId,
dataSetId: synapseService.storage.dataSetId,
},
'Successfully uploaded to Filecoin with Synapse'
)
const result: SynapseUploadResult = {
pieceCid: synapseResult.pieceCid.toString(),
pieceId: synapseResult.pieceId !== undefined ? Number(synapseResult.pieceId) : undefined,
dataSetId: String(synapseService.storage.dataSetId),
providerInfo: synapseService.providerInfo,
}
return result
}