pve-manager/www/manager6/node/ACME.js
Thomas Lamprecht f6710aac38 ui: eslint: fix trailing comma and comma related whitespaces errors
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-01-19 17:31:03 +01:00

742 lines
16 KiB
JavaScript

Ext.define('PVE.node.ACMEAccountCreate', {
extend: 'Proxmox.window.Edit',
mixins: ['Proxmox.Mixin.CBind'],
width: 450,
title: gettext('Register Account'),
isCreate: true,
method: 'POST',
submitText: gettext('Register'),
url: '/cluster/acme/account',
showTaskViewer: true,
defaultExists: false,
items: [
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Account Name'),
name: 'name',
cbind: {
emptyText: (get) => get('defaultExists') ? '' : 'default',
allowBlank: (get) => !get('defaultExists'),
},
},
{
xtype: 'textfield',
name: 'contact',
vtype: 'email',
allowBlank: false,
fieldLabel: gettext('E-Mail'),
},
{
xtype: 'proxmoxComboGrid',
name: 'directory',
allowBlank: false,
valueField: 'url',
displayField: 'name',
fieldLabel: gettext('ACME Directory'),
store: {
autoLoad: true,
fields: ['name', 'url'],
idProperty: ['name'],
proxy: {
type: 'proxmox',
url: '/api2/json/cluster/acme/directories',
},
sorters: {
property: 'name',
order: 'ASC',
},
},
listConfig: {
columns: [
{
header: gettext('Name'),
dataIndex: 'name',
flex: 1,
},
{
header: gettext('URL'),
dataIndex: 'url',
flex: 1,
},
],
},
listeners: {
change: function(combogrid, value) {
var me = this;
if (!value) {
return;
}
var disp = me.up('window').down('#tos_url_display');
var field = me.up('window').down('#tos_url');
var checkbox = me.up('window').down('#tos_checkbox');
disp.setValue(gettext('Loading'));
field.setValue(undefined);
checkbox.setValue(undefined);
checkbox.setHidden(true);
Proxmox.Utils.API2Request({
url: '/cluster/acme/tos',
method: 'GET',
params: {
directory: value,
},
success: function(response, opt) {
field.setValue(response.result.data);
disp.setValue(response.result.data);
checkbox.setHidden(false);
},
failure: function(response, opt) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
},
});
},
},
},
{
xtype: 'displayfield',
itemId: 'tos_url_display',
renderer: PVE.Utils.render_optional_url,
name: 'tos_url_display',
},
{
xtype: 'hidden',
itemId: 'tos_url',
name: 'tos_url',
},
{
xtype: 'proxmoxcheckbox',
itemId: 'tos_checkbox',
boxLabel: gettext('Accept TOS'),
submitValue: false,
validateValue: function(value) {
if (value && this.checked) {
return true;
}
return false;
},
},
],
});
Ext.define('PVE.node.ACMEAccountView', {
extend: 'Proxmox.window.Edit',
width: 600,
fieldDefaults: {
labelWidth: 140,
},
title: gettext('Account'),
items: [
{
xtype: 'displayfield',
fieldLabel: gettext('E-Mail'),
name: 'email',
},
{
xtype: 'displayfield',
fieldLabel: gettext('Created'),
name: 'createdAt',
},
{
xtype: 'displayfield',
fieldLabel: gettext('Status'),
name: 'status',
},
{
xtype: 'displayfield',
fieldLabel: gettext('Directory'),
renderer: PVE.Utils.render_optional_url,
name: 'directory',
},
{
xtype: 'displayfield',
fieldLabel: gettext('Terms of Services'),
renderer: PVE.Utils.render_optional_url,
name: 'tos',
},
],
initComponent: function() {
var me = this;
if (!me.accountname) {
throw "no account name defined";
}
me.url = '/cluster/acme/account/' + me.accountname;
me.callParent();
// hide OK/Reset button, because we just want to show data
me.down('toolbar[dock=bottom]').setVisible(false);
me.load({
success: function(response) {
var data = response.result.data;
data.email = data.account.contact[0];
data.createdAt = data.account.createdAt;
data.status = data.account.status;
me.setValues(data);
},
});
},
});
Ext.define('PVE.node.ACMEDomainEdit', {
extend: 'Proxmox.window.Edit',
alias: 'widget.pveACMEDomainEdit',
subject: gettext('Domain'),
isCreate: false,
width: 450,
onlineHelp: 'sysadmin_certificate_management',
items: [
{
xtype: 'inputpanel',
onGetValues: function(values) {
let me = this;
let win = me.up('pveACMEDomainEdit');
let nodeconfig = win.nodeconfig;
let olddomain = win.domain || {};
let params = {
digest: nodeconfig.digest,
};
let configkey = olddomain.configkey;
let acmeObj = PVE.Parser.parseACME(nodeconfig.acme);
if (values.type === 'dns') {
if (!olddomain.configkey || olddomain.configkey === 'acme') {
// look for first free slot
for (let i = 0; i < PVE.Utils.acmedomain_count; i++) {
if (nodeconfig[`acmedomain${i}`] === undefined) {
configkey = `acmedomain${i}`;
break;
}
}
if (olddomain.domain) {
// we have to remove the domain from the acme domainlist
PVE.Utils.remove_domain_from_acme(acmeObj, olddomain.domain);
params.acme = PVE.Parser.printACME(acmeObj);
}
}
delete values.type;
params[configkey] = PVE.Parser.printPropertyString(values, 'domain');
} else {
if (olddomain.configkey && olddomain.configkey !== 'acme') {
// delete the old dns entry
params.delete = [olddomain.configkey];
}
// add new, remove old and make entries unique
PVE.Utils.add_domain_to_acme(acmeObj, values.domain);
PVE.Utils.remove_domain_from_acme(acmeObj, olddomain.domain);
params.acme = PVE.Parser.printACME(acmeObj);
}
return params;
},
items: [
{
xtype: 'proxmoxKVComboBox',
name: 'type',
fieldLabel: gettext('Challenge Type'),
allowBlank: false,
value: 'standalone',
comboItems: [
['standalone', 'HTTP'],
['dns', 'DNS'],
],
validator: function(value) {
let me = this;
let win = me.up('pveACMEDomainEdit');
let oldconfigkey = win.domain ? win.domain.configkey : undefined;
let val = me.getValue();
if (val === 'dns' && (!oldconfigkey || oldconfigkey === 'acme')) {
// we have to check if there is a 'acmedomain' slot left
let found = false;
for (let i = 0; i < PVE.Utils.acmedomain_count; i++) {
if (!win.nodeconfig[`acmedomain${i}`]) {
found = true;
}
}
if (!found) {
return gettext('Only 5 Domains with type DNS can be configured');
}
}
return true;
},
listeners: {
change: function(cb, value) {
let me = this;
let view = me.up('pveACMEDomainEdit');
let pluginField = view.down('field[name=plugin]');
pluginField.setDisabled(value !== 'dns');
pluginField.setHidden(value !== 'dns');
},
},
},
{
xtype: 'hidden',
name: 'alias',
},
{
xtype: 'pveACMEPluginSelector',
name: 'plugin',
disabled: true,
hidden: true,
allowBlank: false,
},
{
xtype: 'proxmoxtextfield',
name: 'domain',
allowBlank: false,
vtype: 'DnsName',
value: '',
fieldLabel: gettext('Domain'),
},
],
},
],
initComponent: function() {
let me = this;
if (!me.nodename) {
throw 'no nodename given';
}
if (!me.nodeconfig) {
throw 'no nodeconfig given';
}
me.isCreate = !me.domain;
if (me.isCreate) {
me.domain = `${me.nodename}.`; // TODO: FQDN of node
}
me.url = `/api2/extjs/nodes/${me.nodename}/config`;
me.callParent();
if (!me.isCreate) {
me.setValues(me.domain);
} else {
me.setValues({ domain: me.domain });
}
},
});
Ext.define('pve-acme-domains', {
extend: 'Ext.data.Model',
fields: ['domain', 'type', 'alias', 'plugin', 'configkey'],
idProperty: 'domain',
});
Ext.define('PVE.node.ACME', {
extend: 'Ext.grid.Panel',
alias: 'widget.pveACMEView',
margin: '10 0 0 0',
title: 'ACME',
emptyText: gettext('No Domains configured'),
viewModel: {
data: {
domaincount: 0,
account: undefined, // the account we display
configaccount: undefined, // the account set in the config
accountEditable: false,
accountsAvailable: false,
},
formulas: {
canOrder: (get) => !!get('account') && get('domaincount') > 0,
editBtnIcon: (get) => 'fa black fa-' + (get('accountEditable') ? 'check' : 'pencil'),
accountTextHidden: (get) => get('accountEditable') || !get('accountsAvailable'),
accountValueHidden: (get) => !get('accountEditable') || !get('accountsAvailable'),
},
},
controller: {
xclass: 'Ext.app.ViewController',
init: function(view) {
let accountSelector = this.lookup('accountselector');
accountSelector.store.on('load', this.onAccountsLoad, this);
},
onAccountsLoad: function(store, records, success) {
let me = this;
let vm = me.getViewModel();
let configaccount = vm.get('configaccount');
vm.set('accountsAvailable', records.length > 0);
if (me.autoChangeAccount && records.length > 0) {
me.changeAccount(records[0].data.name, () => {
vm.set('accountEditable', false);
me.reload();
});
me.autoChangeAccount = false;
} else if (configaccount) {
if (store.findExact('name', configaccount) !== -1) {
vm.set('account', configaccount);
} else {
vm.set('account', null);
}
}
},
addDomain: function() {
let me = this;
let view = me.getView();
Ext.create('PVE.node.ACMEDomainEdit', {
nodename: view.nodename,
nodeconfig: view.nodeconfig,
apiCallDone: function() {
me.reload();
},
}).show();
},
editDomain: function() {
let me = this;
let view = me.getView();
let selection = view.getSelection();
if (selection.length < 1) return;
Ext.create('PVE.node.ACMEDomainEdit', {
nodename: view.nodename,
nodeconfig: view.nodeconfig,
domain: selection[0].data,
apiCallDone: function() {
me.reload();
},
}).show();
},
removeDomain: function() {
let me = this;
let view = me.getView();
let selection = view.getSelection();
if (selection.length < 1) return;
let rec = selection[0].data;
let params = {};
if (rec.configkey !== 'acme') {
params.delete = rec.configkey;
} else {
let acme = PVE.Parser.parseACME(view.nodeconfig.acme);
PVE.Utils.remove_domain_from_acme(acme, rec.domain);
params.acme = PVE.Parser.printACME(acme);
}
Proxmox.Utils.API2Request({
method: 'PUT',
url: `/nodes/${view.nodename}/config`,
params,
success: function(response, opt) {
me.reload();
},
failure: function(response, opt) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
},
});
},
toggleEditAccount: function() {
let me = this;
let vm = me.getViewModel();
let editable = vm.get('accountEditable');
if (editable) {
me.changeAccount(vm.get('account'), function() {
vm.set('accountEditable', false);
me.reload();
});
} else {
vm.set('accountEditable', true);
}
},
changeAccount: function(account, callback) {
let me = this;
let view = me.getView();
let params = {};
let acme = PVE.Parser.parseACME(view.nodeconfig.acme);
acme.account = account;
params.acme = PVE.Parser.printACME(acme);
Proxmox.Utils.API2Request({
method: 'PUT',
waitMsgTarget: view,
url: `/nodes/${view.nodename}/config`,
params,
success: function(response, opt) {
if (Ext.isFunction(callback)) {
callback();
}
},
failure: function(response, opt) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
},
});
},
order: function() {
let me = this;
let view = me.getView();
Proxmox.Utils.API2Request({
method: 'POST',
params: {
force: 1,
},
url: `/nodes/${view.nodename}/certificates/acme/certificate`,
success: function(response, opt) {
Ext.create('Proxmox.window.TaskViewer', {
upid: response.result.data,
taskDone: function(success) {
me.orderFinished(success);
},
}).show();
},
failure: function(response, opt) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
},
});
},
orderFinished: function(success) {
if (!success) return;
var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!');
Ext.getBody().mask(txt, ['pve-static-mask']);
// reload after 10 seconds automatically
Ext.defer(function() {
window.location.reload(true);
}, 10000);
},
reload: function() {
let me = this;
let view = me.getView();
view.rstore.load();
},
addAccount: function() {
let me = this;
Ext.create('PVE.node.ACMEAccountCreate', {
autoShow: true,
taskDone: function() {
me.reload();
let accountSelector = me.lookup('accountselector');
me.autoChangeAccount = true;
accountSelector.store.load();
},
});
},
},
tbar: [
{
xtype: 'proxmoxButton',
text: gettext('Add'),
handler: 'addDomain',
selModel: false,
},
{
xtype: 'proxmoxButton',
text: gettext('Edit'),
disabled: true,
handler: 'editDomain',
},
{
xtype: 'proxmoxStdRemoveButton',
handler: 'removeDomain',
},
'-',
{
xtype: 'button',
reference: 'order',
text: gettext('Order Certificates Now'),
bind: {
disabled: '{!canOrder}',
},
handler: 'order',
},
'-',
{
xtype: 'displayfield',
value: gettext('Using Account') + ':',
bind: {
hidden: '{!accountsAvailable}',
},
},
{
xtype: 'displayfield',
reference: 'accounttext',
renderer: (val) => val || Proxmox.Utils.NoneText,
bind: {
value: '{account}',
hidden: '{accountTextHidden}',
},
},
{
xtype: 'pveACMEAccountSelector',
hidden: true,
reference: 'accountselector',
bind: {
value: '{account}',
hidden: '{accountValueHidden}',
},
},
{
xtype: 'button',
iconCls: 'fa black fa-pencil',
baseCls: 'x-plain',
userCls: 'pointer',
bind: {
iconCls: '{editBtnIcon}',
hidden: '{!accountsAvailable}',
},
handler: 'toggleEditAccount',
},
{
xtype: 'displayfield',
value: gettext('No Account available.'),
bind: {
hidden: '{accountsAvailable}',
},
},
{
xtype: 'button',
hidden: true,
reference: 'accountlink',
text: gettext('Add ACME Account'),
bind: {
hidden: '{accountsAvailable}',
},
handler: 'addAccount',
},
],
updateStore: function(store, records, success) {
let me = this;
let data = [];
let rec;
if (success && records.length > 0) {
rec = records[0];
} else {
rec = {
data: {},
};
}
me.nodeconfig = rec.data; // save nodeconfig for updates
let account = 'default';
if (rec.data.acme) {
let obj = PVE.Parser.parseACME(rec.data.acme);
(obj.domains || []).forEach(domain => {
if (domain === '') return;
let record = {
domain,
type: 'standalone',
configkey: 'acme',
};
data.push(record);
});
if (obj.account) {
account = obj.account;
}
}
let vm = me.getViewModel();
let oldaccount = vm.get('account');
// account changed, and we do not edit currently, load again to verify
if (oldaccount !== account && !vm.get('accountEditable')) {
vm.set('configaccount', account);
me.lookup('accountselector').store.load();
}
for (let i = 0; i < PVE.Utils.acmedomain_count; i++) {
let acmedomain = rec.data[`acmedomain${i}`];
if (!acmedomain) continue;
let record = PVE.Parser.parsePropertyString(acmedomain, 'domain');
record.type = 'dns';
record.configkey = `acmedomain${i}`;
data.push(record);
}
vm.set('domaincount', data.length);
me.store.loadData(data, false);
},
listeners: {
itemdblclick: 'editDomain',
},
columns: [
{
dataIndex: 'domain',
flex: 5,
text: gettext('Domain'),
},
{
dataIndex: 'type',
flex: 1,
text: gettext('Type'),
},
{
dataIndex: 'plugin',
flex: 1,
text: gettext('Plugin'),
},
],
initComponent: function() {
var me = this;
if (!me.nodename) {
throw "no nodename given";
}
me.rstore = Ext.create('Proxmox.data.UpdateStore', {
interval: 10 * 1000,
autoStart: true,
storeid: `pve-node-domains-${me.nodename}`,
proxy: {
type: 'proxmox',
url: `/api2/json/nodes/${me.nodename}/config`,
},
});
me.store = Ext.create('Ext.data.Store', {
model: 'pve-acme-domains',
sorters: 'domain',
});
me.callParent();
me.mon(me.rstore, 'load', 'updateStore', me);
Proxmox.Utils.monStoreErrors(me, me.rstore);
me.on('destroy', me.rstore.stopUpdate, me.rstore);
},
});