I have a Dust.js template and want to use it as async mode (so stream), to load my stuff.
It works very well, but when I add use(helmet()) it does not show as html anymore, just as plain text
I tried using dust.stream, tried hoffman, tried adaro. nothing works, the second helmet is introduced, it does not work.
hoffman
hoffman = require('hoffman'),
express = require('express'),
helmet = require('helmet'),
request = require('request');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'dust');
app.engine('dust', hoffman.__express());
app.use(helmet());
// This is the important part-- it adds res.stream()
app.use(hoffman.stream);
app.get('/', function (req, res) {
res.stream("hello", {
"async": function(chunk, context, bodies, params) {
return chunk.map(function(chunk) {
// Introducting an artificial delay to make streaming more apparent
setTimeout(function() {
request('http://www.dustjs.com/')
.on('data', chunk.write.bind(chunk))
.on('end', chunk.end.bind(chunk));
}, 3000);
});
}
});
});
const port = process.env.PORT | 3007;
app.listen(port, function () {
console.log(`Visit http://localhost:${port} to see streaming!`);
});
Dust.stream:
var fs = require('fs'),
path = require('path'),
express = require('express'),
request = require('request'),
helmet = require('helmet'),
dust = require('dustjs-linkedin');
dust.config.whitespace = true;
dust.config.cache = false;
// Define a custom `onLoad` function to tell Dust how to load templates
dust.onLoad = function(tmpl, cb) {
fs.readFile(path.join('./views', path.relative('/', path.resolve('/', tmpl + '.dust'))),
{ encoding: 'utf8' }, cb);
};
var app = express();
app.use(helmet());
app.get('/streaming', function(req, res) {
dust.stream('hello', {
"async": request('http://www.dustjs.com/')
}).pipe(res)
.on('end', function() {
console.log('Done streaming!');
});
});
app.get('/rendering', function(req, res) {
dust.render('hello', {
"async": request('http://www.dustjs.com/')
}, function(err, out) {
res.send(out);
console.log('Done rendering!');
});
});
const port = process.env.PORT | 3002;
app.listen(port, function () {
console.log(`Visit http://localhost:${port} to see streaming!`);
});
When not using helmet, the html is rendered, when adding helmet, no rendering is done, and I see the html page as text
Ok, so dust does a nice job with async.
The bit I was missing was:
res.setHeader('Content-Type', 'text/html; charset=utf-8');
and now it works with helmet!
Related
Hey i am making a project where i want to send data to ipc main
my index.js
const {app, BrowserWindow , ipcMain} = require('electron')
const ejse = require('ejs-electron')
app.on('ready', () => {
ipcMain.on("getUrl",(event,url)=>{
console.log(url);
})
mainWindow = new BrowserWindow({
autoHideMenuBar: true,
icon: __dirname + '/logo.ico',
webPreferences: {
devTools: false,
nodeIntegration:true,
webviewTag:true
}
})
mainWindow.loadURL('file://' + __dirname + '/files/index.ejs');
})
index.ejs contains a anchor tag with value as link
<button id="dw" style="display: none;"><a class="button" id="dwl" onclick="heal()" value="#">Download</a></button>
<script src=download.js></script>
download.js i am able to alert the link but ipcRender not working
const { ipcRenderer } = require("electron");
function heal(){
var url = document.getElementById("dwl").value;
alert(url);
ipcRenderer.send("getUrl",url);
}
i am not able to send url from ejs file to main index.js file alert is working in download.js but some error in sending data to main file console.log is not working
The use of contextBridge within your preload.js script safely exposes API's you approve for use in your render
scripts.
Whist understanding how all this works can be challenging at first, becoming familiar with the below content and the
following code should put you in good stead for a solid understanding of how you can wire up your application.
Context Isolation
contextBridge
Enabling Content Isolation for remote content
Inter-Process Communication
ipcMain
ipcRenderer
webContents - contents.send(channel, ...args)
Some people like to implement concrete functions within their preload.js script(s). In this example,
the preload.js script is only used to configure whitelisted channel names for use between the main process and render
process(es). This keeps your code seperated. IE: Separation of concerns.
Let's set the channel name getUrl within the ipc.render.send array.
preload.js (main process)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'getUrl' // Channel name
],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
Let's set the webPreferences.contextIsolation value to true and specify the webPreferences.preload path.
webPreferences.nodeIntegration can be set to false, but in doing so webPreferences.contextIsolation should be
set to true to "truly enforce strong isolation and prevent the use of Node primitives".
Lastly, let's listen for a message from the render process on the getUrl channel.
main.js (main process)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
// Prevent garbage collection
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// Listen for a message on the 'getUrl' channel
electronIpcMain.on('getUrl', (event, url) => {
console.log(url);
})
This file is fairly standard and easy to understand. Just adapt code for use with .ejs
Added Javascript code in between <script> tags for sake of brevity.
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
</head>
<body>
<input type="button" id="dwl" value="#">
</body>
<script>
let button = document.getElementById('dwl');
button.addEventListener('click', () => {
// Send URL on the 'getUrl' channel
window.ipcRender.send('getUrl', button.value);
});
</script>
</html>
Trying to come up with a GUI for Puppeteer project.
I thought about using Electron, but run into error:
Error: Passed function is not well-serializable!
when running Puppeteer functions like:
await page.waitForSelector('.modal', { visible: true });
I found a proper way to serialize when dealing with page.evaluate() but how to proceed in case of page.waitForSelector()?
Is there a work around for Puppeter's API functions to be properly serialized when required?
EDIT
I decided to rewrite
await page.waitForSelector('.modal', { visible: true });
using page.evaluate, here is the code:
// first recreate waitForSelector
const awaitSelector = async (selector) => {
return await new Promise(resolve => {
const selectorInterval = setInterval(() => {
if ($(selector).is(':visible')) {
console.log(`${selector} visible`);
resolve();
clearInterval(selectorInterval);
};
}, 1000);
});
}
and later call that function using page.evaluate():
// remember to pass over selector's name, in this case it is ".modal"
await page.evaluate('(' + awaitSelector.toString() + ')(".modal");');
Firstly context:
Generally you can not run puppeteer from browser environment, it works solely in nodejs. Electron provides 2 processes renderer and main. Whenever you want to use node you have to do it in main one.
About communication between both procesess you can read in docs, there are many ways of handling it. From what I know the best practice is to declare it in preload and use ipc bridge. Other solutions violate contextIsolation rule.
I was w wandering aound from one problem to another: like not serializable function, require not defined and many others.
Finally I rewrote everything from scratch and it works here's my solution:
main.js
const { app, BrowserWindow } = require('electron')
const path = require('path')
const { ipcMain } = require('electron');
const puppeteer = require("puppeteer");
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: true,
},
})
ipcMain.handle('ping', async () => {
await checkPup()
})
async function checkPup() {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
page
.waitForSelector('h1', { visible: true })
.then(() => {
console.log('got it')
});
const [button] = await page.$x("//button[contains(., 'Create account')]");
if (button) {
console.log('button: ', button)
await button.click();
await page.screenshot({ path: 'tinder.png' });
const [button] = await page.$x("//button[contains(., 'Create account')]");
if (button) {
console.log('button: ', button)
await button.click();
await page.screenshot({ path: 'tinder.png' });
}
}
await browser.close();
}
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
// mainWindow.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
// Attach listener in the main process with the given ID
ipcMain.on('request-mainprocess-action', (event, arg) => {
// Displays the object sent from the renderer process:
//{
// message: "Hi",
// someData: "Let's go"
//}
console.log(
arg
);
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
preload.js
// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
const { contextBridge, ipcRenderer } = require('electron')
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
ping: () => ipcRenderer.invoke('ping'),
// we can also expose variables, not just functions
})
renderer.js
const information = document.getElementById('info')
const btn = document.getElementById('btn')
information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`
btn.addEventListener('click', () => {
console.log('habad!!!!!')
func()
})
const func = async () => {
const response = await window.versions.ping()
information.innerText = response;
console.log(response) // prints out 'pong'
}
Sorry for a little bit of a mess I hope it will help someone maybe finding solutions to some other problems
In development ENV, gkm module is worked great. It can track my keypress event. But, when I build the app. gkm is not worked. What should I do? Here my code.
<script>
let count = 0;
const electron = window.require('electron');
const { ipcRenderer } = electron;
const gkm = window.require('gkm');
gkm.events.on('key.*', function(data) {
console.log('listening global keyboard')
});
ipcRenderer.on('item:add', function (e, item) {
const li = document.createElement('li');
const itemText = document.createTextNode(item);
li.appendChild(itemText);
ul.appendChild(li);
});
ipcRenderer.on('item:clear', function () {
ul.innerHTML = ""
});
</script>
I want to set some cookie inside firefox addon contentScipt:
// Content Script ( js/firefox.js )
self.port.on("start", function() {
var request;
document.cookie = "name=value; path=/;";
request = new XMLHttpRequest();
request.open('GET', '/', true);
request.send(null);
});
But when I see request headers and cookies there is nothing about cookie 'name'.
Is it possible to add cookie inside contentScipt?
Main addon script looks like this
// Main Addon Script ( js/index.js )
var data = require("sdk/self").data;
var button = require("sdk/ui/button/toggle").ToggleButton({
id: "button",
label: "button",
icon: "./img/icon/16.png",
onChange: handleChange
});
var panel = require("sdk/panel").Panel({
contentURL: data.url("popup.html"),
contentScriptFile: data.url("js/firefox.js"),
position: button,
onHide: handleHide
});
function handleChange(state) {
if (state.checked) {
panel.show({
position: button
});
}
}
function handleHide() {
button.state('window', {checked: false});
}
panel.on("show", function () {
panel.port.emit("start");
});
Try:
self.port.on("start", function() {
var request;
unsafeWindow.document.cookie = "name=value; path=/;";
request = new XMLHttpRequest();
request.open('GET', '/', true);
request.send(null);
});
When I load the jquery mobile I have an extra empty page that messes with the app (changes to a white page) when I use the options tag in html, for example.
But only when I have data-native-menu="false"
This is the extra page:
<div data-role="page" data-url="/" tabindex="0" class="ui-page ui-body-c" style="min-height: 629px;">
Someone can explain to me how to use the jquery mobile without the extra page?
Code:
main:
require(['app', 'jquery', 'backbone', './router'], function (App, $, Backbone, Router) {
'use strict';
$( document ).on( "mobileinit",
// Set up the "mobileinit" handler before requiring jQuery Mobile's module
function() {
// Prevents all anchor click handling including the addition of active button state and alternate link bluring.
$.mobile.linkBindingEnabled = false;
// Disabling this will prevent jQuery Mobile from handling hash changes
$.mobile.hashListeningEnabled = false;
// $.mobile.selectmenu.prototype.options.nativeMenu = false;
$.mobile.ajaxEnabled = false;
$.mobile.pushStateEnabled = false;
$('div[data-role="page"]').live('pagehide', function (event, ui) {
$(event.currentTarget).remove();
});
}
)
require( ['jquery-mobile-bower'], function() {
// Instantiates a new Backbone.js Mobile Router
var router = new Router();
Backbone.history.start();
});
});
Router:
define([ "jquery", "backbone", './views/views'], function($, Backbone, views) {
var changeView = function(view){
view.render([{}]);
$(view.el).attr('data-role', 'page');
$('body').append(view.el)
var el = view.el;
$.mobile.changePage($(view.el));
}
// Extends Backbone.Router
var CategoryRouter = Backbone.Router.extend( {
// The Router constructor
initialize: function() {
// Tells Backbone to start watching for hashchange events
$('.back').live('click', function(event) {
window.history.back();
return false;
});
},
// Backbone.js Routes
routes: {
"": "index",
"aa": "aa"
},
// Home method
index: function(){
var view = new views.Index();
changeView(view);
view.on('changePage', function(pageName){
})
},
aa: function(){
var view = new views.A();
changeView(view);
}
} );
// Returns the Router class
return CategoryRouter;
} );
Thanks