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/Certificates.js \
|
||||
panel/ACMEAccount.js \
|
||||
panel/ACMEPlugin.js \
|
||||
window/Edit.js \
|
||||
window/PasswordEdit.js \
|
||||
window/SafeDestroy.js \
|
||||
@ -60,6 +61,7 @@ JSSRC= \
|
||||
window/ZFSDetail.js \
|
||||
window/Certificates.js \
|
||||
window/ACMEAccount.js \
|
||||
window/ACMEPluginEdit.js \
|
||||
node/APT.js \
|
||||
node/NetworkEdit.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