how to catch react router change event from outside page - node.js

I have three react-router-dom applications and I want to save browser log data from another my library which is built by node.
So how to catch react router change event from outside application??
The dependency is like this.
[A app + Logging Manager app], [B app + Logging Manager app]. [C app + Logging Manager app]
I already tried winodw.onpopstate, npm History.. but It doesn't work;;
[A App] [B App] [C App]
watching A,B,C router change event from outside
[Logging Manager App]
send logs with api call!!
here is my Logging Manager sample code
// not working!
window.onpopstate = function(event) {
console.log("location: " + document.location + ", state: " + event.state);
};
// not working!
const history = createBrowserHistory();
const location = history.location;
const unlisten = history.listen((location, action) => {
console.log('hello ! : ', action, location.pathname, location.state);
});
// but window.history.state is changed!

Here is my answer to catch react-router change event.
const orgPushState = window.history.pushState;
window.history.pushState = function () {
console.log('here : ', arguments);
orgPushState.apply(this, arguments);
};

Related

Renderer process out of memory after app is packaged into EXE

Expected Behavior
In my main renderer window I'm initializing UI from an imported module. When running the app in dev mode everything works but once I package the app into EXE and try to run it, I'm getting renderer process out of memory error.
The expected behavior is that the packaged app should run like the dev mode.
Actual Behavior
I've checked the electron logs and memory consumption in windows task manager. The memory usage keeps increasing until the renderer process crashes and electron logs oom error.
Since I'm new to electron I've read the documentation but unable to figure out why the same thing is running perfectly in dev mode and crashes in prod.
Log : Renderer process oom - see https://www.electronjs.org/docs/tutorial/application-debugging for potential debugging information.
Another log after some time : [19156:0921/120104.184:ERROR:gpu_init.cc(440)] Passthrough is not supported, GL is swiftshader
So this is what I'm trying to do -
Import my module which is served on localhost:
This module exposes an object - Tesseract through which I initialize my UI.
In the component which is rendered in my main browser window I initialize the UI like this:
File - index.tsx
useEffect(() => {
if (tsToken) {
Tesseract.ui
.init(tsToken.tsToken, "random_id")
.then((sdk: any) => {
console.log("🚀 ~ file: index.js ~ line 3 ~ sdk", sdk);
// After this line nothing is logged and renderer process crashes (But works when I run electron in dev mode)
sdk.init({
...MasterConfig,
RootElement: document.getElementById("tesseract-root"),
});
const authToken = sessionStorage.getItem("ts_token");
ipcRenderer.send("set-globals", { TAC: authToken });
})
.catch((err: any) => console.error(err));
}
}, [tsToken]);
return <></>;
In the above piece of code we pass a root element to the init method. The init method then initializes a react app in that root element.
EDIT:
This is the init method in that module
SDK.prototype.init = async function (config) {
try {
const parsedConfig = JSON.parse(this.META.uiConfig);
window.Tesseract.ui["MasterConfig"] = merge(parsedConfig, config);
window.Tesseract.ui["info"] = this.META;
const projectType = window.Tesseract.ui.MasterConfig.ProjectConfig.type;
console.log("🚀 ~ file: lib.js ~ line 51 ~ projectType", projectType)
const MasterConfig = window.Tesseract.ui["MasterConfig"];
console.log(MasterConfig);
if (
MasterConfig &&
MasterConfig.UserProperties &&
MasterConfig.UserProperties.userId
) {
console.log("React init here");
// Console logs upto this point and then everything fails
// No error logged
try {
RecorderUI.ReactApp({ ...parsedConfig, ...config }).then((obj) => {
// This is never logged
console.log("UI rendered");
return obj;
}).catch(err => console.log(err));
} catch (err) {
console.log(err);
return err;
}
} else {
const e = new Error("User Id Missing");
e.name = "Missing unique user Id";
throw e;
}
} catch (err) {
console.error(err);
return err;
}
};
This is the UI rendering method:
export default async function entry(config) {
// This is never logged so basically something is happening before this
console.log("somemmthing something");
console.log("Root Element: ",
window.Tesseract?.ui?.MasterConfig?.RootElement);
try {
const { RootElement } = config;
RootElement.attachShadow({ mode: "open" });
await IDBStore.create({
version: 1,
name: "ext",
schema: {
globals: "&key",
},
});
const shadowRoot = RootElement.shadowRoot;
const styleElement = document.createElement("style");
styleElement.innerHTML = `${LoaderCss} ${SourcePreviewCSS} ${NotificationCss} ${LibraryCss} ${SelectModeCss} ${WebcamBoxCss} ${CameraBubbleCss} ${countdownCss} ${indexStyles} ${commonCss} ${colorsCss} ${homeCss} ${recordCss} ${advancedOptionsCss} ${recordingTypesCss} ${toggleOptionsCss} ${tourOverlayCss} ${headerCss} ${checkboxCss} ${tooltipCss} ${dropdownCss} ${toggleCss} ${recordButtonCss} ${bannersCss} ${toolsCss} ${canvasCss} ${paintCss} ${recordingControlsCss} ${controlsMainCss}`;
const reactRoot = document.createElement("div");
reactRoot.setAttribute("id", "react-root");
shadowRoot.appendChild(styleElement);
shadowRoot.appendChild(reactRoot);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
reactRoot
);
window.addEventListener("message", handleMessaging, false);
} catch (err) {
console.error(err);
return new Error(err);
}
}
I've read numerous blog posts, github issues but unable to find what's causing this. I need help in determining what is the difference in internal workings of electron which causes such difference b/w running in dev mode vs. packaging the app.

