logs.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*
  2. Technitium DNS Server
  3. Copyright (C) 2021 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 () {
  16. $('#dtpQueryLogStart').datetimepicker({ format: "YYYY-MM-DD HH:mm:ss" });
  17. $('#dtpQueryLogEnd').datetimepicker({ format: "YYYY-MM-DD HH:mm:ss" });
  18. $("#optQueryLogsAppName").change(function () {
  19. if (appsList == null)
  20. return;
  21. var appName = $("#optQueryLogsAppName").val();
  22. var optClassPaths = "";
  23. for (var i = 0; i < appsList.length; i++) {
  24. if (appsList[i].name == appName) {
  25. for (var j = 0; j < appsList[i].dnsApps.length; j++) {
  26. if (appsList[i].dnsApps[j].isQueryLogger)
  27. optClassPaths += "<option>" + appsList[i].dnsApps[j].classPath + "</option>";
  28. }
  29. break;
  30. }
  31. }
  32. $("#optQueryLogsClassPath").html(optClassPaths);
  33. $("#txtAddEditRecordDataData").val("");
  34. });
  35. });
  36. function refreshLogsTab() {
  37. if ($("#logsTabListLogViewer").hasClass("active"))
  38. refreshLogFilesList();
  39. else if ($("#logsTabListQueryLogs").hasClass("active"))
  40. refreshQueryLogsTab();
  41. }
  42. function refreshLogFilesList() {
  43. var lstLogFiles = $("#lstLogFiles");
  44. HTTPRequest({
  45. url: "/api/listLogs?token=" + token,
  46. success: function (responseJSON) {
  47. var logFiles = responseJSON.response.logFiles;
  48. var list = "<div class=\"log\" style=\"font-size: 14px; padding-bottom: 6px;\"><a href=\"#\" onclick=\"deleteAllStats(); return false;\"><b>[delete all stats]</b></a></div>";
  49. if (logFiles.length == 0) {
  50. list += "<div class=\"log\">No Log Was Found</div>";
  51. }
  52. else {
  53. list += "<div class=\"log\" style=\"font-size: 14px; padding-bottom: 6px;\"><a href=\"#\" onclick=\"deleteAllLogs(); return false;\"><b>[delete all logs]</b></a></div>";
  54. for (var i = 0; i < logFiles.length; i++) {
  55. var logFile = logFiles[i];
  56. list += "<div class=\"log\"><a href=\"#\" onclick=\"viewLog('" + logFile.fileName + "'); return false;\">" + logFile.fileName + " [" + logFile.size + "]</a></div>"
  57. }
  58. }
  59. lstLogFiles.html(list);
  60. },
  61. invalidToken: function () {
  62. showPageLogin();
  63. },
  64. objLoaderPlaceholder: lstLogFiles
  65. });
  66. }
  67. function viewLog(logFile) {
  68. var divLogViewer = $("#divLogViewer");
  69. var txtLogViewerTitle = $("#txtLogViewerTitle");
  70. var divLogViewerLoader = $("#divLogViewerLoader");
  71. var preLogViewerBody = $("#preLogViewerBody");
  72. txtLogViewerTitle.text(logFile);
  73. preLogViewerBody.hide();
  74. divLogViewerLoader.show();
  75. divLogViewer.show();
  76. HTTPGetFileRequest({
  77. url: "/log/" + logFile + "?limit=2&token=" + token,
  78. success: function (response) {
  79. divLogViewerLoader.hide();
  80. preLogViewerBody.text(response);
  81. preLogViewerBody.show();
  82. },
  83. objLoaderPlaceholder: divLogViewerLoader
  84. });
  85. }
  86. function downloadLog() {
  87. var logFile = $("#txtLogViewerTitle").text();
  88. window.open("/log/" + logFile + "?token=" + token + "&ts=" + (new Date().getTime()), "_blank");
  89. }
  90. function deleteLog() {
  91. var logFile = $("#txtLogViewerTitle").text();
  92. if (!confirm("Are you sure you want to permanently delete the log file '" + logFile + "'?"))
  93. return;
  94. var btn = $("#btnDeleteLog").button('loading');
  95. HTTPRequest({
  96. url: "/api/deleteLog?token=" + token + "&log=" + logFile,
  97. success: function (responseJSON) {
  98. refreshLogFilesList();
  99. $("#divLogViewer").hide();
  100. btn.button('reset');
  101. showAlert("success", "Log Deleted!", "Log file was deleted successfully.");
  102. },
  103. error: function () {
  104. btn.button('reset');
  105. },
  106. invalidToken: function () {
  107. btn.button('reset');
  108. showPageLogin();
  109. }
  110. });
  111. }
  112. function deleteAllLogs() {
  113. if (!confirm("Are you sure you want to permanently delete all log files?"))
  114. return;
  115. HTTPRequest({
  116. url: "/api/deleteAllLogs?token=" + token,
  117. success: function (responseJSON) {
  118. refreshLogFilesList();
  119. $("#divLogViewer").hide();
  120. showAlert("success", "Logs Deleted!", "All log files were deleted successfully.");
  121. },
  122. invalidToken: function () {
  123. showPageLogin();
  124. }
  125. });
  126. }
  127. function deleteAllStats() {
  128. if (!confirm("Are you sure you want to permanently delete all stats files?"))
  129. return;
  130. HTTPRequest({
  131. url: "/api/deleteAllStats?token=" + token,
  132. success: function (responseJSON) {
  133. showAlert("success", "Stats Deleted!", "All stats files were deleted successfully.");
  134. },
  135. invalidToken: function () {
  136. showPageLogin();
  137. }
  138. });
  139. }
  140. var appsList;
  141. function refreshQueryLogsTab() {
  142. var frmQueryLogs = $("#frmQueryLogs");
  143. var divQueryLogsLoader = $("#divQueryLogsLoader");
  144. var divQueryLogsTable = $("#divQueryLogsTable");
  145. var optQueryLogsAppName = $("#optQueryLogsAppName");
  146. var optQueryLogsClassPath = $("#optQueryLogsClassPath");
  147. var currentAppName = optQueryLogsAppName.val();
  148. var currentClassPath = optQueryLogsClassPath.val();
  149. var loader;
  150. if (appsList == null) {
  151. frmQueryLogs.hide();
  152. divQueryLogsTable.hide();
  153. loader = divQueryLogsLoader;
  154. }
  155. else {
  156. optQueryLogsAppName.prop('disabled', true);
  157. optQueryLogsClassPath.prop('disabled', true);
  158. }
  159. HTTPRequest({
  160. url: "/api/apps/list?token=" + token,
  161. success: function (responseJSON) {
  162. var apps = responseJSON.response.apps;
  163. var optApps = "";
  164. var optClassPaths = "";
  165. for (var i = 0; i < apps.length; i++) {
  166. for (var j = 0; j < apps[i].dnsApps.length; j++) {
  167. if (apps[i].dnsApps[j].isQueryLogger) {
  168. optApps += "<option>" + apps[i].name + "</option>";
  169. if (currentAppName == null)
  170. currentAppName = apps[i].name;
  171. break;
  172. }
  173. }
  174. }
  175. for (var i = 0; i < apps.length; i++) {
  176. if (apps[i].name == currentAppName) {
  177. for (var j = 0; j < apps[i].dnsApps.length; j++) {
  178. if (apps[i].dnsApps[j].isQueryLogger)
  179. optClassPaths += "<option>" + apps[i].dnsApps[j].classPath + "</option>";
  180. }
  181. break;
  182. }
  183. }
  184. optQueryLogsAppName.html(optApps);
  185. optQueryLogsClassPath.html(optClassPaths);
  186. if (currentAppName != null)
  187. optQueryLogsAppName.val(currentAppName);
  188. if (currentClassPath != null)
  189. optQueryLogsClassPath.val(currentClassPath);
  190. if (appsList == null) {
  191. frmQueryLogs.show();
  192. loader.hide();
  193. }
  194. else {
  195. optQueryLogsAppName.prop('disabled', false);
  196. optQueryLogsClassPath.prop('disabled', false);
  197. }
  198. appsList = apps;
  199. },
  200. error: function () {
  201. if (appsList == null) {
  202. frmQueryLogs.show();
  203. divQueryLogsTable.show();
  204. }
  205. else {
  206. optQueryLogsAppName.prop('disabled', false);
  207. optQueryLogsClassPath.prop('disabled', false);
  208. }
  209. },
  210. invalidToken: function () {
  211. showPageLogin();
  212. },
  213. objLoaderPlaceholder: loader
  214. });
  215. }
  216. function queryLogs(pageNumber) {
  217. var btn = $("#btnQueryLogs");
  218. var divQueryLogsLoader = $("#divQueryLogsLoader");
  219. var divQueryLogsTable = $("#divQueryLogsTable");
  220. var name = $("#optQueryLogsAppName").val();
  221. if (name == null) {
  222. showAlert("warning", "Missing!", "Please install a DNS App that supports query logging feature.");
  223. $("#optQueryLogsAppName").focus();
  224. return false;
  225. }
  226. var classPath = $("#optQueryLogsClassPath").val();
  227. if (classPath == null) {
  228. showAlert("warning", "Missing!", "Please select a Class Path to query logs.");
  229. $("#optQueryLogsClassPath").focus();
  230. return false;
  231. }
  232. if (pageNumber == null)
  233. pageNumber = $("#txtQueryLogPageNumber").val();
  234. var entriesPerPage = $("#optQueryLogsEntriesPerPage").val();
  235. var descendingOrder = $("#optQueryLogsDescendingOrder").val();
  236. var start = $("#txtQueryLogStart").val();
  237. if (start != "")
  238. start = moment(start).format("YYYY-MM-DD HH:mm:ss");
  239. var end = $("#txtQueryLogEnd").val();
  240. if (end != "")
  241. end = moment(end).format("YYYY-MM-DD HH:mm:ss");
  242. var clientIpAddress = $("#txtQueryLogClientIpAddress").val();
  243. var protocol = $("#optQueryLogsProtocol").val();
  244. var responseType = $("#optQueryLogsResponseType").val();
  245. var rcode = $("#optQueryLogsResponseCode").val();
  246. var qname = $("#txtQueryLogQName").val();
  247. var qtype = $("#txtQueryLogQType").val();
  248. var qclass = $("#optQueryLogQClass").val();
  249. divQueryLogsTable.hide();
  250. divQueryLogsLoader.show();
  251. btn.button('loading');
  252. HTTPRequest({
  253. url: "/api/queryLogs?token=" + token + "&name=" + encodeURIComponent(name) + "&classPath=" + encodeURIComponent(classPath) + "&pageNumber=" + pageNumber + "&entriesPerPage=" + entriesPerPage + "&descendingOrder=" + descendingOrder +
  254. "&start=" + encodeURIComponent(start) + "&end=" + encodeURIComponent(end) + "&clientIpAddress=" + encodeURIComponent(clientIpAddress) + "&protocol=" + protocol + "&responseType=" + responseType + "&rcode=" + rcode +
  255. "&qname=" + encodeURIComponent(qname) + "&qtype=" + qtype + "&qclass=" + qclass,
  256. success: function (responseJSON) {
  257. var tableHtml = "";
  258. for (var i = 0; i < responseJSON.response.entries.length; i++) {
  259. tableHtml += "<tr><td>" + responseJSON.response.entries[i].rowNumber + "</td><td>" +
  260. moment(responseJSON.response.entries[i].timestamp).local().format("YYYY-MM-DD HH:mm:ss") + "</td><td>" +
  261. responseJSON.response.entries[i].clientIpAddress + "</td><td>" +
  262. responseJSON.response.entries[i].protocol + "</td><td>" +
  263. responseJSON.response.entries[i].responseType + "</td><td>" +
  264. responseJSON.response.entries[i].rcode + "</td><td style=\"word-break: break-all;\">" +
  265. htmlEncode(responseJSON.response.entries[i].qname == "" ? "." : responseJSON.response.entries[i].qname) + "</td><td>" +
  266. (responseJSON.response.entries[i].qtype == null ? "" : responseJSON.response.entries[i].qtype) + "</td><td>" +
  267. (responseJSON.response.entries[i].qclass == null ? "" : responseJSON.response.entries[i].qclass) + "</td><td style=\"word-break: break-all;\">" +
  268. htmlEncode(responseJSON.response.entries[i].answer) + "</td></tr>"
  269. }
  270. var paginationHtml = "";
  271. if (responseJSON.response.pageNumber > 1) {
  272. paginationHtml += "<li><a href=\"#\" aria-label=\"First\" onClick=\"queryLogs(1); return false;\"><span aria-hidden=\"true\">&laquo;</span></a></li>";
  273. paginationHtml += "<li><a href=\"#\" aria-label=\"Previous\" onClick=\"queryLogs(" + (responseJSON.response.pageNumber - 1) + "); return false;\"><span aria-hidden=\"true\">&lsaquo;</span></a></li>";
  274. }
  275. var pageStart = responseJSON.response.pageNumber - 5;
  276. if (pageStart < 1)
  277. pageStart = 1;
  278. var pageEnd = pageStart + 9;
  279. if (pageEnd > responseJSON.response.totalPages) {
  280. var endDiff = pageEnd - responseJSON.response.totalPages;
  281. pageEnd = responseJSON.response.totalPages;
  282. pageStart -= endDiff;
  283. if (pageStart < 1)
  284. pageStart = 1;
  285. }
  286. for (var i = pageStart; i <= pageEnd; i++) {
  287. if (i == responseJSON.response.pageNumber)
  288. paginationHtml += "<li class=\"active\"><a href=\"#\" onClick=\"queryLogs(" + i + "); return false;\">" + i + "</a></li>";
  289. else
  290. paginationHtml += "<li><a href=\"#\" onClick=\"queryLogs(" + i + "); return false;\">" + i + "</a></li>";
  291. }
  292. if (responseJSON.response.pageNumber < responseJSON.response.totalPages) {
  293. paginationHtml += "<li><a href=\"#\" aria-label=\"Next\" onClick=\"queryLogs(" + (responseJSON.response.pageNumber + 1) + "); return false;\"><span aria-hidden=\"true\">&rsaquo;</span></a></li>";
  294. paginationHtml += "<li><a href=\"#\" aria-label=\"Last\" onClick=\"queryLogs(-1); return false;\"><span aria-hidden=\"true\">&raquo;</span></a></li>";
  295. }
  296. $("#tableQueryLogsBody").html(tableHtml);
  297. var statusHtml;
  298. if (responseJSON.response.entries.length > 0)
  299. statusHtml = responseJSON.response.entries[0].rowNumber + "-" + responseJSON.response.entries[responseJSON.response.entries.length - 1].rowNumber + " (" + responseJSON.response.entries.length + ") of " + responseJSON.response.totalEntries + " logs (page " + responseJSON.response.pageNumber + " of " + responseJSON.response.totalPages + ")";
  300. else
  301. statusHtml = "0 logs";
  302. $("#tableQueryLogsTopStatus").html(statusHtml);
  303. $("#tableQueryLogsTopPagination").html(paginationHtml);
  304. $("#tableQueryLogsFooterStatus").html(statusHtml);
  305. $("#tableQueryLogsFooterPagination").html(paginationHtml);
  306. btn.button('reset');
  307. divQueryLogsLoader.hide();
  308. divQueryLogsTable.show();
  309. },
  310. error: function () {
  311. btn.button('reset');
  312. },
  313. invalidToken: function () {
  314. showPageLogin();
  315. },
  316. objLoaderPlaceholder: divQueryLogsLoader
  317. });
  318. }