2017-02-09 22:55:56 +00:00
const compression = require ( "compression" ) ;
2017-02-10 11:12:47 +00:00
const ejs = require ( "ejs" ) ;
2017-02-09 22:55:56 +00:00
const express = require ( "express" ) ;
2017-02-13 11:39:17 +00:00
const fs = require ( "fs" ) ;
2017-02-09 22:55:56 +00:00
const http = require ( "http" ) ;
2017-03-04 21:27:32 +00:00
const jsonFormat = require ( "json-format" ) ;
2017-02-09 22:55:56 +00:00
const path = require ( "path" ) ;
2017-03-04 19:35:26 +00:00
const Promise = require ( "bluebird" ) ;
2017-02-09 22:55:56 +00:00
2017-02-10 11:12:47 +00:00
const database = require ( "./database/database" ) ;
2017-03-04 21:27:32 +00:00
const geojson = require ( "./export/geojson" ) ;
2017-03-01 23:41:16 +00:00
const gpx = require ( "./export/gpx" ) ;
const table = require ( "./export/table" ) ;
2017-02-10 11:12:47 +00:00
const utils = require ( "./utils" ) ;
2017-02-09 22:55:56 +00:00
2017-02-21 23:55:11 +00:00
const frontendPath = path . dirname ( require . resolve ( "facilmap-frontend/package.json" ) ) ; // Do not resolve main property
if ( process . env . FM _DEV )
process . chdir ( frontendPath ) ; // To make sure that webpack finds all the loaders
2017-02-09 22:55:56 +00:00
2017-03-01 23:41:16 +00:00
const staticMiddleware = process . env . FM _DEV
? require ( "webpack-dev-middleware" ) ( require ( "webpack" ) ( require ( "facilmap-frontend/webpack.config" ) ) , { // require the stuff here so that it doesn't fail if devDependencies are not installed
publicPath : "/"
} )
: express . static ( frontendPath + "/build/" ) ;
2017-02-10 11:12:47 +00:00
2017-03-01 23:41:16 +00:00
const webserver = {
init ( database , port , host ) {
2017-02-10 11:12:47 +00:00
const padMiddleware = function ( req , res , next ) {
utils . promiseAuto ( {
2017-03-01 23:41:16 +00:00
template : ( ) => webserver . getFrontendFile ( "index.ejs" ) ,
2017-02-10 11:12:47 +00:00
padData : ( ) => {
if ( req . params && req . params . padId ) {
return database . getPadData ( req . params . padId ) . then ( ( padData ) => {
// We only look up by read ID. At the moment, we only need the data for the search engine
// meta tags, and those should be only enabled in the read-only version anyways.
if ( ! padData )
throw new Error ( ) ;
return padData ;
} ) . catch ( ( ) => {
// Error will be handled on the client side when it tries to fetch the pad data
return {
searchEngines : false ,
description : ""
} ;
} ) ;
}
} ,
render : ( template , padData ) => {
res . type ( "html" ) ;
res . send ( ejs . render ( template , {
padData : padData
} ) ) ;
}
} ) . catch ( next ) ;
} ;
2017-02-09 22:55:56 +00:00
let app = express ( ) ;
app . use ( compression ( ) ) ;
app . get ( "/bundle-:hash.js" , function ( req , res , next ) {
res . setHeader ( 'Cache-Control' , 'public, max-age=31557600' ) ; // one year
next ( ) ;
} ) ;
2017-02-10 11:12:47 +00:00
app . get ( "/" , padMiddleware ) ;
app . get ( "/index.ejs" , padMiddleware ) ;
2017-03-01 19:02:57 +00:00
app . get ( "/table.ejs" , padMiddleware ) ;
2017-02-09 22:55:56 +00:00
2017-02-10 11:12:47 +00:00
app . use ( staticMiddleware ) ;
2017-02-09 22:55:56 +00:00
2017-02-10 11:12:47 +00:00
// If no file with this name has been found, we render a pad
app . get ( "/:padId" , padMiddleware ) ;
2017-02-09 22:55:56 +00:00
2017-02-10 11:41:14 +00:00
app . get ( "/:padId/gpx" , function ( req , res , next ) {
utils . promiseAuto ( {
padData : database . getPadDataByAnyId ( req . params . padId ) . then ( ( padData ) => {
if ( ! padData )
throw new Error ( ` Map with ID ${ req . params . padId } could not be found. ` ) ;
return padData ;
} ) ,
gpx : ( padData ) => {
return gpx . exportGpx ( database , padData ? padData . id : req . params . padId , req . query . useTracks == "1" ) ;
} ,
response : ( padData , gpx ) => {
res . set ( "Content-type" , "application/gpx+xml" ) ;
res . attachment ( padData . name . replace ( /[\\\/:*?"<>|]+/g , '_' ) + ".gpx" ) ;
res . send ( gpx ) ;
}
} ) . catch ( next ) ;
} ) ;
2017-03-01 19:02:57 +00:00
app . get ( "/:padId/table" , function ( req , res , next ) {
2017-03-01 23:41:16 +00:00
return table . createTable ( database , req . params . padId ) . then ( ( renderedTable ) => {
2017-03-01 19:02:57 +00:00
res . type ( "html" ) ;
res . send ( renderedTable ) ;
} ) . catch ( next ) ;
} ) ;
2017-03-04 21:27:32 +00:00
app . get ( "/:padId/geojson" , function ( req , res , next ) {
utils . promiseAuto ( {
padData : database . getPadData ( req . params . padId ) . then ( ( padData ) => {
if ( ! padData )
throw new Error ( ` Map with ID ${ req . params . padId } could not be found. ` ) ;
return padData ;
} ) ,
geojson : ( ) => {
return geojson . exportGeoJson ( database , req . params . padId ) ;
} ,
response : ( padData , geojson ) => {
res . set ( "Content-type" , "application/geo+json" ) ;
res . attachment ( padData . name . replace ( /[\\\/:*?"<>|]+/g , '_' ) + ".geojson" ) ;
res . send ( jsonFormat ( geojson ) ) ;
}
} ) . catch ( next ) ;
} ) ;
2017-02-09 22:55:56 +00:00
let server = http . createServer ( app ) ;
2017-03-04 19:35:26 +00:00
return Promise . promisify ( server . listen . bind ( server ) ) ( port , host ) . then ( ( ) => server ) ;
2017-03-01 23:41:16 +00:00
} ,
getFrontendFile ( path ) {
if ( process . env . FM _DEV ) {
return new Promise ( ( resolve , reject ) => {
staticMiddleware . waitUntilValid ( resolve ) ;
} ) . then ( ( ) => {
return staticMiddleware . fileSystem . readFileSync ( staticMiddleware . getFilenameFromUrl ( ` / ${ path } ` ) , "utf8" ) ;
} ) ;
} else {
// We don't want express.static's ETag handling, as it sometimes returns an empty template,
// so we have to read it directly from the file system
2017-03-04 19:35:26 +00:00
return Promise . promisify ( fs . readFile ) ( ` ${ frontendPath } /build/ ${ path } ` , "utf8" ) ;
2017-03-01 23:41:16 +00:00
}
2017-02-09 22:55:56 +00:00
}
} ;
2017-03-01 23:41:16 +00:00
Object . assign ( exports , webserver ) ;