Slack node js program running session shut down

Sorry to disturb. I am programming a Slack robot to reply user message by using the API
In the morning, it works totally okay. Then after I returned back from my office it just shut down and show me this error
Jiatongs-MacBook-Pro:news-bot jiatongli$ node index.js
Assertion failed: token must be defined
/Users/jiatongli/Desktop/news-bot/node_modules/vow/lib/vow.js:105
throw e;
^
Error: not_authed
at _api.then.fail (/Users/jiatongli/Desktop/news-bot/node_modules/slackbots/index.js:46:33)
at Array.<anonymous> (/Users/jiatongli/Desktop/news-bot/node_modules/vow/lib/vow.js:773:56)
at callFns (/Users/jiatongli/Desktop/news-bot/node_modules/vow/lib/vow.js:24:35)
at process._tickCallback (internal/process/next_tick.js:61:11)
Emitted 'error' event at:
at _api.then.fail (/Users/jiatongli/Desktop/news-bot/node_modules/slackbots/index.js:46:19)
at Array.<anonymous> (/Users/jiatongli/Desktop/news-bot/node_modules/vow/lib/vow.js:773:56)
at callFns (/Users/jiatongli/Desktop/news-bot/node_modules/vow/lib/vow.js:24:35)
at process._tickCallback (internal/process/next_tick.js:61:11)
Jiatongs-MacBook-Pro:news-bot jiatongli$
I personally have no idea what is going on because it seems like the program itself do not have bug. What i miss?
Here is the code in my index.js file:
var SlackBot = require("slackbots");
var request = require("request");
var NewsAPI = require("newsapi");
var unirest = require("unirest");
var API_KEY = process.env.API_KEY;
var Slack_API = process.env.Slack_API;
// create a bot
var bot = new SlackBot({
token: Slack_API,
name: "aloha-ai"
});
bot.on("message", msg => {
switch (msg.type) {
case "message":
// we only want to listen to direct messages that come from the user
if (msg.channel[0] === "D" && msg.bot_id === undefined) {
getRandomTechNews(postMessage, msg.user)
}
break
}
})
const postMessage = (message, user) => {
bot.postMessage(user, message, {
as_user: true
});
}
const getRandomTechNews = (callback, user) => {
unirest.get("https://nuzzel-news-v1.p.rapidapi.com/news?count=10&q=product")
.header("X-RapidAPI-Host", "nuzzel-news-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", API_KEY)
.end(function (response) {
var newsJSON = response.body;
var news = "*Viral News* in product : \n\n\n\n";
for (i = 0; i < newsJSON.results.stories.length; i++) {
news += "_Excerpt:_ \n" + ">" + newsJSON.results.stories[i].excerpt + "\n"
news += "_Let's see the article!_ \n" + newsJSON.results.stories[i].url + "\n\n\n"
};
callback(news, user);
});
}
Your error message seems to indicate that your program is not authenticated with the Slack API: Error: not_authed
Since you are retrieving your API key and token from environment variables:
var API_KEY = process.env.API_KEY;
var Slack_API = process.env.Slack_API;
my guess is that you have started a new terminal session where you have not yet set that environment variable, or you are on a different computer where it is not set.
Before running your program, try exporting those variables from the command line:
export API_KEY=<my-api-key>
export Slack_API=<my-token>
If you have security concerns about your API keys showing up in your bash history you can do one of two things (these are examples of things that I do, but there are probably better, safer practices out there):
You can put an empty space before your command [space]export API_KEY=<my-api-key> instead of export API_KEY=<my-api-key>. This will make it so the command does not show up in your history.
You can put your export commands in a separate file called e.g., ~/.secrets and then run the command source ~/.secrets which will run your export commands.
Probably these will give you a sense of security rather than actual security though, since you can just echo the value of the environment variables, but I personally like taking one of these steps as an extra precaution.
i use this answer and can solve it.
Add a bot https://my.slack.com/services/new/bot and put the token
do you get token for your bot from above url:
did you set this ?

