1
+ import { httpBodyToStream } from '@autonomys/asynchronous'
1
2
import { CompressionOptions , EncryptionOptions } from '@autonomys/auto-drive'
2
3
import { Readable } from 'stream'
3
4
import { withRetries } from './utils'
4
-
5
5
interface FetchedFile {
6
6
length : bigint
7
7
compression : CompressionOptions | undefined
@@ -40,15 +40,14 @@ export const createAutoFilesApi = (baseUrl: string, apiSecret: string) => {
40
40
const getChunk = async ( cid : string , chunk : number ) : Promise < ArrayBuffer | null > => {
41
41
const response = await authFetch ( `${ baseUrl } /files/${ cid } /partial?chunk=${ chunk } ` )
42
42
if ( ! response . ok ) {
43
- throw new Error ( ' Error fetching chunk' )
43
+ throw new Error ( ` Error fetching chunk: ${ response . status } ${ response . statusText } ` )
44
44
}
45
45
46
46
if ( response . status === 204 ) {
47
47
return null
48
48
}
49
49
50
50
const buffer = await response . arrayBuffer ( )
51
- console . log ( 'Chunk download finished:' , buffer . byteLength )
52
51
return buffer
53
52
}
54
53
@@ -61,19 +60,21 @@ export const createAutoFilesApi = (baseUrl: string, apiSecret: string) => {
61
60
* @returns A Promise that resolves to a FetchedFile object containing the file data and metadata
62
61
* @throws Error if the file metadata fetch fails
63
62
*/
64
- const getFile = async (
63
+ const getChunkedFile = async (
65
64
cid : string ,
66
65
{
67
66
retriesPerFetch = 3 ,
68
67
onProgress,
69
68
} : { retriesPerFetch ?: number ; onProgress ?: ( progress : number ) => void } = { } ,
70
69
) : Promise < FetchedFile > => {
71
- const response = await withRetries (
72
- ( ) => authFetch ( `${ baseUrl } /files/${ cid } /metadata` ) ,
73
- retriesPerFetch ,
74
- )
70
+ const response = await withRetries ( ( ) => authFetch ( `${ baseUrl } /files/${ cid } /metadata` ) , {
71
+ retries : retriesPerFetch ,
72
+ onRetry : ( error , pendingRetries ) => {
73
+ console . error ( `Error fetching file header, pending retries: ${ pendingRetries } ` , error )
74
+ } ,
75
+ } )
75
76
if ( ! response . ok ) {
76
- throw new Error ( ' Error fetching file header' )
77
+ throw new Error ( ` Error fetching file header: ${ response . status } ${ response . statusText } ` )
77
78
}
78
79
79
80
const metadata = await response . json ( )
@@ -90,7 +91,9 @@ export const createAutoFilesApi = (baseUrl: string, apiSecret: string) => {
90
91
return {
91
92
data : new Readable ( {
92
93
async read ( ) {
93
- const chunk = await withRetries ( ( ) => getChunk ( cid , i ++ ) , retriesPerFetch )
94
+ const chunk = await withRetries ( ( ) => getChunk ( cid , i ++ ) , {
95
+ retries : retriesPerFetch ,
96
+ } )
94
97
this . push ( chunk ? Buffer . from ( chunk ) : null )
95
98
totalDownloaded += BigInt ( chunk ?. byteLength ?? 0 )
96
99
onProgress ?.( Number ( ( BigInt ( precision ) * totalDownloaded ) / length ) / precision )
@@ -102,5 +105,64 @@ export const createAutoFilesApi = (baseUrl: string, apiSecret: string) => {
102
105
}
103
106
}
104
107
105
- return { getFile }
108
+ /**
109
+ * Checks if a file is cached on the gateway
110
+ * @param cid - The content identifier of the file
111
+ * @returns A Promise that resolves to true if the file is cached, false otherwise
112
+ * @throws Error if the file status check fails
113
+ */
114
+ const isFileCached = async ( cid : string ) => {
115
+ const response = await authFetch ( `${ baseUrl } /files/${ cid } /status` )
116
+ if ( ! response . ok ) {
117
+ throw new Error ( `Error checking file status: ${ response . status } ${ response . statusText } ` )
118
+ }
119
+
120
+ const status = await response . json ( )
121
+
122
+ return status . isCached
123
+ }
124
+
125
+ const getFileFromCache = async ( cid : string ) : Promise < Readable > => {
126
+ const response = await authFetch ( `${ baseUrl } /files/${ cid } ` )
127
+ if ( ! response . ok ) {
128
+ throw new Error ( `Error fetching file from cache: ${ response . status } ${ response . statusText } ` )
129
+ }
130
+
131
+ if ( ! response . body ) {
132
+ throw new Error ( 'No body in response' )
133
+ }
134
+
135
+ return httpBodyToStream ( response . body )
136
+ }
137
+
138
+ /**
139
+ * Fetches a complete file from the API with support for progress tracking and retries
140
+ * @param cid - The content identifier of the file to fetch
141
+ * @returns A Promise that resolves to a FetchedFile object containing the file data and metadata
142
+ * @throws Error if the file metadata fetch fails
143
+ */
144
+ const getFile = async (
145
+ cid : string ,
146
+ {
147
+ retriesPerFetch = 3 ,
148
+ onProgress,
149
+ ignoreCache = false ,
150
+ } : {
151
+ retriesPerFetch ?: number
152
+ onProgress ?: ( progress : number ) => void
153
+ ignoreCache ?: boolean
154
+ } = { } ,
155
+ ) => {
156
+ if ( ! ignoreCache && ( await isFileCached ( cid ) ) ) {
157
+ return getFileFromCache ( cid )
158
+ }
159
+
160
+ const file = await getChunkedFile ( cid , {
161
+ retriesPerFetch,
162
+ onProgress,
163
+ } )
164
+ return file . data
165
+ }
166
+
167
+ return { getFile, isFileCached, getChunkedFile }
106
168
}
0 commit comments