form.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. const _ = require('underscore');
  2. const Mn = require('backbone.marionette');
  3. const App = require('../../main');
  4. const CertificateModel = require('../../../models/certificate');
  5. const template = require('./form.ejs');
  6. const i18n = require('../../i18n');
  7. const dns_providers = require('../../../../../global/certbot-dns-plugins');
  8. require('jquery-serializejson');
  9. require('selectize');
  10. module.exports = Mn.View.extend({
  11. template: template,
  12. className: 'modal-dialog',
  13. max_file_size: 102400,
  14. ui: {
  15. form: 'form',
  16. loader_content: '.loader-content',
  17. non_loader_content: '.non-loader-content',
  18. le_error_info: '#le-error-info',
  19. domain_names: 'input[name="domain_names"]',
  20. buttons: '.modal-footer button',
  21. cancel: 'button.cancel',
  22. save: 'button.save',
  23. other_certificate: '#other_certificate',
  24. other_certificate_label: '#other_certificate_label',
  25. other_certificate_key: '#other_certificate_key',
  26. dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
  27. dns_challenge_content: '.dns-challenge',
  28. dns_provider: 'select[name="meta[dns_provider]"]',
  29. credentials_file_content: '.credentials-file-content',
  30. dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
  31. propagation_seconds: 'input[name="meta[propagation_seconds]"]',
  32. other_certificate_key_label: '#other_certificate_key_label',
  33. other_intermediate_certificate: '#other_intermediate_certificate',
  34. other_intermediate_certificate_label: '#other_intermediate_certificate_label'
  35. },
  36. events: {
  37. 'change @ui.dns_challenge_switch': function () {
  38. const checked = this.ui.dns_challenge_switch.prop('checked');
  39. if (checked) {
  40. this.ui.dns_provider.prop('required', 'required');
  41. const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
  42. if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){
  43. this.ui.dns_provider_credentials.prop('required', 'required');
  44. }
  45. this.ui.dns_challenge_content.show();
  46. } else {
  47. this.ui.dns_provider.prop('required', false);
  48. this.ui.dns_provider_credentials.prop('required', false);
  49. this.ui.dns_challenge_content.hide();
  50. }
  51. },
  52. 'change @ui.dns_provider': function () {
  53. const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
  54. if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
  55. this.ui.dns_provider_credentials.prop('required', 'required');
  56. this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
  57. this.ui.credentials_file_content.show();
  58. } else {
  59. this.ui.dns_provider_credentials.prop('required', false);
  60. this.ui.credentials_file_content.hide();
  61. }
  62. },
  63. 'click @ui.save': function (e) {
  64. e.preventDefault();
  65. this.ui.le_error_info.hide();
  66. if (!this.ui.form[0].checkValidity()) {
  67. $('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
  68. $(this).removeClass('btn-loading');
  69. return;
  70. }
  71. let view = this;
  72. let data = this.ui.form.serializeJSON();
  73. data.provider = this.model.get('provider');
  74. let domain_err = false;
  75. if (!data.meta.dns_challenge) {
  76. data.domain_names.split(',').map(function (name) {
  77. if (name.match(/\*/im)) {
  78. domain_err = true;
  79. }
  80. });
  81. }
  82. if (domain_err) {
  83. alert(i18n('ssl', 'no-wildcard-without-dns'));
  84. return;
  85. }
  86. // Manipulate
  87. if (typeof data.meta === 'undefined') data.meta = {};
  88. data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
  89. data.meta.dns_challenge = data.meta.dns_challenge == 1;
  90. if(!data.meta.dns_challenge){
  91. data.meta.dns_provider = undefined;
  92. data.meta.dns_provider_credentials = undefined;
  93. data.meta.propagation_seconds = undefined;
  94. } else {
  95. if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
  96. }
  97. if (typeof data.domain_names === 'string' && data.domain_names) {
  98. data.domain_names = data.domain_names.split(',');
  99. }
  100. let ssl_files = [];
  101. // check files are attached
  102. if (this.model.get('provider') === 'other' && !this.model.hasSslFiles()) {
  103. if (!this.ui.other_certificate[0].files.length || !this.ui.other_certificate[0].files[0].size) {
  104. alert('Certificate file is not attached');
  105. return;
  106. } else {
  107. if (this.ui.other_certificate[0].files[0].size > this.max_file_size) {
  108. alert('Certificate file is too large (> 100kb)');
  109. return;
  110. }
  111. ssl_files.push({name: 'certificate', file: this.ui.other_certificate[0].files[0]});
  112. }
  113. if (!this.ui.other_certificate_key[0].files.length || !this.ui.other_certificate_key[0].files[0].size) {
  114. alert('Certificate key file is not attached');
  115. return;
  116. } else {
  117. if (this.ui.other_certificate_key[0].files[0].size > this.max_file_size) {
  118. alert('Certificate key file is too large (> 100kb)');
  119. return;
  120. }
  121. ssl_files.push({name: 'certificate_key', file: this.ui.other_certificate_key[0].files[0]});
  122. }
  123. if (this.ui.other_intermediate_certificate[0].files.length && this.ui.other_intermediate_certificate[0].files[0].size) {
  124. if (this.ui.other_intermediate_certificate[0].files[0].size > this.max_file_size) {
  125. alert('Intermediate Certificate file is too large (> 100kb)');
  126. return;
  127. }
  128. ssl_files.push({name: 'intermediate_certificate', file: this.ui.other_intermediate_certificate[0].files[0]});
  129. }
  130. }
  131. this.ui.loader_content.show();
  132. this.ui.non_loader_content.hide();
  133. // compile file data
  134. let form_data = new FormData();
  135. if (view.model.get('provider') && ssl_files.length) {
  136. ssl_files.map(function (file) {
  137. form_data.append(file.name, file.file);
  138. });
  139. }
  140. new Promise(resolve => {
  141. if (view.model.get('provider') === 'other') {
  142. resolve(App.Api.Nginx.Certificates.validate(form_data));
  143. } else {
  144. resolve();
  145. }
  146. })
  147. .then(() => {
  148. return App.Api.Nginx.Certificates.create(data);
  149. })
  150. .then(result => {
  151. view.model.set(result);
  152. // Now upload the certs if we need to
  153. if (view.model.get('provider') === 'other') {
  154. return App.Api.Nginx.Certificates.upload(view.model.get('id'), form_data)
  155. .then(result => {
  156. view.model.set('meta', _.assign({}, view.model.get('meta'), result));
  157. });
  158. }
  159. })
  160. .then(() => {
  161. App.UI.closeModal(function () {
  162. App.Controller.showNginxCertificates();
  163. });
  164. })
  165. .catch(err => {
  166. let more_info = '';
  167. if(err.code === 500 && err.debug){
  168. try{
  169. more_info = JSON.parse(err.debug).debug.stack.join("\n");
  170. } catch(e) {}
  171. }
  172. this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>`:''}`;
  173. this.ui.le_error_info.show();
  174. this.ui.le_error_info[0].scrollIntoView();
  175. this.ui.loader_content.hide();
  176. this.ui.non_loader_content.show();
  177. });
  178. },
  179. 'change @ui.other_certificate_key': function(e){
  180. this.setFileName("other_certificate_key_label", e)
  181. },
  182. 'change @ui.other_certificate': function(e){
  183. this.setFileName("other_certificate_label", e)
  184. },
  185. 'change @ui.other_intermediate_certificate': function(e){
  186. this.setFileName("other_intermediate_certificate_label", e)
  187. }
  188. },
  189. setFileName(ui, e){
  190. this.getUI(ui).text(e.target.files[0].name)
  191. },
  192. templateContext: {
  193. getLetsencryptEmail: function () {
  194. return typeof this.meta.letsencrypt_email !== 'undefined' ? this.meta.letsencrypt_email : App.Cache.User.get('email');
  195. },
  196. getLetsencryptAgree: function () {
  197. return typeof this.meta.letsencrypt_agree !== 'undefined' ? this.meta.letsencrypt_agree : false;
  198. },
  199. getUseDnsChallenge: function () {
  200. return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false;
  201. },
  202. getDnsProvider: function () {
  203. return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
  204. },
  205. getDnsProviderCredentials: function () {
  206. return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
  207. },
  208. getPropagationSeconds: function () {
  209. return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
  210. },
  211. dns_plugins: dns_providers,
  212. },
  213. onRender: function () {
  214. this.ui.domain_names.selectize({
  215. delimiter: ',',
  216. persist: false,
  217. maxOptions: 15,
  218. create: function (input) {
  219. return {
  220. value: input,
  221. text: input
  222. };
  223. },
  224. createFilter: /^(?:[^.]+\.?)+[^.]$/
  225. });
  226. this.ui.dns_challenge_content.hide();
  227. this.ui.credentials_file_content.hide();
  228. this.ui.loader_content.hide();
  229. this.ui.le_error_info.hide();
  230. },
  231. initialize: function (options) {
  232. if (typeof options.model === 'undefined' || !options.model) {
  233. this.model = new CertificateModel.Model({provider: 'letsencrypt'});
  234. }
  235. }
  236. });