I am trying to create an application using Electron (formerly Atom Shell). This application wraps an AngularJS application and interacts with endpoints created in nodejs to edit and save the HTML content. I am able to create the application with no issues.
When I try to access "/saveContent" from electron causes close button (Windows close on top right corner) to become unresponsive, however minimize and maximize works fine without issue. If I access any other endpoint through electron this issue doesn't come up. I have tried with both sync file write and otherwise too. So I assume "/saveContent" in main.js is cause of the issue.
If I end node.exe in "Windows task Manager" this closes the whole application.
I have the main process code below
'use strict';
var fs = require("fs");
const util = require('util')
var cheerio = require("cheerio");
var express = require('express');
var exapp = express();
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({extended: false});
exapp.use(bodyParser.json());
const electron = require('electron');
const app = electron.app; // Module to control application life.
const BrowserWindow = electron.BrowserWindow; // Module to create native browser window.
const path = require('path')
const url = require('url')
var mainWindow = null;
app.on('ready', function() {
mainWindow = new BrowserWindow({width: 1280, height: 900, title: "2018JL", "nodeIntegration":false});
//mainWindow.loadURL(__dirname + '/app/index.html');
mainWindow.loadURL('http://localhost:5001/');
mainWindow.on('closed', function() {
mainWindow = null;
});
});
app.on('window-all-closed', function() {
if (process.platform != 'darwin') {
app.quit();
}
});
exapp.get('/editPage', function(req,res){
if(req){
//console.log("req.query.editURL "+ req.query.editURL);
var url = req.query.editURL;
var editURL = path.join(__dirname + '/app/views'+ url+".html");
fs.exists(editURL, function(fileok){
if(fileok){
fs.readFile(editURL, 'utf8', function (err, data) {
if (err) {
console.log("error.... "+ err);
return console.error(err);
}
//console.log("data "+ editURL);
res.send(JSON.stringify({path:editURL, content:data}));
});
}else{
console.log("file not found");
}
});
}
});
exapp.post('/saveContent', function (req, res) {
//console.log(util.inspect(req, false, null))
if (req) {
//console.log(req.query.url + " ------ " + req.query.content);
var $ = cheerio.load(req.query.content);
var htmlContent = $('body').children();
console.log('htmlContent '+htmlContent);
fs.writeFile(req.query.url, htmlContent, function(err) {
if (err) {
res.send("Error");
}
console.log("End of write file");
res.send("success");
});
}
console.log("End of function .....");
});
exapp.get('/test', function (req, res) {
res.send("Test success ");
});
exapp.use(express.static(__dirname + '/app'));
exapp.listen(process.env.PORT || 5001);
Client code below
$scope.editPage = function () {
$http({method: "GET",
url: "/editPage",
params: {editURL: $location.path()}
}).then(function success(response) {
//var result = JSON.parse(response.data);
//console.log("HTTP Success "+response.data.path);
$scope.showEditor = true;
$scope.editURL = response.data.path;
tinymce.get('contentEditor').setContent(response.data.content);
}, function error(response) {
console.log("HTTP Error " + response.statusText);
});
};
Commenting file write code in '/saveContent' doesn't cause electron close button to become unresponsive.
I replace the code for mainWindow close to as below and works fine
mainWindow.on('close', function(e) {
e.preventDefault();
mainWindow.destroy();
});
https://github.com/electron/electron/blob/master/docs/api/browser-window.md#windestroy
Related
I know about #reZach's answer about the electron security, and I know about his electron secured template.. I tried to use it, but it's just too much for today's me, I don't know anything about webpack, I know react not completely etc, the overall complexity of the template is too much for the current me. So I wanted to ask (maybe my code is good enough to be secured and safe?). I made a trading app, so I cannot give it to people not being sure that it is safe to use. I used just vanilla JS, express, localtunnel for receiving webhooks, and several modules. In short here's the code:
main.js
const { app, BrowserWindow, ipcMain } = require('electron');
const fs = require('fs');
const path = require('path');
const Store = require('electron-store');
const store = new Store();
const CryptoJS = require('crypto-js');
const axios = require('axios');
const WebSocket = require('ws');
const express = require('express');
const bodyParser = require('body-parser');
const localtunnel = require('localtunnel');
(async () => {
const tunnel = await localtunnel({ port: 3000, subdomain: 'mysubdomain' });
// the assigned public url for your tunnel
// i.e. https://abcdefgjhij.localtunnel.me
console.log(tunnel.url);
tunnel.on('close', () => {
// tunnels are closed
console.log('connection closed');
});
})();
const server = express();
const PORT = 3000;
let webhookMsg;
server.use(bodyParser.text());
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));
server.get('/', function (req, res) {
res.send('Server is ready!');
});
server.post('/', (req, res) => {
webhookMsg = req.body;
res.sendStatus(200);
});
let win;
async function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 500,
height: 550,
titleBarStyle: 'hidden',
webPreferences: {
nodeIntegration: false, // is default value after Electron v5
contextIsolation: true, // protect against prototype pollution
enableRemoteModule: false, // turn off remote
preload: path.join(app.getAppPath(), 'preload.js'), // use a preload script
backgroundThrottling: false,
},
});
// Load app
win.loadFile(path.join(__dirname, './index.html'));
win.setAlwaysOnTop(true, 'screen');
// win.removeMenu();
// rest of code..
}
app.on('ready', createWindow);
// NEXT IS MY APP MAIN LOGICS ON RECEIVING MESSAGES AND SENDING API REQUEST TO AN EXCHANGE PLATFORM AND GIVING BACK THE ANSWERS TO THE 'front-end script js file'
//for example
ipcMain.on('loadPrefs', () => {
const allPrefs = store.get('prefs');
win.webContents.send('prefs', allPrefs);
});
ipcMain.on('giveBalance', async e => {
const [apiKey, secret] = fs.readFileSync('api.txt').toString('UTF8').replace(/\r/g, '').split('\n');
const timestamp = Date.now().toString();
const params = {
api_key: apiKey,
timestamp: timestamp,
};
let orderedParams = '';
Object.keys(params)
.sort()
.forEach(function (key) {
orderedParams += key + '=' + params[key] + '&';
});
orderedParams = orderedParams.substring(0, orderedParams.length - 1);
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, secret);
hmac.update(orderedParams);
const sign = hmac.finalize().toString(CryptoJS.enc.Hex);
try {
const res = await axios.get(`https://api.bybit.com/v2/private/wallet/balance?&api_key=${apiKey}&sign=${sign}×tamp=${timestamp}`);
// console.log(res.data);
const responseObj = res.data.result.USDT.equity;
win.webContents.send('balance', { responseObj });
} catch (error) {
// console.log(error);
}
});
preload.js
const { contextBridge, ipcRenderer } = require('electron');
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('api', {
send: (channel, data) => {
ipcRenderer.removeAllListeners(channel);
let validChannels = [
// myChannelsList
];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
ipcRenderer.removeAllListeners(channel);
let validChannels = [
// myResponseChannelsList
];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
},
});
In short, I just copied this reZach's example from this answer and added my JS logics to it.
Could you please tell is it secured and safe to use, I just worry that somebody can get the user's api keys for example and take their money away:) Thank you
Im trying to host a election website.
My Current Method explained:
There is server.js file with serves the public file. If its a POST request(sent by frontend.js to send data to server) edits the poll.json file based on the data sent.
I have a public file having index.html, frontend.js, poll.json where poll.json stores all the data.
My current code work all well in my localhost.
But when running it in Heroku I get a error in the POST request line saying 'POST http://localhost:53390/ net::ERR_CONNECTION_REFUSED1
*********.herokuapp.com/:1 Uncaught (in promise) TypeError: Failed to fetch'
My server.js code:
const createServer = require('http').createServer;
const express = require('express');
const app = express();
let pollData = require(__dirname+'/public/poll.json');
const fs = require('fs');
var all_usn_no=[];
const listening_port=process.env.PORT || 4000;
function get_roll_and_usn(pollData){
for(var i=0;i<=pollData.students.length-1;i++){
//all_roll_no.push(pollData.students[i][0]);
all_usn_no.push(pollData.students[i][1]);
}
}
function roll_to_row(in_usn){
get_roll_and_usn(pollData)
return all_usn_no.indexOf(in_usn);
}
function write_vote(votes){
var checking_row=roll_to_row(votes[1]);
pollData.students[checking_row]=votes;
fs.writeFile(__dirname+'/public/poll.json', JSON.stringify(pollData), (err) => {
if (err) throw err;
console.log('Data written to file');
});
}
write_vote([listening_port,0]);
app.use(express.static(__dirname+'/public'));
app.get('/', (req, res) => {
res.sendFile(__dirname);// + '/index.html');
});
app.post('/', (req, res) => {
let body = '';
req.on('data', data => body += data)
req.on('end', () => {
res.writeHead(200, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET',
});
body=body.split(",");
console.log(body.toString());
write_vote(body);
res.end(`{ "response": "${body ? body : 'No body sent' }" }`);
})
});
app.listen(listening_port, () => {
console.log('Example app listening at http://localhost:'+listening_port)
});
my frontend.js program:
var roll='';
var client_ip='';
var usn='';
var date='';
const all_roll_no=[]
const all_usn_no=[]
var port='';
function check(roll,usn){
var data='';
const url='poll.json';
const Http = new XMLHttpRequest();
Http.open("GET", url);
Http.send();
Http.onload = () =>{
data=JSON.parse(Http.response);
get_roll_and_usn(data);
check_validity(roll,usn,data);
}
}
function get_roll_and_usn(pollData){
for(var i=0;i<=pollData.students.length-1;i++){
//all_roll_no.push(pollData.students[i][0]);
all_usn_no.push(pollData.students[i][1]);
}
}
function usn_to_row(in_usn){
return all_usn_no.indexOf(in_usn);
}
function check_validity(checking_roll,checking_usn,data){
var checking_row=usn_to_row(checking_usn);
port=data.students[0][0];
//if(all_roll_no.indexOf(checking_roll)>=0 && checking_usn==all_usn_no[all_roll_no.indexOf(checking_roll)] && data.students[checking_row][2]==0){
if(all_usn_no.indexOf(checking_usn)>=0 && data.students[checking_row][2]==0){
//console.log("valid");
document.getElementById("page_2").style.display = "none";
document.getElementById("page_3").style.display = "block";
fetch('https://api.ipify.org/?format=json').then(results=> results.json()).then(data => client_ip=data.ip);
}
else{
alert("You cannot vote/ You have already voted")
//console.log('invalid');
}
}
document.getElementById("startbutton").onclick = function(){
roll = document.getElementById("roll_no").value;
usn = document.getElementById("usn_no").value;
date = Date();
check(roll,usn);
}
document.getElementById("next").onclick = function() {
document.getElementById("page_1").style.display = "none";
document.getElementById("page_2").style.display = "block";
}
document.getElementById("finish").onclick = function() {
var splb=[document.getElementById("splb1").checked,document.getElementById("splb2").checked,document.getElementById("splb3").checked,document.getElementById("splb4").checked,document.getElementById("splb5").checked,document.getElementById("splb6").checked,document.getElementById("splb7").checked];
var splg=[document.getElementById("splg1").checked,document.getElementById("splg2").checked,document.getElementById("splg3").checked,document.getElementById("splg4").checked,document.getElementById("splg5").checked,document.getElementById("splg6").checked,document.getElementById("splg7").checked];
var asplb=[document.getElementById("asplb1").checked,document.getElementById("asplb2").checked,document.getElementById("asplb3").checked,document.getElementById("asplb4").checked,document.getElementById("asplb5").checked,document.getElementById("asplb6").checked,document.getElementById("asplb7").checked];
var asplg=[document.getElementById("asplg1").checked,document.getElementById("asplg2").checked,document.getElementById("asplg3").checked,document.getElementById("asplg4").checked,document.getElementById("asplg5").checked,document.getElementById("asplg6").checked,document.getElementById("asplg7").checked];
var csb=[document.getElementById("csb1").checked,document.getElementById("csb2").checked,document.getElementById("csb3").checked,document.getElementById("csb4").checked,document.getElementById("csb5").checked,document.getElementById("csb6").checked,document.getElementById("csb7").checked];
var csg=[document.getElementById("csg1").checked,document.getElementById("csg2").checked,document.getElementById("csg3").checked,document.getElementById("csg4").checked,document.getElementById("csg5").checked,document.getElementById("csg6").checked,document.getElementById("csg7").checked];
var acsb=[document.getElementById("acsb1").checked,document.getElementById("acsb2").checked,document.getElementById("acsb3").checked,document.getElementById("acsb4").checked,document.getElementById("acsb5").checked,document.getElementById("acsb6").checked,document.getElementById("acsb7").checked];
var acsg=[document.getElementById("acsg1").checked,document.getElementById("acsg2").checked,document.getElementById("acsg3").checked,document.getElementById("acsg4").checked,document.getElementById("acsg5").checked,document.getElementById("acsg6").checked,document.getElementById("acsg7").checked];
var scb=[document.getElementById("scb1").checked,document.getElementById("scb2").checked,document.getElementById("scb3").checked,document.getElementById("scb4").checked,document.getElementById("scb5").checked,document.getElementById("scb6").checked,document.getElementById("scb7").checked];
var scg=[document.getElementById("scg1").checked,document.getElementById("scg2").checked,document.getElementById("scg3").checked,document.getElementById("scg4").checked,document.getElementById("scg5").checked,document.getElementById("scg6").checked,document.getElementById("scg7").checked];
var ascb=[document.getElementById("ascb1").checked,document.getElementById("ascb2").checked,document.getElementById("ascb3").checked,document.getElementById("ascb4").checked,document.getElementById("ascb5").checked,document.getElementById("ascb6").checked,document.getElementById("ascb7").checked];
var ascg=[document.getElementById("ascg1").checked,document.getElementById("ascg2").checked,document.getElementById("ascg3").checked,document.getElementById("ascg4").checked,document.getElementById("ascg5").checked,document.getElementById("ascg6").checked,document.getElementById("ascg7").checked];
var vote=[String(splb.indexOf(true)),String(splg.indexOf(true)),String(asplb.indexOf(true)),String(asplg.indexOf(true)),String(csb.indexOf(true)),String(csg.indexOf(true)),String(acsb.indexOf(true)),String(acsg.indexOf(true)),String(scb.indexOf(true)),String(scg.indexOf(true)),String(ascb.indexOf(true)),String(ascg.indexOf(true))]
var update=[String(roll),String(usn),"1",String(client_ip),String(date)].concat(vote);
if (update.indexOf("-1")<0){
alert("Pls vote for all posts")
}
else{
document.getElementById("page_1").style.display = "none";
document.getElementById("page_2").style.display = "none";
document.getElementById("page_3").style.display = "none";
fetch('http://localhost:'+port,{method:'POST',body:update}).then(results=> results.json()).then(console.log);
//console.log(update);
alert("Your vote has been registered")
}
}
You can just ignore the function as they are just to process the data and do necessary funtions.
Main problem: PORT request from frontend.js to server.js sending data to edit the poll.json file return error.
Thanks in advance.
Try using const PORT = process.env.PORT || 3333;
with
app.listen(PORT, () => {
console.log(`Server is starting on ${PORT}`);
})
I am using xvbf in node application to run a chrome client for capturing web site screens in background. From a web page i am passing data to my webservice as at the bottom of this code part to START_CAPTURE and STOP_CAPTURE.
const puppeteer = require('puppeteer');
const Xvfb = require('xvfb');
var exec = require('child_process').exec;
const os = require('os');
const homedir = os.homedir();
const platform = os.platform();
const http = require('http');
const StringDecoder = require('string_decoder').StringDecoder;
const http_port = 8000;
const ffmpegServer = config.ffmpegServer + ":" + config.ffmpegServerPort;
var xvfb = new Xvfb({
silent: true,
xvfb_args: ["-screen", "0", "1280x800x24", "-ac", "-nolisten", "tcp", "-dpi", "96", "+extension", "RANDR"]
});
var action = "";
var width = 1280;
var height = 720;
var options = {
headless: false,
args: [
'--enable-usermedia-screen-capturing',
'--allow-http-screen-capture',
'--load-extension=' + __dirname,
'--disable-extensions-except=' + __dirname,
'--disable-infobars',
'--start-fullscreen',
'--app=https://www.google.com/'`
],
}
options.executablePath = "/usr/bin/google-chrome"
async function main() {
try{
if(platform == "linux"){
xvfb.startSync()
}
var exportname = 'video.webm'
var url = 'https://www.google.com/';
var duration = 30;
const browser = await puppeteer.launch(options)
const pages = await browser.pages()
const page = pages[0]
page.on('console', msg => {
var m = msg.text();
console.log('PAGE LOG:', m)
});
await page._client.send('Emulation.clearDeviceMetricsOverride')
await page.goto(url, {waitUntil: 'networkidle2'})
await page.setBypassCSP(true);
if(duration > 0){
await page.waitFor((duration * 1000))
}else{
while(action!="STOP_CAPTURE"){
try {
await page.waitForSelector('[class^=modal] > [class^=content] > button[description="Logs you out"]', {
timeout: 1000
});
} catch (err) {
// Do nothing
}
}
}
await page.close()
await browser.close()
if(platform == "linux"){
xvfb.stopSync()
}
fs.unlinkSync(homedir + "/video.webm");
}catch(err) {
console.log(err)
}
}
// Instantiate the HTTP server.
const httpServer = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
if (req.method == 'POST') {
var jsonString = '';
var buffers = [];
req.on('data', function (chunk) {
buffers.push(chunk);
});
req.on('error', (err) => {
// This prints the error message and stack trace to `stderr`.
console.log("Error: %s \n" + err.stack);
});
req.on('end', function () {
try {
jsonString = JSON.parse(Buffer.concat(buffers).toString());
var action = jsonString.markers[0]['action'];
if(action=="START_CAPTURE"){
main();
}
}
catch(e) {
jsonString = JSON.parse(JSON.stringify(Buffer.concat(buffers).toString()));
console.log('\nError \n ' + JSON.stringify(jsonString));
}
});
}
res.end();
console.log('Response ended here. \n ');
});
httpServer.listen(http_port, () => {
console.log("Web server is listening on port %s \n", http_port);
});
This is how it works.
I am clicking a button (START_CAPTURE) from my web site to start capturing. Then capture start. Then i am clicking a button (STOP_CAPTURE) to stop capturing.
everything is working fine. If i do like this.
But if i click START_CAPTURE using two different browsers, then i clicked on STOP_CAPTURE button from any browser window, both instances are getting stop.
I know it is happened because of this lines.
while(action!="STOP_CAPTURE"){
try {
await page.waitForSelector('[class^=modal] > [class^=content] > button[description="Logs you out"]', {
timeout: 1000
});
} catch (err) {
// Do nothing
}
}
Is there anyway to stop only requested instance ? How can i achieve it ?
I'm unable to set the themeSource of my electron app. Platform is Windows 8.1.
const electron = require('electron');
const app = electron.app;
if (app) app.on('ready', function() {
nativeTheme = electron.nativeTheme;
nativeTheme.themeSource = 'dark';
});
This produces an error in a modal pop-up alert saying nativeTheme is undefined.
I'm definitely doing something wrong, but for the life of me I can't see it.
Everything else I'm doing in Electron works like a charm.
Here's my entire app.js:
// server-side jquery
const jsdom = require('jsdom');
const jquery = require('jquery');
const { JSDOM } = jsdom;
const dom = new JSDOM('<!DOCTYPE html>');
const $ = jquery(dom.window);
global.jq = $;
// for priming the webapp
const request = require('request');
// electron config stuff
var backgroundColor = '#1A1A1A';
var width = 800, height = 600;
// get electron
const electron = require('electron');
// prime electron app
const app = electron.app;
// flags: don't enter GUI launch until both sails & electron report ready
var electronIsReady = false;
var sailsIsReady = false;
var gruntIsReady = false;
// block repeat launches (edge contingency)
var windowIsLaunching = false;
var splashIsUp = false;
var splashResponse = 'pong';
// electron window(s)
var mainWindow = null;
var splashWindow = null;
// delay after all preflight checks pass
var windowCreationDelay = 0;
// sails app address
const appAddress = 'http://127.0.0.1';
const appPort = 1337;
const BrowserWindow = electron.BrowserWindow;
if (app) app.on('ready', tryLaunchingForElectron);
else electronIsReady = true;
function tryLaunchingForSails() {
sailsIsReady = true;
try {
// "prime" the webapp by requesting content early
request(`${appAddress}:${appPort}`, (error,response,body) => {/*nada*/});
if (app && electronIsReady && gruntIsReady) createWindow();
}
catch (e) { console.error(e); }
}
function tryLaunchingForElectron() {
// try to prevent multiple instances of the app running
app.requestSingleInstanceLock();
electronIsReady = true;
if (!splashIsUp) {
splashIsUp = true;
// show splash screen
splashWindow = new BrowserWindow({
width: width, height: height,
transparent: true, frame: false, alwaysOnTop: true,
focusable: false, fullscreenable: false,
webPreferences: { nodeIntegration: true }
});
splashWindow.loadURL(`file://${__dirname}/splash.html`);
}
// enter UI phase if sails is also ready
if (app && sailsIsReady && gruntIsReady) createWindow();
}
function createWindow() {
if (windowIsLaunching === true) return -1;
windowIsLaunching = true;
// optional: give sails time to get it fully together
setTimeout(function() {
try {
// tell the splash page to close
splashResponse = 'close';
// create main window
mainWindow = new BrowserWindow({show: false, width: width, height: height,
backgroundColor: backgroundColor
});
// hide menu bar where available
mainWindow.setMenuBarVisibility(false);
// maximize the window
mainWindow.maximize();
// bring to the front
mainWindow.focus();
// go to the sails app
mainWindow.loadURL(`${appAddress}:${appPort}/`);
// show javascript & DOM consoles
mainWindow.webContents.openDevTools();
// show browser only when it's ready to render itself
mainWindow.once('ready-to-show', function() {
// get the splash out of the way
splashWindow.setAlwaysOnTop(false);
// show the main window
mainWindow.setAlwaysOnTop(true);
mainWindow.show();
mainWindow.setAlwaysOnTop(false);
app.focus();
});
// setup close function
mainWindow.on('closed', function() {
mainWindow = null;
});
}
catch (e) { console.error(e); }
}, windowCreationDelay);
}
// tell the splash window when it's time to hide & close
if (app) app.on('ready', function() {
var ipcMain = electron.ipcMain;
ipcMain.on('splashPing', (event, arg) => {
try {
event.sender.send('splashPing', splashResponse);
} catch (e) { console.log(e); }
if (splashResponse === 'close') {
//splashWindow = null;
ipcMain.removeAllListeners('splashPing');
}
// console.log(`${arg}||${splashResponse}`);
});
});
// quit when all windows are closed
if (app) app.on('window-all-closed', function() {
if (process.platform !== 'darwin') {
sails.lower(function (err) {
if (err) {
console.log(err);
app.exit(1);
} else
app.quit();
setTimeout(()=>{app.quit();},5000);
});
}
});
// probably for mobile
if (app) app.on('activate', function() {
if (mainWindow === null) {
createWindow();
}
})
if (app) app.on('ready', function() {
nativeTheme = electron.nativeTheme;
nativeTheme.themeSource = 'dark';
});
// sails wants this
process.chdir(__dirname);
// import sails & rc
var sails;
var rc;
try {
sails = require('sails');
sails.on('hook:grunt:done', () => {
gruntIsReady = true;
tryLaunchingForSails();
});
rc = require('sails/accessible/rc');
} catch (err) {
console.error(err);
}
// Start server
try {
sails.lift(rc('sails'), tryLaunchingForSails );
}
catch (e) { console.log(e); }
nativeTheme = electron.nativeTheme;
This is the problem. You need to do:
const nativeTheme = electron.nativeTheme;
Although in this case there's no need for the extra variable - just do electron.nativeTheme.themeSource = 'dark';.
I strongly suggest you use Typescript - it would tell you this:
Edit: I'm sure I mentioned this in the comments but it seems to have been removed somehow: You also need to make sure you are using Electron 7.0.0 or greater - nativeTheme was not available before that.
I have the following function that is called on every request.
async function checkForNewData() {
var now = moment();
var lastUpdateUnix = fs.readFileSync('.data/last-update.txt').toString();
var lastUpdate = moment.duration(lastUpdateUnix, 'seconds');
now.subtract(lastUpdate);
if (now.hour() >= 1) {
console.log("Last update is over 1 hour old. Getting new data.");
// Schedule
console.log("Getting new schedule.")
let res = await axios.get('https://splatoon2.ink/data/schedules.json', { headers: { "User-Agent": "Splatoon2.ink caching server at glitch.com/~splatoon2-ink-cache" } });
fs.writeFileSync('.data/rotations.json', JSON.stringify(res.data));
// Image
console.log("Getting new image.");
let resImage = await axios.get('https://splatoon2.ink/twitter-images/schedule.png', { headers: { "User-Agent": "Splatoon2.ink caching server at glitch.com/~splatoon2-ink-cache" }, responseType: 'stream' });
const path = Path.resolve(__dirname, '.data', 'image.png');
const writer = fs.createWriteStream(path);
resImage.data.pipe(writer);
fs.writeFileSync('.data/last-update.txt', moment().unix());
console.log("Data is now up to date.");
return;
}
}
On every Express route I have something similar to this.
app.get('/', function(request, response) {
console.log("Request for schedule.");
checkForNewData().then(function() {
console.log("Sending schedule.");
response.sendFile(__dirname + '/.data/rotations.json');
});
});
My goal is to run the function which will check for if the current data is outdated (using the moment library) and if it is, then it gets new data. But for some reason, when using axios to get the image part, it won't be written to the file system. I've tried using different approaches to saving it but everything I've tried won't work.
Here's my full file.
// server.js
// where your node app starts
// init project
const express = require('express');
const app = express();
const moment = require('moment');
const fs = require("fs");
const Path = require('path');
const axios = require("axios")
fs.writeFileSync('.data/last-update.txt', '0');
fs.writeFileSync('.data/rotations.json', '{}');
// we've started you off with Express,
// but feel free to use whatever libs or frameworks you'd like through `package.json`.
// http://expressjs.com/en/starter/static-files.html
app.use(express.static('public'));
async function checkForNewData() {
var now = moment();
var lastUpdateUnix = fs.readFileSync('.data/last-update.txt').toString();
var lastUpdate = moment.duration(lastUpdateUnix, 'seconds');
now.subtract(lastUpdate);
if (now.hour() >= 1) {
console.log("Last update is over 1 hour old. Getting new data.");
// Schedule
console.log("Getting new schedule.")
let res = await axios.get('https://splatoon2.ink/data/schedules.json', { headers: { "User-Agent": "Splatoon2.ink caching server at glitch.com/~splatoon2-ink-cache" } });
fs.writeFileSync('.data/rotations.json', JSON.stringify(res.data));
// Image
console.log("Getting new image.");
let resImage = await axios.get('https://splatoon2.ink/twitter-images/schedule.png', { headers: { "User-Agent": "Splatoon2.ink caching server at glitch.com/~splatoon2-ink-cache" }, responseType: 'stream' });
const path = Path.resolve(__dirname, '.data', 'image.png');
const writer = fs.createWriteStream(path);
resImage.data.pipe(writer);
fs.writeFileSync('.data/last-update.txt', moment().unix());
console.log("Data is now up to date.");
return;
}
}
// http://expressjs.com/en/starter/basic-routing.html
app.get('/', function(request, response) {
console.log("Request for schedule.");
checkForNewData().then(function() {
console.log("Sending schedule.");
response.sendFile(__dirname + '/.data/rotations.json');
});
});
app.get('/image', function(request, response) {
console.log("Request for image.");
checkForNewData().then(function() {
console.log("Sending image.");
response.type('png');
response.sendFile(__dirname + '/.data/image.png');
});
});
// listen for requests :)
const listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
});