add ACME plugin editing
Like with the account panel, the 'acmeUrl' base needs to be specified, otherwise this is copied from PVE Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
5df894de26
commit
658bfdff32
@ -51,6 +51,7 @@ JSSRC= \
|
|||||||
panel/GaugeWidget.js \
|
panel/GaugeWidget.js \
|
||||||
panel/Certificates.js \
|
panel/Certificates.js \
|
||||||
panel/ACMEAccount.js \
|
panel/ACMEAccount.js \
|
||||||
|
panel/ACMEPlugin.js \
|
||||||
window/Edit.js \
|
window/Edit.js \
|
||||||
window/PasswordEdit.js \
|
window/PasswordEdit.js \
|
||||||
window/SafeDestroy.js \
|
window/SafeDestroy.js \
|
||||||
@ -60,6 +61,7 @@ JSSRC= \
|
|||||||
window/ZFSDetail.js \
|
window/ZFSDetail.js \
|
||||||
window/Certificates.js \
|
window/Certificates.js \
|
||||||
window/ACMEAccount.js \
|
window/ACMEAccount.js \
|
||||||
|
window/ACMEPluginEdit.js \
|
||||||
node/APT.js \
|
node/APT.js \
|
||||||
node/NetworkEdit.js \
|
node/NetworkEdit.js \
|
||||||
node/NetworkView.js \
|
node/NetworkView.js \
|
||||||
|
116
src/panel/ACMEPlugin.js
Normal file
116
src/panel/ACMEPlugin.js
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
Ext.define('Proxmox.panel.ACMEPluginView', {
|
||||||
|
extend: 'Ext.grid.Panel',
|
||||||
|
alias: 'widget.pmxACMEPluginView',
|
||||||
|
|
||||||
|
title: gettext('Challenge Plugins'),
|
||||||
|
acmeUrl: undefined,
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
xclass: 'Ext.app.ViewController',
|
||||||
|
|
||||||
|
addPlugin: function() {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
Ext.create('Proxmox.window.ACMEPluginEdit', {
|
||||||
|
acmeUrl: view.acmeUrl,
|
||||||
|
url: `${view.acmeUrl}/plugins`,
|
||||||
|
isCreate: true,
|
||||||
|
apiCallDone: function() {
|
||||||
|
me.reload();
|
||||||
|
},
|
||||||
|
}).show();
|
||||||
|
},
|
||||||
|
|
||||||
|
editPlugin: function() {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
let selection = view.getSelection();
|
||||||
|
if (selection.length < 1) return;
|
||||||
|
let plugin = selection[0].data.plugin;
|
||||||
|
Ext.create('Proxmox.window.ACMEPluginEdit', {
|
||||||
|
acmeUrl: view.acmeUrl,
|
||||||
|
url: `${view.acmeUrl}/plugins/${plugin}`,
|
||||||
|
apiCallDone: function() {
|
||||||
|
me.reload();
|
||||||
|
},
|
||||||
|
}).show();
|
||||||
|
},
|
||||||
|
|
||||||
|
reload: function() {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
view.getStore().rstore.load();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
minHeight: 150,
|
||||||
|
emptyText: gettext('No Plugins configured'),
|
||||||
|
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
dataIndex: 'plugin',
|
||||||
|
text: gettext('Plugin'),
|
||||||
|
renderer: Ext.String.htmlEncode,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'api',
|
||||||
|
text: 'API',
|
||||||
|
renderer: Ext.String.htmlEncode,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
itemdblclick: 'editPlugin',
|
||||||
|
},
|
||||||
|
|
||||||
|
store: {
|
||||||
|
type: 'diff',
|
||||||
|
autoDestroy: true,
|
||||||
|
autoDestroyRstore: true,
|
||||||
|
rstore: {
|
||||||
|
type: 'update',
|
||||||
|
storeid: 'proxmox-acme-plugins',
|
||||||
|
model: 'proxmox-acme-plugins',
|
||||||
|
autoStart: true,
|
||||||
|
filters: item => !!item.data.api,
|
||||||
|
},
|
||||||
|
sorters: 'plugin',
|
||||||
|
},
|
||||||
|
|
||||||
|
initComponent: function() {
|
||||||
|
let me = this;
|
||||||
|
|
||||||
|
if (!me.acmeUrl) {
|
||||||
|
throw "no acmeUrl given";
|
||||||
|
}
|
||||||
|
me.url = `${me.acmeUrl}/plugins`;
|
||||||
|
|
||||||
|
Ext.apply(me, {
|
||||||
|
tbar: [
|
||||||
|
{
|
||||||
|
xtype: 'proxmoxButton',
|
||||||
|
text: gettext('Add'),
|
||||||
|
handler: 'addPlugin',
|
||||||
|
selModel: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'proxmoxButton',
|
||||||
|
text: gettext('Edit'),
|
||||||
|
handler: 'editPlugin',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'proxmoxStdRemoveButton',
|
||||||
|
callback: 'reload',
|
||||||
|
baseurl: `${me.acmeUrl}/plugins`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
me.callParent();
|
||||||
|
|
||||||
|
me.store.rstore.proxy.setUrl(`/api2/json/${me.acmeUrl}/plugins`);
|
||||||
|
},
|
||||||
|
});
|
242
src/window/ACMEPluginEdit.js
Normal file
242
src/window/ACMEPluginEdit.js
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
Ext.define('Proxmox.window.ACMEPluginEdit', {
|
||||||
|
extend: 'Proxmox.window.Edit',
|
||||||
|
xtype: 'pmxACMEPluginEdit',
|
||||||
|
mixins: ['Proxmox.Mixin.CBind'],
|
||||||
|
|
||||||
|
//onlineHelp: 'sysadmin_certs_acme_plugins',
|
||||||
|
|
||||||
|
isAdd: true,
|
||||||
|
isCreate: false,
|
||||||
|
|
||||||
|
width: 550,
|
||||||
|
|
||||||
|
acmeUrl: undefined,
|
||||||
|
|
||||||
|
subject: 'ACME DNS Plugin',
|
||||||
|
|
||||||
|
cbindData: function(config) {
|
||||||
|
let me = this;
|
||||||
|
return {
|
||||||
|
challengeSchemaUrl: `/api2/json/${me.acmeUrl}/challenge-schema`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'inputpanel',
|
||||||
|
// we dynamically create fields from the given schema
|
||||||
|
// things we have to do here:
|
||||||
|
// * save which fields we created to remove them again
|
||||||
|
// * split the data from the generic 'data' field into the boxes
|
||||||
|
// * on deletion collect those values again
|
||||||
|
// * save the original values of the data field
|
||||||
|
createdFields: {},
|
||||||
|
createdInitially: false,
|
||||||
|
originalValues: {},
|
||||||
|
createSchemaFields: function(schema) {
|
||||||
|
let me = this;
|
||||||
|
// we know where to add because we define it right below
|
||||||
|
let container = me.down('container');
|
||||||
|
let datafield = me.down('field[name=data]');
|
||||||
|
let hintfield = me.down('field[name=hint]');
|
||||||
|
if (!me.createdInitially) {
|
||||||
|
[me.originalValues] = Proxmox.Utils.parseACMEPluginData(datafield.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect values from custom fields and add it to 'data'',
|
||||||
|
// then remove the custom fields
|
||||||
|
let data = [];
|
||||||
|
for (const [name, field] of Object.entries(me.createdFields)) {
|
||||||
|
let value = field.getValue();
|
||||||
|
if (value !== undefined && value !== null && value !== '') {
|
||||||
|
data.push(`${name}=${value}`);
|
||||||
|
}
|
||||||
|
container.remove(field);
|
||||||
|
}
|
||||||
|
let datavalue = datafield.getValue();
|
||||||
|
if (datavalue !== undefined && datavalue !== null && datavalue !== '') {
|
||||||
|
data.push(datavalue);
|
||||||
|
}
|
||||||
|
datafield.setValue(data.join('\n'));
|
||||||
|
|
||||||
|
me.createdFields = {};
|
||||||
|
|
||||||
|
if (typeof schema.fields !== 'object') {
|
||||||
|
schema.fields = {};
|
||||||
|
}
|
||||||
|
// create custom fields according to schema
|
||||||
|
let gotSchemaField = false;
|
||||||
|
for (const [name, definition] of Object
|
||||||
|
.entries(schema.fields)
|
||||||
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||||
|
) {
|
||||||
|
let xtype;
|
||||||
|
switch (definition.type) {
|
||||||
|
case 'string':
|
||||||
|
xtype = 'proxmoxtextfield';
|
||||||
|
break;
|
||||||
|
case 'integer':
|
||||||
|
xtype = 'proxmoxintegerfield';
|
||||||
|
break;
|
||||||
|
case 'number':
|
||||||
|
xtype = 'numberfield';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(`unknown type '${definition.type}'`);
|
||||||
|
xtype = 'proxmoxtextfield';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let label = name;
|
||||||
|
if (typeof definition.name === "string") {
|
||||||
|
label = definition.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
let field = Ext.create({
|
||||||
|
xtype,
|
||||||
|
name: `custom_${name}`,
|
||||||
|
fieldLabel: label,
|
||||||
|
width: '100%',
|
||||||
|
labelWidth: 150,
|
||||||
|
labelSeparator: '=',
|
||||||
|
emptyText: definition.default || '',
|
||||||
|
autoEl: definition.description ? {
|
||||||
|
tag: 'div',
|
||||||
|
'data-qtip': definition.description,
|
||||||
|
} : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
me.createdFields[name] = field;
|
||||||
|
container.add(field);
|
||||||
|
gotSchemaField = true;
|
||||||
|
}
|
||||||
|
datafield.setHidden(gotSchemaField); // prefer schema-fields
|
||||||
|
|
||||||
|
if (schema.description) {
|
||||||
|
hintfield.setValue(schema.description);
|
||||||
|
hintfield.setHidden(false);
|
||||||
|
} else {
|
||||||
|
hintfield.setValue('');
|
||||||
|
hintfield.setHidden(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse data from field and set it to the custom ones
|
||||||
|
let extradata = [];
|
||||||
|
[data, extradata] = Proxmox.Utils.parseACMEPluginData(datafield.getValue());
|
||||||
|
for (const [key, value] of Object.entries(data)) {
|
||||||
|
if (me.createdFields[key]) {
|
||||||
|
me.createdFields[key].setValue(value);
|
||||||
|
me.createdFields[key].originalValue = me.originalValues[key];
|
||||||
|
} else {
|
||||||
|
extradata.push(`${key}=${value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
datafield.setValue(extradata.join('\n'));
|
||||||
|
if (!me.createdInitially) {
|
||||||
|
datafield.resetOriginalValue();
|
||||||
|
me.createdInitially = true; // save that we initally set that
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onGetValues: function(values) {
|
||||||
|
let me = this;
|
||||||
|
let win = me.up('pmxACMEPluginEdit');
|
||||||
|
if (win.isCreate) {
|
||||||
|
values.id = values.plugin;
|
||||||
|
values.type = 'dns'; // the only one for now
|
||||||
|
}
|
||||||
|
delete values.plugin;
|
||||||
|
|
||||||
|
Proxmox.Utils.delete_if_default(values, 'validation-delay', '30', win.isCreate);
|
||||||
|
|
||||||
|
let data = '';
|
||||||
|
for (const [name, field] of Object.entries(me.createdFields)) {
|
||||||
|
let value = field.getValue();
|
||||||
|
if (value !== null && value !== undefined && value !== '') {
|
||||||
|
data += `${name}=${value}\n`;
|
||||||
|
}
|
||||||
|
delete values[`custom_${name}`];
|
||||||
|
}
|
||||||
|
values.data = Ext.util.Base64.encode(data + values.data);
|
||||||
|
return values;
|
||||||
|
},
|
||||||
|
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'pmxDisplayEditField',
|
||||||
|
cbind: {
|
||||||
|
editable: (get) => get('isCreate'),
|
||||||
|
submitValue: (get) => get('isCreate'),
|
||||||
|
},
|
||||||
|
editConfig: {
|
||||||
|
flex: 1,
|
||||||
|
xtype: 'proxmoxtextfield',
|
||||||
|
allowBlank: false,
|
||||||
|
},
|
||||||
|
name: 'plugin',
|
||||||
|
labelWidth: 150,
|
||||||
|
fieldLabel: gettext('Plugin ID'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'proxmoxintegerfield',
|
||||||
|
name: 'validation-delay',
|
||||||
|
labelWidth: 150,
|
||||||
|
fieldLabel: gettext('Validation Delay'),
|
||||||
|
emptyText: 30,
|
||||||
|
cbind: {
|
||||||
|
deleteEmpty: '{!isCreate}',
|
||||||
|
},
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 48*60*60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'pmxACMEApiSelector',
|
||||||
|
name: 'api',
|
||||||
|
labelWidth: 150,
|
||||||
|
cbind: {
|
||||||
|
url: '{challengeSchemaUrl}',
|
||||||
|
},
|
||||||
|
listeners: {
|
||||||
|
change: function(selector) {
|
||||||
|
let schema = selector.getSchema();
|
||||||
|
selector.up('inputpanel').createSchemaFields(schema);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'textarea',
|
||||||
|
fieldLabel: gettext('API Data'),
|
||||||
|
labelWidth: 150,
|
||||||
|
name: 'data',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'displayfield',
|
||||||
|
fieldLabel: gettext('Hint'),
|
||||||
|
labelWidth: 150,
|
||||||
|
name: 'hint',
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
initComponent: function() {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
if (!me.acmeUrl) {
|
||||||
|
throw "no acmeUrl given";
|
||||||
|
}
|
||||||
|
|
||||||
|
me.callParent();
|
||||||
|
|
||||||
|
if (!me.isCreate) {
|
||||||
|
me.load({
|
||||||
|
success: function(response, opts) {
|
||||||
|
me.setValues(response.result.data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
me.method = 'POST';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user