Reload module with Node.js require() from a watcher trigger - node.js

I have an index.js that forms a web tier using app.listen() after defining some posts and gets. In it, I have watchr set to watch another .js file in a ./libs folder for any changes to it. Upon a change, I want it to propagate an update from a function it exports to index.js web services without rebooting them.
In index.js...
var bodyParser = require('body-parser');
const express = require('express');
var app = express();
var myObjFile = require('./libs/myObjFile.js');
var watchr = require('watchr');
var myWatchPath = process.cwd() + '\\libs\\myObjFile.js';
function myListener (changeTyle, fullPath, currentStat, previousStat) {
try {
switch(changeType) {
case 'update':
delete require.cache[require.resolve('./libs/myObjFile.js');
//for (const path in require.cache) {
// if (path.endsWith('myObjFile.js') {
// delete require.cache[require.resolve('./libs/myObjFile.js');
// }
//}
//myObjFile = {};
myObjFile = require('./libs/myObjFile.js');
}
}
catch (err) {
console.log('err watcher: ' + err.message);
}
function next (err) {
return err.message;
}
var stalker = watchr.open(myWatchPath , myListener, next);
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "origin, X-Requested-With, Content-Type, Accept");
next();
});
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json( {
extended: try
}));
app.get('/getStuff/', function (req, res) {
var thisObj = new myObjFile.objCreate('my type', myUUID);
thisObj.consoleTest();
})
app.listen(1234);
You can see some of the stuff I have tried commented out in the middle of the watchr function.
//Now inside myObjFile.js
exports.objCreate = function (type, uuid) {
...
function consoleTest() {
console.log('test123');
}
...
return {
...
consoleTest: consoleTest,
...
};
What I expect to happen is that after I start the node.js index.js file with npm start, I can first call the web service get() and see 'test123' print to the console. Then I want to change 'test123' to 'testABC', and save the file, then when I call the get() again, I expect to see 'testABC' print to the console. For now, I continue to see 'test123' until I reboot the server, which I'd like to avoid if possible.
Is there a way to reload a require() at run time?

Related

node + Swagger behind nginx proxy_pass

I'm trying to get node and swagger to work with nginx dynamically
server_name ~^backend(?<PORTSERVER>[^.]+)\.domain\.com$;
location /swagger
{
proxy_pass http://127.0.0.1:$PORTSERVER/swagger/;
}
location /api
{
proxy_pass http://127.0.0.1:$PORTSERVER/api;
}
this is an example of virtual host the PORTSERVER variable is taking from gitlab-ci it takes id number of merge request + 2000
when i put the port directly in place of $PORTSEVER every thing is working swagger and api
any advice is appreciated thank you
this is index.js file
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
require("reflect-metadata");
const typeorm_1 = require("typeorm");
const express = require("express");
// var router = express.Router();
const fileUpload = require("express-fileupload");
const bodyParser = require("body-parser");
const routes_1 = require("./routes");
const cors = require("cors");
const typeorm_pagination_1 = require("typeorm-pagination");
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('../../swagger.json');
var path = require('path');
typeorm_1.createConnection()
.then((connection) => __awaiter(void 0, void 0, void 0, function* () {
// create express app
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(fileUpload());
app.use(express.static(path.join(__dirname, '..', 'public')));
console.log(path.join(__dirname, '..', 'public'));
// register express routes from defined application routes
routes_1.Routes.forEach((route) => {
app[route.method]('/api' + route.route, (req, res, next) => {
const result = new route.controller()[route.action](req, res, next);
res.header('Access-Control-Allow-Origin', '*');
if (result instanceof Promise) {
result.then((result) => (result !== null && result !== undefined ? res.send(result) : undefined));
}
else if (result !== null && result !== undefined) {
res.json(result);
}
});
});
// setup express app here
// ...
app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
app.use(express.json());
app.use(cors());
app.use(typeorm_pagination_1.pagination); // Register the pagination middleware
// start express server
// app.listen(process.env.SERVER_Port);
app.listen(process.env.PORTSERVER);
console.log('Express server has started on port ' + process.env.PORTSERVER);
}))
.catch((error) => console.log(error));
//# sourceMappingURL=index.js.map
The key is his sentence is "when i put the port directly in place of $PORTSEVER every thing is working swagger and api"
Based on the description you gave, I think that gitlabci is miss generating the port number, or miss understanding the syntax.. Both gitlabci and nginx uses $VAR syntax.. Can be a miss interpretation of the 1st line regex too..
Also, I think you need to check the content of process.env.PORTSERVERa used in the js file.. It can have different port than nginx..
For this, I would approach the issue by preventing the the job from restarting nginx to not cause down time for other vhosts.. Deploy a broken config then from the server I run nginx -t and/or diff -u a working config and a broken one..
The 1st source of truth would be nginx -t and nginx logs.. If, ever, nginx manage the starts, the HTTP code it's returning can reveal more paths to pursuit.
One thing you forgot to share is the content of your gitlabci YML.. That can help identify the issue too.

Nodejs calling api updated data not refreshing

Trying to figure out why my api data not refreshing in node. I'm using local node and express calling api which works fine.
The issue I'm have getting the data from the api using setInterval doesn't output the new data.
I will need to stop/restart nodemon app.js each time, to get the updated api data.
Here's my setup.
const app = express();
const port = 3000;
const path = require('path');
app.use(cors());
app.options('*', cors());
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8888');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
async function getCandleTicks(interval) {
try {
const candles = await client.getTicks(
'stocktick', //
'symbol', //
);
app.get('/', (req, res) => {
console.log('load data');
res.send(candles);
});
// do something with the data
} catch (error) {
// handle the error
console.log('error', error);
}
}
//Calling api using set interval works but the data is the same when I refresh the browser. I need to stop and restart to see updated stock ticks
getCandleTicks('1m'); // Calling to get data the first time
var t = setInterval(function () {
getCandleTicks('1m'); // Doesn't reload data.
}, 6000);
app.listen(port, () => console.log(`Hello world app listening on port ${port}!`));
What you're doing is basically trying to reassign the function. Try to avoid it and instead have a variable that keeps changing and send the variable as such:
let candles = 0;
router.get('/', (req, res, next) => {
res.send({
candles
});
});
function updateCandles() {
candles += 1;
// recursive function, keep updating every second
setTimeout(updateCandles, 1000);
}
updateCandles();

Serving VueJS Builds via Express.js using history mode

I want to serve vue js dist/ via express js. I am using history router in vue js app.
The following are the api calls
api/
s-file/sending/:id
terms/get/:which
As i have figured out a solution in python here. I don't know how to do it in node js with express
The code i am using right now is
app.use(function (req, res, next) {
if (/api/.test(req.url))
next();
else {
var file = "";
if (req.url.endsWith(".js")) {
file = path.resolve(path.join(distPath, req.url))
res.header("Content-Type", "application/javascript; charset=utf-8");
res.status(200);
res.send(fs.readFileSync(file).toString());
} else if (req.url.endsWith(".css")) {
file = path.resolve(path.join(distPath, req.url))
res.header("Content-Type", "text/css; charset=utf-8");
res.status(200);
res.send(fs.readFileSync(file).toString());
} else {
file = path.resolve(path.join(distPath, "index.html"))
res.header("Content-Type", "text/html; charset=utf-8");
res.status(200);
res.send(fs.readFileSync(file).toString());
}
}
})
Have a look at connect-history-api-fallback that is referenced in the vue docs.
This should solve your problems.
Example using connect-history-api-fallback
var express = require('express');
var history = require('connect-history-api-fallback');
var app = express();
// Middleware for serving '/dist' directory
const staticFileMiddleware = express.static('dist');
// 1st call for unredirected requests
app.use(staticFileMiddleware);
// Support history api
// this is the HTTP request path not the path on disk
app.use(history({
index: '/index.html'
}));
// 2nd call for redirected requests
app.use(staticFileMiddleware);
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
The very simpler one if anyone wants to use
Just add this below all the valid routes and above app.listen
app.all("*", (_req, res) => {
try {
res.sendFile('/absolute/path/to/index.html');
} catch (error) {
res.json({ success: false, message: "Something went wrong" });
}
});
Make sure you have included
app.use(express.static('/path/to/dist/directory'));

node, express - restrict access to downloadable file

I am using express as server for micro-services rest api. Endpoints are built from directory structure. There are few downloadable pdf files which are currently at client side. And it can be downloadable (with the href URL) even if user is not logged into the portal. So, I put all the pdf files to server.
Directory structure on server:
pdf files are inside docs directory. Please find below the code of server:
/* global __dirname */
import morgan from 'morgan';
import logger, { webStream } from './services/logger';
import { socket } from './services';
// set env variables before all else
import { GATEWAY_PORT, CORS_ORIGINS } from './config';
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')();
const version = require('./services/utils').version();
const authentication = require('./services/authentication');
const utils = require('./services/utils');
// set up app and middleware
const app = express();
app.use(morgan('User::req[user-id] Correlation::req[x-correlation-id] Method::method URL::url Status::status :res[content-length] - :response-time ms', { stream: webStream }));
logger.info('Starting...');
app.use(cookieParser);
app.use(bodyParser.json({ limit: '50mb' }));
app.disable('x-powered-by');
// CORS headers to allow running client/server on different ports
app.use((req, res, next) => {
// Check if the origin is whitelisted in the env vars
const actual = req.headers.origin || '';
if (utils.matchCors(actual, CORS_ORIGINS.split(','))) {
res.set({ 'Access-Control-Allow-Origin': actual });
}
res.set({
// standard CORS headers
'Access-Control-Allow-Headers': 'Content-Type, Authorization, Accept, Accept-Language',
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Methods': 'PATCH,POST,GET,DELETE',
// addresses security issues identified by automated pen testing
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'X-XSS-Protection': 1,
});
next();
});
// set the user property of the request object
app.use((req, res, next) => {
const token = req.cookies[authentication.cookieName];
if (!token) {
req.user = false;
} else {
req.user = authentication.decodeJWT(token);
authentication.setCookie(res, token, req.user);
}
utils.setCorrelationId(req.headers['x-correlation-id']);
req.correlationId = req.headers['x-correlation-id'];
next();
});
// helper function returning middleware to reject unauthorised users
function requiredRoles(roles, abcOnly) {
return function requireRolesHandler(req, res, next) {
if (
!req.user
|| (abcOnly && !req.user.isabc)
|| !authentication.hasRole(req.user, roles)) {
const error = new Error('UNAUTHORISED');
error.status = 403;
next(error);
} else {
next();
}
};
}
// Add the endpoints to express.
// Reversed to get literal routes before # capture groups.
utils.parseDirectory(`${__dirname}/rest`, [], true).reverse().forEach((endpoint) => {
const { auth, functions } = endpoint.handler;
if (auth) {
functions.unshift(requiredRoles(auth.roles, auth.abcOnly));
}
app[endpoint.method](
endpoint.url,
functions,
);
});
// setup server
const server = app.listen(GATEWAY_PORT, () => {
logger.info(`Allowed CORS: ${CORS_ORIGINS}`);
logger.info(`Started ${version.name} (${version.number}) listening on ${GATEWAY_PORT}`);
});
socket.createServer(server);
How do I serve pdf files from server to client only to authorized user when user clicks on link on a page ?
Have a route to download file e.g. GET /api/download?file=abc.pdf
Now in the middleware,
Check if the req.user exists or not.
Check if the user has sufficient rights to download the file or
not
If 1 and 2 satisfy, then serve the file
Code would look more or less like this:
app.get('/api/download', (req, res, next) => {
// Check if the request had valid token or not
if(!req.user) {
const error = new Error('UNAUTHORISED');
error.status = 403;
return next(error);
}
const { user } = req;
const { file } = req.query;
// If you want to have some additional logic wherein
// you want to restrict the download of the file,
// you can put that logic in this function
const isAllowed = canDownload(user, file);
if(isAllowed) {
return res.sendFile(path.join(__dirname, 'docs', path.sep, file));
}
const error = new Error('UNAUTHORISED');
error.status = 403;
return next(error);
})
You might need to require path, implement canDownload or solve no such file or directory errors because of __dirname usage. All of those are trivial. If you need help for those as well, let me know in the comments.
Here is the reference to response.sendFile()
And this might be helpful too.

Node.js HTML5 Boilerplate Server Config Integration

I'm setting up the structure of a new project which will be built using Node.js and Express. I 'm using HTML5 Boilerplate for an optimal starting point. It comes with configuration files for multiple types of servers: Apache, Nginx, Node.js, etc. The following is the Node.js server configuration file provided by the HTML5 Boilerplate team:
/* h5bp server-configs project
*
* maintainer: #xonecas
* contributors: #niftylettuce
*
* NOTES:
* compression: use the compress middleware provided by connect 2.x to enable gzip/deflate compression
* http://www.senchalabs.org/connect/compress.html
*
* concatenation: use on of the following middlewares to enable automatic concatenation of static assets
* - https://github.com/mape/connect-assetmanager
* - https://github.com/TrevorBurnham/connect-assets
*/
var h5bp = module.exports,
_http = require('http'),
_parse = require('url').parse;
// send the IE=Edge and chrome=1 headers for IE browsers
// on html/htm requests.
h5bp.ieEdgeChromeFrameHeader = function () {
return function (req, res, next) {
var url = req.url,
ua = req.headers['user-agent'];
if (ua && ua.indexOf('MSIE') > -1 && /html?($|\?|#)/.test(url)) {
res.setHeader('X-UA-Compatible', 'IE=Edge,chrome=1');
}
next();
};
};
// block access to hidden files and directories.
h5bp.protectDotfiles = function () {
return function (req, res, next) {
var error;
if (/(^|\/)\./.test(req.url)) {
error = new Error(_http.STATUS_CODES[405]); // 405, not allowed
error.status = 405;
}
next(error);
};
};
// block access to backup and source files
h5bp.blockBackupFiles = function () {
return function (req, res, next) {
var error;
if (/\.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)|~/.test(req.url)) {
error = new Error(_http.STATUS_CODES[405]); // 405, not allowed
error.status = 405;
}
next(error);
};
};
// Do we want to advertise what kind of server we're running?
h5bp.removePoweredBy = function () {
return function (req, res, next) {
res.removeHeader('X-Powered-By');
next();
};
};
// Enable CORS cross domain rules, more info at http://enble-cors.org/
h5bp.crossDomainRules = function () {
return function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With');
next();
};
};
// Suppress or force 'www' in the urls
// #param suppress = boolean
h5bp.suppressWww = function (suppress) {
return function (req, res, next) {
var url = req.url;
if (suppress && /^www\./.test(url)) {
res.statusCode = 302;
res.setHeader('Location', url.replace(/^www\./,''));
}
if (!suppress && !/^www\./.test(url)) {
res.statusCode = 302;
res.setHeader('Location', "www."+url);
}
next();
};
};
// Far expire headers
// use this when not using connect.static for your own expires/etag control
h5bp.expireHeaders = function (maxAge) {
return function (req, res, next) {
res.setHeader('Cache-Control', 'public, max-age='+ (maxAge));
next();
};
};
// Etag removal
// only use this is you are setting far expires for your files
// ** WARNING ** connect.static overrides this.
h5bp.removeEtag = function () {
return function (req, res, next) {
res.removeHeader('Last-Modified');
res.removeHeader('ETag');
next();
};
};
// set proper content type
// #param mime = reference to the mime module (https://github.com/bentomas/node-mime)
h5bp.setContentType = function (mime) {
return function (req, res, next) {
// I'm handling the dependency by having it passed as an argument
// we depend on the mime module to determine proper content types
// connect also has the same dependency for the static provider
// ** #TODO ** maybe connect/express expose this module somehow?
var path = _parse(req.url).pathname,
type = mime.lookup(path);
res.setHeader('Content-Type', type);
next();
};
};
// return a express/connect server with the default middlewares.
// #param serverConstructor = express/connect server instance
// #param options = {
// root: 'path/to/public/files',
// maxAge: integer, time in miliseconds ex: 1000 * 60 * 60 * 24 * 30 = 30 days,
// mime: reference to the mime module ex: require('mime')
// }
// Depends:
// express or connect server
// mime module [optional]
h5bp.server = function (serverConstructor, options) {
var server = serverConstructor.createServer(),
stack = [
this.suppressWww(true),
this.protectDotfiles(),
this.blockBackupFiles(),
this.crossDomainRules(),
this.ieEdgeChromeFrameHeader()
//,this.expireHeaders(options.maxAge),
// this.removeEtag(),
// this.setContentType(require('mime'))
];
// express/connect
if (server.use) {
stack.unshift(serverConstructor.logger('dev'));
stack.push(
//serverConstructor.compress(), // express doesn't seem to expose this middleware
serverConstructor['static'](options.root, { maxAge: options.maxAge }), // static is a reserved
serverConstructor.favicon(options.root, { maxAge: options.maxAge }),
serverConstructor.errorHandler({
stack: true,
message: true,
dump: true
})
);
for (var i = 0, len = stack.length; i < len; ++i) server.use(stack[i]);
} else {
server.on('request', function (req, res) {
var newStack = stack,
func;
(function next (err) {
if (err) {
throw err;
return;
} else {
func = newStack.shift();
if (func) func(req, res, next);
return;
}
})();
});
}
return server;
};
My question is this: how exactly do I go about integrating this with Express? The section of code that specifically confuses me is the bottom portion:
// return a express/connect server with the default middlewares.
// #param serverConstructor = express/connect server instance
// #param options = {
// root: 'path/to/public/files',
// maxAge: integer, time in miliseconds ex: 1000 * 60 * 60 * 24 * 30 = 30 days,
// mime: reference to the mime module ex: require('mime')
// }
// Depends:
// express or connect server
// mime module [optional]
h5bp.server = function (serverConstructor, options) {
var server = serverConstructor.createServer(),
stack = [
this.suppressWww(true),
this.protectDotfiles(),
this.blockBackupFiles(),
this.crossDomainRules(),
this.ieEdgeChromeFrameHeader()
//,this.expireHeaders(options.maxAge),
// this.removeEtag(),
// this.setContentType(require('mime'))
];
// express/connect
if (server.use) {
stack.unshift(serverConstructor.logger('dev'));
stack.push(
//serverConstructor.compress(), // express doesn't seem to expose this middleware
serverConstructor['static'](options.root, { maxAge: options.maxAge }), // static is a reserved
serverConstructor.favicon(options.root, { maxAge: options.maxAge }),
serverConstructor.errorHandler({
stack: true,
message: true,
dump: true
})
);
for (var i = 0, len = stack.length; i < len; ++i) server.use(stack[i]);
} else {
server.on('request', function (req, res) {
var newStack = stack,
func;
(function next (err) {
if (err) {
throw err;
return;
} else {
func = newStack.shift();
if (func) func(req, res, next);
return;
}
})();
});
}
return server;
};
My JavaScript isn't exactly at a beginners level but I wouldn't say I'm advanced either. This code is beyond me. Any pointers as to what I can read, watch, or do, to learn what I'm obviously missing here would be appreciated.
Most of the file is made up of a series of functions that generate middleware for frameworks, like Express, that conform to Connect's middleware specification. The second code listing is designed to create an HTTP server that uses all these functions. From what I can tell, it looks like you're supposed to pass in whatever you would normally call createServer on, and h5bp will do the creation and setup for you. For example, if you would normally do:
var express = require('express');
var server = express.createServer();
You would instead pass express to h5bp.server, which calls createServer on whatever you pass in right off the bat:
var express = require('express');
var server = h5bp.server(express, options);
After a bit of setup, it checks to see if the server has a function called use (the line is if (server.use)), and if so uses it to inject all the middleware it set up into the server. If it doesn't, then it assumes you're using a raw Node.js HTTP server, and sets up the necessary code to pass the request through each of the items in stack manually (this is what Connect/Express does for you).
It's worth noting that, in Express 3 (currently in release candidate stage), the applications created by Express no longer inherit from Node's HTTP server, so you don't call createServer on express; instead, you should just call express() and then pass the results of that into http.createServer. (See "Application function" at Migrating from 2.x to 3.x on the Express wiki for more information.) This means that this script is not compatible with the latest version of Express.
[update]
If you take a look at the test directory on GitHub, you can see an example app:
var express = require('express'),
h5bp = require('../node.js'),
server = h5bp.server(express, {
root: __dirname,
maxAge: 1000 * 60 * 60 * 30
});
server.listen(8080);
console.log('ok');
There was a major update of h5bp for node.js.
You can use it as an express middleware now.
The repository has moved here : https://github.com/h5bp/node-server-config.
From the documentation:
var express = require('express'),
h5bp = require('h5bp');
var app = express();
// ...
app.use(h5bp({ root: __dirname + '/public' }));
app.use(express.compress());
app.use(express.static(__dirname + '/public'));
// ...
app.listen(3000);

Resources