import { AuthConfig , IAuthConfigSettings } from 'node-sp-auth-config' ; import * as express from 'express' ; import * as bodyParser from 'body-parser' ; import * as cors from 'cors' ; import * as path from 'path' ; import * as fs from 'fs' ; import * as https from 'https' ; import * as http from 'http' ; import { Logger } from '../utils/logger' ; import { RestGetRouter } from './routers/restGet' ; import { RestPostRouter } from './routers/restPost' ; import { RestBatchRouter } from './routers/restBatch' ; import { CsomRouter } from './routers/csom' ; import { SoapRouter } from './routers/soap' ; import { PostRouter } from './routers/genericPost' ; import { GetRouter } from './routers/genericGet' ; import { Server as GatewayServer } from '../gateway/server' ; import { Client as GatewayClient } from '../gateway/client' ; import { IProxySettings , IProxyContext , IRouters , IGatewayServerSettings , IGatewayClientSettings , IProxyCallback , IProxyErrorCallback } from './interfaces' ; import { RestDeleteRouter } from './routers/restDelete' ; export default class RestProxy { private app : express . Application ; private settings : IProxySettings ; private routers : IRouters ; private logger : Logger ; private isExtApp = false ; constructor ( settings = { } , app ? ) { const authConfigSettings = settings . authConfigSettings || { } ; this . settings = { ... settings , protocol : typeof settings . protocol !== 'undefined' ? settings . protocol : 'http' , hostname : settings . hostname || process . env . HOSTNAME || 'localhost' , port : settings . port || parseInt ( process . env . PORT , 10 ) || 8080 , staticRoot : path . resolve ( settings . staticRoot || path . join ( __dirname , '/../../static' ) ) , rawBodyLimitSize : settings . rawBodyLimitSize || '10MB' , jsonPayloadLimitSize : settings . jsonPayloadLimitSize || '2MB' , metadata : require ( path . join ( __dirname , '/../../package.json' ) ) , strictRelativeUrls : typeof settings . strictRelativeUrls !== 'undefined' ? settings . strictRelativeUrls : false , authConfigSettings : { ... authConfigSettings , configPath : path . resolve ( authConfigSettings . configPath || settings . configPath || './config/private.json' ) , defaultConfigPath : authConfigSettings . defaultConfigPath || settings . defaultConfigPath , encryptPassword : typeof authConfigSettings . encryptPassword !== 'undefined' ? authConfigSettings . encryptPassword : true , saveConfigOnDisk : typeof authConfigSettings . saveConfigOnDisk !== 'undefined' ? authConfigSettings . saveConfigOnDisk : true } } ; this . logger = new Logger ( this . settings . logLevel ) ; if ( typeof app !== 'undefined' ) { this . app = app ; this . isExtApp = true ; } else { this . app = express ( ) ; } this . routers = { apiRestRouter : express . Router ( ) , apiCsomRouter : express . Router ( ) , apiSoapRouter : express . Router ( ) , genericPostRouter : express . Router ( ) , genericGetRouter : express . Router ( ) } ; } public serveProxy ( callback ? ) { this . serve ( callback ) ; } public serveGateway = ( settings ) : void => { new GatewayServer ( settings , this . settings , this . app ) . init ( ) ; } ; public serveClient = ( settings ) : void => { new GatewayClient ( settings , this . settings ) . init ( ) ; this . serve ( ) ; } ; public serve ( callback ? , errorCallback ? ) { ( async ( ) => { const ctx = await new AuthConfig ( this . settings . authConfigSettings ) . getContext ( ) ; const context = { ... ctx , proxyHostUrl : `${ this . settings . protocol }://${ this . settings . hostname }:${ this . settings . port }` } as unknown as IProxyContext ; const bodyParserRaw = bodyParser . raw ( { type : ( ) => true , limit : this . settings . rawBodyLimitSize , verify : ( req , _res , buf , encoding ) => { if ( buf && buf . length ) { ( req as unknown as { rawBody : string ; } ) . rawBody = buf . toString ( encoding as BufferEncoding || 'utf8' ) ; ( req as unknown as { buffer : Buffer ; } ) . buffer = buf ; } } } ) ; const bodyParserUrlencoded = bodyParser . urlencoded ( { extended : true } ) ; this . routers . apiRestRouter . post ( `/*(${ [ '/attachmentfiles/add' , '/files/add' , '/startUpload' , '/continueUpload' , '/finishUpload' , '/_layouts/15/Upload' ] . join ( '|' ) })*` , bodyParserRaw , new RestPostRouter ( context , this . settings ) . router ) ; this . routers . apiRestRouter . post ( '/[$]batch' , bodyParserRaw , new RestBatchRouter ( context , this . settings ) . router ) ; this . routers . apiRestRouter . get ( '/*' , new RestGetRouter ( context , this . settings ) . router ) ; this . routers . apiRestRouter . post ( '/*' , bodyParser . json ( { limit : this . settings . jsonPayloadLimitSize } ) , new RestPostRouter ( context , this . settings ) . router ) ; this . routers . apiRestRouter . delete ( '/*' , new RestDeleteRouter ( context , this . settings ) . router ) ; ( ( ) => { this . routers . apiRestRouter . put ( '/*' , bodyParser . json ( { limit : this . settings . jsonPayloadLimitSize } ) , new RestPostRouter ( context , this . settings ) . router ) ; this . routers . apiRestRouter . patch ( '/*' , bodyParser . json ( { limit : this . settings . jsonPayloadLimitSize } ) , new RestPostRouter ( context , this . settings ) . router ) ; } ) ( ) ; this . routers . apiCsomRouter . post ( '/*' , bodyParserUrlencoded , new CsomRouter ( context , this . settings ) . router ) ; this . routers . apiSoapRouter . post ( '/*' , bodyParserUrlencoded , new SoapRouter ( context , this . settings ) . router ) ; this . routers . genericGetRouter . get ( '/*' , new GetRouter ( context , this . settings ) . router ) ; this . routers . genericPostRouter . post ( '/*' , bodyParserUrlencoded , new PostRouter ( context , this . settings ) . router ) ; this . app . use ( cors ( ) ) ; this . app . use ( '*/_api' , this . routers . apiRestRouter ) ; this . app . use ( '*/_vti_bin/client.svc/ProcessQuery' , this . routers . apiCsomRouter ) ; this . app . use ( '*/_vti_bin/*.asmx' , this . routers . apiSoapRouter ) ; this . app . use ( '*/_vti_bin/ListData.svc' , this . routers . genericPostRouter ) ; this . app . use ( '*/_vti_bin/ListData.svc' , this . routers . genericGetRouter ) ; this . app . use ( '/' , this . routers . genericPostRouter ) ; this . app . use ( '/' , this . routers . genericGetRouter ) ; if ( this . isExtApp ) { return ; } const upCallback = ( s , cx , settings , cb ? ) => { this . logger . info ( `SharePoint REST Proxy has been started on ${ cx . proxyHostUrl }` ) ; if ( cb && typeof cb === 'function' ) { cb ( s , cx , settings ) ; } } ; let server = null ; if ( this . settings . protocol === 'https' ) { if ( typeof this . settings . ssl === 'undefined' ) { this . settings . ssl = { cert : path . join ( __dirname , './../../ssl/cert.crt' ) , key : path . join ( __dirname , './../../ssl/key.pem' ) } ; } const options = { cert : fs . existsSync ( this . settings . ssl . cert ) ? fs . readFileSync ( this . settings . ssl . cert ) : this . settings . ssl . cert , key : fs . existsSync ( this . settings . ssl . key ) ? fs . readFileSync ( this . settings . ssl . key ) : this . settings . ssl . key } ; server = https . createServer ( options , this . app ) ; } else { server = require ( 'http' ) . Server ( this . app ) ; } if ( server ) { server . listen ( this . settings . port , this . settings . hostname , ( ) => { upCallback ( server , context , this . settings , callback ) ; } ) ; } } ) ( ) . catch ( ( error ) => { this . logger . error ( error ) ; if ( errorCallback ) { errorCallback ( error ) ; } } ) ; } } export { IProxySettings , IProxyContext , IGatewayClientSettings , IGatewayServerSettings } from './interfaces' ;