form.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. const Mn = require('backbone.marionette');
  2. const App = require('../../main');
  3. const DeadHostModel = require('../../../models/dead-host');
  4. const template = require('./form.ejs');
  5. const certListItemTemplate = require('../certificates-list-item.ejs');
  6. const Helpers = require('../../../lib/helpers');
  7. require('jquery-serializejson');
  8. require('selectize');
  9. module.exports = Mn.View.extend({
  10. template: template,
  11. className: 'modal-dialog',
  12. ui: {
  13. form: 'form',
  14. domain_names: 'input[name="domain_names"]',
  15. buttons: '.modal-footer button',
  16. cancel: 'button.cancel',
  17. save: 'button.save',
  18. certificate_select: 'select[name="certificate_id"]',
  19. ssl_forced: 'input[name="ssl_forced"]',
  20. hsts_enabled: 'input[name="hsts_enabled"]',
  21. hsts_subdomains: 'input[name="hsts_subdomains"]',
  22. http2_support: 'input[name="http2_support"]',
  23. cloudflare_switch: 'input[name="meta[cloudflare_use]"]',
  24. cloudflare_token: 'input[name="meta[cloudflare_token]"',
  25. cloudflare: '.cloudflare',
  26. letsencrypt: '.letsencrypt'
  27. },
  28. events: {
  29. 'change @ui.certificate_select': function () {
  30. let id = this.ui.certificate_select.val();
  31. if (id === 'new') {
  32. this.ui.letsencrypt.show().find('input').prop('disabled', false);
  33. this.ui.cloudflare.hide();
  34. } else {
  35. this.ui.letsencrypt.hide().find('input').prop('disabled', true);
  36. }
  37. let enabled = id === 'new' || parseInt(id, 10) > 0;
  38. let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
  39. inputs
  40. .prop('disabled', !enabled)
  41. .parents('.form-group')
  42. .css('opacity', enabled ? 1 : 0.5);
  43. if (!enabled) {
  44. inputs.prop('checked', false);
  45. }
  46. inputs.trigger('change');
  47. },
  48. 'change @ui.ssl_forced': function () {
  49. let checked = this.ui.ssl_forced.prop('checked');
  50. this.ui.hsts_enabled
  51. .prop('disabled', !checked)
  52. .parents('.form-group')
  53. .css('opacity', checked ? 1 : 0.5);
  54. if (!checked) {
  55. this.ui.hsts_enabled.prop('checked', false);
  56. }
  57. this.ui.hsts_enabled.trigger('change');
  58. },
  59. 'change @ui.hsts_enabled': function () {
  60. let checked = this.ui.hsts_enabled.prop('checked');
  61. this.ui.hsts_subdomains
  62. .prop('disabled', !checked)
  63. .parents('.form-group')
  64. .css('opacity', checked ? 1 : 0.5);
  65. if (!checked) {
  66. this.ui.hsts_subdomains.prop('checked', false);
  67. }
  68. },
  69. 'change @ui.cloudflare_switch': function() {
  70. let checked = this.ui.cloudflare_switch.prop('checked');
  71. if (checked) {
  72. this.ui.cloudflare_token.prop('required', 'required');
  73. this.ui.cloudflare.show();
  74. } else {
  75. this.ui.cloudflare_token.prop('required', false);
  76. this.ui.cloudflare.hide();
  77. }
  78. },
  79. 'click @ui.save': function (e) {
  80. e.preventDefault();
  81. if (!this.ui.form[0].checkValidity()) {
  82. $('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
  83. return;
  84. }
  85. let view = this;
  86. let data = this.ui.form.serializeJSON();
  87. // Manipulate
  88. data.hsts_enabled = !!data.hsts_enabled;
  89. data.hsts_subdomains = !!data.hsts_subdomains;
  90. data.http2_support = !!data.http2_support;
  91. data.ssl_forced = !!data.ssl_forced;
  92. if (typeof data.domain_names === 'string' && data.domain_names) {
  93. data.domain_names = data.domain_names.split(',');
  94. }
  95. // Check for any domain names containing wildcards, which are not allowed with letsencrypt
  96. if (data.certificate_id === 'new') {
  97. let domain_err = false;
  98. if (!data.meta.cloudflare_use) {
  99. data.domain_names.map(function (name) {
  100. if (name.match(/\*/im)) {
  101. domain_err = true;
  102. }
  103. });
  104. }
  105. if (domain_err) {
  106. alert('Cannot request Let\'s Encrypt Certificate for wildcard domains without CloudFlare DNS.');
  107. return;
  108. }
  109. data.meta.cloudflare_use = data.meta.cloudflare_use === '1';
  110. data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
  111. } else {
  112. data.certificate_id = parseInt(data.certificate_id, 10);
  113. }
  114. let method = App.Api.Nginx.DeadHosts.create;
  115. let is_new = true;
  116. if (this.model.get('id')) {
  117. // edit
  118. is_new = false;
  119. method = App.Api.Nginx.DeadHosts.update;
  120. data.id = this.model.get('id');
  121. }
  122. this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
  123. this.ui.save.addClass('btn-loading');
  124. method(data)
  125. .then(result => {
  126. view.model.set(result);
  127. App.UI.closeModal(function () {
  128. if (is_new) {
  129. App.Controller.showNginxDead();
  130. }
  131. });
  132. })
  133. .catch(err => {
  134. alert(err.message);
  135. this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
  136. this.ui.save.removeClass('btn-loading');
  137. });
  138. }
  139. },
  140. templateContext: {
  141. getLetsencryptEmail: function () {
  142. return App.Cache.User.get('email');
  143. }
  144. },
  145. onRender: function () {
  146. let view = this;
  147. // Domain names
  148. this.ui.domain_names.selectize({
  149. delimiter: ',',
  150. persist: false,
  151. maxOptions: 15,
  152. create: function (input) {
  153. return {
  154. value: input,
  155. text: input
  156. };
  157. },
  158. createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/
  159. });
  160. // Certificates
  161. this.ui.letsencrypt.hide();
  162. this.ui.certificate_select.selectize({
  163. valueField: 'id',
  164. labelField: 'nice_name',
  165. searchField: ['nice_name', 'domain_names'],
  166. create: false,
  167. preload: true,
  168. allowEmptyOption: true,
  169. render: {
  170. option: function (item) {
  171. item.i18n = App.i18n;
  172. item.formatDbDate = Helpers.formatDbDate;
  173. return certListItemTemplate(item);
  174. }
  175. },
  176. load: function (query, callback) {
  177. App.Api.Nginx.Certificates.getAll()
  178. .then(rows => {
  179. callback(rows);
  180. })
  181. .catch(err => {
  182. console.error(err);
  183. callback();
  184. });
  185. },
  186. onLoad: function () {
  187. view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id'));
  188. }
  189. });
  190. },
  191. initialize: function (options) {
  192. if (typeof options.model === 'undefined' || !options.model) {
  193. this.model = new DeadHostModel.Model();
  194. }
  195. }
  196. });