UIWindowLogin.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /**
  2. * Copyright (C) 2024 Puter Technologies Inc.
  3. *
  4. * This file is part of Puter.
  5. *
  6. * Puter is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published
  8. * by the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import UIWindow from './UIWindow.js'
  20. import UIWindowSignup from './UIWindowSignup.js'
  21. import UIWindowRecoverPassword from './UIWindowRecoverPassword.js'
  22. async function UIWindowLogin(options){
  23. options = options ?? {};
  24. options.reload_on_success = options.reload_on_success ?? false;
  25. options.has_head = options.has_head ?? true;
  26. options.send_confirmation_code = options.send_confirmation_code ?? false;
  27. options.show_password = options.show_password ?? false;
  28. return new Promise(async (resolve) => {
  29. const internal_id = window.uuidv4();
  30. let h = ``;
  31. h += `<div style="max-width: 500px; min-width: 340px;">`;
  32. if(!options.has_head && options.show_close_button !== false)
  33. h += `<div class="generic-close-window-button"> &times; </div>`;
  34. h += `<div style="padding: 20px; border-bottom: 1px solid #ced7e1; width: 100%; box-sizing: border-box;">`;
  35. // title
  36. h += `<h1 class="login-form-title">${i18n('log_in')}</h1>`;
  37. // login form
  38. h += `<form class="login-form">`;
  39. // error msg
  40. h += `<div class="login-error-msg"></div>`;
  41. // username/email
  42. h += `<div style="overflow: hidden;">`;
  43. h += `<label for="email_or_username-${internal_id}">${i18n('email_or_username')}</label>`;
  44. h += `<input id="email_or_username-${internal_id}" class="email_or_username" type="text" name="email_or_username" spellcheck="false" autocorrect="off" autocapitalize="off" data-gramm_editor="false" autocomplete="username"/>`;
  45. h += `</div>`;
  46. // password with conditional type based based on options.show_password
  47. h += `<div style="overflow: hidden; margin-top: 20px; margin-bottom: 20px; position: relative;">`;
  48. h += `<label for="password-${internal_id}">${i18n('password')}</label>`;
  49. h += `<input id="password-${internal_id}" class="password" type="${options.show_password ? "text" : "password"}" name="password" autocomplete="current-password"/>`;
  50. // show/hide icon
  51. h += `<span style="position: absolute; right: 5%; top: 50%; cursor: pointer;" id="toggle-show-password-${internal_id}">
  52. <img class="toggle-show-password-icon" src="${options.show_password ? window.icons["eye-closed.svg"] : window.icons["eye-open.svg"]}" width="20" height="20">
  53. </span>`;
  54. h += `</div>`;
  55. // login
  56. h += `<button class="login-btn button button-primary button-block button-normal">${i18n('log_in')}</button>`;
  57. // password recovery
  58. h += `<p style="text-align:center; margin-bottom: 0;"><span class="forgot-password-link">${i18n('forgot_pass_c2a')}</span></p>`;
  59. h += `</form>`;
  60. h += `</div>`;
  61. // create account link
  62. if(options.show_signup_button === undefined || options.show_signup_button){
  63. h += `<div class="c2a-wrapper" style="padding:20px;">`;
  64. h += `<button class="signup-c2a-clickable">${i18n('create_free_account')}</button>`;
  65. h += `</div>`;
  66. }
  67. h += `</div>`;
  68. const el_window = await UIWindow({
  69. title: null,
  70. app: 'login',
  71. single_instance: true,
  72. icon: null,
  73. uid: null,
  74. is_dir: false,
  75. body_content: h,
  76. has_head: true,
  77. selectable_body: false,
  78. draggable_body: false,
  79. allow_context_menu: false,
  80. is_draggable: options.is_draggable ?? true,
  81. is_droppable: false,
  82. is_resizable: false,
  83. stay_on_top: false,
  84. allow_native_ctxmenu: true,
  85. allow_user_select: true,
  86. ...options.window_options,
  87. width: 350,
  88. dominant: true,
  89. on_close: ()=>{
  90. resolve(false)
  91. },
  92. onAppend: function(this_window){
  93. $(this_window).find(`.email_or_username`).get(0).focus({preventScroll:true});
  94. },
  95. window_class: 'window-login',
  96. window_css:{
  97. height: 'initial',
  98. },
  99. body_css: {
  100. width: 'initial',
  101. padding: '0',
  102. 'background-color': 'rgb(255 255 255)',
  103. 'backdrop-filter': 'blur(3px)',
  104. 'display': 'flex',
  105. 'flex-direction': 'column',
  106. 'justify-content': 'center',
  107. 'align-items': 'center',
  108. }
  109. })
  110. $(el_window).find('.forgot-password-link').on('click', function(e){
  111. UIWindowRecoverPassword({
  112. window_options: {
  113. backdrop: true,
  114. close_on_backdrop_click: false,
  115. }
  116. });
  117. })
  118. $(el_window).find('.login-btn').on('click', function(e){
  119. const email_username = $(el_window).find('.email_or_username').val();
  120. const password = $(el_window).find('.password').val();
  121. let data;
  122. if(is_email(email_username)){
  123. data = JSON.stringify({
  124. email: email_username,
  125. password: password
  126. })
  127. }else{
  128. data = JSON.stringify({
  129. username: email_username,
  130. password: password
  131. })
  132. }
  133. $(el_window).find('.login-error-msg').hide();
  134. let headers = {};
  135. if(window.custom_headers)
  136. headers = window.custom_headers;
  137. $.ajax({
  138. url: gui_origin + "/login",
  139. type: 'POST',
  140. async: false,
  141. headers: headers,
  142. contentType: "application/json",
  143. data: data,
  144. success: function (data){
  145. update_auth_data(data.token, data.user);
  146. if(options.reload_on_success){
  147. window.onbeforeunload = null;
  148. window.location.replace('/');
  149. }else
  150. resolve(true);
  151. $(el_window).close();
  152. },
  153. error: function (err){
  154. const $errorMessage = $(el_window).find('.login-error-msg');
  155. if (err.status === 404) {
  156. // Don't include the whole 404 page
  157. $errorMessage.html(`Error 404: "${gui_origin}/login" not found`);
  158. } else if (err.responseText) {
  159. $errorMessage.html(err.responseText);
  160. } else {
  161. // No message was returned. *Probably* this means we couldn't reach the server.
  162. // If this is a self-hosted instance, it's probably a configuration issue.
  163. if (app_domain !== 'puter.com') {
  164. $errorMessage.html(`<div style="text-align: left;">
  165. <p>Error reaching "${gui_origin}/login". This is likely to be a configuration issue.</p>
  166. <p>Make sure of the following:</p>
  167. <ul style="padding-left: 2em;">
  168. <li><code>domain</code> in config.json is set to the domain you're using to access puter</li>
  169. <li>DNS resolves for the domain, and the <code>api.</code> subdomain on that domain</li>
  170. <li><code>http_port</code> is set to the port Puter is listening on (<code>auto</code> will use <code>4100</code> unless that port is in use)</li>
  171. <li><code>pub_port</code> is set to the external port (ex: <code>443</code> if you're using a reverse proxy that serves over https)</li>
  172. </ul>
  173. </div>`);
  174. } else {
  175. $errorMessage.html(`Failed to log in: Error ${err.status}`);
  176. }
  177. }
  178. $(el_window).find('.login-error-msg').fadeIn();
  179. }
  180. });
  181. })
  182. $(el_window).find('.login-form').on('submit', function(e){
  183. e.preventDefault();
  184. e.stopPropagation();
  185. return false;
  186. })
  187. $(el_window).find('.signup-c2a-clickable').on('click', async function(e){
  188. //destroy this window
  189. $(el_window).close();
  190. // create Signup window
  191. const signup = await UIWindowSignup({
  192. referrer: options.referrer,
  193. show_close_button: options.show_close_button,
  194. reload_on_success: options.reload_on_success,
  195. window_options: options.window_options,
  196. send_confirmation_code: options.send_confirmation_code,
  197. });
  198. if(signup)
  199. resolve(true);
  200. })
  201. $(el_window).find(`#toggle-show-password-${internal_id}`).on("click", function (e) {
  202. options.show_password = !options.show_password;
  203. // hide/show password and update icon
  204. $(el_window).find(".password").attr("type", options.show_password ? "text" : "password");
  205. $(el_window).find(".toggle-show-password-icon").attr("src", options.show_password ? window.icons["eye-closed.svg"] : window.icons["eye-open.svg"],
  206. )
  207. })
  208. })
  209. }
  210. export default UIWindowLogin