Create an admin configuration API HTTPS endpoint


#1

Hello,
I need to create an admin configuration API HTTPS endpoint to look up additional data at configuration time. I found information about Serial node: https://github.com/node-red/cookbook.nodered.org/wiki/Create-an-admin-configuration-API-endpoint.

RED.httpAdmin.get("/serialports", RED.auth.needsPermission('serial.read'), function(req,res) {
    serialp.list(function (err, ports) {
        res.json(ports);
    });
});
$.getJSON('serialports',function(data) {
    //... does stuff with data
});

This example use relative URL and HTTP.
It is possible to create an HTTPS endpoint? Please, I need a litle example to start with.

Please help, I've searched everywhere, but have not found anything and can not go on.

Many, many thanks.


#2

This example is relative to the admin url, so if your Node-red admin interface is running under https then the config service is also running under https.

i.e. for this example, if your Admin URL is https://node-red.example.com/admin ... the URL for the service would be: https://node-red.example.com/admin/serialports

Note: The default URL would be http://127.0.0.1:8080/serialports as the admin "root" is set to "/" by default

The admin url/root is set in settings.js

 // By default, the Node-RED UI is available at http://localhost:1880/
// The following property can be used to specify a different root path.
// If set to false, this is disabled.
//httpAdminRoot: '/admin',

#3

Hey Dustin,
wow! Quick response! :-))
Thanks a lot!!!


#4

Hey!
Please, help me again!
My config node 'connector' have properties, that are empty at the begin.
Config%20node%20connector
Because of this I get an TypeError: Cannot read property 'config' of undefined in the.js file:

function Connect(config)
	{
		RED.nodes.createNode(this, config);
                node = this;
		this.config = {
                name: config.name,
	        deviceIP: config.deviceIP,
		username: config.username,
		password: config.password,
		key: config.key
    };
   
	}
  RED.nodes.registerType("connector", Connect);
