import Koa from 'koa' ; import Router from 'koa-router' ; import session from 'koa-session' ; import bodyParser from 'koa-bodyparser' ; const serve = require ( 'koa-static-server' ) ; const IO = require ( 'koa-socket-2' ) ; import { Application } from "../application" ; import { STOREX_HUB_API_v0 , StorexHubApi_v0 , StorexHubCallbacks_v0 , AllStorexHubCallbacks_v0 } from '../public-api' ; import { Server } from 'http' ; import { SocketSessionMap } from './socket-session-map' ; export async function createHttpServer ( application , options ) { const router = new Router ( ) ; router . get ( '/' , async ( ctx ) => { ctx . body = "Storex Hub is running. For documentation, go here:\nhttps://worldbrain.github.io/storex-docs/#/storex-hub/" ; } ) ; const app = new Koa ( ) ; app . keys = [ options . secretKey ] ; app . use ( bodyParser ( ) ) ; app . use ( session ( { } , app ) ) ; if ( options . frontendDir ) { app . use ( serve ( { rootDir : options . frontendDir , rootPath : '/management' , notFoundFile : 'index.html' } ) ) ; } const io = new IO ( ) as SocketIO . Server ; io . attach ( app ) ; setupWebsocketServer ( io , application ) ; const sessions = { } ; for ( const [ methodName , methodDescription ] of Object . entries ( STOREX_HUB_API_v0 ) ) { router . post ( methodDescription . path , async ( ctx ) => { const identifiedAppName = ctx [ 'session' ] . appName ; const api = identifiedAppName ? sessions [ identifiedAppName ] : await application . api ( ) ; if ( ! api ) { throw new Error ( 'TODO: do something useful here' ) ; } const methodOptions = ctx . request [ 'body' ] ; const result = await api [ methodName ] ( methodOptions ) ; if ( ! identifiedAppName ) { const sessionInfo = await api . getSessionInfo ( ) ; if ( sessionInfo . appIdentifier ) { sessions [ sessionInfo . appIdentifier ] = api ; ctx [ 'session' ] . appName = sessionInfo . appIdentifier ; } } ctx . body = result ; } ) ; } app . use ( router . routes ( ) ) ; let server ; return { app , start : async ( options ? ) => { server = app . listen ( options ?. port ?? parseInt ( process . env . PORT || '3000' ) ) ; } , stop : async ( ) => { if ( server ) { server . close ( ) ; } } } ; } function setupWebsocketServer ( io , application ) { let requestsSent = 0 ; const sessions = new SocketSessionMap < StorexHubApi_v0 > ( { createSession : ( socket ) => application . api ( { callbacks : new Proxy ( { } , { get : < MethodName extends keyof StorexHubCallbacks_v0 > ( _ , key ) => { return ( options ) => { return new Promise ( ( resolve , reject ) => { const requestId = ++ requestsSent ; const handler = ( message ) => { if ( message . requestId !== requestId ) { return ; } socket . removeListener ( 'request' , handler ) ; resolve ( message . response ) ; } ; socket . on ( 'response' , handler ) ; socket . emit ( 'request' , { requestId , methodName : key , methodOptions : options , } ) ; } ) ; } ; } } ) as AllStorexHubCallbacks_v0 } ) , destroySession : session => session . destroySession ( ) } ) ; sessions . setup ( io ) ; io . on ( 'request' , async ( message ) => { const api = await sessions . getSession ( message . socket ) ; let result ; let success = false ; try { result = await api [ message . data . methodName ] ( message . data . methodOptions ) ; success = true ; } catch ( e ) { console . error ( e ) ; } if ( success ) { message . socket . emit ( 'response' , { requestId : message . data . requestId , result } ) ; } else { message . socket . emit ( 'response' , { requestId : message . data . requestId , error : 'internal' } ) ; } } ) ; return io ; }