Electron set cookie - node.js

I am new to electron and converting an web app to desktop application.I am loading pages from file system.Cookies are working if pages are served from web server but when I load pages from local folder I am not able to save them. I am saving cookie using document.cookie in web.I want to know how I can I enable file:// cookies in electron .
Regards

Well, I want to answer my question in case somebody is having the same problem. I have fixed the cookie problem by registerStandardSchemes. The sample code is as follows and code works for saving cookies from web pages as well:
protocol.registerStandardSchemes(["app"], {
secure: true
});
and on ready event
protocol.registerFileProtocol('app', (request, callback) => {
const urls = request.url.substr(6)
const parsedUrl = url.parse(urls);
// extract URL path
const pathname = `.${parsedUrl.pathname}`;
// based on the URL path, extract the file extention. e.g. .js, .doc, ...
const ext = path.parse(pathname).ext;
callback({
path: path.normalize(`${__dirname}/${parsedUrl.pathname}`)
})
}, (error) => {
if (error) {
console.error('Failed to register protocol');
}
});

Follow the documentation to get it done, and use the standard.https://electronjs.org/docs/api/cookies
const {session} = require('electron')
// Query all cookies.
session.defaultSession.cookies.get({}, (error, cookies) => {
console.log(error, cookies)
})
// Query all cookies associated with a specific url.
session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => {
console.log(error, cookies)
})
// Set a cookie with the given cookie data;
// may overwrite equivalent cookies if they exist.
const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'}
session.defaultSession.cookies.set(cookie, (error) => {
if (error) console.error(error)
})

OK, I got it working with Electron 5. Below are the relevant bits based on #zahid-nisar's solution, and below that a full sample Electron main.js to show how it all fits together. Obviously, change the location of your app in mainWindow.loadURL('app://www/index.html');.
Relevant code to insert in main.js:
const { protocol } = require('electron');
protocol.registerSchemesAsPrivileged([{
scheme: 'app',
privileges: {
standard: true,
secure: true
}
}]);
Inside app.on('ready') function:
protocol.registerFileProtocol('app', (request, callback) => {
const url = request.url.substr(6);
callback({
path: path.normalize(`${__dirname}/${url}`)
});
}, (error) => {
if (error) console.error('Failed to register protocol');
});
Then, inside your createWindow function, load your app like this:
mainWindow.loadURL('app://www/index.html');
And finally, here is a complete sample main.js with the above code (plus extras that I need, like Service Worker):
// Modules to control application life and create native browser window
const {
app,
protocol,
BrowserWindow
} = require('electron');
const path = require('path');
// This is used to set capabilities of the app: protocol in onready event below
protocol.registerSchemesAsPrivileged([{
scheme: 'app',
privileges: {
standard: true,
secure: true,
allowServiceWorkers: true,
supportFetchAPI: true
}
}]);
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600
//, webPreferences: {
// preload: path.join(__dirname, 'preload.js')
// }
});
// and load the index.html of the app.
mainWindow.loadURL('app://www/index.html');
// DEV: Enable code below to check cookies saved by app in console log
// mainWindow.webContents.on('did-finish-load', function() {
// mainWindow.webContents.session.cookies.get({}, (error, cookies) => {
// console.log(cookies);
// });
// });
// Open the DevTools.
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
}
// 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.on('ready', () => {
protocol.registerFileProtocol('app', (request, callback) => {
const url = request.url.substr(6);
callback({
path: path.normalize(`${__dirname}/${url}`)
});
}, (error) => {
if (error) console.error('Failed to register protocol');
});
// Create the new window
createWindow();
});
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') app.quit();
});
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 (mainWindow === null) createWindow();
});
// 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.

Related

How to dynamically add CORS sites to Google Cloud App Engine Node API

