1 // Copyright MOSSDeF, 2023 Stan Grishin <stangri@melmac.ca>
2 // This code wouldn't have been possible without help from:
3 // - [@stokito](https://github.com/stokito)
4 // - [@vsviridov](https://github.com/vsviridov)
14 return "https-dns-proxy";
17 return "https://docs.openwrt.melmac.net/" + pkg
.Name
+ "/";
19 templateToRegexp: function (template
) {
25 let placeholder
= part
.match(/^\{(\w+)\}$/);
26 if (placeholder
) return `(?<${placeholder[1]}>.*?)`;
27 else return part
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
35 var getInitList
= rpc
.declare({
36 object
: "luci." + pkg
.Name
,
37 method
: "getInitList",
41 var getInitStatus
= rpc
.declare({
42 object
: "luci." + pkg
.Name
,
43 method
: "getInitStatus",
47 var getPlatformSupport
= rpc
.declare({
48 object
: "luci." + pkg
.Name
,
49 method
: "getPlatformSupport",
53 var getProviders
= rpc
.declare({
54 object
: "luci." + pkg
.Name
,
55 method
: "getProviders",
59 var getRuntime
= rpc
.declare({
60 object
: "luci." + pkg
.Name
,
65 var _setInitAction
= rpc
.declare({
66 object
: "luci." + pkg
.Name
,
67 method
: "setInitAction",
68 params
: ["name", "action"],
69 expect
: { result
: false },
74 on: function (event
, callback
) {
75 var pair
= { event
: event
, callback
: callback
};
76 this.listeners
.push(pair
);
77 return function unsubscribe() {
78 this.listeners
= this.listeners
.filter(function (listener
) {
79 return listener
!== pair
;
83 emit: function (event
, data
) {
84 this.listeners
.forEach(function (listener
) {
85 if (listener
.event
=== event
) {
86 listener
.callback(data
);
90 getInitList: function (name
) {
91 getInitList(name
).then(
93 this.emit("getInitList", result
);
97 getInitStatus: function (name
) {
98 getInitStatus(name
).then(
100 this.emit("getInitStatus", result
);
104 getPlatformSupport: function (name
) {
105 getPlatformSupport(name
).then(
107 this.emit("getPlatformSupport", result
);
111 getProviders: function (name
) {
112 getProviders(name
).then(
114 this.emit("getProviders", result
);
118 getRuntime: function (name
) {
119 getRuntime(name
).then(
121 this.emit("getRuntime", result
);
125 setInitAction: function (name
, action
) {
126 _setInitAction(name
, action
).then(
128 this.emit("setInitAction", result
);
134 var status
= baseclass
.extend({
135 render: function () {
137 L
.resolveDefault(getInitStatus(pkg
.Name
), {}),
138 L
.resolveDefault(getProviders(pkg
.Name
), {}),
139 L
.resolveDefault(getRuntime(pkg
.Name
), {}),
140 ]).then(function (data
) {
143 status
: (data
[0] && data
[0][pkg
.Name
]) || {
146 force_dns_active
: null,
149 providers
: (data
[1] && data
[1][pkg
.Name
]) || { providers
: [] },
150 runtime
: (data
[2] && data
[2][pkg
.Name
]) || { instances
: [] },
152 reply
.providers
.sort(function (a
, b
) {
153 return _(a
.title
).localeCompare(_(b
.title
));
155 reply
.providers
.push({
157 template
: "{option}",
158 params
: { option
: { type
: "text" } },
161 var header
= E("h2", {}, _("HTTPS DNS Proxy - Status"));
164 { class: "cbi-value-title" },
167 if (reply
.status
.version
) {
168 if (reply
.status
.running
) {
169 text
= _("Version %s - Running.").format(reply
.status
.version
);
170 if (reply
.status
.force_dns_active
) {
171 text
+= "<br />" + _("Force DNS ports:");
172 reply
.status
.force_dns_ports
.forEach((element
) => {
173 text
+= " " + element
;
178 if (reply
.status
.enabled
) {
179 text
= _("Version %s - Stopped.").format(reply
.status
.version
);
181 text
= _("Version %s - Stopped (Disabled).").format(
187 text
= _("Not installed or not found");
189 var statusText
= E("div", {}, text
);
190 var statusField
= E("div", { class: "cbi-value-field" }, statusText
);
191 var statusDiv
= E("div", { class: "cbi-value" }, [
196 var instancesDiv
= [];
197 if (reply
.runtime
.instances
) {
198 var instancesTitle
= E(
200 { class: "cbi-value-title" },
201 _("Service Instances")
203 text
= _("See the %sREADME%s for details.").format(
206 '#a-word-about-default-routing " target="_blank">',
209 var instancesDescr
= E("div", { class: "cbi-value-description" }, "");
212 Object
.values(reply
.runtime
.instances
).forEach((element
) => {
219 element
.command
.forEach((param
, index
, arr
) => {
220 if (param
=== "-r") resolver
= arr
[index
+ 1];
221 if (param
=== "-a") address
= arr
[index
+ 1];
222 if (param
=== "-p") port
= arr
[index
+ 1];
224 resolver
= resolver
|| "Unknown";
225 address
= address
|| "127.0.0.1";
226 port
= port
|| "Unknown";
227 reply
.providers
.forEach((prov
) => {
228 let regexp
= pkg
.templateToRegexp(prov
.template
);
229 if (!found
&& regexp
.test(resolver
)) {
231 name
= _(prov
.title
);
232 let match
= resolver
.match(regexp
);
233 if (match
[1] != null) {
236 prov
.params
.option
&&
237 prov
.params
.option
.options
239 prov
.params
.option
.options
.forEach((opt
) => {
240 if (opt
.value
=== match
[1]) option
= _(opt
.description
);
242 name
+= " (" + option
+ ")";
244 if (match
[1] != "") name
+= " (" + match
[1] + ")";
249 if (address
=== "127.0.0.1")
250 text
+= _("%s%s%s proxy on port %s.%s").format(
258 text
+= _("%s%s%s proxy at %s on port %s.%s").format(
267 var instancesText
= E("div", {}, text
);
268 var instancesField
= E("div", { class: "cbi-value-field" }, [
272 instancesDiv
= E("div", { class: "cbi-value" }, [
278 var btn_gap
= E("span", {}, "  ");
279 var btn_gap_long
= E(
282 "        "
287 class: "btn cbi-button cbi-button-apply",
289 click: function (ev
) {
293 { class: "spinning" },
294 _("Starting %s service").format(pkg
.Name
)
297 return RPC
.setInitAction(pkg
.Name
, "start");
306 class: "btn cbi-button cbi-button-apply",
308 click: function (ev
) {
312 { class: "spinning" },
313 _("Restarting %s service").format(pkg
.Name
)
316 return RPC
.setInitAction(pkg
.Name
, "restart");
325 class: "btn cbi-button cbi-button-reset",
327 click: function (ev
) {
331 { class: "spinning" },
332 _("Stopping %s service").format(pkg
.Name
)
335 return RPC
.setInitAction(pkg
.Name
, "stop");
344 class: "btn cbi-button cbi-button-apply",
346 click: function (ev
) {
350 { class: "spinning" },
351 _("Enabling %s service").format(pkg
.Name
)
354 return RPC
.setInitAction(pkg
.Name
, "enable");
363 class: "btn cbi-button cbi-button-reset",
365 click: function (ev
) {
369 { class: "spinning" },
370 _("Disabling %s service").format(pkg
.Name
)
373 return RPC
.setInitAction(pkg
.Name
, "disable");
379 if (reply
.status
.enabled
) {
380 btn_enable
.disabled
= true;
381 btn_disable
.disabled
= false;
382 if (reply
.status
.running
) {
383 btn_start
.disabled
= true;
384 btn_action
.disabled
= false;
385 btn_stop
.disabled
= false;
387 btn_start
.disabled
= false;
388 btn_action
.disabled
= true;
389 btn_stop
.disabled
= true;
392 btn_start
.disabled
= true;
393 btn_action
.disabled
= true;
394 btn_stop
.disabled
= true;
395 btn_enable
.disabled
= false;
396 btn_disable
.disabled
= true;
399 var buttonsTitle
= E(
401 { class: "cbi-value-title" },
404 var buttonsText
= E("div", {}, [
415 var buttonsField
= E("div", { class: "cbi-value-field" }, buttonsText
);
416 var buttonsDiv
= reply
.status
.version
?
417 E('div', {class: 'cbi-value'}, [
421 return E("div", {}, [header
, statusDiv
, instancesDiv
, buttonsDiv
]);
426 RPC
.on("setInitAction", function (reply
) {
431 return L
.Class
.extend({
433 getPlatformSupport
: getPlatformSupport
,
434 getProviders
: getProviders
,
435 getRuntime
: getRuntime
,