With node-direct, you can upload server-side .js files and access them in the same way as .php scripts: example.com/foo.srv.js .
Installation
npm install -g node-direct
Nginx configuration
location ~ \.srv\.js$ { root <path_to_website_files>; proxy_pass http://localhost:<port>; proxy_set_header X-Requested-File-Path $document_root$uri; }
Run
node-direct --port=<port>
The script foo.srv.js, where req
and res
created by the express server.
module.exports = function(req, res) { const someModule = require('some-module'); res.send('Hello world!'); }
When NodeJS became more or less popular, it was not easy for me to realize that everything is not as easy with it as with PHP. Using the latter one could create a .php file, upload it to the server, contact example.com/path/name.php and enjoy it. Such simplicity of script deployment was one of the reasons why "puff" became so popular.
In turn, NodeJS, regardless of the complexity of the application, forces many things to do by hand.
pm2 makes it easier to update and run the application after a reboot or after exceptions thrown by the application. But with him, firstly, you need to get your hand, and secondly, to force the launch of the next application to repeat the routine actions.
All of the above is rather an advantage than a disadvantage if you are developing a large application. But what if the application is very simple? What if this is not an application at all, but an ordinary site, with a small “apishka” (for example, a proxy for cross-domain requests)? What if a lot of sites are chasing a VPS, and for everyone setting up a NodeJS server is too lazy.
node-direct is a tool that allows you to deploy server-side JavaScript scripts as easy as .php :
In this case, a single node-direct instance can be used on different sites.
For most of the "magic" is the nginx config. It needs to be taught to handle requests to .srv.js files (you can use any extension).
location ~ \.srv\.js$ { root <path_to_website_files>; proxy_pass http://localhost:<port>; proxy_set_header X-Requested-File-Path $document_root$uri; }
path_to_website_files
- path to the site filesport
- the port on which node-direct lives (by default, 8123)An example of a complete config:
server { listen 80; server_name example.com; # Serve static files location / { root /var/web/example.com/public; index index.srv.js index.html index.htm; } location ~ \.srv\.js$ { root /var/web/example.com/public; proxy_pass http://localhost:8123; proxy_set_header X-Requested-File-Path $document_root$uri; } }
That is, statics will be served using nginx (this is known to be several times faster than statics that express
not need to be cached), plus, even .php can be added to the list of supported server files (usable for legacy projects).
node-direct --port=8000
Node-direct uses the good old Express . Server files must export a function that accepts request
and response
.
Hello world:
module.exports = function(req, res) { const someModule = require('some-module'); res.send('Hello world!'); }
JSON API:
module.exports = function(req, res) { if(req.method === 'POST') { req.json({ message: 'Everything is awesome' }); } else { req.status(400).json({ message: 'Only POST requests are allowed' }); } }
Rendering
const fs = require('fs'); const ejs = require('ejs'); const template = ejs.compile(fs.readFileSync('./templates/index.html')); module.exports = function(req, res) { res.type('html').send(template({ foo: 'bar' })); }
For more information, see the Express documentation.
An example of a hypothetical application:
/package.json - dependencies and devDependencies /index.html - HTML /js/app.js - client-side JavaScript /css/style.css - /node_modules/ - "npm install" ( ) /foo/index.srv.js - JSON API, /foo/ /bar/index.srv.js - HTML , /bar/
--port
- node-direct server port (8123 by default)
In this mode, an HTTP server is created that distributes statics and does not require nginx. The mode is used by the developer to run on a local machine.
--standalone
- turns on standalone mode--root
- path to static files ( process.cwd()
by default)--ext
- extension of server js files ( .srv.js by default)
node-direct --port=8000 --standalone --root=./foo/bar --ext=.serverside.js
You can add a new task to crontab, call crontab -e
and add a new task to the file.
@reboot <path_to_node> <path_to_installed_module> [<flags>]
path_to_node
- the absolute path to the NodeJS binary (you can call which node
)path_to_installed_module
- the absolute path to the installed node-directflags
- flagsExample:
@reboot /usr/local/bin/node /usr/local/lib/node_modules/node-direct/index.js --port=8000
As you know, NodeJS caches the values that the require
function returns. When require('foo')
is called two or more times, the function returns the same value. node-direct updates the cache automatically when the .srv.js file is changed (for example, you uploaded a new file to the server) and there is no need to restart node-direct . A problem may occur when .srv.js requests other modules.
// foo.srv.js module.exports = function(req, res) { const bar = require('./bar'); // ... }
When foo.srv.js changes, the cache is updated as expected, but when ./bar changes, its value remains the same. A node-direct could update all requested modules on its own, but this would cause side effects with unpredictable behavior in other modules. This problem can be solved by creating yesterday, which cleans the cache when the specified module is updated.
In the example below, hot swap is enabled for ./bar , but not for ./baz .
// foo.srv.js // , const fs = require('fs'); const barPath = require.resolve('./bar'); const watcher = fs.watch(barPath, (eventType) => { if (eventType === 'change') { delete require.cache[barPath]; watcher.close(); } }); module.exports = function(req, res) { const bar = require('./bar'); const baz = require('./baz'); // ... }
If it looks too hard, use a small fresh-up module that does the same.
// foo.srv.js // , const freshUp = require('fresh-up'); freshUp(require.resolve('./bar'); module.exports = function(req, res) { const bar = require('./bar'); const baz = require('./baz'); // ... }
X-Requested-File-Path
headerAs you may have noticed, nginx sends the address of the requested node-direct file to the server as an X-Requested-File-Path
header. Using this header, an attacker can invoke arbitrary javascript files on your server. In order to do something bad, a hacker needs to know the address of the "dangerous" file, relative to the root. To close the vulnerability, it is necessary to prohibit access to the node-direct port from the outside.
Here's how to do this in Linux using the ufw firewall .
sudo ufw deny 8123
Source: https://habr.com/ru/post/312558/
All Articles