I am new to API deployment.
I have a Node Express API that has CORS enabled in the root app.js, for the API and a socket.io implementation:
var app = express();
app.use(cors({
origin : ["http://localhost:8080", "http://localhost:8081"],
credentials: true
}))
and
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: ["http://localhost:8080", "http://localhost:8081"],
credentials: true,
methods: ["GET"]
}
});
I will set up a sales website that allows a customer to pay for a license to use the API with their site, i.e. https://www.customersite.com
My question is how can I dynamically add the customer's website (say after they submit a form from another site) to the CORS list? Ideally it would be via an API call. The only option which I can think of (that is not automated) is to manually maintain a global js file (i.e. config.js) with the cors list from within the Google platform using the file explorer / editor, and to iterate over it as an array similar to process.env.customerList. This will not work for me as I need to have this step happen automatically.
Any and all suggestions are appreciated.
Solution: Use a process manager like pm2 to 'reload' the API gracefully with close to no downtime.
PM2 reloads can be triggered programmatically. I made a PUT endpoint for modifying CORS list /cors/modify that sent a programmatic pm2 message when a successful modification was done.
Note: on Windows OS must use programmatic messaging:
pm2.list(function(err, list) {
pm2.sendDataToProcessId(list[0].pm2_env.pm_id,
{
type : 'process:msg',
data : {
msg : 'shutdown'
},
topic: true
},
function(err, res) {
console.log(err);
pm2.disconnect(); // Disconnects from PM2
}
);
if (err) {
console.log(err);
pm2.disconnect(); // Disconnects from PM2
}
});
which can then be caught with
process.on('message', async function(msg) {
if (msg == "shutdown" || msg.data.msg == 'shutdown') {
console.log("Disconnecting from DB...");
mongoose.disconnect((e => {
if (e) {
process.exit(1)
} else {
console.log("Mongoose connection removed");
httpServer.close((err) => {
if (err) {
console.error(err)
process.exit(1)
}
process.exit(0);
})
}
}));
}
});

Electron - Detect and handle SIGINT/SIGTERM Signals in Linux

You can listen for SIGINT/SIGTERM and stop your app from exiting when the User presses CTRL-C in the terminal (if it was launched from one) in node js using process.on, but the same did not work in my electron application. I also tried binding handlers to the "quit", "before-quit" and "will-quit" events of the app object but to no avail. Is it possible to detect if the user opened the app from the terminal and pressed CTRL-C in an electron app, and to bind handlers to it and stop the app from exiting. And if yes, then how would you achieve this?
Here's how I am using process.on and app.on in a basic electron Main Process, in case I am doing something wrong (my original script contains alot of code not relevant to the question and what I want to achieve does not work with basic electron-quickstart app either):
const { app, BrowserWindow } = require("electron");
const path = require("path");
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
},
});
win.loadFile("index.html");
}
app.whenReady().then(() => {
createWindow();
app.on("activate", function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on("will-quit", (e) => {
console.log("App recieved request to quit");
e.preventDefault();
});
app.on("before-quit", (e) => {
console.log("App recieved request to quit");
e.preventDefault();
});
app.on("quit", (e) => {
console.log("App recieved request to quit");
e.preventDefault();
});
process.on("SIGINT SIGTERM", () => {
console.log("Detected SIGINT/SIGTERM");
});
process.on("SIGTERM", () => {
console.log("Detected SIGTERM");
});
This question concerns Linux only.
You're registering the event listeners too early. I modified your code to include all the listeners in the block after whenReady and got the results you were expecting.

Electron production build - ProtocolDeprecateCallback: The callback argument of protocol module APIs is no longer needed

I have a working dev app in electron that will refuse to run in production due to this strange error
(node:10285) ProtocolDeprecateCallback: The callback argument of protocol module APIs is no longer needed.
writeOut # internal/process/warning.js:32
I've build the app in past with success and was working, but now I have this problem.
This is the background.js file of the electron app I think that the error can be cause from something inside this file
'use strict'
import { app, protocol, BrowserWindow } from 'electron';
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib';
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
import path from 'path';
import './ipc-services';
const isDevelopment = process.env.NODE_ENV !== 'production';
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
]);
async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
//If I enable context isolation window.ipcRenderer will stop working also with preload.js script loaded
//contextIsolation: true,
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
preload: path.join(__dirname, 'preload.js'),
icon: path.join(__static, 'icon.png')
}
});
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools();
} else {
createProtocol('app');
// Load the index.html when not in development
win.loadURL(path.join(__dirname, 'index.html'));
}
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
})
app.on('activate', () => {
// 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();
})
// 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.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installExtension(VUEJS_DEVTOOLS);
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString());
}
}
createWindow();
});
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit();
}
})
} else {
process.on('SIGTERM', () => {
app.quit();
});
}
}
Is there a fix that I can apply?
After a lot of research and debug I've discovered two problems in my code. The first one is that process.env.IS_TEST is not defined, I've removed that line from my code. No problem occur in electron:serve mode. The main problem with the blank screen in production instead, was because win.loadURL(path.join(__dirname, 'index.html')) was trying to load the files ufing the file: protocol instead of the custom one registered from the app.
I've reverted back to the original code and used the win.loadURL('app://./index.html')
After this little modification the app seems working fine also in production.

