@@ -13,6 +13,7 @@ import {
13
13
} from '../../../logic/project-files'
14
14
import { getCatalystBaseUrl } from '../../../logic/config'
15
15
import { Workspace } from '../../../logic/workspace-validations'
16
+ import { ProjectUnion , WearableProject } from '../../../logic/project-validations'
16
17
17
18
function smartWearableNameToId ( name : string ) {
18
19
return name . toLocaleLowerCase ( ) . replace ( / / g, '-' )
@@ -29,13 +30,11 @@ export async function setupEcs6Endpoints(
29
30
) {
30
31
const catalystUrl = new URL ( await getCatalystBaseUrl ( components ) )
31
32
32
- const baseFolders = workspace . projects . map ( ( $ ) => $ . workingDirectory )
33
-
34
- // handle old preview scene.json
33
+ // handle old preview scene.json DEPRECATED
35
34
router . get ( '/scene.json' , async ( ) => {
36
35
return {
37
36
headers : { 'content-type' : 'application/json' } ,
38
- body : components . fs . createReadStream ( path . join ( baseFolders [ 0 ] , 'scene.json' ) )
37
+ body : components . fs . createReadStream ( path . join ( workspace . projects [ 0 ] . workingDirectory , 'scene.json' ) )
39
38
}
40
39
} )
41
40
@@ -70,8 +69,7 @@ export async function setupEcs6Endpoints(
70
69
const baseUrl = `${ ctx . url . protocol } //${ ctx . url . host } /content/contents`
71
70
72
71
try {
73
- const previewWearables = await getAllPreviewWearables ( components , {
74
- baseFolders,
72
+ const previewWearables = await getAllPreviewWearables ( components , workspace , {
75
73
baseUrl
76
74
} )
77
75
@@ -144,29 +142,52 @@ export async function setupEcs6Endpoints(
144
142
145
143
// TODO: get workspace scenes & wearables...
146
144
147
- await serveFolders ( components , router , baseFolders )
145
+ await serveFolders ( components , router , workspace )
148
146
}
149
147
150
148
async function serveFolders (
151
149
components : Pick < CliComponents , 'fs' | 'logger' | 'fetch' | 'config' > ,
152
150
router : Router < PreviewComponents > ,
153
- baseFolders : string [ ]
151
+ workspace : Workspace
154
152
) {
155
153
const catalystUrl = await getCatalystBaseUrl ( components )
156
154
157
- router . get ( '/content/contents/:hash' , async ( ctx : any , next : any ) => {
155
+ router . get ( '/content/contents/:hash' , async ( ctx , next ) => {
158
156
if ( ctx . params . hash && ctx . params . hash . startsWith ( 'b64-' ) ) {
159
157
const fullPath = path . resolve ( Buffer . from ( ctx . params . hash . replace ( / ^ b 6 4 - / , '' ) , 'base64' ) . toString ( 'utf8' ) )
160
158
159
+ // find a project that we are talking about. NOTE: this filter is not exhaustive
160
+ // relative paths should be used instead
161
+ const baseProject = workspace . projects . find ( ( project ) => fullPath . startsWith ( project . workingDirectory ) )
162
+
161
163
// only return files IF the file is within a baseFolder
162
- if ( ! baseFolders . find ( ( folder : string ) => fullPath . startsWith ( folder ) ) ) {
164
+ if ( ! baseProject ) {
163
165
return next ( )
164
166
}
165
167
168
+ if ( path . resolve ( fullPath ) === path . resolve ( baseProject . workingDirectory ) ) {
169
+ // if we are talking about the root directory, then we must return the json of the entity
170
+ const entity = await fakeEntityV3FromProject ( components , baseProject , b64HashingFunction )
171
+
172
+ if ( ! entity ) return { status : 404 }
173
+
174
+ return {
175
+ headers : {
176
+ 'x-timestamp' : Date . now ( ) . toString ( ) ,
177
+ 'x-sent' : 'true' ,
178
+ 'cache-control' : 'no-cache,private,max-age=1'
179
+ } ,
180
+ body : entity
181
+ }
182
+ }
183
+
184
+ if ( ! ( await components . fs . fileExists ( fullPath ) ) ) return { status : 404 }
185
+ if ( await components . fs . directoryExists ( fullPath ) ) return { status : 404 }
186
+
166
187
return {
167
188
headers : {
168
- 'x-timestamp' : Date . now ( ) ,
169
- 'x-sent' : true ,
189
+ 'x-timestamp' : Date . now ( ) . toString ( ) ,
190
+ 'x-sent' : ' true' ,
170
191
'cache-control' : 'no-cache,private,max-age=1'
171
192
} ,
172
193
body : components . fs . createReadStream ( fullPath )
@@ -185,7 +206,8 @@ async function serveFolders(
185
206
pointers && typeof pointers === 'string' ? [ pointers as string ] : ( pointers as string [ ] )
186
207
)
187
208
188
- const resultEntities = await getSceneJson ( components , baseFolders , Array . from ( requestedPointers ) )
209
+ const resultEntities = await getSceneJson ( components , workspace , Array . from ( requestedPointers ) )
210
+
189
211
const remote = fetchEntityByPointer (
190
212
components ,
191
213
catalystUrl . toString ( ) ,
@@ -214,9 +236,8 @@ async function serveFolders(
214
236
215
237
router . get ( '/preview-wearables/:id' , async ( ctx ) => {
216
238
const baseUrl = `${ ctx . url . protocol } //${ ctx . url . host } /content/contents`
217
- const wearables = await getAllPreviewWearables ( components , {
218
- baseUrl,
219
- baseFolders
239
+ const wearables = await getAllPreviewWearables ( components , workspace , {
240
+ baseUrl
220
241
} )
221
242
const wearableId = ctx . params . id
222
243
return {
@@ -232,43 +253,49 @@ async function serveFolders(
232
253
return {
233
254
body : {
234
255
ok : true ,
235
- data : await getAllPreviewWearables ( components , { baseUrl, baseFolders } )
256
+ data : await getAllPreviewWearables ( components , workspace , { baseUrl } )
236
257
}
237
258
}
238
259
} )
239
260
}
240
261
241
262
async function getAllPreviewWearables (
242
263
components : Pick < CliComponents , 'fs' | 'logger' > ,
243
- { baseFolders, baseUrl } : { baseFolders : string [ ] ; baseUrl : string }
264
+ workspace : Workspace ,
265
+ { baseUrl } : { baseUrl : string }
244
266
) {
267
+ // NOTE: the explorers should use the /entities/active endpoint to retrieve the wearables. This endpoint should be removed
245
268
const wearablePathArray : string [ ] = [ ]
246
- for ( const wearableDir of baseFolders ) {
247
- const wearableJsonPath = path . resolve ( wearableDir , 'wearable.json' )
248
- if ( await components . fs . fileExists ( wearableJsonPath ) ) {
249
- wearablePathArray . push ( wearableJsonPath )
269
+
270
+ for ( const project of workspace . projects ) {
271
+ if ( project . kind === 'wearable' ) {
272
+ const wearableJsonPath = path . resolve ( project . workingDirectory , 'wearable.json' )
273
+ if ( await components . fs . fileExists ( wearableJsonPath ) ) {
274
+ wearablePathArray . push ( wearableJsonPath )
275
+ }
250
276
}
251
277
}
252
278
253
279
const ret : LambdasWearable [ ] = [ ]
254
- for ( const wearableJsonPath of wearablePathArray ) {
280
+ for ( const project of workspace . projects ) {
255
281
try {
256
- ret . push ( await serveWearable ( components , wearableJsonPath , baseUrl ) )
282
+ if ( project . kind === 'wearable' ) ret . push ( await serveWearable ( components , project , baseUrl ) )
257
283
} catch ( err ) {
258
284
components . logger . error (
259
- `Couldn't mock the wearable ${ wearableJsonPath } . Please verify the correct format and scheme.` + err
285
+ `Couldn't mock the wearable ${ project . workingDirectory } . Please verify the correct format and scheme.` + err
260
286
)
261
287
}
262
288
}
289
+
263
290
return ret
264
291
}
265
292
266
293
async function serveWearable (
267
294
components : Pick < CliComponents , 'fs' | 'logger' > ,
268
- wearableJsonPath : string ,
295
+ project : WearableProject ,
269
296
baseUrl : string
270
297
) : Promise < LambdasWearable > {
271
- const wearableDir = path . dirname ( wearableJsonPath )
298
+ const wearableJsonPath = path . join ( project . workingDirectory , 'wearable.json' )
272
299
const wearableJson = JSON . parse ( ( await components . fs . readFile ( wearableJsonPath ) ) . toString ( ) )
273
300
274
301
if ( ! WearableJson . validate ( wearableJson ) ) {
@@ -278,8 +305,12 @@ async function serveWearable(
278
305
throw new Error ( `Invalid wearable.json (${ wearableJsonPath } )` )
279
306
}
280
307
281
- const projectFiles = await getProjectPublishableFilesWithHashes ( components , wearableDir , b64HashingFunction )
282
- const contentFiles = projectFilesToContentMappings ( wearableDir , projectFiles )
308
+ const projectFiles = await getProjectPublishableFilesWithHashes (
309
+ components ,
310
+ project . workingDirectory ,
311
+ b64HashingFunction
312
+ )
313
+ const contentFiles = projectFilesToContentMappings ( project . workingDirectory , projectFiles )
283
314
284
315
const thumbnailFiltered = contentFiles . filter ( ( $ ) => $ . file === 'thumbnail.png' )
285
316
const thumbnail =
@@ -320,19 +351,20 @@ async function serveWearable(
320
351
321
352
async function getSceneJson (
322
353
components : Pick < CliComponents , 'fs' | 'logger' > ,
323
- projectRoots : string [ ] ,
354
+ workspace : Workspace ,
324
355
pointers : string [ ]
325
356
) : Promise < Entity [ ] > {
326
357
const requestedPointers = new Set < string > ( pointers )
327
358
const resultEntities : Entity [ ] = [ ]
328
359
329
360
const allDeployments = await Promise . all (
330
- projectRoots . map ( async ( projectRoot ) => fakeEntityV3FromFolder ( components , projectRoot , b64HashingFunction ) )
361
+ workspace . projects . map ( ( project ) => fakeEntityV3FromProject ( components , project , b64HashingFunction ) )
331
362
)
332
363
333
364
for ( const pointer of Array . from ( requestedPointers ) ) {
334
365
// get deployment by pointer
335
366
const theDeployment = allDeployments . find ( ( $ ) => $ && $ . pointers . includes ( pointer ) )
367
+
336
368
if ( theDeployment ) {
337
369
// remove all the required pointers from the requestedPointers set
338
370
// to prevent sending duplicated entities
@@ -403,6 +435,10 @@ function serveStatic(components: Pick<CliComponents, 'fs'>, workspace: Workspace
403
435
return next ( )
404
436
}
405
437
438
+ if ( await components . fs . directoryExists ( fullPath ) ) {
439
+ return { status : 404 }
440
+ }
441
+
406
442
const headers : Record < string , any > = {
407
443
'x-timestamp' : Date . now ( ) ,
408
444
'x-sent' : true ,
@@ -436,51 +472,55 @@ function serveStatic(components: Pick<CliComponents, 'fs'>, workspace: Workspace
436
472
} )
437
473
}
438
474
439
- async function fakeEntityV3FromFolder (
475
+ async function fakeEntityV3FromProject (
440
476
components : Pick < CliComponents , 'fs' | 'logger' > ,
441
- projectRoot : string ,
477
+ project : ProjectUnion ,
442
478
hashingFunction : ( filePath : string ) => Promise < string >
443
479
) : Promise < Entity | null > {
444
- const sceneJsonPath = path . resolve ( projectRoot , 'scene.json' )
445
- let isParcelScene = true
480
+ const projectFiles = await getProjectPublishableFilesWithHashes ( components , project . workingDirectory , hashingFunction )
481
+ const contentFiles = projectFilesToContentMappings ( project . workingDirectory , projectFiles )
446
482
447
- const wearableJsonPath = path . resolve ( projectRoot , 'wearable.json' )
448
- if ( await components . fs . fileExists ( wearableJsonPath ) ) {
449
- try {
450
- const wearableJson = JSON . parse ( await components . fs . readFile ( wearableJsonPath , 'utf-8' ) )
451
- if ( ! WearableJson . validate ( wearableJson ) ) {
452
- const errors = ( WearableJson . validate . errors || [ ] ) . map ( ( a ) => `${ a . data } ${ a . message } ` ) . join ( '' )
453
-
454
- components . logger . error ( `Unable to validate wearable.json properly, please check its schema.` + errors )
455
- components . logger . error ( `Invalid wearable.json (${ wearableJsonPath } )` )
456
- } else {
457
- isParcelScene = false
458
- }
459
- } catch ( err : any ) {
460
- components . logger . error ( `Unable to load wearable.json` )
461
- components . logger . error ( err )
462
- }
463
- }
464
-
465
- if ( ( await components . fs . fileExists ( sceneJsonPath ) ) && isParcelScene ) {
483
+ if ( project . kind === 'scene' ) {
484
+ const sceneJsonPath = path . resolve ( project . workingDirectory , 'scene.json' )
466
485
const sceneJson = JSON . parse ( await components . fs . readFile ( sceneJsonPath , 'utf-8' ) )
467
486
const { base, parcels } : { base : string ; parcels : string [ ] } = sceneJson . scene
468
487
const pointers = new Set < string > ( )
469
488
pointers . add ( base )
470
489
parcels . forEach ( ( $ ) => pointers . add ( $ ) )
471
490
472
- const projectFiles = await getProjectPublishableFilesWithHashes ( components , projectRoot , hashingFunction )
473
- const contentFiles = projectFilesToContentMappings ( projectRoot , projectFiles )
474
-
475
491
return {
476
492
version : 'v3' ,
477
493
type : EntityType . SCENE ,
478
- id : await hashingFunction ( projectRoot ) ,
494
+ id : await hashingFunction ( project . workingDirectory ) ,
479
495
pointers : Array . from ( pointers ) ,
480
496
timestamp : Date . now ( ) ,
481
497
metadata : sceneJson ,
482
498
content : contentFiles
483
499
}
500
+ } else if ( project . kind === 'wearable' ) {
501
+ const wearableJsonPath = path . resolve ( project . workingDirectory , 'wearable.json' )
502
+ try {
503
+ const wearableJson = JSON . parse ( await components . fs . readFile ( wearableJsonPath , 'utf-8' ) )
504
+ if ( ! WearableJson . validate ( wearableJson ) ) {
505
+ const errors = ( WearableJson . validate . errors || [ ] ) . map ( ( a ) => `${ a . data } ${ a . message } ` ) . join ( '' )
506
+
507
+ components . logger . error ( `Unable to validate wearable.json properly, please check its schema.` + errors )
508
+ components . logger . error ( `Invalid wearable.json (${ wearableJsonPath } )` )
509
+ }
510
+
511
+ return {
512
+ version : 'v3' ,
513
+ type : EntityType . WEARABLE ,
514
+ id : await hashingFunction ( project . workingDirectory ) ,
515
+ pointers : Array . from ( [ await hashingFunction ( project . workingDirectory ) ] ) ,
516
+ timestamp : Date . now ( ) ,
517
+ metadata : wearableJson ,
518
+ content : contentFiles
519
+ }
520
+ } catch ( err : any ) {
521
+ components . logger . error ( `Unable to load wearable.json` )
522
+ components . logger . error ( err )
523
+ }
484
524
}
485
525
486
526
return null
0 commit comments