[pve-devel] [PATCH manager 1/2] add node/ACME.js
Thomas Lamprecht
t.lamprecht at proxmox.com
Mon Apr 30 14:25:31 CEST 2018
On 4/25/18 11:41 AM, Dominik Csapak wrote:
> this provides the grid for editing domains for letsencrypt,
> order/renew the certificates, and the window for creating an
> ACME account
>
> Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
> ---
> www/manager6/Makefile | 1 +
> www/manager6/Parser.js | 29 ++++
> www/manager6/node/ACME.js | 328 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 358 insertions(+)
> create mode 100644 www/manager6/node/ACME.js
>
> diff --git a/www/manager6/Makefile b/www/manager6/Makefile
> index 60e8103e..c29824bf 100644
> --- a/www/manager6/Makefile
> +++ b/www/manager6/Makefile
> @@ -97,6 +97,7 @@ JSSRC= \
> node/StatusView.js \
> node/Summary.js \
> node/Subscription.js \
> + node/ACME.js \
> node/Config.js \
> window/Migrate.js \
> window/BulkAction.js \
> diff --git a/www/manager6/Parser.js b/www/manager6/Parser.js
> index 8253bd80..13dce766 100644
> --- a/www/manager6/Parser.js
> +++ b/www/manager6/Parser.js
> @@ -5,6 +5,35 @@ Ext.define('PVE.Parser', { statics: {
>
> // this class only contains static functions
>
> + parseACME: function(value) {
> + if (!value) {
> + return;
> + }
> +
> + var res = {};
> + var errors = false;
> +
> + Ext.Array.each(value.split(','), function(p) {
> + if (!p || p.match(/^\s*$/)) {
> + return; //continue
> + }
> +
> + var match_res;
> + if ((match_res = p.match(/^(?:domains=)?((?:[a-zA-Z0-9\-\.]+[;, ]?)+)$/)) !== null) {
> + res.domains = match_res[1].split(/[;, ]/);
> + } else {
> + errors = true;
> + return false;
> + }
> + });
> +
> + if (errors || !res) {
> + return;
> + }
> +
> + return res;
> + },
> +
> parseBoolean: function(value, default_value) {
> if (!Ext.isDefined(value)) {
> return default_value;
> diff --git a/www/manager6/node/ACME.js b/www/manager6/node/ACME.js
> new file mode 100644
> index 00000000..fc6fbd50
> --- /dev/null
> +++ b/www/manager6/node/ACME.js
> @@ -0,0 +1,328 @@
> +Ext.define('PVE.node.ACMEEditor', {
> + extend: 'Proxmox.window.Edit',
> + xtype: 'pveACMEEditor',
> +
> + subject: gettext('Domains'),
> + items: [
> + {
> + xtype: 'inputpanel',
> + items: [
> + {
> + xtype: 'textfield',
I'd make this a text area and give the user information
that he can add multiple domains through newline separation,
either as emptyText or as tooltip.
> + fieldLabel: gettext('Domains'),
> + emptyText: Proxmox.Utils.noneText,
> + name: 'domains'
> + }
> + ],
> + onGetValues: function(values) {
> + if (!values.domains) {
> + return {
> + 'delete': 'acme'
> + };
> + }
> + var domains = values.domains.split(/[,; ]/).join(';');
> + return {
> + 'acme': 'domains=' + domains
> + };
> + }
> + }
> + ],
> +
> + initComponent: function() {
> + var me = this;
> + me.callParent();
> +
> + me.load({
> + success: function(response, opts) {
> + var res = PVE.Parser.parseACME(response.result.data.acme);
> + if (res) {
> + res.domains = res.domains.join(' ');
> + me.setValues(res);
> + }
> + }
> + });
> + }
> +});
> +
> +Ext.define('PVE.node.ACMEAccountCreate', {
> + extend: 'Proxmox.window.Edit',
> +
> + width: 400,
> + title: gettext('Register Account'),
> + isCreate: true,
> + method: 'POST',
> + submitText: gettext('Register'),
> + url: '/cluster/acme/account',
> + showTaskViewer: true,
> +
> + items: [
> + {
> + 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);
> +
> + Proxmox.Utils.API2Request({
> + url: '/cluster/acme/tos',
> + method: 'GET',
> + params: {
> + directory: value
> + },
> + success: function(response, opt) {
> + me.up('window').down('#tos_url').setValue(response.result.data);
> + me.up('window').down('#tos_url_display').setValue(response.result.data);
> + },
> + failure: function(response, opt) {
> + Ext.Msg.alert(gettext('Error'), response.htmlStatus);
> + }
> + });
> + }
> + }
> + },
> + {
> + xtype: 'displayfield',
> + itemId: 'tos_url_display',
> + fieldLabel: gettext('Terms of Service'),
> + name: 'tos_url_display'
> + },
I'd add a fa-external-link icon add the end, either if https?://
is detected or always (as currently we only get url here (as your
field name suggests ;) ))
> + {
> + xtype: 'hidden',
> + itemId: 'tos_url',
> + name: 'tos_url'
> + },
> + {
> + xtype: 'proxmoxcheckbox',
> + itemId: 'tos_checkbox',
> + fieldLabel: gettext('Accept TOS'),
> + submitValue: false,
> + validateValue: function(value) {
> + if (value && this.checked) {
> + return true;
> + }
> + return false;
> + }
> + },
> + {
> + xtype: 'textfield',
> + name: 'contact',
> + vtype: 'email',
> + allowBlank: false,
> + fieldLabel: gettext('E-Mail')
> + }
> + ]
> +
> +});
> +
> +Ext.define('PVE.node.ACME', {
> + extend: 'Proxmox.grid.ObjectGrid',
> + xtype: 'pveACMEView',
> +
> + margin: '10 0 0 0',
> + title: 'ACME',
> +
> + tbar: [
> + {
> + xtype: 'button',
> + itemId: 'edit',
> + text: gettext('Edit'),
> + handler: function() {
> + this.up('grid').run_editor();
> + }
Edit what?
Here, currently, it's not quite clear what will get edited if
I just opened this panel, as no row must be selected, currently
there's only the domain field to edit so maybe change it to:
'Edit Domains'.
> + },
> + {
> + xtype: 'button',
> + itemId: 'createaccount',
> + text: gettext('Register Account'),
> + handler: function() {
> + var me = this.up('grid');
> + var win = Ext.create('PVE.node.ACMEAccountCreate', {
> + taskDone: function() {
> + me.reload();
> + }
> + });
> + win.show();
> + }
I would give some (minimal) feedback about an created account.
> + },
> + {
> + xtype: 'button',
> + itemId: 'order',
> + text: gettext('Order Certificate'),
> + handler: function() {
> + var me = this.up('grid');
> +
> + Proxmox.Utils.API2Request({
> + method: 'POST',
> + params: {
> + force: 1
> + },
> + url: '/nodes/' + me.nodename + '/certificates/acme/certificate',
> + success: function(response, opt) {
> + var win = Ext.create('Proxmox.window.TaskViewer', {
> + upid: response.result.data
> + });
> + win.show();
> + },
> + failure: function(response, opt) {
> + Ext.Msg.alert(gettext('Error'), response.htmlStatus);
> + }
> + });
> + }
> + },
> + {
> + xtype: 'button',
> + itemId: 'renew',
> + text: gettext('Renew Certificate'),
> + handler: function() {
> + var me = this.up('grid');
> + console.log('test');
> + Proxmox.Utils.API2Request({
> + method: 'PUT',
> + params: {
> + force: 1
> + },
> + url: '/nodes/' + me.nodename + '/certificates/acme/certificate',
> + success: function(response, opt) {
> + console.log('tes2');
> + var win = Ext.create('Proxmox.window.TaskViewer', {
> + upid: response.result.data
> + });
> + win.show();
> + },
> + failure: function(response, opt) {
> + Ext.Msg.alert(gettext('Error'), response.htmlStatus);
> + }
> + });
> + }
> + }
> + ],
> +
> + set_button_status: function() {
> + var me = this;
> +
> + var account = !!me.account;
> + var acmeObj = PVE.Parser.parseACME(me.getObjectValue('acme'));
> + var domains = acmeObj ? acmeObj.domains.length : 0;
> +
> + var order = me.down('#order');
> + var renew = me.down('#renew');
> + order.setVisible(account);
> + order.setDisabled(!account || !domains);
> + renew.setVisible(account);
> + renew.setDisabled(!account || !domains);
> +
> +
> + me.down('#createaccount').setVisible(!account);
> + },
> +
> + load_account: function() {
> + var me = this;
> +
> + // for now we only use the 'default' account
> + Proxmox.Utils.API2Request({
> + url: '/cluster/acme/account/default',
> + success: function(response, opt) {
> + me.account = response.result.data;
> + me.set_button_status();
> + },
> + failure: function(response, opt) {
> + me.account = undefined;
> + me.set_button_status();
> + }
> + });
> + },
> +
> + run_editor: function() {
> + var me = this;
> + var win = Ext.create(me.rows.acme.editor, me.editorConfig);
> + win.show();
> + win.on('destroy', me.reload, me);
> + },
> +
> + listeners: {
> + itemdblclick: 'run_editor'
> + },
> +
> + // account data gets loaded here
> + account: undefined,
> +
> + disableSelection: true,
> +
> + initComponent: function() {
> + var me = this;
> +
> + if (!me.nodename) {
> + throw "no nodename given";
> + }
> +
> + me.url = '/api2/json/nodes/' + me.nodename + '/config';
> +
> + me.editorConfig = {
> + url: '/api2/extjs/nodes/' + me.nodename + '/config'
> + };
> + /*jslint confusion: true*/
> + /*acme is a string above*/
> + me.rows = {
> + acme: {
> + defaultValue: '',
> + header: gettext('Domains'),
> + editor: 'PVE.node.ACMEEditor',
> + renderer: function(value) {
> + var acmeObj = PVE.Parser.parseACME(value);
> + if (acmeObj) {
> + return acmeObj.domains.join('<br>');
> + }
> + return Proxmox.Utils.noneText;
> + }
> + }
> + };
> + /*jslint confusion: false*/
> +
> + me.callParent();
> + me.mon(me.rstore, 'load', me.set_button_status, me);
> + me.rstore.startUpdate();
> + me.load_account();
> + }
> +});
>
More information about the pve-devel
mailing list