utils.html 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. <div class="standardContainer">
  2. <div class="ui basic segment">
  3. <h2>Utilities</h2>
  4. <p>You might find these tools or information helpful when setting up your gateway server</p>
  5. </div>
  6. <div class="ui top attached tabular menu">
  7. <a class="utils item active" data-tab="utiltab1"><i class="ui user circle blue icon"></i> Accounts</a>
  8. <a class="utils item" data-tab="utiltab2">Toolbox</a>
  9. <a class="utils item" data-tab="utiltab3">System</a>
  10. </div>
  11. <div class="ui bottom attached tab segment utilitiesTabs active" data-tab="utiltab1">
  12. <div class="extAuthOnly" style="display:none;">
  13. <div class="ui basic segment">
  14. <i class="ui green circle check icon"></i> Account options are not available due to -noauth flag is set to true.
  15. </div>
  16. </div>
  17. <div class="selfauthOnly">
  18. <h3>Change Password</h3>
  19. <p>Update the current account credentials</p>
  20. <div class="ui basic segment">
  21. <h5><i class="chevron down icon"></i> Change Password</h5>
  22. <div class="ui form">
  23. <div class="field">
  24. <label>Current Password</label>
  25. <input type="password" name="oldPassword" placeholder="Current Password">
  26. </div>
  27. <div class="field">
  28. <label>New Password</label>
  29. <input type="password" name="newPassword" placeholder="New Password">
  30. </div>
  31. <div class="field">
  32. <label>Confirm New Password</label>
  33. <input type="password" name="confirmNewPassword" placeholder="Confirm New Password">
  34. </div>
  35. <button class="ui basic button" onclick="changePassword()"><i class="ui teal key icon"></i> Change Password</button>
  36. </div>
  37. <div id="passwordChangeSuccMsg" class="ui green message" style="display:none;">
  38. <i class="ui circle checkmark green icon "></i> Password Updated
  39. </div>
  40. </div>
  41. <div class="ui divider"></div>
  42. <h3>Forget Password Email</h3>
  43. <p>The following SMTP settings help you to reset your password in case you have lost your management account.</p>
  44. <form id="email-form" class="ui form">
  45. <div class="field">
  46. <label>Sender Address</label>
  47. <input type="text" name="senderAddr" placeholder="E.g. [email protected]">
  48. </div>
  49. <div class="field">
  50. <p><i class="caret down icon"></i> Connection setup for email service provider</p>
  51. <div class="fields">
  52. <div class="twelve wide field">
  53. <label>SMTP Provider Hostname</label>
  54. <input type="text" name="hostname" placeholder="E.g. mail.gandi.net">
  55. </div>
  56. <div class="four wide field">
  57. <label>Port</label>
  58. <input type="number" name="port" placeholder="E.g. 587" value="587">
  59. </div>
  60. </div>
  61. </div>
  62. <div class="field">
  63. <p><i class="caret down icon"></i> Credentials for SMTP server authentications</p>
  64. <div class="two fields">
  65. <div class="field">
  66. <label>Sender Username</label>
  67. <input type="text" name="username" placeholder="E.g. admin">
  68. </div>
  69. <div class="field">
  70. <label>Sender Domain</label>
  71. <div class="ui labeled input">
  72. <div class="ui basic label">
  73. @
  74. </div>
  75. <input type="text" name="domain" min="1" max="65534" placeholder="E.g. arozos.com">
  76. </div>
  77. </div>
  78. </div>
  79. </div>
  80. <div class="field">
  81. <label>Sender Password</label>
  82. <input type="password" name="password" placeholder="Password of the email account">
  83. <small>Leave empty to use the old password</small>
  84. </div>
  85. <p> <i class="caret down icon"></i> Email for sending account reset link</p>
  86. <div class="field">
  87. <label>Admin / Receiver Address</label>
  88. <input type="text" name="recvAddr" placeholder="E.g. [email protected]">
  89. </div>
  90. <button class="ui basic button" type="submit"><i class="blue save icon"></i> Set SMTP Configs</button>
  91. <button class="ui basic button" onclick="event.preventDefault(); sendTestEmail(this);"><i class="teal mail icon"></i> Send Test Email</button>
  92. </form>
  93. </div>
  94. </div>
  95. <div class="ui bottom attached tab segment utilitiesTabs" data-tab="utiltab2">
  96. <h3> IP Address to CIDR</h3>
  97. <p>No experience with CIDR notations? Here are some tools you can use to make setting up easier.</p>
  98. <div class="ui basic segment">
  99. <h5><i class="chevron down icon"></i> IP Range to CIDR Conversion</h5>
  100. <div class="ui message">
  101. <i class="info circle icon"></i> Note that the CIDR generated here covers additional IP address before or after the given range. If you need more details settings, please use CIDR with a smaller range and add additional IPs for detail range adjustment.
  102. </div>
  103. <div class="ui input">
  104. <input type="text" placeholder="Start IP" id="startIpInput">
  105. </div>
  106. <div class="ui input">
  107. <input type="text" placeholder="End IP" id="endIpInput">
  108. </div>
  109. <br>
  110. <button style="margin-top: 0.6em;" class="ui basic button" onclick="convertToCIDR()">Convert</button>
  111. <p>Results: <div id="cidrOutput">N/A</div></p>
  112. </div>
  113. <div class="ui basic segment">
  114. <h5><i class="chevron down icon"></i> CIDR to IP Range Conversion</h5>
  115. <div class="ui action input">
  116. <input type="text" placeholder="CIDR" id="cidrInput">
  117. <button class="ui basic button" onclick="convertToIPRange()">Convert</button>
  118. </div>
  119. <p>Results: <div id="ipRangeOutput">N/A</div></p>
  120. </div>
  121. <div class="ui divider"></div>
  122. </div>
  123. <div class="ui bottom attached tab segment utilitiesTabs" data-tab="utiltab3">
  124. <!-- Config Tools -->
  125. <h3>System Backup & Restore</h3>
  126. <p>Options related to system backup, migrate and restore.</p>
  127. <button class="ui basic button" onclick="showSideWrapper('snippet/configTools.html');">Open Config Tools</button>
  128. <div class="ui divider"></div>
  129. <!-- System Information -->
  130. <div id="zoraxyinfo">
  131. <h3 class="ui header">
  132. System Information
  133. </h3>
  134. <p>Basic information about this zoraxy host</p>
  135. <table class="ui very basic collapsing celled table">
  136. <tbody>
  137. <tr>
  138. <td>Host UUID</td>
  139. <td class="uuid"></td>
  140. </tr>
  141. <tr>
  142. <td>Version</td>
  143. <td class="version"></td>
  144. </tr>
  145. <tr>
  146. <td>Build</td>
  147. <td class="development"></td>
  148. </tr>
  149. <tr>
  150. <td>Running Since</td>
  151. <td class="boottime"></td>
  152. </tr>
  153. <tr>
  154. <td>ZeroTier Linked</td>
  155. <td class="zt"></td>
  156. </tr>
  157. <tr>
  158. <td>Enable SSH Loopback</td>
  159. <td class="sshlb"></td>
  160. </tr>
  161. </tbody>
  162. </table>
  163. <p>Zoraxy is developed by tobychui for <a href="//imuslab.com" target="_blank">imuslab</a> and open source under <a href="https://www.gnu.org/licenses/agpl-3.0.txt">AGPL</a></p>
  164. </div>
  165. </div>
  166. <br>
  167. </div>
  168. <script>
  169. $('.menu .utils.item').tab();
  170. // Switch tabs when clicking on the menu items
  171. $('.menu .utils.item').on('click', function() {
  172. $('.menu .utils.item').removeClass('active');
  173. $(this).addClass('active');
  174. var tab = $(this).attr('data-tab');
  175. $('.utilitiesTabs.tab.segment').removeClass('active');
  176. $('div[data-tab="' + tab + '"]').addClass('active');
  177. });
  178. /*
  179. Account Password utilities
  180. */
  181. $.get("/api/auth/userCount", function(data){
  182. if (data == 0){
  183. //Using external auth manager. Hide options
  184. $(".selfauthOnly").hide();
  185. $(".extAuthOnly").show();
  186. }
  187. });
  188. $.get("/api/info/x", function(data){
  189. function timeConverter(UNIX_timestamp){
  190. var a = new Date(UNIX_timestamp * 1000);
  191. var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  192. var year = a.getFullYear();
  193. var month = months[a.getMonth()];
  194. var date = a.getDate();
  195. var hour = a.getHours();
  196. var min = a.getMinutes();
  197. var sec = a.getSeconds();
  198. var time = date + ' ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec ;
  199. return time;
  200. }
  201. function secondsToDhms(seconds) {
  202. seconds = Number(seconds);
  203. var d = Math.floor(seconds / (3600*24));
  204. var h = Math.floor(seconds % (3600*24) / 3600);
  205. var m = Math.floor(seconds % 3600 / 60);
  206. var s = Math.floor(seconds % 60);
  207. var dDisplay = d > 0 ? d + (d == 1 ? " day, " : " days, ") : "";
  208. var hDisplay = h > 0 ? h + (h == 1 ? " hour, " : " hours, ") : "";
  209. var mDisplay = m > 0 ? m + (m == 1 ? " minute, " : " minutes, ") : "";
  210. var sDisplay = s > 0 ? s + (s == 1 ? " second" : " seconds") : "";
  211. return dDisplay + hDisplay + mDisplay + sDisplay;
  212. }
  213. console.log(data);
  214. $("#zoraxyinfo .uuid").text(data.NodeUUID);
  215. $("#zoraxyinfo .development").text(data.Development?"Development":"Release");
  216. $("#zoraxyinfo .version").text(data.Version);
  217. $("#zoraxyinfo .boottime").text(timeConverter(data.BootTime) + ` ( ${secondsToDhms(parseInt(Date.now()/1000) - data.BootTime)} ago)`);
  218. $("#zoraxyinfo .zt").html(data.ZerotierConnected?`<i class="ui green check icon"></i> Connected`:`<i class="ui red times icon"></i> Link Error`);
  219. $("#zoraxyinfo .sshlb").html(data.EnableSshLoopback?`<i class="ui yellow exclamation triangle icon"></i> Enabled`:`Disabled`);
  220. });
  221. function changePassword() {
  222. const oldPassword = document.getElementsByName('oldPassword')[0].value;
  223. const newPassword = document.getElementsByName('newPassword')[0].value;
  224. const confirmNewPassword = document.getElementsByName('confirmNewPassword')[0].value;
  225. $.ajax({
  226. type: "POST",
  227. url: "/api/auth/changePassword",
  228. data: {
  229. oldPassword: oldPassword,
  230. newPassword: newPassword,
  231. confirmPassword: confirmNewPassword,
  232. },
  233. success: function (data) {
  234. if (data.error != undefined){
  235. alert(data.error);
  236. }else{
  237. $("#passwordChangeSuccMsg").stop().finish().slideDown("fast").delay(3000).slideUp("fast");
  238. $('[name="oldPassword"]').val('');
  239. $('[name="newPassword"]').val('');
  240. $('[name="confirmNewPassword"]').val('');
  241. }
  242. },
  243. error: function (xhr, status, error) {
  244. alert("Error changing password: " + error);
  245. },
  246. });
  247. }
  248. /*
  249. SMTP Settings
  250. */
  251. //Bind events to the form
  252. $('#email-form').submit(function(e) {
  253. e.preventDefault();
  254. var data = {
  255. hostname: $('input[name=hostname]').val(),
  256. domain: $('input[name=domain]').val(),
  257. port: parseInt($('input[name=port]').val()),
  258. username: $('input[name=username]').val(),
  259. password: $('input[name=password]').val(),
  260. senderAddr: $('input[name=senderAddr]').val(),
  261. adminAddr: $('input[name=recvAddr]').val()
  262. };
  263. var inputValid = validateSMTPInputs();
  264. if (!inputValid){
  265. msgbox("SMTP input not valid", false, 5000);
  266. return;
  267. }
  268. $.ajax({
  269. type: "POST",
  270. url: "/api/tools/smtp/set",
  271. data: data,
  272. success: function(data) {
  273. if (data.error != undefined){
  274. msgbox(data.error, false, 5000);
  275. }else{
  276. msgbox("SMTP Account Updated")
  277. }
  278. },
  279. error: function(xhr, status, error) {
  280. msgbox(xhr.responseText, false, 5000);
  281. }
  282. });
  283. });
  284. function initSMTPSettings(){
  285. $.get("/api/tools/smtp/get", function(data){
  286. $('#email-form input[name=hostname]').val(data.Hostname);
  287. $('#email-form input[name=domain]').val(data.Domain);
  288. $('#email-form input[name=port]').val(data.Port);
  289. $('#email-form input[name=username]').val(data.Username);
  290. $('#email-form input[name=senderAddr]').val(data.SenderAddr);
  291. });
  292. $.get("/api/tools/smtp/admin", function(data){
  293. $('#email-form input[name=recvAddr]').val(data);
  294. });
  295. }
  296. initSMTPSettings();
  297. function sendTestEmail(btn){
  298. $(btn).addClass("loading").addClass("disabled");
  299. $.get("/api/tools/smtp/test", function(data){
  300. if (data.error !== undefined){
  301. msgbox(data.error, false, 5000);
  302. }else{
  303. msgbox("Test Email Sent")
  304. }
  305. $(btn).removeClass("loading").removeClass("disabled");
  306. })
  307. }
  308. function validateSMTPInputs() {
  309. let isValid = true;
  310. const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // email regex pattern
  311. const domainRegex = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i; // domain/subdomain regex pattern
  312. const form = $('#email-form');
  313. // validate hostname
  314. const hostname = form.find('input[name="hostname"]').val().trim();
  315. if (!domainRegex.test(hostname)) {
  316. form.find('input[name="hostname"]').parent().addClass('error');
  317. isValid = false;
  318. } else {
  319. form.find('input[name="hostname"]').parent().removeClass('error');
  320. }
  321. // validate domain, now allow empty string (for smtp that dont use domain as input)
  322. /*
  323. const domain = form.find('input[name="domain"]').val().trim();
  324. if (!domainRegex.test(domain)) {
  325. form.find('input[name="domain"]').parent().addClass('error');
  326. isValid = false;
  327. } else {
  328. form.find('input[name="domain"]').parent().removeClass('error');
  329. }
  330. */
  331. // validate username
  332. const username = form.find('input[name="username"]').val().trim();
  333. if (username === '') {
  334. form.find('input[name="username"]').parent().addClass('error');
  335. isValid = false;
  336. } else {
  337. form.find('input[name="username"]').parent().removeClass('error');
  338. }
  339. // validate password
  340. const password = form.find('input[name="password"]').val().trim();
  341. if (password === '') {
  342. form.find('input[name="password"]').parent().addClass('error');
  343. isValid = false;
  344. } else {
  345. form.find('input[name="password"]').parent().removeClass('error');
  346. }
  347. // validate sender address
  348. const senderAddr = form.find('input[name="senderAddr"]').val().trim();
  349. if (!emailRegex.test(senderAddr)) {
  350. form.find('input[name="senderAddr"]').parent().addClass('error');
  351. isValid = false;
  352. } else {
  353. form.find('input[name="senderAddr"]').parent().removeClass('error');
  354. }
  355. // validate receiver address
  356. const recvAddr = form.find('input[name="recvAddr"]').val().trim();
  357. if (!emailRegex.test(recvAddr)) {
  358. form.find('input[name="recvAddr"]').parent().addClass('error');
  359. isValid = false;
  360. } else {
  361. form.find('input[name="recvAddr"]').parent().removeClass('error');
  362. }
  363. return isValid;
  364. }
  365. /*
  366. IP Address Utilities
  367. */
  368. //events handler
  369. function convertToCIDR() {
  370. const startIp = document.getElementById('startIpInput').value.trim();
  371. const endIp = document.getElementById('endIpInput').value.trim();
  372. const cidrOutput = document.getElementById('cidrOutput');
  373. const cidr = ipRangeToCIDR(startIp, endIp);
  374. const ipRange = cidrToRange(cidr);
  375. cidrOutput.innerHTML = `CIDR: ${cidr} <br> (Cover range: ${ipRange[0]} to ${ipRange[1]})`;
  376. }
  377. // CIDR to IP Range Conversion
  378. function convertToIPRange() {
  379. const cidr = document.getElementById('cidrInput').value.trim();
  380. const ipRangeOutput = document.getElementById('ipRangeOutput');
  381. const ipRange = cidrToRange(cidr);
  382. ipRangeOutput.innerHTML = `Start IP: ${ipRange[0]}<br>End IP: ${ipRange[1]}`;
  383. }
  384. //Ip conversion function
  385. function cidrToRange(cidr) {
  386. var range = [2];
  387. cidr = cidr.split('/');
  388. var cidr_1 = parseInt(cidr[1])
  389. range[0] = long2ip((ip2long(cidr[0])) & ((-1 << (32 - cidr_1))));
  390. start = ip2long(range[0])
  391. range[1] = long2ip( start + Math.pow(2, (32 - cidr_1)) - 1);
  392. return range;
  393. }
  394. function ipRangeToCIDR(ipStart, ipEnd) {
  395. var start = ip2long(ipStart);
  396. var end = ip2long(ipEnd);
  397. var cidr = 32;
  398. while (start != end) {
  399. start >>= 1;
  400. end >>= 1;
  401. cidr--;
  402. }
  403. return ipStart + '/' + cidr;
  404. }
  405. function ip2long (argIP) {
  406. // discuss at: https://locutus.io/php/ip2long/
  407. // original by: Waldo Malqui Silva (https://waldo.malqui.info)
  408. // improved by: Victor
  409. // revised by: fearphage (https://my.opera.com/fearphage/)
  410. // revised by: Theriault (https://github.com/Theriault)
  411. // estarget: es2015
  412. // example 1: ip2long('192.0.34.166')
  413. // returns 1: 3221234342
  414. // example 2: ip2long('0.0xABCDEF')
  415. // returns 2: 11259375
  416. // example 3: ip2long('255.255.255.256')
  417. // returns 3: false
  418. let i = 0
  419. // PHP allows decimal, octal, and hexadecimal IP components.
  420. // PHP allows between 1 (e.g. 127) to 4 (e.g 127.0.0.1) components.
  421. const pattern = new RegExp([
  422. '^([1-9]\\d*|0[0-7]*|0x[\\da-f]+)',
  423. '(?:\\.([1-9]\\d*|0[0-7]*|0x[\\da-f]+))?',
  424. '(?:\\.([1-9]\\d*|0[0-7]*|0x[\\da-f]+))?',
  425. '(?:\\.([1-9]\\d*|0[0-7]*|0x[\\da-f]+))?$'
  426. ].join(''), 'i')
  427. argIP = argIP.match(pattern) // Verify argIP format.
  428. if (!argIP) {
  429. // Invalid format.
  430. return false
  431. }
  432. // Reuse argIP variable for component counter.
  433. argIP[0] = 0
  434. for (i = 1; i < 5; i += 1) {
  435. argIP[0] += !!((argIP[i] || '').length)
  436. argIP[i] = parseInt(argIP[i]) || 0
  437. }
  438. // Continue to use argIP for overflow values.
  439. // PHP does not allow any component to overflow.
  440. argIP.push(256, 256, 256, 256)
  441. // Recalculate overflow of last component supplied to make up for missing components.
  442. argIP[4 + argIP[0]] *= Math.pow(256, 4 - argIP[0])
  443. if (argIP[1] >= argIP[5] ||
  444. argIP[2] >= argIP[6] ||
  445. argIP[3] >= argIP[7] ||
  446. argIP[4] >= argIP[8]) {
  447. return false
  448. }
  449. return argIP[1] * (argIP[0] === 1 || 16777216) +
  450. argIP[2] * (argIP[0] <= 2 || 65536) +
  451. argIP[3] * (argIP[0] <= 3 || 256) +
  452. argIP[4] * 1
  453. }
  454. function long2ip (ip) {
  455. // discuss at: https://locutus.io/php/long2ip/
  456. // original by: Waldo Malqui Silva (https://fayr.us/waldo/)
  457. // example 1: long2ip( 3221234342 )
  458. // returns 1: '192.0.34.166'
  459. if (!isFinite(ip)) {
  460. return false
  461. }
  462. return [ip >>> 24 & 0xFF, ip >>> 16 & 0xFF, ip >>> 8 & 0xFF, ip & 0xFF].join('.')
  463. }
  464. </script>