UNPKG

@adobe/helix-cli

Version:
121 lines (110 loc) 3.82 kB
/* * Copyright 2018 Adobe. All rights reserved. * This file is licensed to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. You may obtain a copy * of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ import { promisify } from 'util'; import path from 'path'; import compression from 'compression'; import utils from './utils.js'; import RequestContext from './RequestContext.js'; import { asyncHandler, BaseServer } from './BaseServer.js'; import LiveReload from './LiveReload.js'; export class HelixServer extends BaseServer { /** * Creates a new HelixServer for the given project. * @param {HelixProject} project */ constructor(project) { super(project); this._liveReload = null; this._enableLiveReload = false; this._app.use(compression()); } withLiveReload(value) { this._enableLiveReload = value; return this; } /** * Proxy Mode route handler * @param {Express.Request} req request * @param {Express.Response} res response */ async handleProxyModeRequest(req, res) { const sendFile = promisify(res.sendFile).bind(res); const ctx = new RequestContext(req, this._project); const { log } = this; const proxyUrl = new URL(this._project.proxyUrl); const filePath = path.join(this._project.directory, ctx.path); if (path.relative(this._project.directory, filePath).startsWith('..')) { log.info(`refuse to serve file outside the project directory: ${filePath}`); res.status(403).send(''); return; } const liveReload = this._liveReload; if (liveReload) { liveReload.startRequest(ctx.requestId, ctx.path); } // try to serve static try { log.debug('trying to serve local file', filePath); await sendFile(filePath, { dotfiles: 'allow', headers: { 'access-control-allow-origin': '*', }, }); if (liveReload) { liveReload.registerFile(ctx.requestId, filePath); } return; } catch (e) { log.debug(`Error while delivering resource ${ctx.path} - ${e.stack || e}`); } finally { if (liveReload) { liveReload.endRequest(ctx.requestId); } } // use proxy try { const url = new URL(ctx.url, proxyUrl); for (const [key, value] of proxyUrl.searchParams.entries()) { url.searchParams.append(key, value); } await utils.proxyRequest(ctx, url.href, req, res, { injectLiveReload: this._project.liveReload, headHtml: this._project.headHtml, indexer: this._project.indexer, cacheDirectory: this._project.cacheDirectory, file404html: this._project.file404html, }); } catch (err) { log.error(`Failed to proxy Franklin request ${ctx.path}: ${err.message}`); res.status(502).send(`Failed to proxy Franklin request: ${err.message}`); } this.emit('request', req, res, ctx); } async setupApp() { await super.setupApp(); if (this._enableLiveReload) { this._liveReload = new LiveReload(this.log); await this._liveReload.init(this.app, this._server); } const handler = asyncHandler(this.handleProxyModeRequest.bind(this)); this.app.get('*', handler); this.app.post('*', handler); } async doStop() { await super.stop(); if (this._liveReload) { await this._liveReload.stop(); delete this._liveReload; } } }