find-webclient.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. function loadScript(src) {
  2. return new Promise((resolve, reject) => {
  3. const s = document.createElement('script');
  4. s.src = src;
  5. s.onload = resolve;
  6. s.onerror = reject;
  7. document.head.appendChild(s);
  8. });
  9. }
  10. async function createApi() {
  11. await loadScript('qrc:///qtwebchannel/qwebchannel.js');
  12. const channel = await new Promise((resolve) => {
  13. /*global QWebChannel */
  14. new QWebChannel(window.qt.webChannelTransport, resolve);
  15. });
  16. return channel.objects;
  17. }
  18. window.apiPromise = createApi();
  19. async function tryConnect(server) {
  20. document.getElementById('connect-button').disabled = true;
  21. try {
  22. if (!server.startsWith("http")) {
  23. server = "http://" + server;
  24. }
  25. const url = new URL("/System/Info/Public", server);
  26. const response = await fetch(url);
  27. if (response.ok && (await response.json()).Id) {
  28. const htmlResponse = await fetch(server);
  29. if (!htmlResponse.ok) {
  30. throw new Error("Status not ok");
  31. }
  32. if (response.headers.get("content-security-policy")) {
  33. // Sigh... If we just navigate to the URL, the server's CSP will block us loading other resources.
  34. // So we have to parse the HTML, set a new base href, and then write it back to the page.
  35. // We also have to override the history functions to make sure they use the correct URL.
  36. console.log("Using CSP workaround");
  37. const webUrl = htmlResponse.url.replace(/\/[^\/]*$/, "/");
  38. const realUrl = window.location.href;
  39. const html = await htmlResponse.text();
  40. const parser = new DOMParser();
  41. const doc = parser.parseFromString(html, "text/html");
  42. const base = doc.createElement("base");
  43. base.href = webUrl
  44. doc.head.insertBefore(base, doc.head.firstChild);
  45. const oldPushState = window.history.pushState;
  46. window.history.pushState = function(state, title, url) {
  47. url = (new URL(url, realUrl)).toString();
  48. return oldPushState.call(window.history, state, title, url);
  49. };
  50. const oldReplaceState = window.history.replaceState;
  51. window.history.replaceState = function(state, title, url) {
  52. url = (new URL(url, realUrl)).toString();
  53. return oldReplaceState.call(window.history, state, title, url);
  54. };
  55. document.open();
  56. document.write((new XMLSerializer()).serializeToString(doc));
  57. document.close();
  58. } else {
  59. console.log("Using normal navigation");
  60. window.location = server;
  61. }
  62. const api = await window.apiPromise;
  63. await new Promise(resolve => {
  64. api.settings.setValue('main', 'userWebClient', server, resolve);
  65. });
  66. return true;
  67. }
  68. } catch (e) {
  69. console.error(e);
  70. document.getElementById('connect-button').disabled = false;
  71. return false;
  72. }
  73. }
  74. document.getElementById('connect-form').addEventListener('submit', async (e) => {
  75. e.preventDefault();
  76. const server = document.getElementById('address').value;
  77. const result = await tryConnect(server);
  78. if (!result) {
  79. document.getElementById('backdrop').style.display = 'block';
  80. }
  81. });
  82. document.getElementById('connect-fail-button').addEventListener('click', () => {
  83. document.getElementById('backdrop').style.display = 'none';
  84. });
  85. // load the server if we have one
  86. (async() => {
  87. const api = await window.apiPromise;
  88. const savedServer = await new Promise(resolve => {
  89. api.settings.value('main', 'userWebClient', resolve);
  90. });
  91. if (!savedServer || !(await tryConnect(savedServer))) {
  92. document.getElementById('splash').style.display = 'none';
  93. document.getElementById('main').style.display = 'block';
  94. }
  95. })();