Load custom configuration runtime

I have a nuxt application in which I will need to append data from a generated configuration file when the application is first started. The reason I cannot do this in the actual build is because the configuration file does not exists at this point; it is generated just before calling npm start by a bootstrap script.
Why don't I generated the configuration file before starting the application you may ask and this is because the application is run in a docker container and the built image cannot include environment specific configuration files since it should be used on different environments such as testing, staging and production.
Currently I am trying to use a hook to solve this, but I am not really sure on how to actually set the configuration data in the application so it can be used everywhere:
# part of nuxt.config.js
hooks: {
listen(server, listener) {
# load the custom configuration file.
fs.readFile('./config.json', (err, data) => {
let configData = JSON.parse(data));
});
}
},
The above hook is fired when the application first starts to listen for connecting clients. Not sure this is the best or even a possible way to go.
I also made an attempt of using a plugin to solve this:
import axios from ‘axios’;
export default function (ctx, inject) {
// server-side logic
if (ctx.isServer) {
// here I would like to simply use fs.readFile to load the configuration, but this is not working?
} else {
// client-side logic
axios.get(‘/config.json’)
.then((res) => {
inject(‘storeViews’, res.data);
});
}
};
In the above code I have problems both with using the fs module and axios.
I was also thinking about using a middleware to do this, but not sure on how to proceed.
If someone else has this kind of problem here is the solution I came up with in the end:
// plugins/config.js
class Settings
{
constructor (app, req) {
if (process.server) {
// Server side we load the file simply by using fs
const fs = require('fs');
this.json = fs.readFileSync('config.json');
} else {
// Client side we make a request to the server
fetch('/config')
.then((response) => {
if (response.ok) {
return response.json();
}
})
.then((json) => {
this.json = json;
});
}
}
}
export default function ({ req, app }, inject) {
inject('config', new Settings(app, req));
};
For this to work we need to use a server middleware:
// api/config.js
const fs = require('fs');
const express = require('express');
const app = express();
// Here we pick up requests to /config and reads and return the
// contents of the configuration file
app.get('/', (req, res) => {
fs.readFile('config.json', (err, contents) => {
if (err) {
throw err;
}
res.set('Content-Type', 'application/json');
res.end(contents);
});
});
module.exports = {
path: '/config',
handler: app
};

Associate file extension with Electron application

When a user double clicks on a file with an extension of cmf, I would like to automatically launch the Electron application that I've built. I've been searching around and I've seen several mentions of the electron-builder but no examples of how this can be used to create this association.
You want to look at the protocol functionality. I don't have enough experience with it to understand the finer points: like which app takes precedence if multiple app register the same protocol. Some of that might be user-defined.
const { app, protocol } = require('electron')
const path = require('path')
app.on('ready', () => {
protocol.registerFileProtocol('atom', (request, callback) => {
const url = request.url.substr(7)
callback({ path: path.normalize(`${__dirname}/${url}`) })
}, (error) => {
if (error) console.error('Failed to register protocol')
})
})

Resources