RED.httpAdmin.post('/connector', RED.auth.needsPermission("connector.write"), function(req, res)
  { 
     let client = new Client(node.config.deviceIP, node.config.username, node.config.password, '');
    ...
  }

the .html file looks like:

<script type="text/javascript">

	RED.nodes.registerType('connector', {
		category: "config",
		color: '#ffaaaa',
		defaults: {
      name: {value:"Connector", required: true},
      deviceIP: {value:"", required: true},
      username: {value:"", required: true},
      password: {value:"", required: true},
      key: {value:"", required: true}
    },
    icon: "light.png",
		paletteLabel: "Connector",
		label: function() {
			return this.name;
		},
		oneditprepare: function()
		{ 
                  ....
                }

#5

Please, how can I do this to work?
Many, many thanks.


#6

I'm not sure what exactly you are trying to do, what event is causing the call to /connector path?

The function that is handling the /connector admin endpoint would not have access to node.config without some sort of help (it might not even exist yet) ... you might need to pass the information as part of the post.

If you are passing that data to the admin endpoint already, but it's causing an error because it's empty, you might have to wrap the call with some If-statements to stop it from being called before the data is entered.


#7

Hey Dustin!
thank you for the quick response. :-))

I'm trying to create an admin endpoint to get a Client Token, after the user has filled deviceIP, username and password (please, see the image, that I sent) in the config node.

When the user click the editor-button to register, then this click is causing the call to /connector path. Why is this too early? The user had filled deviceID, username and password.

The Error is:

TypeError: Cannot read property 'config' of undefined
    at C:\Users\stamer\Documents\NodeRED\node-red-gds-iot\node-red-control\connector.js:54:74
    at Layer.handle [as handle_request] (C:\Program Files\nodejs\node_modules\express\lib\router\layer.js:95:5)
    at next (C:\Program Files\nodejs\node_modules\express\lib\router\route.js:137:13)
    at C:\Program Files\nodejs\node_modules\node-red\red\api\auth\index.js:57:28
    at complete (C:\Program Files\nodejs\node_modules\node-red\node_modules\passport\lib\middleware\authenticate.js:263:13)
    at C:\Program Files\nodejs\node_modules\node-red\node_modules\passport\lib\middleware\authenticate.js:270:15
    at pass (C:\Program Files\nodejs\node_modules\node-red\node_modules\passport\lib\authenticator.js:431:14)
    at Authenticator.transformAuthInfo (C:\Program Files\nodejs\node_modules\node-red\node_modules\passport\lib\authenticator.js:453:5)
    at C:\Program Files\nodejs\node_modules\node-red\node_modules\passport\lib\middleware\authenticate.js:267:22
    at IncomingMessage.req.login.req.logIn (C:\Program Files\nodejs\node_modules\node-red\node_modules\passport\lib\http\request.js:55:13)    at Strategy.strategy.success (C:\Program Files\nodejs\node_modules\node-red\node_modules\passport\lib\middleware\authenticate.js:248:13)
    at verified (C:\Program Files\nodejs\node_modules\node-red\node_modules\passport-http-bearer\lib\strategy.js:126:10)
    at C:\Program Files\nodejs\node_modules\node-red\red\api\auth\strategies.js:37:21
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

If I try this (in the connector.js):

 // Registers the admin endpoint
  RED.httpAdmin.post('/connector', RED.auth.needsPermission("connector.write"), function(req, res)
  { 
    //if(node !== undefined)  
    //{
      //if(node.config !== undefined)
      //{
           let client = new Client('192.168.0.100', 'the username', 'password', ''); //hard coded :-(
           client.accesstoken.getToken()
           .then(token => {
              client.setToken = JSON.stringify(token);
              console.log('The Client AccessToken has been set successfully! ' + client.setToken);
              var accToken = JSON.stringify(token);
              res.end(accToken);
            })
           .catch(error => {
              res.sendStatus(500).send(error.stack);
          })
    
           console.log('The Client has been registered successfully: ' + node.config.key);
       //} 
     // }
   }

then, everything works and a response (with the Client Token!) come from post-request.

I have also tried -> "If you are passing that data to the admin endpoint already, but it's causing an error because it's empty, you might have to wrap the call with some If-statements to stop it from being called before the data is entered." -> No response from post response come. :frowning:

So, what can I do to make this to work? Please help.

Here is a fragment of the code in the connector.html file:

<script type="text/javascript">
 
	RED.nodes.registerType('connector', {
		category: "config",
		color: '#ffaaaa',
		defaults: {
                       name: {value:"REST API Connector", required: true},
			deviceIP: {value:"", required: true},
			username: {value:"", required: true},
                        password: {value:"", required: true},
                        key: {value:"", required: true}
               },
               icon: "light.png",
		paletteLabel: "REST API Connector",
		label: function() {
			return this.name;
		},
		oneditprepare: function()
		{ 
                   ...
                   $.ajax({
                    url: "connector",
                    type: "POST",
                   success: function(data, textStatus, jqXHR) 
                   {
                      if(data == "error")
		     {
			RED.notify("Please retry within 5 seconds…", "error");
			$('#node-config-input-key').attr("placeholder", "");
                   }
              
                   var accessToken = JSON.parse(data);

                  $('#node-config-input-key').val(accessToken);
                  RED.notify("AccessToken were retrieved successfully: " + accessToken);
                              
                  $('#node-config-input-name').val('REST API Connector');             
             
                },
                complete: function(jqXHR, textStatus)
                {
                    if(jqXHR.status === 0)
                    {
                       RED.error("Error in Complete callback! " + jqXHR.status);
                    }
                },
                error: function(jqXHR, textStatus, errorThrown)
                { 
                  var msg = '';
                  if (jqXHR.status === 401)
                  {
                      msg = jqXHR.status + ' Unauthorized: Missing or invalid authorization';
                  }
                  else if (jqXHR.status === 0) 
                  {
                     msg = 'Status ' + jqXHR.status + ' Not connect.\n Verify Network.';
                  }
                  ....
                }
              ....

Many, many thanks.


#8

Hi @stamer

I've edited your posts to format the code properly as described in this post: How to share code or flow json

The code inside the RED.httpAdmin.post handler is not tied to any node instance. node doesn't exist inside the function. You need to identify the node you want to handle the request for as part of the request itself. You can see how the Inject node does it here: https://github.com/node-red/node-red/blob/master/nodes/core/core/20-inject.js#L113-L114

It defines the admin end point like this:

RED.httpAdmin.post("/inject/:id", ...

The :id part of the url is a parameter that will contain the id of the node. The function can then go lookup that node:

var node = RED.nodes.getNode(req.params.id);

However, that is a reference to the current deployed node in the runtime. If the user has just dragged on their node to the workspace and edited it to provide a user/password, then none of that information will exist in the runtime yet. In that scenario, you'd need to provide the user/password (and any other node configuration information) in the request itself.


#9

Hi Nick,

many thanks for response. :slight_smile:
Sorry, for the unformatted code. :frowning:


#10

Hi Nick,

I'm back from holiday. :slight_smile:

How can I access the username and password from the config node???
This is for me the big question!!! That's why I created the admin endpoint. I need to get the username and password, that the user will type in the workspace.
Then, the deviceIP, username and password will be passed to an REST API by using the POST method to get an AccessToken

Please, can you give me an example?

I will really appreciate this!!!

Thank you so much!!!


#11

Hi,

i got it!!! :slight_smile: