apps.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. Technitium DNS Server
  3. Copyright (C) 2023 Shreyas Zare (shreyas@technitium.com)
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. function refreshApps() {
  16. var divViewAppsLoader = $("#divViewAppsLoader");
  17. var divViewApps = $("#divViewApps");
  18. divViewApps.hide();
  19. divViewAppsLoader.show();
  20. HTTPRequest({
  21. url: "/api/apps/list?token=" + sessionData.token,
  22. success: function (responseJSON) {
  23. var apps = responseJSON.response.apps;
  24. var tableHtmlRows = "";
  25. for (var i = 0; i < apps.length; i++) {
  26. tableHtmlRows += getAppRowHtml(apps[i]);
  27. }
  28. $("#tableAppsBody").html(tableHtmlRows);
  29. if (apps.length > 0)
  30. $("#tableAppsFooter").html("<tr><td colspan=\"3\"><b>Total Apps: " + apps.length + "</b></td></tr>");
  31. else
  32. $("#tableAppsFooter").html("<tr><td colspan=\"3\" align=\"center\">No Apps Found</td></tr>");
  33. divViewAppsLoader.hide();
  34. divViewApps.show();
  35. },
  36. error: function () {
  37. divViewAppsLoader.hide();
  38. divViewApps.show();
  39. },
  40. invalidToken: function () {
  41. showPageLogin();
  42. },
  43. objLoaderPlaceholder: divViewAppsLoader
  44. });
  45. }
  46. function getAppRowId(appName) {
  47. return btoa(appName).replace(/=/g, "");
  48. }
  49. function getAppRowHtml(app) {
  50. var name = app.name;
  51. var version = app.version;
  52. var updateVersion = app.updateVersion;
  53. var updateUrl = app.updateUrl;
  54. var updateAvailable = app.updateAvailable;
  55. var dnsAppsTable = null;
  56. //dnsApps
  57. if (app.dnsApps.length > 0) {
  58. dnsAppsTable = "<table class=\"table\" style=\"margin-bottom: 10px; background: transparent;\"><thead><th>Class Path</th><th>Description</th></thead><tbody>";
  59. for (var j = 0; j < app.dnsApps.length; j++) {
  60. var labels = "";
  61. var description = null;
  62. if (app.dnsApps[j].isAppRecordRequestHandler) {
  63. labels += "<span class=\"label label-info\" style=\"margin-right: 4px;\">APP Record</span>";
  64. description = "<p>" + htmlEncode(app.dnsApps[j].description).replace(/\n/g, "<br />") + "</p>" + (app.dnsApps[j].recordDataTemplate == null ? "" : "<div><b>Record Data Template</b><pre>" + htmlEncode(app.dnsApps[j].recordDataTemplate) + "</pre></div>");
  65. }
  66. if (app.dnsApps[j].isRequestController)
  67. labels += "<span class=\"label label-info\" style=\"margin-right: 4px;\">Access Control</span>";
  68. if (app.dnsApps[j].isAuthoritativeRequestHandler)
  69. labels += "<span class=\"label label-info\" style=\"margin-right: 4px;\">Authoritative</span>";
  70. if (app.dnsApps[j].isRequestBlockingHandler)
  71. labels += "<span class=\"label label-info\" style=\"margin-right: 4px;\">Blocking</span>";
  72. if (app.dnsApps[j].isQueryLogger)
  73. labels += "<span class=\"label label-info\" style=\"margin-right: 4px;\">Query Logs</span>";
  74. if (app.dnsApps[j].isPostProcessor)
  75. labels += "<span class=\"label label-info\" style=\"margin-right: 4px;\">Post Processor</span>";
  76. if (labels == "")
  77. labels = "<span class=\"label label-info\" style=\"margin-right: 4px;\">Generic</span>";
  78. if (description == null)
  79. description = htmlEncode(app.dnsApps[j].description).replace(/\n/g, "<br />");
  80. dnsAppsTable += "<tr><td>" + htmlEncode(app.dnsApps[j].classPath) + "</br>" + labels + "</td><td>" + description + "</td></tr>";
  81. }
  82. dnsAppsTable += "</tbody></table>"
  83. }
  84. var id = getAppRowId(name);
  85. var tableHtmlRow = "<tr id=\"trApp" + id + "\"><td><div><span style=\"font-weight: bold; font-size: 16px;\">" + htmlEncode(name) + "</span><br /><span id=\"trAppVersion" + id + "\" class=\"label label-primary\">Version " + htmlEncode(version) + "</span> <span id=\"trAppUpdateVersion" + id + "\" class=\"label label-warning\" style=\"" + (updateAvailable ? "" : "display: none;") + "\">Update " + htmlEncode(updateVersion) + "</span></div>";
  86. if (app.description != null)
  87. tableHtmlRow += "<div style=\"margin-top: 10px;\">" + htmlEncode(app.description).replace(/\n/g, "<br />") + "</div>";
  88. if (dnsAppsTable != null) {
  89. tableHtmlRow += "<div style=\"margin-top: 10px;\"><a href=\"#" + id + "\" class=\"collapsed\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"" + id + "\">More Details <span class=\"glyphicon glyphicon-chevron-down\" style=\"font-size: 10px;\" aria-hidden=\"true\"></span></a>";
  90. tableHtmlRow += "<div id=\"" + id + "\" class=\"collapse\" aria-expanded=\"false\">";
  91. tableHtmlRow += dnsAppsTable;
  92. tableHtmlRow += "</div></div>";
  93. }
  94. tableHtmlRow += "</td>";
  95. tableHtmlRow += "<td><button type=\"button\" class=\"btn btn-default\" style=\"font-size: 12px; padding: 2px 0px; width: 80px; margin-bottom: 6px; display: block;\" onclick=\"showAppConfigModal(this, '" + name + "');\" data-loading-text=\"Loading...\">Config</button>";
  96. tableHtmlRow += "<button type=\"button\" class=\"btn btn-warning\" style=\"font-size: 12px; padding: 2px 0px; width: 80px; margin-bottom: 6px; display: block;\" onclick=\"showUpdateAppModal('" + name + "');\">Update</button>";
  97. tableHtmlRow += "<button id=\"btnAppsStoreUpdate" + id + "\" type=\"button\" data-id=\"" + id + "\" class=\"btn btn-warning\" style=\"font-size: 12px; padding: 2px 0px; width: 80px; margin-bottom: 6px; " + (updateAvailable ? "" : "display: none;") + "\" onclick=\"updateStoreApp(this, '" + name + "', '" + updateUrl + "', false);\" data-loading-text=\"Updating...\">Store Update</button>";
  98. tableHtmlRow += "<button type=\"button\" data-id=\"" + id + "\" class=\"btn btn-danger\" style=\"font-size: 12px; padding: 2px 0px; width: 80px; margin-bottom: 6px; display: block;\" onclick=\"uninstallApp(this, '" + name + "');\" data-loading-text=\"Uninstalling...\">Uninstall</button></td></tr>";
  99. return tableHtmlRow
  100. }
  101. function showStoreAppsModal() {
  102. var divStoreAppsAlert = $("#divStoreAppsAlert");
  103. var divStoreAppsLoader = $("#divStoreAppsLoader");
  104. var divStoreApps = $("#divStoreApps");
  105. divStoreAppsLoader.show();
  106. divStoreApps.hide();
  107. $("#modalStoreApps").modal("show");
  108. HTTPRequest({
  109. url: "/api/apps/listStoreApps?token=" + sessionData.token,
  110. success: function (responseJSON) {
  111. var storeApps = responseJSON.response.storeApps;
  112. var tableHtmlRows = "";
  113. for (var i = 0; i < storeApps.length; i++) {
  114. var id = Math.floor(Math.random() * 10000);
  115. var name = storeApps[i].name;
  116. var version = storeApps[i].version;
  117. var description = storeApps[i].description;
  118. var url = storeApps[i].url;
  119. var size = storeApps[i].size;
  120. var installed = storeApps[i].installed;
  121. var installedVersion = storeApps[i].installedVersion;
  122. var updateAvailable = installed ? storeApps[i].updateAvailable : false;
  123. var displayVersion = installed ? installedVersion : version;
  124. description = htmlEncode(description).replace(/\n/g, "<br />");
  125. tableHtmlRows += "<tr id=\"trStoreApp" + id + "\"><td><div style=\"margin-bottom: 14px;\"><span style=\"font-weight: bold; font-size: 16px;\">" + htmlEncode(name) + "</span><br /><span id=\"spanStoreAppDisplayVersion" + id + "\" class=\"label label-primary\">Version " + htmlEncode(displayVersion) + "</span> <span id=\"spanStoreAppUpdateVersion" + id + "\" class=\"label label-warning\" style=\"" + (updateAvailable ? "" : "display: none;") + "\">Update " + htmlEncode(version) + "</span></div>";
  126. tableHtmlRows += "<div style=\"margin-bottom: 10px;\">" + description + "</div><div><b>App Zip File</b>: " + htmlEncode(url) + "<br /><b>Size</b>: " + htmlEncode(size) + "</div></td><td>";
  127. tableHtmlRows += "<button id=\"btnStoreAppInstall" + id + "\" type=\"button\" data-id=\"" + id + "\" class=\"btn btn-primary\" style=\"font-size: 12px; padding: 2px 0px; width: 80px; margin-bottom: 6px; " + (installed ? "display: none;" : "") + "\" onclick=\"installStoreApp(this, '" + name + "', '" + url + "');\" data-loading-text=\"Installing...\">Install</button>";
  128. tableHtmlRows += "<button id=\"btnStoreAppUpdate" + id + "\" type=\"button\" data-id=\"" + id + "\" class=\"btn btn-warning\" style=\"font-size: 12px; padding: 2px 0px; width: 80px; margin-bottom: 6px; " + (updateAvailable ? "" : "display: none;") + "\" onclick=\"updateStoreApp(this, '" + name + "', '" + url + "', true);\" data-loading-text=\"Updating...\">Update</button>";
  129. tableHtmlRows += "<button id=\"btnStoreAppUninstall" + id + "\" type=\"button\" data-id=\"" + id + "\" class=\"btn btn-danger\" style=\"font-size: 12px; padding: 2px 0px; width: 80px; margin-bottom: 6px; " + (installed ? "" : "display: none;") + "\" onclick=\"uninstallStoreApp(this, '" + name + "');\" data-loading-text=\"Uninstalling...\">Uninstall</button>";
  130. tableHtmlRows += "</td></tr>";
  131. }
  132. $("#tableStoreAppsBody").html(tableHtmlRows);
  133. if (storeApps.length > 0)
  134. $("#tableStoreAppsFooter").html("<tr><td colspan=\"3\"><b>Total Apps: " + storeApps.length + "</b></td></tr>");
  135. else
  136. $("#tableStoreAppsFooter").html("<tr><td colspan=\"3\" align=\"center\">No Apps Found</td></tr>");
  137. divStoreAppsLoader.hide();
  138. divStoreApps.show();
  139. },
  140. error: function () {
  141. divStoreAppsLoader.hide();
  142. divStoreApps.show();
  143. },
  144. invalidToken: function () {
  145. $("#modalStoreApps").modal("hide");
  146. showPageLogin();
  147. },
  148. objAlertPlaceholder: divStoreAppsAlert,
  149. objLoaderPlaceholder: divStoreAppsLoader
  150. });
  151. }
  152. function showInstallAppModal() {
  153. $("#divInstallAppAlert").html("");
  154. $("#txtInstallApp").val("");
  155. $("#fileAppZip").val("");
  156. $("#btnInstallApp").button("reset");
  157. $("#modalInstallApp").modal("show");
  158. setTimeout(function () {
  159. $("#txtInstallApp").focus();
  160. }, 1000);
  161. }
  162. function showUpdateAppModal(appName) {
  163. $("#divUpdateAppAlert").html("");
  164. $("#txtUpdateApp").val(appName);
  165. $("#fileUpdateAppZip").val("");
  166. $("#btnUpdateApp").button("reset");
  167. $("#modalUpdateApp").modal("show");
  168. }
  169. function installStoreApp(objBtn, appName, url) {
  170. var divStoreAppsAlert = $("#divStoreAppsAlert");
  171. var btn = $(objBtn);
  172. btn.button('loading');
  173. HTTPRequest({
  174. url: "/api/apps/downloadAndInstall?token=" + sessionData.token + "&name=" + encodeURIComponent(appName) + "&url=" + encodeURIComponent(url),
  175. success: function (responseJSON) {
  176. btn.button('reset');
  177. btn.hide();
  178. var id = btn.attr("data-id");
  179. $("#btnStoreAppUninstall" + id).show();
  180. var tableHtmlRow = getAppRowHtml(responseJSON.response.installedApp);
  181. $("#tableAppsBody").prepend(tableHtmlRow);
  182. updateAppsFooterCount();
  183. showAlert("success", "Store App Installed!", "DNS application '" + appName + "' was installed successfully from DNS App Store.", divStoreAppsAlert);
  184. },
  185. error: function () {
  186. btn.button('reset');
  187. },
  188. invalidToken: function () {
  189. $("#modalStoreApps").modal("hide");
  190. showPageLogin();
  191. },
  192. objAlertPlaceholder: divStoreAppsAlert
  193. });
  194. }
  195. function updateStoreApp(objBtn, appName, url, isModal) {
  196. var divStoreAppsAlert;
  197. if (isModal)
  198. divStoreAppsAlert = $("#divStoreAppsAlert");
  199. var btn = $(objBtn);
  200. btn.button('loading');
  201. HTTPRequest({
  202. url: "/api/apps/downloadAndUpdate?token=" + sessionData.token + "&name=" + encodeURIComponent(appName) + "&url=" + encodeURIComponent(url),
  203. success: function (responseJSON) {
  204. btn.button('reset');
  205. btn.hide();
  206. if (isModal) {
  207. var id = btn.attr("data-id");
  208. $("#spanStoreAppUpdateVersion" + id).hide();
  209. $("#spanStoreAppDisplayVersion" + id).text($("#spanStoreAppUpdateVersion" + id).text().replace(/Update/g, "Version"));
  210. }
  211. var tableHtmlRow = getAppRowHtml(responseJSON.response.updatedApp);
  212. var id = getAppRowId(responseJSON.response.updatedApp.name);
  213. $("#trApp" + id).replaceWith(tableHtmlRow);
  214. showAlert("success", "Store App Updated!", "DNS application '" + appName + "' was updated successfully from DNS App Store.", divStoreAppsAlert);
  215. },
  216. error: function () {
  217. btn.button('reset');
  218. },
  219. invalidToken: function () {
  220. $("#modalStoreApps").modal("hide");
  221. showPageLogin();
  222. },
  223. objAlertPlaceholder: divStoreAppsAlert
  224. });
  225. }
  226. function uninstallStoreApp(objBtn, appName) {
  227. if (!confirm("Are you sure you want to uninstall the DNS application '" + appName + "'?"))
  228. return;
  229. var divStoreAppsAlert = $("#divStoreAppsAlert");
  230. var btn = $(objBtn);
  231. btn.button('loading');
  232. HTTPRequest({
  233. url: "/api/apps/uninstall?token=" + sessionData.token + "&name=" + encodeURIComponent(appName),
  234. success: function (responseJSON) {
  235. btn.button('reset');
  236. btn.hide();
  237. var id = btn.attr("data-id");
  238. $("#btnStoreAppInstall" + id).show();
  239. $("#btnStoreAppUpdate" + id).hide();
  240. $("#spanStoreAppVersion" + id).attr("class", "label label-primary");
  241. var id = getAppRowId(appName);
  242. $("#trApp" + id).remove();
  243. updateAppsFooterCount();
  244. showAlert("success", "Store App Uninstalled!", "DNS application '" + appName + "' was uninstalled successfully.", divStoreAppsAlert);
  245. },
  246. error: function () {
  247. btn.button('reset');
  248. },
  249. invalidToken: function () {
  250. $("#modalStoreApps").modal("hide");
  251. showPageLogin();
  252. },
  253. objAlertPlaceholder: divStoreAppsAlert
  254. });
  255. }
  256. function installApp() {
  257. var divInstallAppAlert = $("#divInstallAppAlert");
  258. var appName = $("#txtInstallApp").val();
  259. if ((appName === null) || (appName === "")) {
  260. showAlert("warning", "Missing!", "Please enter an application name.", divInstallAppAlert);
  261. $("#txtInstallApp").focus();
  262. return;
  263. }
  264. var fileAppZip = $("#fileAppZip");
  265. if (fileAppZip[0].files.length === 0) {
  266. showAlert("warning", "Missing!", "Please select an application zip file to install.", divInstallAppAlert);
  267. fileAppZip.focus();
  268. return;
  269. }
  270. var formData = new FormData();
  271. formData.append("fileAppZip", $("#fileAppZip")[0].files[0]);
  272. var btn = $("#btnInstallApp").button('loading');
  273. HTTPRequest({
  274. url: "/api/apps/install?token=" + sessionData.token + "&name=" + encodeURIComponent(appName),
  275. method: "POST",
  276. data: formData,
  277. contentType: false,
  278. processData: false,
  279. success: function (responseJSON) {
  280. $("#modalInstallApp").modal("hide");
  281. var tableHtmlRow = getAppRowHtml(responseJSON.response.installedApp);
  282. $("#tableAppsBody").prepend(tableHtmlRow);
  283. updateAppsFooterCount();
  284. showAlert("success", "App Installed!", "DNS application '" + appName + "' was installed successfully.");
  285. },
  286. error: function () {
  287. btn.button('reset');
  288. },
  289. invalidToken: function () {
  290. $("#modalInstallApp").modal("hide");
  291. showPageLogin();
  292. },
  293. objAlertPlaceholder: divInstallAppAlert
  294. });
  295. }
  296. function updateApp() {
  297. var divUpdateAppAlert = $("#divUpdateAppAlert");
  298. var appName = $("#txtUpdateApp").val();
  299. var fileAppZip = $("#fileUpdateAppZip");
  300. if (fileAppZip[0].files.length === 0) {
  301. showAlert("warning", "Missing!", "Please select an application zip file to update.", divUpdateAppAlert);
  302. fileAppZip.focus();
  303. return;
  304. }
  305. var formData = new FormData();
  306. formData.append("fileAppZip", $("#fileUpdateAppZip")[0].files[0]);
  307. var btn = $("#btnUpdateApp").button('loading');
  308. HTTPRequest({
  309. url: "/api/apps/update?token=" + sessionData.token + "&name=" + encodeURIComponent(appName),
  310. method: "POST",
  311. data: formData,
  312. contentType: false,
  313. processData: false,
  314. success: function (responseJSON) {
  315. $("#modalUpdateApp").modal("hide");
  316. var tableHtmlRow = getAppRowHtml(responseJSON.response.updatedApp);
  317. var id = getAppRowId(responseJSON.response.updatedApp.name);
  318. $("#trApp" + id).replaceWith(tableHtmlRow);
  319. showAlert("success", "App Updated!", "DNS application '" + appName + "' was updated successfully.");
  320. },
  321. error: function () {
  322. btn.button('reset');
  323. },
  324. invalidToken: function () {
  325. $("#modalUpdateApp").modal("hide");
  326. showPageLogin();
  327. },
  328. objAlertPlaceholder: divUpdateAppAlert
  329. });
  330. }
  331. function uninstallApp(objBtn, appName) {
  332. if (!confirm("Are you sure you want to uninstall the DNS application '" + appName + "'?"))
  333. return;
  334. var btn = $(objBtn);
  335. btn.button('loading');
  336. HTTPRequest({
  337. url: "/api/apps/uninstall?token=" + sessionData.token + "&name=" + encodeURIComponent(appName),
  338. success: function (responseJSON) {
  339. var id = btn.attr("data-id");
  340. $("#trApp" + id).remove();
  341. updateAppsFooterCount();
  342. showAlert("success", "App Uninstalled!", "DNS application '" + appName + "' was uninstalled successfully.");
  343. },
  344. error: function () {
  345. btn.button('reset');
  346. },
  347. invalidToken: function () {
  348. showPageLogin();
  349. }
  350. });
  351. }
  352. function updateAppsFooterCount() {
  353. var totalApps = $('#tableApps >tbody >tr').length;
  354. if (totalApps > 0)
  355. $("#tableAppsFooter").html("<tr><td colspan=\"3\"><b>Total Apps: " + totalApps + "</b></td></tr>");
  356. else
  357. $("#tableAppsFooter").html("<tr><td colspan=\"3\" align=\"center\">No App Found</td></tr>");
  358. }
  359. function showAppConfigModal(objBtn, appName) {
  360. var btn = $(objBtn);
  361. btn.button('loading');
  362. HTTPRequest({
  363. url: "/api/apps/config/get?token=" + sessionData.token + "&name=" + encodeURIComponent(appName),
  364. success: function (responseJSON) {
  365. btn.button('reset');
  366. $("#divAppConfigAlert").html("");
  367. $("#lblAppConfigName").html(appName);
  368. $("#txtAppConfig").val(responseJSON.response.config);
  369. $("#btnAppConfig").button("reset");
  370. $("#modalAppConfig").modal("show");
  371. setTimeout(function () {
  372. $("#txtAppConfig").focus();
  373. }, 1000);
  374. },
  375. error: function () {
  376. btn.button('reset');
  377. },
  378. invalidToken: function () {
  379. showPageLogin();
  380. }
  381. });
  382. }
  383. function saveAppConfig() {
  384. var divAppConfigAlert = $("#divAppConfigAlert");
  385. var appName = $("#lblAppConfigName").text();
  386. var config = $("#txtAppConfig").val();
  387. var btn = $("#btnAppConfig").button("loading");
  388. HTTPRequest({
  389. url: "/api/apps/config/set?token=" + sessionData.token + "&name=" + encodeURIComponent(appName),
  390. method: "POST",
  391. data: "config=" + encodeURIComponent(config),
  392. processData: false,
  393. success: function (responseJSON) {
  394. $("#modalAppConfig").modal("hide");
  395. showAlert("success", "App Config Saved!", "The DNS application '" + appName + "' config was saved and reloaded successfully.");
  396. },
  397. error: function () {
  398. btn.button('reset');
  399. },
  400. invalidToken: function () {
  401. $("#modalAppConfig").modal("hide");
  402. showPageLogin();
  403. },
  404. objAlertPlaceholder: divAppConfigAlert
  405. });
  406. }