events.html 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. <!--
  2. Copyright (C) 2024 Nicola Murino
  3. This WebUI uses the KeenThemes Mega Bundle, a proprietary theme:
  4. https://keenthemes.com/products/templates-mega-bundle
  5. KeenThemes HTML/CSS/JS components are allowed for use only within the
  6. SFTPGo product and restricted to be used in a resealable HTML template
  7. that can compete with KeenThemes products anyhow.
  8. This WebUI is allowed for use only within the SFTPGo product and
  9. therefore cannot be used in derivative works/products without an
  10. explicit grant from the SFTPGo Team ([email protected]).
  11. -->
  12. {{template "base" .}}
  13. {{- define "extra_css"}}
  14. <link href="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.css" rel="stylesheet" type="text/css"/>
  15. {{- end}}
  16. {{- define "page_body"}}
  17. {{- template "errmsg" ""}}
  18. <div class="card shadow-sm">
  19. <div class="card-header bg-light">
  20. <h3 data-i18n="events.search" class="card-title section-title">Search logs</h3>
  21. </div>
  22. <div id="card_body" class="card-body">
  23. <div class="form-group row">
  24. <div class="col-md-3 mt-5">
  25. <select class="form-select" id="idEventType" name="events_type" data-control="i18n-select2" data-hide-search="true">
  26. <option value="1" data-i18n="events.fs_events">Fs events</option>
  27. <option value="2" data-i18n="events.provider_events">Provider events</option>
  28. <option value="3" data-i18n="events.other_events">Other events</option>
  29. </select>
  30. </div>
  31. <div class="col-md-4 mt-5">
  32. <select class="form-select" id="idActions" name="actions" data-control="i18n-select2" data-hide-search="true"
  33. data-close-on-select="false" data-i18n="[data-placeholder]general.actions" multiple>
  34. </select>
  35. </div>
  36. <div class="col-md-3 mt-5">
  37. <input type="text" class="form-control" id="idUsername" name="username" data-i18n="[placeholder]login.username" spellcheck="false">
  38. </div>
  39. <div class="col-md-2 mt-5">
  40. <input type="text" class="form-control" id="idIp" name="ip" data-i18n="[placeholder]defender.ip">
  41. </div>
  42. </div>
  43. <div class="form-group row">
  44. <div class="col-md-3 mt-5">
  45. <select class="form-select fs-events" id="idStatuses" name="statuses" data-control="i18n-select2" data-hide-search="true"
  46. data-close-on-select="false" data-i18n="[data-placeholder]general.status" multiple>
  47. <option value="1" data-i18n="general.ok">OK</option>
  48. <option value="2" data-i18n="general.failed">KO</option>
  49. <option value="3" data-i18n="events.quota_exceeded">Quota exceeded</option>
  50. </select>
  51. </div>
  52. <div class="col-md-4 mt-5">
  53. <select class="form-select fs-events log-events" id="idProtocols" name="protocols" data-control="i18n-select2" data-hide-search="true"
  54. data-close-on-select="false" data-i18n="[data-placeholder]ip_list.protocols" multiple>
  55. <option value="SFTP">SFTP</option>
  56. <option value="SCP">SCP</option>
  57. <option value="SSH">SSH</option>
  58. <option value="FTP">FTP</option>
  59. <option value="DAV">DAV</option>
  60. <option value="HTTP">HTTP</option>
  61. <option value="OIDC">OIDC</option>
  62. <option value="HTTPShare">HTTPShare</option>
  63. <option value="DataRetention">DataRetention</option>
  64. <option value="EventAction">EventAction</option>
  65. </select>
  66. </div>
  67. <div class="col-md-5 mt-5">
  68. <input id="dateTimeRange" class="form-control" data-i18n="[placeholder]events.date_range" />
  69. </div>
  70. </div>
  71. <div class="d-flex justify-content-end mt-10 mb-10">
  72. <button id="export_button" class="btn btn-secondary px-10 me-10">
  73. <span data-i18n="general.export" class="indicator-label">
  74. Export
  75. </span>
  76. <span data-i18n="general.wait" class="indicator-progress">
  77. Please wait...
  78. <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
  79. </span>
  80. </button>
  81. <button id="search_button" class="btn btn-primary px-10">
  82. <span data-i18n="general.search" class="indicator-label">
  83. Search
  84. </span>
  85. <span data-i18n="general.wait" class="indicator-progress">
  86. Please wait...
  87. <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
  88. </span>
  89. </button>
  90. </div>
  91. <table id="dataTableFs" class="table align-middle table-row-dashed fs-6 gy-5 fs-events">
  92. <thead>
  93. <tr class="text-start text-muted fw-bold fs-6 gs-0">
  94. <th data-i18n="events.datetime">Date and time</th>
  95. <th data-i18n="events.action">Action</th>
  96. <th data-i18n="events.path">Path</th>
  97. <th data-i18n="login.username">Username</th>
  98. <th data-i18n="general.protocol">Protocol</th>
  99. <th data-i18n="defender.ip">IP</th>
  100. <th data-i18n="general.info">Info</th>
  101. </tr>
  102. </thead>
  103. <tbody id="table_body" class="text-gray-800 fw-semibold"></tbody>
  104. </table>
  105. <table id="dataTableProvider" class="table align-middle table-row-dashed fs-6 gy-5 provider-events">
  106. <thead>
  107. <tr class="text-start text-muted fw-bold fs-6 gs-0">
  108. <th data-i18n="events.datetime">Date and time</th>
  109. <th data-i18n="events.action">Action</th>
  110. <th data-i18n="events.object">Object</th>
  111. <th data-i18n="login.username">Username</th>
  112. <th data-i18n="defender.ip">IP</th>
  113. </tr>
  114. </thead>
  115. <tbody id="table_body" class="text-gray-800 fw-semibold"></tbody>
  116. </table>
  117. <table id="dataTableLog" class="table align-middle table-row-dashed fs-6 gy-5 log-events">
  118. <thead>
  119. <tr class="text-start text-muted fw-bold fs-6 gs-0">
  120. <th data-i18n="events.datetime">Date and time</th>
  121. <th data-i18n="events.event">Event</th>
  122. <th data-i18n="login.username">Username</th>
  123. <th data-i18n="general.protocol">Protocol</th>
  124. <th data-i18n="defender.ip">IP</th>
  125. <th data-i18n="general.info">Info</th>
  126. </tr>
  127. </thead>
  128. <tbody id="table_body" class="text-gray-800 fw-semibold"></tbody>
  129. </table>
  130. <div id="paginationContainer" class="d-flex mt-4 mb-4 justify-content-end d-none">
  131. <div class="btn-group" role="group" aria-label="Pagination">
  132. <button id="pagePrevious" data-i18n="general.previous" type="button" class="btn btn-outline btn-active-primary disabled">Previous</button>
  133. <button id="pageNext" data-i18n="general.next" type="button" class="btn btn-outline btn-active-primary disabled">Next</button>
  134. </div>
  135. </div>
  136. </div>
  137. </div>
  138. {{- end}}
  139. {{- define "extra_js"}}
  140. <script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.js"></script>
  141. <script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/assets/plugins/custom/flatpickr/l10n/it.js"></script>
  142. <script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/vendor/humanize-duration/humanize-duration.min.js"></script>
  143. <script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
  144. const pageSize = 20;
  145. const paginationData = new Map();
  146. function resetPagination() {
  147. $('#pagePrevious').addClass("disabled");
  148. $('#pageNext').addClass("disabled");
  149. $('#paginationContainer').addClass("d-none");
  150. paginationData.delete("firstId");
  151. paginationData.delete("firstTs");
  152. paginationData.delete("lastId");
  153. paginationData.delete("lastTs");
  154. paginationData.set("prevClicked",false);
  155. paginationData.set("nextClicked",false);
  156. }
  157. function prevClicked(){
  158. paginationData.set("prevClicked",true);
  159. paginationData.set("nextClicked",false);
  160. doSearch();
  161. }
  162. function nextClicked(){
  163. paginationData.set("prevClicked",false);
  164. paginationData.set("nextClicked",true);
  165. doSearch();
  166. }
  167. function handleResponseData(data) {
  168. let length = data.length;
  169. let isNext = paginationData.get("nextClicked");
  170. let isPrev = paginationData.get("prevClicked");
  171. if (length > pageSize) {
  172. data.pop();
  173. length--;
  174. if (isPrev || isNext){
  175. $('#pagePrevious').removeClass("disabled");
  176. }
  177. $('#pageNext').removeClass("disabled");
  178. } else {
  179. if (isPrev){
  180. $('#pagePrevious').addClass("disabled");
  181. $('#pageNext').removeClass("disabled");
  182. } else if (isNext){
  183. $('#pagePrevious').removeClass("disabled");
  184. $('#pageNext').addClass("disabled");
  185. } else {
  186. $('#pageNext').addClass("disabled");
  187. }
  188. }
  189. if (isPrev){
  190. data = data.reverse();
  191. }
  192. if (length > 0){
  193. paginationData.set("lastId",data[0].id);
  194. paginationData.set("lastTs",data[0].timestamp);
  195. paginationData.set("firstId",data[length-1].id);
  196. paginationData.set("firstTs",data[length-1].timestamp);
  197. $('#paginationContainer').removeClass("d-none");
  198. } else {
  199. resetPagination();
  200. }
  201. return data;
  202. }
  203. function humanizeMilliseconds(val) {
  204. let units = ["d", "h", "m", "s", "ms"];
  205. let decimalPoints = 1;
  206. if (val > 1000){
  207. units = ["d", "h", "m", "s"]
  208. }
  209. if (val > 60000){
  210. decimalPoints = 0;
  211. }
  212. return humanizeDuration(val, {
  213. language: i18next.resolvedLanguage,
  214. fallbacks: ["en"],
  215. maxDecimalPoints: decimalPoints,
  216. units: units
  217. })
  218. }
  219. function getSearchURL(csvExport) {
  220. let url = "";
  221. let eventType = $('#idEventType').val();
  222. let order = "DESC";
  223. let limit = pageSize + 1;
  224. if (csvExport){
  225. order = "ASC";
  226. }
  227. if (eventType == 1){
  228. url = "{{.FsEventsSearchURL}}?limit="+limit;
  229. let protocols = [];
  230. $('#idProtocols').find('option:selected').each(function(){
  231. protocols.push($(this).val());
  232. });
  233. if (protocols.length > 0){
  234. url+="&protocols="+encodeURIComponent(String(protocols));
  235. }
  236. let statuses = [];
  237. $('#idStatuses').find('option:selected').each(function(){
  238. statuses.push($(this).val());
  239. });
  240. if (statuses.length > 0){
  241. url+="&statuses="+encodeURIComponent(String(statuses));
  242. }
  243. } else if (eventType == 2) {
  244. url = "{{.ProviderEventsSearchURL}}?omit_object_data=true&limit="+limit;
  245. } else {
  246. url = "{{.LogEventsSearchURL}}?limit="+limit;
  247. let protocols = [];
  248. $('#idProtocols').find('option:selected').each(function(){
  249. protocols.push($(this).val());
  250. });
  251. if (protocols.length > 0){
  252. url+="&protocols="+encodeURIComponent(String(protocols));
  253. }
  254. }
  255. let actions = [];
  256. $('#idActions').find('option:selected').each(function(){
  257. actions.push($(this).val());
  258. });
  259. if (actions.length > 0){
  260. if (eventType == 3){
  261. url+="&events="+encodeURIComponent(String(actions));
  262. } else {
  263. url+="&actions="+encodeURIComponent(String(actions));
  264. }
  265. }
  266. let username = $('#idUsername').val();
  267. if (username){
  268. url+="&username="+encodeURIComponent(username);
  269. }
  270. let ip = $('#idIp').val();
  271. if (ip){
  272. url+="&ip="+encodeURIComponent(ip);
  273. }
  274. const dateRangePicker = document.querySelector("#dateTimeRange")._flatpickr;
  275. let drp = dateRangePicker.selectedDates;
  276. let fromID = "";
  277. let start_ts = 0;
  278. if (!csvExport && paginationData.get("prevClicked") && paginationData.has("lastId") && paginationData.has("lastTs")){
  279. order = "ASC";
  280. start_ts = paginationData.get("lastTs");
  281. fromID = paginationData.get("lastId");
  282. } else {
  283. if (drp.length > 0){
  284. let d = drp[0];
  285. if (d) {
  286. start_ts = d.getTime()*1000000;
  287. }
  288. }
  289. }
  290. let end_ts = 0;
  291. if (!csvExport && paginationData.get("nextClicked") && paginationData.has("firstId") && paginationData.has("firstTs")){
  292. end_ts = paginationData.get("firstTs");
  293. fromID = paginationData.get("firstId");
  294. } else {
  295. if (drp.length > 1){
  296. let d = drp[1];
  297. if (d) {
  298. end_ts = d.getTime()*1000000;
  299. }
  300. }
  301. }
  302. url+="&start_timestamp="+encodeURIComponent(start_ts);
  303. url+="&end_timestamp="+encodeURIComponent(end_ts);
  304. if (fromID){
  305. url+="&from_id="+encodeURIComponent(fromID);
  306. }
  307. url+="&order="+order;
  308. if (csvExport){
  309. url+="&csv_export=true";
  310. }
  311. return url;
  312. }
  313. function onExportClicked() {
  314. paginationData.set("prevClicked",false);
  315. paginationData.set("nextClicked",false);
  316. let exportURL = getSearchURL(true);
  317. let ts = new Date().getTime().toString();
  318. window.open(`${exportURL}&_=${ts}`);
  319. }
  320. function onSearchClicked() {
  321. resetPagination();
  322. doSearch();
  323. }
  324. function doSearch() {
  325. let eventType = $('#idEventType').val();
  326. switch (eventType){
  327. case "1":
  328. datatableFsEvents.init();
  329. return;
  330. case "2":
  331. datatableProviderEvents.init();
  332. return;
  333. case "3":
  334. datatableLogEvents.init();
  335. return;
  336. default:
  337. console.log(`unsupported event type "${eventType}"`);
  338. }
  339. }
  340. function selectFsEvents(){
  341. let idActions = $('#idActions');
  342. idActions.empty();
  343. idActions.append(new Option($.t('events.upload'),"upload",false,false));
  344. idActions.append(new Option($.t('events.download'),"download",false,false));
  345. idActions.append(new Option($.t('events.mkdir'),"mkdir",false,false));
  346. idActions.append(new Option($.t('events.rmdir'),"rmdir",false,false));
  347. idActions.append(new Option($.t('events.rename'),"rename",false,false));
  348. idActions.append(new Option($.t('events.delete'),"delete",false,false));
  349. idActions.append(new Option($.t('events.first_upload'),"first-upload",false,false));
  350. idActions.append(new Option($.t('events.first_download'),"first-download",false,false));
  351. idActions.append(new Option($.t('events.ssh_cmd'),"ssh_cmd",false,false));
  352. idActions.trigger('change');
  353. $('#idUsername').val("");
  354. $('#idIp').val("");
  355. $('.provider-events').hide();
  356. $('.log-events').hide();
  357. $('.fs-events').show();
  358. onSearchClicked();
  359. }
  360. function selectLogEvents(){
  361. let idActions = $('#idActions');
  362. idActions.empty();
  363. idActions.append(new Option($.t('events.login_ok'),"5",false,false));
  364. idActions.append(new Option($.t('events.login_failed'),"1",false,false));
  365. idActions.append(new Option($.t('events.login_missing_user'),"2",false,false));
  366. idActions.append(new Option($.t('events.no_login_tried'),"3",false,false));
  367. idActions.append(new Option($.t('events.algo_negotiation_failed'),"4",false,false));
  368. idActions.trigger('change');
  369. $('#idUsername').val("");
  370. $('#idIp').val("");
  371. $('.provider-events').hide();
  372. $('.fs-events').hide();
  373. $('.log-events').show();
  374. onSearchClicked();
  375. }
  376. function selectProviderEvents(){
  377. let idActions = $('#idActions');
  378. idActions.empty();
  379. idActions.append(new Option($.t('events.add'),"add",false,false));
  380. idActions.append(new Option($.t('events.update'),"update",false,false));
  381. idActions.append(new Option($.t('events.delete'),"delete",false,false));
  382. idActions.trigger('change');
  383. $('#idUsername').val("");
  384. $('#idIp').val("");
  385. $('.fs-events').hide();
  386. $('.log-events').hide();
  387. $('.provider-events').show();
  388. onSearchClicked();
  389. }
  390. function onEventChanged(val){
  391. switch (val){
  392. case '1':
  393. selectFsEvents();
  394. break;
  395. case '2':
  396. selectProviderEvents();
  397. break;
  398. case '3':
  399. selectLogEvents();
  400. break;
  401. default:
  402. console.log(`unsupported event type: ${val}`);
  403. }
  404. }
  405. var datatableFsEvents = function(){
  406. var dt;
  407. var initDatatable = function () {
  408. if (dt){
  409. dt.clear().draw();
  410. dt.ajax.url(getSearchURL(false)).load();
  411. return;
  412. }
  413. $('#errorMsg').addClass("d-none");
  414. dt = $('#dataTableFs').DataTable({
  415. ajax: {
  416. url: getSearchURL(false),
  417. dataSrc: handleResponseData,
  418. error: function ($xhr, textStatus, errorThrown) {
  419. $(".dt-processing").hide();
  420. let txt = "";
  421. if ($xhr) {
  422. let json = $xhr.responseJSON;
  423. if (json) {
  424. if (json.message){
  425. txt = json.message;
  426. }
  427. }
  428. }
  429. if (!txt){
  430. txt = "general.error500";
  431. }
  432. setI18NData($('#errorTxt'), txt);
  433. $('#errorMsg').removeClass("d-none");
  434. }
  435. },
  436. columns: [
  437. {
  438. data: "timestamp",
  439. render: function(data, type, row) {
  440. if (type === 'display') {
  441. if (data){
  442. let ts = data/1000000;
  443. if (ts){
  444. return $.t('general.datetime', {
  445. val: new Date(ts),
  446. formatParams: {
  447. val: { year: '2-digit', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
  448. }
  449. });
  450. }
  451. }
  452. return "";
  453. }
  454. return data;
  455. }
  456. },
  457. {
  458. data: "action",
  459. defaultContent: "",
  460. render: function(data, type, row) {
  461. if (type === 'display') {
  462. switch (data){
  463. case "upload":
  464. return $.t('events.upload');
  465. case "download":
  466. return $.t('events.download');
  467. case "mkdir":
  468. return $.t('events.mkdir');
  469. case "rmdir":
  470. return $.t('events.rmdir');
  471. case "rename":
  472. return $.t('events.rename');
  473. case "delete":
  474. return $.t('events.delete');
  475. case "first-upload":
  476. return $.t('events.first_upload');
  477. case "first-download":
  478. return $.t('events.first_download');
  479. case "ssh_cmd":
  480. return $.t('events.ssh_cmd');
  481. default:
  482. console.log(`unknown fs action "${data}"`);
  483. return "";
  484. }
  485. }
  486. return data;
  487. }
  488. },
  489. {
  490. data: "virtual_path",
  491. defaultContent: "",
  492. width: '20%',
  493. render: function(data, type, row) {
  494. if (type === 'display') {
  495. if (!data){
  496. return "";
  497. }
  498. if (row.virtual_target_path){
  499. return escapeHTML(`${data} => ${row.virtual_target_path}`);
  500. }
  501. return escapeHTML(data);
  502. }
  503. return data;
  504. }
  505. },
  506. {
  507. data: "username",
  508. defaultContent: "",
  509. render: function(data, type, row) {
  510. if (type === 'display') {
  511. if (!data){
  512. return "";
  513. }
  514. return escapeHTML(data);
  515. }
  516. return data;
  517. }
  518. },
  519. {
  520. data: "protocol",
  521. defaultContent: "",
  522. render: function(data, type, row) {
  523. if (!data){
  524. return "";
  525. }
  526. if (type === 'display') {
  527. return escapeHTML(data);
  528. }
  529. return data;
  530. }
  531. },
  532. {
  533. data: "ip",
  534. defaultContent: "",
  535. render: function(data, type, row) {
  536. if (type === 'display') {
  537. if (!data){
  538. return "";
  539. }
  540. return escapeHTML(data);
  541. }
  542. return data;
  543. }
  544. },
  545. {
  546. data: "status",
  547. defaultContent: "",
  548. width: '15%',
  549. render: function(data, type, row) {
  550. if (type === 'display') {
  551. let info = "";
  552. switch (data){
  553. case 1:
  554. info = $.t('general.ok');
  555. break;
  556. case 2:
  557. info = $.t('general.failed');
  558. break;
  559. case 3:
  560. info = $.t('events.quota_exceeded');
  561. break;
  562. default:
  563. console.log(`unknow status ${data}`);
  564. }
  565. if (row.file_size || row.file_size == 0){
  566. let humanSize = fileSizeIEC(row["file_size"]);
  567. info+=`. ${humanSize}`;
  568. }
  569. if (row.elapsed){
  570. let elapsed = humanizeMilliseconds(row.elapsed);
  571. info+=`. ${elapsed}`;
  572. }
  573. if (row.ssh_cmd){
  574. info+=". "+$.t('events.ssh_cmd')+` "${row.ssh_cmd}"`;
  575. }
  576. return info;
  577. }
  578. return data;
  579. }
  580. },
  581. ],
  582. deferRender: true,
  583. processing: true,
  584. lengthChange: false,
  585. searching: false,
  586. paging: false,
  587. info: false,
  588. ordering: false,
  589. language: {
  590. info: $.t('datatable.info'),
  591. infoEmpty: $.t('datatable.info_empty'),
  592. infoFiltered: $.t('datatable.info_filtered'),
  593. loadingRecords: "",
  594. processing: $.t('datatable.processing'),
  595. zeroRecords: "",
  596. emptyTable: $.t('datatable.no_records')
  597. },
  598. });
  599. dt.on('draw', drawAction);
  600. }
  601. function drawAction() {
  602. $('#table_body').localize();
  603. }
  604. return {
  605. init: function () {
  606. initDatatable();
  607. }
  608. }
  609. }();
  610. var datatableProviderEvents = function(){
  611. var dt;
  612. var initDatatable = function () {
  613. if (dt){
  614. dt.clear().draw();
  615. dt.ajax.url(getSearchURL(false)).load();
  616. return;
  617. }
  618. $('#errorMsg').addClass("d-none");
  619. dt = $('#dataTableProvider').DataTable({
  620. ajax: {
  621. url: getSearchURL(false),
  622. dataSrc: handleResponseData,
  623. error: function ($xhr, textStatus, errorThrown) {
  624. $(".dt-processing").hide();
  625. let txt = "";
  626. if ($xhr) {
  627. let json = $xhr.responseJSON;
  628. if (json) {
  629. if (json.message){
  630. txt = json.message;
  631. }
  632. }
  633. }
  634. if (!txt){
  635. txt = "general.error500";
  636. }
  637. setI18NData($('#errorTxt'), txt);
  638. $('#errorMsg').removeClass("d-none");
  639. }
  640. },
  641. columns: [
  642. {
  643. data: "timestamp",
  644. render: function(data, type, row) {
  645. if (type === 'display') {
  646. if (data){
  647. let ts = data/1000000;
  648. if (ts){
  649. return $.t('general.datetime', {
  650. val: new Date(ts),
  651. formatParams: {
  652. val: { year: '2-digit', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
  653. }
  654. });
  655. }
  656. }
  657. return "";
  658. }
  659. return data;
  660. }
  661. },
  662. {
  663. data: "action",
  664. defaultContent: "",
  665. render: function(data, type, row) {
  666. if (type === 'display') {
  667. switch (data){
  668. case "add":
  669. return $.t('events.add');
  670. case "update":
  671. return $.t('events.update');
  672. case "delete":
  673. return $.t('events.delete');
  674. console.log(`unknown provider action "${data}"`);
  675. return "";
  676. }
  677. }
  678. return data;
  679. }
  680. },
  681. {
  682. data: "object_type",
  683. defaultContent: "",
  684. render: function(data, type, row) {
  685. if (type === 'display') {
  686. if (!data){
  687. return "";
  688. }
  689. let message;
  690. switch (data){
  691. case "user":
  692. message = $.t('provider_objects.user');
  693. break;
  694. case "folder":
  695. message = $.t('provider_objects.folder');
  696. break;
  697. case "group":
  698. message = $.t('provider_objects.group');
  699. break;
  700. case "admin":
  701. message = $.t('provider_objects.admin');
  702. break;
  703. case "api_key":
  704. message = $.t('provider_objects.api_key');
  705. break;
  706. case "share":
  707. message = $.t('provider_objects.share');
  708. break;
  709. case "event_action":
  710. message = $.t('provider_objects.event_action');
  711. break;
  712. case "event_rule":
  713. message = $.t('provider_objects.event_rule');
  714. break;
  715. case "role":
  716. message = $.t('provider_objects.role');
  717. break;
  718. case "ip_list_entry":
  719. message = $.t('provider_objects.ip_list_entry');
  720. break;
  721. case "configs":
  722. message = $.t('provider_objects.configs');
  723. break;
  724. default:
  725. console.log("uknown object type: "+data);
  726. return ""
  727. }
  728. if (row.object_name && data != "configs"){
  729. return message+= escapeHTML(` "${row.object_name}"`);
  730. }
  731. return message;
  732. }
  733. return data;
  734. }
  735. },
  736. {
  737. data: "username",
  738. defaultContent: "",
  739. render: function(data, type, row) {
  740. if (type === 'display') {
  741. if (!data){
  742. return "";
  743. }
  744. return escapeHTML(data);
  745. }
  746. return data;
  747. }
  748. },
  749. {
  750. data: "ip",
  751. defaultContent: "",
  752. render: function(data, type, row) {
  753. if (type === 'display') {
  754. if (!data){
  755. return "";
  756. }
  757. return escapeHTML(data);
  758. }
  759. return data;
  760. }
  761. },
  762. ],
  763. deferRender: true,
  764. processing: true,
  765. lengthChange: false,
  766. searching: false,
  767. paging: false,
  768. info: false,
  769. ordering: false,
  770. language: {
  771. info: $.t('datatable.info'),
  772. infoEmpty: $.t('datatable.info_empty'),
  773. infoFiltered: $.t('datatable.info_filtered'),
  774. loadingRecords: "",
  775. processing: $.t('datatable.processing'),
  776. zeroRecords: "",
  777. emptyTable: $.t('datatable.no_records')
  778. },
  779. });
  780. dt.on('draw', drawAction);
  781. }
  782. function drawAction() {
  783. $('#table_body').localize();
  784. }
  785. return {
  786. init: function () {
  787. initDatatable();
  788. }
  789. }
  790. }();
  791. var datatableLogEvents = function(){
  792. var dt;
  793. var initDatatable = function () {
  794. if (dt){
  795. dt.clear().draw();
  796. dt.ajax.url(getSearchURL(false)).load();
  797. return;
  798. }
  799. $('#errorMsg').addClass("d-none");
  800. dt = $('#dataTableLog').DataTable({
  801. ajax: {
  802. url: getSearchURL(false),
  803. dataSrc: handleResponseData,
  804. error: function ($xhr, textStatus, errorThrown) {
  805. $(".dt-processing").hide();
  806. let txt = "";
  807. if ($xhr) {
  808. let json = $xhr.responseJSON;
  809. if (json) {
  810. if (json.message){
  811. txt = json.message;
  812. }
  813. }
  814. }
  815. if (!txt){
  816. txt = "general.error500";
  817. }
  818. setI18NData($('#errorTxt'), txt);
  819. $('#errorMsg').removeClass("d-none");
  820. }
  821. },
  822. columns: [
  823. {
  824. data: "timestamp",
  825. render: function(data, type, row) {
  826. if (type === 'display') {
  827. if (data){
  828. let ts = data/1000000;
  829. if (ts){
  830. return $.t('general.datetime', {
  831. val: new Date(ts),
  832. formatParams: {
  833. val: { year: '2-digit', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
  834. }
  835. });
  836. }
  837. }
  838. return "";
  839. }
  840. return data;
  841. }
  842. },
  843. {
  844. data: "event",
  845. defaultContent: "",
  846. render: function(data, type, row) {
  847. if (type === 'display') {
  848. switch (data){
  849. case 1:
  850. return $.t('events.login_failed');
  851. case 2:
  852. return $.t('events.login_missing_user');
  853. case 3:
  854. return $.t('events.no_login_tried');
  855. case 4:
  856. return $.t('events.algo_negotiation_failed');
  857. case 5:
  858. return $.t('events.login_ok');
  859. default:
  860. console.log(`unknown log action "${data}"`);
  861. return "";
  862. }
  863. }
  864. return data;
  865. }
  866. },
  867. {
  868. data: "username",
  869. defaultContent: "",
  870. render: function(data, type, row) {
  871. if (type === 'display') {
  872. if (!data){
  873. return "";
  874. }
  875. return escapeHTML(data);
  876. }
  877. return data;
  878. }
  879. },
  880. {
  881. data: "protocol",
  882. defaultContent: "",
  883. render: function(data, type, row) {
  884. if (type === 'display') {
  885. if (!data){
  886. return "";
  887. }
  888. return escapeHTML(data);
  889. }
  890. return data;
  891. }
  892. },
  893. {
  894. data: "ip",
  895. defaultContent: "",
  896. render: function(data, type, row) {
  897. if (type === 'display') {
  898. if (!data){
  899. return "";
  900. }
  901. if (data){
  902. return escapeHTML(data);
  903. }
  904. }
  905. return data;
  906. }
  907. },
  908. {
  909. data: "message",
  910. defaultContent: "",
  911. width: '25%',
  912. render: function(data, type, row) {
  913. if (type === 'display') {
  914. if (data){
  915. return escapeHTML(data);
  916. }
  917. }
  918. return "";
  919. }
  920. },
  921. ],
  922. deferRender: true,
  923. processing: true,
  924. lengthChange: false,
  925. searching: false,
  926. paging: false,
  927. info: false,
  928. ordering: false,
  929. language: {
  930. info: $.t('datatable.info'),
  931. infoEmpty: $.t('datatable.info_empty'),
  932. infoFiltered: $.t('datatable.info_filtered'),
  933. loadingRecords: "",
  934. processing: $.t('datatable.processing'),
  935. zeroRecords: "",
  936. emptyTable: $.t('datatable.no_records')
  937. },
  938. });
  939. dt.on('draw', drawAction);
  940. }
  941. function drawAction() {
  942. $('#table_body').localize();
  943. }
  944. return {
  945. init: function () {
  946. initDatatable();
  947. }
  948. }
  949. }();
  950. $(document).on("i18nload", function(){
  951. $('#dateTimeRange').flatpickr({
  952. enableTime: true,
  953. time_24hr: true,
  954. formatDate: (date, format, locale) => {
  955. return $.t('general.datetime', {
  956. val: new Date(date),
  957. formatParams: {
  958. val: { year: '2-digit', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
  959. }
  960. });
  961. },
  962. mode: "range",
  963. defaultDate: [moment().add(-1,'hour').format('YYYY-MM-DD HH:mm')],
  964. minuteIncrement: 1,
  965. locale: i18next.resolvedLanguage
  966. });
  967. onEventChanged('1');
  968. });
  969. $(document).on("i18nshow", function(){
  970. $('#idEventType').on("change", function(){
  971. onEventChanged(this.value);
  972. });
  973. $('#search_button').on("click", function(e){
  974. e.preventDefault();
  975. this.blur();
  976. onSearchClicked();
  977. });
  978. $('#export_button').on("click", function(e){
  979. e.preventDefault();
  980. this.blur();
  981. onExportClicked();
  982. });
  983. $('#pagePrevious').on("click", function(e){
  984. e.preventDefault();
  985. this.blur();
  986. prevClicked();
  987. });
  988. $('#pageNext').on("click", function(e){
  989. e.preventDefault();
  990. this.blur();
  991. nextClicked();
  992. });
  993. });
  994. </script>
  995. {{- end}}