1010 * This file expects that `build:tsc` was ran first, and this will be handled
1111 * for you if ran via `npm run start`.
1212 */
13+ import { existsSync , createReadStream } from 'node:fs'
14+ import { createServer , Server } from 'node:http'
15+ import { join , extname } from 'node:path'
1316import { pathToFileURL } from 'node:url'
14- import { logger } from '@libp2p/logger'
1517import { execa } from 'execa'
1618import { createKuboNode } from './test-e2e/fixtures/create-kubo-node.js'
1719import { loadIpnsRecords } from './test-e2e/fixtures/load-ipns-records.js'
1820import { downloadFixtures , getIpfsNsMap , loadCarFixtures } from './test-e2e/fixtures/load-kubo-fixtures.js'
1921import { setupIpfsGateway } from './test-e2e/ipfs-gateway.js'
20- import { createReverseProxy } from './test-e2e/reverse-proxy.js'
2122import type { KuboNode } from 'ipfsd-ctl'
2223
23- const log = logger ( 'serve' )
24-
2524// if user passes "--load-fixtures" flag, load all the fixtures
2625const args = process . argv . slice ( 2 )
2726
@@ -34,14 +33,49 @@ async function loadFixtures (): Promise<{
3433 const controller = await createKuboNode ( IPFS_NS_MAP )
3534 await controller . start ( )
3635
37- await loadCarFixtures ( )
38- await loadIpnsRecords ( controller , log )
36+ await loadCarFixtures ( controller )
37+ await loadIpnsRecords ( controller )
3938
4039 return {
4140 controller
4241 }
4342}
4443
44+ const MIME_TYPES : Record < string , string > = {
45+ '.js' : 'text/javascript; charset=utf8' ,
46+ '.png' : 'image/png' ,
47+ '.map' : 'text/plain' ,
48+ '.css' : 'text/css; charset=utf8' ,
49+ '.svg' : 'image/svg+xml' ,
50+ '.json' : 'application/json; charset=utf8' ,
51+ '.html' : 'text/html; charset=utf8'
52+ }
53+
54+ /**
55+ * create a web server that serves an asset if it exists or index.html if not,
56+ * this simulates the behaviour of Cloudflare with `./dist/_redirects`
57+ */
58+ function createFrontend ( ) : Server {
59+ return createServer ( ( req , res ) => {
60+ let file = req . url
61+
62+ if ( file == null || file === '/' ) {
63+ file = 'index.html'
64+ }
65+
66+ let asset = join ( './dist' , file )
67+
68+ if ( ! existsSync ( asset ) ) {
69+ // serve index.html instead of 404ing
70+ asset = './dist/index.html'
71+ }
72+
73+ res . statusCode = 200
74+ res . setHeader ( 'content-type' , MIME_TYPES [ extname ( asset ) ] ?? 'application/octet-stream' )
75+ createReadStream ( asset ) . pipe ( res )
76+ } )
77+ }
78+
4579export interface ServeOptions {
4680 shouldLoadFixtures ? : boolean
4781 shouldStartFrontend ? : boolean
@@ -54,37 +88,42 @@ export async function serve ({ shouldLoadFixtures = false, shouldStartFrontend =
5488 controller: KuboNode
5589} > {
5690 let controller : KuboNode
91+
5792 if ( shouldLoadFixtures ) {
5893 const fixtures = await loadFixtures ( )
5994 controller = fixtures . controller
60- // IPFS_NS_MAP = fixtures.IPFS_NS_MAP
6195 } else {
6296 controller = await createKuboNode ( )
6397 }
6498
6599 // sets up kubo node and ipfs gateway
66100 const ipfsGateway = await setupIpfsGateway ( )
67-
101+ /*
68102 // sets up reverse proxy for front-end assets being auto-loaded
69103 const reverseProxy = createReverseProxy({
70104 backendPort: shouldStartFrontend ? 8345 : 3000, // front-end server port. 3000 if playwright is running, 8345 if build.js starts the server
71105 proxyPort: 3333
72106 })
107+ */
108+ // rebuild the app on changes
109+ const build = shouldStartFrontend ? execa ( 'node' , [ 'build.js' , '--watch' ] ) : undefined
110+ build ?. stdout ?. pipe ( process . stdout )
111+ build ?. stderr ?. pipe ( process . stderr )
73112
74- // call build.js with --serve and --watch flags, piping the output to the console
75- const frontend = shouldStartFrontend ? execa ( 'node' , [ 'build.js' , '--serve' , '--watch' ] ) : undefined
76-
77- frontend ?. stdout ?. pipe ( process . stdout )
78- frontend ?. stderr ?. pipe ( process . stderr )
113+ // serve the dist folder similarly to how cloudflare does
114+ const frontend = createFrontend ( )
115+ frontend . listen ( 3333 )
79116
80117 const cleanup = async ( ) : Promise < void > => {
81- frontend ?. kill ( )
82- reverseProxy . close ( )
118+ build ?. kill ( )
119+ // reverseProxy.close()
83120 await ipfsGateway . stop ( )
121+ frontend . close ( )
122+ frontend . closeAllConnections ( )
84123 }
85124
86125 // when the process exits, stop the reverse proxy
87- frontend ?. on ( 'exit' , ( ) => { void cleanup ( ) } )
126+ build ?. on ( 'exit' , ( ) => { void cleanup ( ) } )
88127 process . on ( 'SIGINT' , ( ) => { void cleanup ( ) } )
89128 process . on ( 'SIGTERM' , ( ) => { void cleanup ( ) } )
90129
@@ -95,5 +134,14 @@ export async function serve ({ shouldLoadFixtures = false, shouldStartFrontend =
95134
96135// Run main function if this file is being executed directly
97136if ( import . meta. url === pathToFileURL ( process . argv [ 1 ] ) . href ) {
98- await serve ( { shouldLoadFixtures : args . includes ( '--load-fixtures' ) , shouldStartFrontend : true } )
137+ const {
138+ controller
139+ } = await serve ( { shouldLoadFixtures : args . includes ( '--load-fixtures' ) , shouldStartFrontend : true } )
140+
141+ const info = await controller . info ( )
142+
143+ // eslint-disable-next-line no-console
144+ console . info ( 'Kubo gateway:' , info . gateway )
145+ // eslint-disable-next-line no-console
146+ console . info ( 'Kubo RPC API:' , info . api )
99147}
0 commit comments