]>
git.proxmox.com Git - pve-manager.git/blob - www/manager6/node/ACME.js
1 Ext
.define('PVE.node.ACMEAccountCreate', {
2 extend
: 'Proxmox.window.Edit',
3 mixins
: ['Proxmox.Mixin.CBind'],
6 title
: gettext('Register Account'),
9 submitText
: gettext('Register'),
10 url
: '/cluster/acme/account',
16 xtype
: 'proxmoxtextfield',
17 fieldLabel
: gettext('Account Name'),
20 emptyText
: (get) => get('defaultExists') ? '' : 'default',
21 allowBlank
: (get) => !get('defaultExists'),
29 fieldLabel
: gettext('E-Mail'),
32 xtype
: 'proxmoxComboGrid',
37 fieldLabel
: gettext('ACME Directory'),
40 fields
: ['name', 'url'],
44 url
: '/api2/json/cluster/acme/directories',
54 header
: gettext('Name'),
59 header
: gettext('URL'),
66 change: function(combogrid
, value
) {
72 var disp
= me
.up('window').down('#tos_url_display');
73 var field
= me
.up('window').down('#tos_url');
74 var checkbox
= me
.up('window').down('#tos_checkbox');
76 disp
.setValue(gettext('Loading'));
77 field
.setValue(undefined);
78 checkbox
.setValue(undefined);
79 checkbox
.setHidden(true);
81 Proxmox
.Utils
.API2Request({
82 url
: '/cluster/acme/tos',
87 success: function(response
, opt
) {
88 field
.setValue(response
.result
.data
);
89 disp
.setValue(response
.result
.data
);
90 checkbox
.setHidden(false);
92 failure: function(response
, opt
) {
93 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
100 xtype
: 'displayfield',
101 itemId
: 'tos_url_display',
102 renderer
: PVE
.Utils
.render_optional_url
,
103 name
: 'tos_url_display',
111 xtype
: 'proxmoxcheckbox',
112 itemId
: 'tos_checkbox',
113 boxLabel
: gettext('Accept TOS'),
115 validateValue: function(value
) {
116 if (value
&& this.checked
) {
126 Ext
.define('PVE.node.ACMEAccountView', {
127 extend
: 'Proxmox.window.Edit',
134 title
: gettext('Account'),
138 xtype
: 'displayfield',
139 fieldLabel
: gettext('E-Mail'),
143 xtype
: 'displayfield',
144 fieldLabel
: gettext('Created'),
148 xtype
: 'displayfield',
149 fieldLabel
: gettext('Status'),
153 xtype
: 'displayfield',
154 fieldLabel
: gettext('Directory'),
155 renderer
: PVE
.Utils
.render_optional_url
,
159 xtype
: 'displayfield',
160 fieldLabel
: gettext('Terms of Services'),
161 renderer
: PVE
.Utils
.render_optional_url
,
166 initComponent: function() {
169 if (!me
.accountname
) {
170 throw "no account name defined";
173 me
.url
= '/cluster/acme/account/' + me
.accountname
;
177 // hide OK/Reset button, because we just want to show data
178 me
.down('toolbar[dock=bottom]').setVisible(false);
181 success: function(response
) {
182 var data
= response
.result
.data
;
183 data
.email
= data
.account
.contact
[0];
184 data
.createdAt
= data
.account
.createdAt
;
185 data
.status
= data
.account
.status
;
192 Ext
.define('PVE.node.ACMEDomainEdit', {
193 extend
: 'Proxmox.window.Edit',
194 alias
: 'widget.pveACMEDomainEdit',
196 subject
: gettext('Domain'),
199 onlineHelp
: 'sysadmin_certificate_management',
204 onGetValues: function(values
) {
206 let win
= me
.up('pveACMEDomainEdit');
207 let nodeconfig
= win
.nodeconfig
;
208 let olddomain
= win
.domain
|| {};
211 digest
: nodeconfig
.digest
,
214 let configkey
= olddomain
.configkey
;
215 let acmeObj
= PVE
.Parser
.parseACME(nodeconfig
.acme
);
217 if (values
.type
=== 'dns') {
218 if (!olddomain
.configkey
|| olddomain
.configkey
=== 'acme') {
219 // look for first free slot
220 for (let i
= 0; i
< PVE
.Utils
.acmedomain_count
; i
++) {
221 if (nodeconfig
[`acmedomain${i}`] === undefined) {
222 configkey
= `acmedomain${i}`;
226 if (olddomain
.domain
) {
227 // we have to remove the domain from the acme domainlist
228 PVE
.Utils
.remove_domain_from_acme(acmeObj
, olddomain
.domain
);
229 params
.acme
= PVE
.Parser
.printACME(acmeObj
);
234 params
[configkey
] = PVE
.Parser
.printPropertyString(values
, 'domain');
236 if (olddomain
.configkey
&& olddomain
.configkey
!== 'acme') {
237 // delete the old dns entry
238 params
.delete = [olddomain
.configkey
];
241 // add new, remove old and make entries unique
242 PVE
.Utils
.add_domain_to_acme(acmeObj
, values
.domain
);
243 PVE
.Utils
.remove_domain_from_acme(acmeObj
, olddomain
.domain
);
244 params
.acme
= PVE
.Parser
.printACME(acmeObj
);
251 xtype
: 'proxmoxKVComboBox',
253 fieldLabel
: gettext('Challenge Type'),
257 ['standalone', 'HTTP'],
260 validator: function(value
) {
262 let win
= me
.up('pveACMEDomainEdit');
263 let oldconfigkey
= win
.domain
? win
.domain
.configkey
: undefined;
264 let val
= me
.getValue();
265 if (val
=== 'dns' && (!oldconfigkey
|| oldconfigkey
=== 'acme')) {
266 // we have to check if there is a 'acmedomain' slot left
268 for (let i
= 0; i
< PVE
.Utils
.acmedomain_count
; i
++) {
269 if (!win
.nodeconfig
[`acmedomain${i}`]) {
274 return gettext('Only 5 Domains with type DNS can be configured');
281 change: function(cb
, value
) {
283 let view
= me
.up('pveACMEDomainEdit');
284 let pluginField
= view
.down('field[name=plugin]');
285 pluginField
.setDisabled(value
!== 'dns');
286 pluginField
.setHidden(value
!== 'dns');
295 xtype
: 'pveACMEPluginSelector',
302 xtype
: 'proxmoxtextfield',
307 fieldLabel
: gettext('Domain'),
313 initComponent: function() {
317 throw 'no nodename given';
320 if (!me
.nodeconfig
) {
321 throw 'no nodeconfig given';
324 me
.isCreate
= !me
.domain
;
326 me
.domain
= `${me.nodename}.`; // TODO: FQDN of node
329 me
.url
= `/api2/extjs/nodes/${me.nodename}/config`;
334 me
.setValues(me
.domain
);
336 me
.setValues({ domain
: me
.domain
});
341 Ext
.define('pve-acme-domains', {
342 extend
: 'Ext.data.Model',
343 fields
: ['domain', 'type', 'alias', 'plugin', 'configkey'],
344 idProperty
: 'domain',
347 Ext
.define('PVE.node.ACME', {
348 extend
: 'Ext.grid.Panel',
349 alias
: 'widget.pveACMEView',
354 emptyText
: gettext('No Domains configured'),
359 account
: undefined, // the account we display
360 configaccount
: undefined, // the account set in the config
361 accountEditable
: false,
362 accountsAvailable
: false,
366 canOrder
: (get) => !!get('account') && get('domaincount') > 0,
367 editBtnIcon
: (get) => 'fa black fa-' + (get('accountEditable') ? 'check' : 'pencil'),
368 editBtnText
: (get) => get('accountEditable') ? gettext('Apply') : gettext('Edit'),
369 accountTextHidden
: (get) => get('accountEditable') || !get('accountsAvailable'),
370 accountValueHidden
: (get) => !get('accountEditable') || !get('accountsAvailable'),
375 xclass
: 'Ext.app.ViewController',
377 init: function(view
) {
378 let accountSelector
= this.lookup('accountselector');
379 accountSelector
.store
.on('load', this.onAccountsLoad
, this);
382 onAccountsLoad: function(store
, records
, success
) {
384 let vm
= me
.getViewModel();
385 let configaccount
= vm
.get('configaccount');
386 vm
.set('accountsAvailable', records
.length
> 0);
387 if (me
.autoChangeAccount
&& records
.length
> 0) {
388 me
.changeAccount(records
[0].data
.name
, () => {
389 vm
.set('accountEditable', false);
392 me
.autoChangeAccount
= false;
393 } else if (configaccount
) {
394 if (store
.findExact('name', configaccount
) !== -1) {
395 vm
.set('account', configaccount
);
397 vm
.set('account', null);
402 addDomain: function() {
404 let view
= me
.getView();
406 Ext
.create('PVE.node.ACMEDomainEdit', {
407 nodename
: view
.nodename
,
408 nodeconfig
: view
.nodeconfig
,
409 apiCallDone: function() {
415 editDomain: function() {
417 let view
= me
.getView();
419 let selection
= view
.getSelection();
420 if (selection
.length
< 1) return;
422 Ext
.create('PVE.node.ACMEDomainEdit', {
423 nodename
: view
.nodename
,
424 nodeconfig
: view
.nodeconfig
,
425 domain
: selection
[0].data
,
426 apiCallDone: function() {
432 removeDomain: function() {
434 let view
= me
.getView();
435 let selection
= view
.getSelection();
436 if (selection
.length
< 1) return;
438 let rec
= selection
[0].data
;
440 if (rec
.configkey
!== 'acme') {
441 params
.delete = rec
.configkey
;
443 let acme
= PVE
.Parser
.parseACME(view
.nodeconfig
.acme
);
444 PVE
.Utils
.remove_domain_from_acme(acme
, rec
.domain
);
445 params
.acme
= PVE
.Parser
.printACME(acme
);
448 Proxmox
.Utils
.API2Request({
450 url
: `/nodes/${view.nodename}/config`,
452 success: function(response
, opt
) {
455 failure: function(response
, opt
) {
456 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
461 toggleEditAccount: function() {
463 let vm
= me
.getViewModel();
464 let editable
= vm
.get('accountEditable');
466 me
.changeAccount(vm
.get('account'), function() {
467 vm
.set('accountEditable', false);
471 vm
.set('accountEditable', true);
475 changeAccount: function(account
, callback
) {
477 let view
= me
.getView();
480 let acme
= PVE
.Parser
.parseACME(view
.nodeconfig
.acme
);
481 acme
.account
= account
;
482 params
.acme
= PVE
.Parser
.printACME(acme
);
484 Proxmox
.Utils
.API2Request({
487 url
: `/nodes/${view.nodename}/config`,
489 success: function(response
, opt
) {
490 if (Ext
.isFunction(callback
)) {
494 failure: function(response
, opt
) {
495 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
502 let view
= me
.getView();
504 Proxmox
.Utils
.API2Request({
509 url
: `/nodes/${view.nodename}/certificates/acme/certificate`,
510 success: function(response
, opt
) {
511 Ext
.create('Proxmox.window.TaskViewer', {
512 upid
: response
.result
.data
,
513 taskDone: function(success
) {
514 me
.orderFinished(success
);
518 failure: function(response
, opt
) {
519 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
524 orderFinished: function(success
) {
525 if (!success
) return;
526 var txt
= gettext('pveproxy will be restarted with new certificates, please reload the GUI!');
527 Ext
.getBody().mask(txt
, ['pve-static-mask']);
528 // reload after 10 seconds automatically
529 Ext
.defer(function() {
530 window
.location
.reload(true);
536 let view
= me
.getView();
540 addAccount: function() {
542 Ext
.create('PVE.node.ACMEAccountCreate', {
544 taskDone: function() {
546 let accountSelector
= me
.lookup('accountselector');
547 me
.autoChangeAccount
= true;
548 accountSelector
.store
.load();
556 xtype
: 'proxmoxButton',
557 text
: gettext('Add'),
558 handler
: 'addDomain',
562 xtype
: 'proxmoxButton',
563 text
: gettext('Edit'),
565 handler
: 'editDomain',
568 xtype
: 'proxmoxStdRemoveButton',
569 handler
: 'removeDomain',
575 text
: gettext('Order Certificates Now'),
577 disabled
: '{!canOrder}',
583 xtype
: 'displayfield',
584 value
: gettext('Using Account') + ':',
586 hidden
: '{!accountsAvailable}',
590 xtype
: 'displayfield',
591 reference
: 'accounttext',
592 renderer
: (val
) => val
|| Proxmox
.Utils
.NoneText
,
595 hidden
: '{accountTextHidden}',
599 xtype
: 'pveACMEAccountSelector',
601 reference
: 'accountselector',
604 hidden
: '{accountValueHidden}',
609 iconCls
: 'fa black fa-pencil',
611 iconCls
: '{editBtnIcon}',
612 text
: '{editBtnText}',
613 hidden
: '{!accountsAvailable}',
615 handler
: 'toggleEditAccount',
618 xtype
: 'displayfield',
619 value
: gettext('No Account available.'),
621 hidden
: '{accountsAvailable}',
627 reference
: 'accountlink',
628 text
: gettext('Add ACME Account'),
630 hidden
: '{accountsAvailable}',
632 handler
: 'addAccount',
636 updateStore: function(store
, records
, success
) {
640 if (success
&& records
.length
> 0) {
648 me
.nodeconfig
= rec
.data
; // save nodeconfig for updates
650 let account
= 'default';
653 let obj
= PVE
.Parser
.parseACME(rec
.data
.acme
);
654 (obj
.domains
|| []).forEach(domain
=> {
655 if (domain
=== '') return;
665 account
= obj
.account
;
669 let vm
= me
.getViewModel();
670 let oldaccount
= vm
.get('account');
672 // account changed, and we do not edit currently, load again to verify
673 if (oldaccount
!== account
&& !vm
.get('accountEditable')) {
674 vm
.set('configaccount', account
);
675 me
.lookup('accountselector').store
.load();
678 for (let i
= 0; i
< PVE
.Utils
.acmedomain_count
; i
++) {
679 let acmedomain
= rec
.data
[`acmedomain${i}`];
680 if (!acmedomain
) continue;
682 let record
= PVE
.Parser
.parsePropertyString(acmedomain
, 'domain');
684 record
.configkey
= `acmedomain${i}`;
688 vm
.set('domaincount', data
.length
);
689 me
.store
.loadData(data
, false);
693 itemdblclick
: 'editDomain',
700 text
: gettext('Domain'),
705 text
: gettext('Type'),
710 text
: gettext('Plugin'),
714 initComponent: function() {
718 throw "no nodename given";
721 me
.rstore
= Ext
.create('Proxmox.data.UpdateStore', {
724 storeid
: `pve-node-domains-${me.nodename}`,
727 url
: `/api2/json/nodes/${me.nodename}/config`,
731 me
.store
= Ext
.create('Ext.data.Store', {
732 model
: 'pve-acme-domains',
737 me
.mon(me
.rstore
, 'load', 'updateStore', me
);
738 Proxmox
.Utils
.monStoreErrors(me
, me
.rstore
);
739 me
.on('destroy', me
.rstore
.stopUpdate
, me
.rstore
);