Complete noob questions about js and LuCi

Hi

I'll say in advance that I know very little about js (not even syntax), and even less about LuCi+js. I'm currently just experimenting to get a taste of the thing before deciding if I want to learn it more thoroughly.

So my initial goal is to get an output of some shell command in LuCi. The shell command happens to be '/usr/bin/geoip-shell status'

So far I have this js code:

'use strict';
'require view';
'require poll';
'require fs';
'require ui';
'require uci';
'require form';

return view.extend({
	render: function(result) {
		var m, s, o;
		m = new form.Map('firewall', 'geoip-shell', _('Bruh!'));
		var statusRes

		poll.add(function() {
			L.resolveDefault(fs.exec_direct('/usr/bin/geoip-shell', ['status'])).then(
				function(result) {
					if (result) {
						statusRes = document.getElementById('status');
						if (statusRes) {
							statusRes.textContent = result || 'bruhhhhh';
						}
					} else {
						poll.stop();
			}});

		}, 1);

		s = m.section(form.NamedSection, 'StatusSection');
		s.render = L.bind(function(view, section_id) {
			return E('div', {
				'class': 'cbi-section'
			}, [E('h3', _('Status')), E('div', {
				'class': 'cbi-value'
			}, [E('div', {
				'class': 'cbi-value-field',
				'id': 'status'
			}, '')])
		]);
		}, o, this);
		this.pollData;
		return m.render();
	},
});

This produces the expected output (although it doesn't display newlines and colors correctly but that's not my focus rn).

My problem is that LuCi.form.map wants a UCI config for its first parameter. I don't need that config at all. For now, to make it happy, I'm just pointing it to the firewall config. However I'd like to do this properly, without loading config which I don't need. Probably I should use some other class to achieve this? If so then which (a basic example would be appreciated as well)?

(also if parts of the code don't make sense then please correct me)

Do you work for an Internet blocking company? If so, I hope no one can tell you ))

Dear @fkl7834456, I'm developing an open-source project, completely unrelated to my work.

1 Like

Hi @antonk, I was googling "luci pollData" and this post comes up first...not sure if you got your answer elsewhere, but I figured I'd answer and try to return the favour for all the shell scripting help you've provided!

So the quick fix for what you're doing, I think, would be to use form.JSONMap instead of form.Map. With form.JSONMap you provide an object as the data source, and I would guess you could simply pass {} because you have no data.

But since you have no data and so don't benefit from the Map/JSONMap functionality, I think the better fix would be to remove form.Map altogether. I believe everything related to m, s, and o could be removed, so you'd be left with something like (edited by hand, did not test):

return view.extend({
	render: function(result) {
		poll.add(function() {
			L.resolveDefault(fs.exec_direct('/usr/bin/geoip-shell', ['status'])).then(
				function(result) {
					if (result) {
						var statusRes = document.getElementById('status');
						if (statusRes) {
							statusRes.textContent = result || 'bruhhhhh';
						}
					} else {
						poll.stop();
			}});

		}, 1);

		// Not sure what this does, if anything, so may or may not be needed
		// (It's why I googled "luci pollData" in the first place)
		this.pollData;

		return E('div', {
			'class': 'cbi-section'
		}, [E('h3', _('Status')), E('div', {
			'class': 'cbi-value'
		}, [E('div', {
			'class': 'cbi-value-field',
			'id': 'status'
		}, '')]);
	},
});

Also, this assumes you want to poll for status information every second. If you want to do that, it's great, in the case of adblock-lean I don't do that because that would result in check_for_updates() downloading adblock-lean from GitHub every second, which would be overkill.

If geoip-shell status does something similar, you might want to query the status information once when the page loads, and you'd do that by providing a load method along with the render method, something like:

return view.extend({
	load: function () {
		return Promise.all([
			L.resolveDefault('/usr/bin/geoip-shell', ['status'])
		]);
	},
	render: function(result) {
		// The result arg will be an array containing the result for each Promise
		// set in the load function above, so you'll only have result[0] with the status info

		return E('div', {
			'class': 'cbi-section'
		}, [E('h3', _('Status')), E('div', {
			'class': 'cbi-value'
		}, [E('div', {
			'class': 'cbi-value-field',
			'id': 'status'
		}, result[0])]);
	},
});

Again the above was edited by hand, so I may have made a mistake. A working example (you can just ignore the saving portion) would be the edit the adblock-lean config in one big textarea view

1 Like

Thank you! I'll read your reply carefully when I have time.

Looking (for the first time, haha) at js syntax, I think pollData is supposed to be a method, however since it's not defined anywhere, and it's not mentioned in the API, it probably doesn't do anything :slight_smile:

BTW here's the API.

https://openwrt.github.io/luci/jsapi/LuCI.html

Yeah I was thinking the same thing. poll.add() indicates that it will automatically start the polling loop, but maybe in previous versions of the API it did not and a call to this.pollData() was how you started it.

Out of curiosity were you referencing the luci-app-banIP package when you put your code together? That's where I saw the this.pollData; line and wondered "what could this possibly be for" and googled and came across this post.

1 Like

Yep, I took that code as a starting point since it was doing something seemingly similar.

The code I posted doesn't actually run fs.exec_direct every second. That command runs once, and then polling is there to get the output. I don't really understand how this works but it works. I'll try your ideas as well.

You don't see a call to cgi-exec every second on the network tab of developer tools? With banIP I see one (although every 2 seconds in their case), so I assumed it was the polling loop doing that, but maybe it's something else.

It's been a while since I actually tested this code (in April), but pretty sure it was not calling geoip-shell every second. I didn't check in the developer tools.