undefined on server console after make-runnable-output

I have created a simple html application on nodejs. Here is the code os server.ts
import express = require('express');
import http = require('http');
import path = require('path');
import cookieParser = require('cookie-parser');
export class Server {
static startServer() {
let app = express();
app.use(cookieParser());
app.use(express.static(__dirname + "/public"));
let server = http.createServer(app);
server.listen(7000, () => {
console.log('Up and running on port : ' + 7000);
});
}
}
exports.startServer = Server.startServer;
// Call a module's exported functions directly from the command line.
require('make-runnable');
I have used make-runnable module to run exported functions directly from the command line. And this is my start script in package.json
"start": "concurrently \"tsc --watch \" \"nodemon server.js startServer\""
the application is working fine, but this is printing undefined on the screen that annoying and this should be solved.
[1] [nodemon] restarting due to changes...
[1] [nodemon] starting `node server.js startServer`
[1] Up and running on port : 7000
[1] --------make-runnable-output--------
[1] undefined
[1] ------------------------------------
[0] 4:08:52 PM - Compilation complete. Watching for file changes.
What is the reason for this?
Solution
The make-runnable package provides you with the ability to clear the output frame by import the "custom" module and specifying a boolean value for printOutputFrame. Rather than require('make-runnable'), use:
require('make-runnable/custom')({
printOutputFrame: false
})
However, this doesn't remove the "undefined" (or output) message because make-runnable is expecting some return value to log as it resolves the promise.
Instead, return some value from your function for Bluebird (or make-runnable) to print on its own:
static startServer() {
let app = express();
app.use(cookieParser());
app.use(express.static(__dirname + "/public"));
let server = http.createServer(app);
- server.listen(7000, () => {
- console.log('Up and running on port : ' + 7000);
- });
+ server.listen(7000)
+ return 'Up and running on port : ' + 7000;
}
Note: I haven't verified that this won't cause the server to stop listening, but this is rather illustrating what the package will expect from your function.
Explanation
*Bit of an older question but I came across the same issue recently so I figured I'd provide a solution.
The reason why "undefined" is being printed is that it's expecting some return value from the function that you're calling. Consider this snippet of a function (that I was working on and ran across this issue):
generate: function() {
// ... some preliminary code
fs.writeFile(file, modifiedFileData, error => {
if (error) throw error
console.log('App key generated successfully.')
})
}
Output:
$ node utils/EncryptionKey.js generate
--------make-runnable-output--------
undefined
------------------------------------
App key generated successfully.
Done in 0.27s.
By default, I just want that message to be logged to the console. Without make-runnable it worked fine since I initially just ran the function, but wanted to put this into an export. Looking at the source code (engine.js) for make-runnable, the following function:
function printOutput(returnValue) {
Bluebird.resolve(returnValue)
.then(function (output) {
if (options.printOutputFrame) {
console.log('--------make-runnable-output--------');
}
console.log(output);
if (options.printOutputFrame) {
console.log('------------------------------------');
}
}).catch(printError);
}
expects some return value, since Bluebird wraps the function you're executing as a promise. I instead changed my initial code to:
generate: function () {
// ... some preliminary code
fs.writeFile(file, modifiedFileData, error => {
if (error) throw error
return true
})
return 'App key generated successfully.'
}
and removed the "undefined" value, and printed my app key message.
Output:
$ node utils/EncryptionKey.js generate
App key generated successfully.
Done in 0.26s.

Electron app - logging to file in production

I want to get logs if something wrong happens to my electron app when in production mode i.e after giving .exe file to a user wrt windows platform.
How to go about it, how can i basically write my errors to a file which will be in cyclic in nature.
Take a look at electron log
// Setup file logging
const log = require('electron-log');
log.transports.file.level = 'info';
log.transports.file.file = __dirname + 'log.log';
// Log a message
log.info('log message');
EDIT:
As mentioned in the comments the "log.transports.file.file" is deprecated.
Instead I would suggest to use the following method.
log.transports.file.resolvePath = () => __dirname + "/log.log";
Create a file next to you electron.js called logger.js
const path = require("path");
const log = require('electron-log');
log.transports.file.resolvePath = () => path.join(__dirname, '/logsmain.log');
log.transports.file.level = "info";
exports.log = (entry) => log.info(entry)
then on your app
const logger = require('electron').remote.require('./logger');
logger.log("some text")
Please have a look here:
https://www.electronjs.org/docs/api/net-log
const { netLog } = require('electron')
app.whenReady().then(async () => {
await netLog.startLogging('/path/to/net-log')
// After some network events
const path = await netLog.stopLogging()
console.log('Net-logs written to', path)
})

require path works in root but not in elsewhere

Working in Node.js, I am splitting one working snippet into two snippets that produce the same result. The working snippet has a require statement and is in the root directory. The split two segments work at the first stage in the root directory but the second stage fails and reports the eTrade library cannot be found. Here is the require statement.
var etrade = require('./lib/etrade');
When executed from server.js in the root, everything works but in the split project, capturing the variable now appears in index.js in the routes folder and does not work. The client side reports that the eTrade library cannot be found. There is something here I'm not understanding, probably about how to resolve the path specification in the require.
In the meanwhile, I have made the thing work using a global variable that I pass from app.js in the root to index.js in the routes folder. I can continue development this way, but it would be much better if I understood how to make the first variant work.
please edit the question and show your files hierarchy, then show us how you merge the files? which tools you used, and where was the output
server.js runs in the root and works very well.
app.js runs in the root and is part A of the merged system and works.
index.js run in the root/routes folder and is part B of the merged system and fails.
root/lib/eTrade contains the eTrade modules.
The working snippet (server.js) opens a confirmation window on the eTrade site and the user copy/pastes back a confirmation code which is echoed back to the console on the server side. The failing snippet, which is a combination of A and B, refuses to pass the variable (the confirmation code) to B and because it reports a failure to find the eTrade library, I think the problem is the path in the require statement, which is
var etrade = require('./lib/etrade');
The merged system performs the first part of the task but does not pass the variable to the second part of the task and I think that's because the variable in B is outside of scope.
Here is the working snippet:
/*
* Module dependencies
*/
const port = 3000
var express = require('express')
, stylus = require('stylus')
, nib = require('nib')
, logger = require('morgan')
, routes = require('./routes/index')
, users = require('./routes/users')
,app = express()
function compile(str, path) {
return stylus(str)
.set('filename', path)
.use(nib());
}
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
//app.use('/', routes);
//app.use('/users', users);
app.use(logger('dev'));
app.use(stylus.middleware(
{ src: __dirname + '/public'
, compile: compile
}
));
app.use(express.static(__dirname + '/public'))
//from expressSite
// from readme
var etrade = require('./lib/etrade');
var configuration =
{
useSandbox : true, // true if not provided
key : '', //actual value deleted
secret : '' //actual value deleted
}
var et = new etrade(configuration);
//here we send the user a credentials link
et.getRequestToken(
function(authorizationUrl) {
// Your service requires users, who will need to visit
// the following URL and, after logging in and
// authorizing your service to access their account
// data, paste the E*TRADE provided verification
// code back into your application.
app.get('/', function (req, res) {
res.render('AuthApp',
{ authLink : authorizationUrl }
)
});
console.log("AuthorizationURL " + authorizationUrl + " "); },
function(error) {
console.log("Error encountered while attempting " +
"to retrieve a request token: " +
error);
}
); //end getRequestToken
//user sends confirmation code and we get acesss token
app.get('/users/sendcode', function (req, res) {
console.log('verification code is '+req.query.vCode);
//end get verification code
et.getAccessToken(req.query.vCode,
function() {
// Your app can start using other E*TRADE API now
// begin main interaction
// this is where we should land first after oath
// hand it over to the db page?
//et.listAccounts();
//console.log(a);
res.render('ETQuery');
console.log('thread entered getAccessToken function')
// console.log(AccessToken)
},
function(error) {
console.log("Error encountered while attempting " +
"to exchange request token for access token: " +
error);
}
);
})
app.listen(port, (err) => {
if (err) {
return console.log('something bad happened', err)
}
console.log(`CIA is listening to the FSB on ${port}`)
})
Here is the new part A code in app.js in the root. You can see how I've patched it to use a global.
var etrade = require('./lib/etrade');
var configuration =
{
useSandbox : true, // true if not provided
key : '', //actual value deleted
secret : '' //actual value deleted
}
var et = new etrade(configuration);
// here we send the user a credentials link
et.getRequestToken(
function(authorizationUrl) {
// Your service requires users, who will need to visit
// the following URL and, after logging in and
// authorizing your service to access their account
// data, paste the E*TRADE provided verification
// code back into your application.
// app.get('/', function (req, res) {
// res.render('index',
// { authLink : authorizationUrl }
// )
// });
console.log("AuthorizationURL is " + authorizationUrl + " ");
global.ETauthUrl = authorizationUrl;
},
function(error) {
console.log("Error encountered while attempting " +
"to retrieve a request token: " +
error);
}
); //end getRequestToken
But part B does not capture the token. What is odd here is that the failure is reported as to finding the eTrade library, while that library is nowhere referenced in B. In the working snippet, I cannot pass the token beyond server.js yet but I can report it on the server side after the user pastes it in and hits the send button on the client side. Here is B.
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
res.render('index',
{ authLink : ETauthUrl }
)
});
This works only because of the global variable.
The ./... tells NodeJS to load the applicable code from the current directory. This will work in server.js, from the root of the app. However, to properly reference said file from another directory, you'll need to properly reference it's path.
E.g. given:
bootstrap/
├── lib/
│ ├── etrade.js
├── modules/
│ ├── foo.js
| ├── bar/
| ├── bar.js
└── server.js
You'll need to reference etrade in server.js as:
var etrade = require('./lib/etrade');
And in modules/foo.js as:
var etrade = require('../lib/etrade');
And in modules/bar/bar.js as:
var etrade = require('../../lib/etrade');
More on how NodeJS require resolves files.
require(X) from module at path Y
1. If X is a core module, a. return the core module b. STOP
2. If X begins with './' or '/' or '../' a. LOAD_AS_FILE(Y + X) b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"
LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text. STOP
2. If X.js is a file, load X.js as JavaScript text. STOP
3. If X.json is a file, parse X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP
LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file, a. Parse X/package.json, and look for "main" field. b. let M = X + (json main field) c. LOAD_AS_FILE(M)
2. If X/index.js is a file, load X/index.js as JavaScript text. STOP
3. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
4. If X/index.node is a file, load X/index.node as binary addon. STOP
LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS: a. LOAD_AS_FILE(DIR/X) b. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0, a. if PARTS[I] = "node_modules" CONTINUE b. DIR = path join(PARTS[0 .. I] + "node_modules") c. DIRS = DIRS + DIR d. let I = I - 1
5. return DIRS

Resources