This repository has been archived by the owner on Apr 7, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor, image support, improve error handling
- Loading branch information
Showing
16 changed files
with
733 additions
and
141 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
{ | ||
"env": { | ||
"node": true, | ||
"mocha": true | ||
}, | ||
"parser": "babel-eslint", | ||
"extends": "airbnb/base" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
FROM node:slim | ||
|
||
ENV RENDERER_ACCESS_KEY=changeme CONCURRENCY=1 | ||
MAINTAINER Mihkel Sokk <[email protected]> | ||
|
||
ENV RENDERER_ACCESS_KEY=changeme CONCURRENCY=1 WINDOW_WIDTH=1024 WINDOW_HEIGHT=768 | ||
|
||
# Add subpixel hinting | ||
COPY .fonts.conf /root/.fonts.conf | ||
|
@@ -16,4 +18,5 @@ RUN npm install --production && \ | |
apt-get clean | ||
|
||
EXPOSE 3000 | ||
CMD xvfb-run --server-args="-screen 0 800x600x24" npm start | ||
|
||
CMD xvfb-run --server-args="-screen 0 $WINDOW_WIDTHx$WINDOW_HEIGHTx24" npm start |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
'use strict'; | ||
|
||
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); | ||
|
||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.renderWorker = renderWorker; | ||
exports.createWindow = createWindow; | ||
|
||
var _package = require('../package.json'); | ||
|
||
var _package2 = _interopRequireDefault(_package); | ||
|
||
var _electron = require('electron'); | ||
|
||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
|
||
const TIMEOUT = process.env.TIMEOUT || 30; | ||
const WINDOW_WIDTH = process.env.WINDOW_WIDTH || 1024; | ||
const WINDOW_HEIGHT = process.env.WINDOW_HEIGHT || 768; | ||
const LIMIT = 3000; // Constrain screenshots to 3000x3000px | ||
|
||
const DEFAULT_HEADERS = 'Cache-Control: no-cache, no-store, must-revalidate'; | ||
|
||
/** | ||
* Render PDF | ||
*/ | ||
function renderPDF(_ref, done) { | ||
let options = _ref.options; | ||
|
||
this.webContents.printToPDF(options, done); | ||
} | ||
|
||
/** | ||
* Render image | ||
*/ | ||
function renderImage(_ref2, done) { | ||
let type = _ref2.type; | ||
let options = _ref2.options; | ||
|
||
const handleCapture = image => { | ||
done(null, type === 'png' ? image.toPng() : image.toJpeg(parseInt(options.quality, 10) || 80)); | ||
}; | ||
|
||
// Sanitize rect | ||
const validKeys = ['x', 'y', 'width', 'height']; | ||
const rect = {}; | ||
Object.keys(options).map(k => [k, options[k]]).filter(_ref3 => { | ||
var _ref4 = _slicedToArray(_ref3, 2); | ||
|
||
let k = _ref4[0]; | ||
let v = _ref4[1]; | ||
return validKeys.includes(k) && !isNaN(parseInt(v, 10)); | ||
}).forEach(_ref5 => { | ||
var _ref6 = _slicedToArray(_ref5, 2); | ||
|
||
let k = _ref6[0]; | ||
let v = _ref6[1]; | ||
return rect[k] = parseInt(v, 10); | ||
}); | ||
|
||
// Use explicit browser size or rect size, capped by LIMIT, default to ENV variable | ||
const browserSize = { | ||
width: Math.min(parseInt(options.browserWidth, 10) || rect.width, LIMIT) || WINDOW_WIDTH, | ||
height: Math.min(parseInt(options.browserHeight, 10) || rect.height, LIMIT) || WINDOW_HEIGHT | ||
}; | ||
|
||
if (Object.keys(rect).length === 4) { | ||
// Avoid stretching by adding rect coordinates to size | ||
this.setSize(browserSize.width + rect.x, browserSize.height + rect.y); | ||
this.capturePage(rect, handleCapture); | ||
} else { | ||
this.setSize(browserSize.width, browserSize.height); | ||
this.capturePage(handleCapture); | ||
} | ||
} | ||
|
||
/** | ||
* Handle loading failure errors | ||
*/ | ||
function handleLoadingError(done, e, code, desc) { | ||
switch (code) { | ||
case -105: | ||
done({ statusCode: 500, code: 'NAME_NOT_RESOLVED', | ||
message: `The host name could not be resolved.` }); | ||
break; | ||
case -300: | ||
done({ statusCode: 500, code: 'INVALID_URL', message: 'The URL is invalid.' }); | ||
break; | ||
case -501: | ||
done({ statusCode: 500, code: 'INSECURE_RESPONSE', | ||
message: 'The server\'s response was insecure (e.g. there was a cert error).' }); | ||
break; | ||
case -3: | ||
done({ statusCode: 500, code: 'ABORTED', message: 'User aborted loading.' }); | ||
break; | ||
default: | ||
done({ statusCode: 500, code: 'GENERIC_ERROR', message: `${ code } - ${ desc }` }); | ||
} | ||
} | ||
|
||
/** | ||
* Render job with error handling | ||
*/ | ||
function renderWorker(window, task, done) { | ||
const webContents = window.webContents; | ||
|
||
// Prevent loading of malicious chrome:// URLS | ||
|
||
if (task.url.startsWith('chrome://')) { | ||
return done({ statusCode: 500, code: 'INVALID_URL', message: 'The URL is invalid.' }); | ||
} | ||
|
||
// Loading failures | ||
webContents.once('did-fail-load', function () { | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
|
||
return handleLoadingError(done, ...args); | ||
}); | ||
|
||
// Renderer process has crashed | ||
webContents.once('crashed', () => { | ||
done({ statusCode: 500, code: 'RENDERER_CRASH', message: `Render process crashed.` }); | ||
}); | ||
|
||
webContents.once('did-finish-load', () => { | ||
(task.type === 'pdf' ? renderPDF : renderImage).call(window, task, done); | ||
}); | ||
|
||
webContents.once('timeout', () => { | ||
done({ statusCode: 524, code: 'RENDERER_TIMEOUT', message: `Renderer timed out.` }); | ||
}); | ||
|
||
// Timeout render job | ||
webContents.timeoutTimer = setTimeout(() => webContents.emit('timeout'), TIMEOUT * 1000); | ||
|
||
webContents.loadURL(task.url, { extraHeaders: DEFAULT_HEADERS }); | ||
} | ||
|
||
/** | ||
* Create BrowserWindow | ||
*/ | ||
function createWindow() { | ||
const window = new _electron.BrowserWindow({ | ||
width: WINDOW_WIDTH, height: WINDOW_HEIGHT, | ||
frame: false, show: false | ||
}); | ||
|
||
// Set user agent | ||
const webContents = window.webContents; | ||
|
||
webContents.setUserAgent(`${ webContents.getUserAgent() } ${ _package2.default.name }/${ _package2.default.version }`); | ||
|
||
return window; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.