@@ -141,13 +141,19 @@ export class BlobsServer {
141
141
142
142
const { dataPath, key, metadataPath, rootPath } = this . getLocalPaths ( url )
143
143
144
- if ( ! dataPath || ! metadataPath ) {
144
+ // If there's no root path, the request is invalid.
145
+ if ( ! rootPath ) {
145
146
return this . sendResponse ( req , res , 400 )
146
147
}
147
148
149
+ // If there's no data or metadata paths, it means we're listing stores.
150
+ if ( ! dataPath || ! metadataPath ) {
151
+ return this . listStores ( req , res , rootPath , url . searchParams . get ( 'prefix' ) ?? '' )
152
+ }
153
+
148
154
// If there is no key in the URL, it means a `list` operation.
149
155
if ( ! key ) {
150
- return this . list ( { dataPath, metadataPath, rootPath, req, res, url } )
156
+ return this . listBlobs ( { dataPath, metadataPath, rootPath, req, res, url } )
151
157
}
152
158
153
159
this . onRequest ( { type : Operation . GET } )
@@ -213,7 +219,7 @@ export class BlobsServer {
213
219
res . end ( )
214
220
}
215
221
216
- async list ( options : {
222
+ async listBlobs ( options : {
217
223
dataPath : string
218
224
metadataPath : string
219
225
rootPath : string
@@ -248,6 +254,19 @@ export class BlobsServer {
248
254
return this . sendResponse ( req , res , 200 , JSON . stringify ( result ) )
249
255
}
250
256
257
+ async listStores ( req : http . IncomingMessage , res : http . ServerResponse , rootPath : string , prefix : string ) {
258
+ try {
259
+ const allStores = await fs . readdir ( rootPath )
260
+ const filteredStores = allStores . filter ( ( store ) => store . startsWith ( prefix ) )
261
+
262
+ return this . sendResponse ( req , res , 200 , JSON . stringify ( { stores : filteredStores } ) )
263
+ } catch ( error ) {
264
+ this . logDebug ( 'Could not list stores:' , error )
265
+
266
+ return this . sendResponse ( req , res , 500 )
267
+ }
268
+ }
269
+
251
270
async put ( req : http . IncomingMessage , res : http . ServerResponse ) {
252
271
const apiMatch = this . parseAPIRequest ( req )
253
272
@@ -304,18 +323,24 @@ export class BlobsServer {
304
323
305
324
const [ , siteID , rawStoreName , ...key ] = url . pathname . split ( '/' )
306
325
307
- if ( ! siteID || ! rawStoreName ) {
326
+ if ( ! siteID ) {
308
327
return { }
309
328
}
310
329
330
+ const rootPath = resolve ( this . directory , 'entries' , siteID )
331
+
332
+ if ( ! rawStoreName ) {
333
+ return { rootPath }
334
+ }
335
+
311
336
// On Windows, file paths can't include the `:` character, which is used in
312
337
// deploy-scoped stores.
313
338
const storeName = platform === 'win32' ? encodeURIComponent ( rawStoreName ) : rawStoreName
314
- const rootPath = resolve ( this . directory , 'entries' , siteID , storeName )
315
- const dataPath = resolve ( rootPath , ...key )
339
+ const storePath = resolve ( rootPath , storeName )
340
+ const dataPath = resolve ( storePath , ...key )
316
341
const metadataPath = resolve ( this . directory , 'metadata' , siteID , storeName , ...key )
317
342
318
- return { dataPath, key : key . join ( '/' ) , metadataPath, rootPath }
343
+ return { dataPath, key : key . join ( '/' ) , metadataPath, rootPath : storePath }
319
344
}
320
345
321
346
handleRequest ( req : http . IncomingMessage , res : http . ServerResponse ) {
0 commit comments