notification: add gui for sendmail notification endpoints

This commit adds a new panel 'NotificationConfigView' that is supposed
to be embedded in the datacenter configuration side-bar.
This new view lists all notification endpoints, allowing to
add/modify/delete/test them.

Furthermore, this commits adds the dialog for adding/modifying
sendmail endpoints. The dialog is 'plugin-in' based, meaning that it
consists of a base window (EndpointEditBase) and a panel that holds
the actual fields for the endpoint type configuration. This will show
be beneficial once the GUI for other endpoint types is added.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
Lukas Wagner 2023-08-03 14:17:14 +02:00 committed by Wolfgang Bumiller
parent a60c8dc0c0
commit 84f70dfaad
6 changed files with 398 additions and 0 deletions

View File

@ -22,6 +22,7 @@ JSSRC= \
data/ObjectStore.js \
data/RRDStore.js \
data/TimezoneStore.js \
data/model/NotificationConfig.js \
data/model/Realm.js \
data/model/Certificates.js \
data/model/ACME.js \
@ -59,6 +60,7 @@ JSSRC= \
panel/InfoWidget.js \
panel/LogView.js \
panel/NodeInfoRepoStatus.js \
panel/NotificationConfigView.js \
panel/JournalView.js \
panel/PermissionView.js \
panel/PruneKeepPanel.js \
@ -68,6 +70,7 @@ JSSRC= \
panel/ACMEAccount.js \
panel/ACMEPlugin.js \
panel/ACMEDomains.js \
panel/SendmailEditPanel.js \
panel/StatusView.js \
panel/TfaView.js \
panel/NotesView.js \
@ -83,6 +86,7 @@ JSSRC= \
window/ACMEAccount.js \
window/ACMEPluginEdit.js \
window/ACMEDomains.js \
window/EndpointEditBase.js \
window/FileBrowser.js \
window/AuthEditBase.js \
window/AuthEditOpenId.js \

View File

@ -37,6 +37,14 @@ Ext.define('Proxmox.Schema', { // a singleton
}
},
notificationEndpointTypes: {
sendmail: {
name: gettext('Sendmail'),
ipanel: 'pmxSendmailEditPanel',
iconCls: 'fa-envelope-o',
},
},
pxarFileTypes: {
b: { icon: 'cube', label: gettext('Block Device') },
c: { icon: 'tty', label: gettext('Character Device') },

View File

@ -0,0 +1,8 @@
Ext.define('proxmox-notification-endpoints', {
extend: 'Ext.data.Model',
fields: ['name', 'type', 'comment'],
proxy: {
type: 'proxmox',
},
idProperty: 'name',
});

View File

@ -0,0 +1,196 @@
Ext.define('Proxmox.panel.NotificationConfigView', {
extend: 'Ext.panel.Panel',
alias: 'widget.pmxNotificationConfigView',
mixins: ['Proxmox.Mixin.CBind'],
layout: {
type: 'border',
},
items: [
{
region: 'center',
border: false,
xtype: 'pmxNotificationEndpointView',
cbind: {
baseUrl: '{baseUrl}',
},
},
],
});
Ext.define('Proxmox.panel.NotificationEndpointView', {
extend: 'Ext.grid.Panel',
alias: 'widget.pmxNotificationEndpointView',
title: gettext('Notification Targets'),
controller: {
xclass: 'Ext.app.ViewController',
openEditWindow: function(endpointType, endpoint) {
let me = this;
if (endpoint === 'mail-to-root') {
return;
}
Ext.create('Proxmox.window.EndpointEditBase', {
baseUrl: me.getView().baseUrl,
type: endpointType,
name: endpoint,
autoShow: true,
listeners: {
destroy: () => me.reload(),
},
});
},
openEditForSelectedItem: function() {
let me = this;
let view = me.getView();
let selection = view.getSelection();
if (selection.length < 1) {
return;
}
me.openEditWindow(selection[0].data.type, selection[0].data.name);
},
reload: function() {
let me = this;
let view = me.getView();
view.getStore().rstore.load();
},
testEndpoint: function() {
let me = this;
let view = me.getView();
let selection = view.getSelection();
if (selection.length < 1) {
return;
}
let target = selection[0].data.name;
Ext.Msg.confirm(
gettext("Notification Target Test"),
gettext(`Do you want to send a test notification to '${target}'?`),
function(decision) {
if (decision !== "yes") {
return;
}
Proxmox.Utils.API2Request({
method: 'POST',
url: `${view.baseUrl}/targets/${target}/test`,
success: function(response, opt) {
Ext.Msg.show({
title: gettext('Notification Target Test'),
message: gettext(`Sent test notification to '${target}'.`),
buttons: Ext.Msg.OK,
icon: Ext.Msg.INFO,
});
},
autoErrorAlert: true,
});
});
},
},
listeners: {
itemdblclick: 'openEditForSelectedItem',
activate: 'reload',
},
emptyText: gettext('No notification targets configured'),
columns: [
{
dataIndex: 'name',
text: gettext('Target Name'),
renderer: Ext.String.htmlEncode,
flex: 1,
},
{
dataIndex: 'type',
text: gettext('Type'),
renderer: Ext.String.htmlEncode,
flex: 1,
},
{
dataIndex: 'comment',
text: gettext('Comment'),
renderer: Ext.String.htmlEncode,
flex: 1,
},
],
store: {
type: 'diff',
autoDestroy: true,
autoDestroyRstore: true,
rstore: {
type: 'update',
storeid: 'proxmox-notification-endpoints',
model: 'proxmox-notification-endpoints',
autoStart: true,
},
sorters: 'name',
},
initComponent: function() {
let me = this;
let menuItems = [];
for (const [endpointType, config] of Object.entries(
Proxmox.Schema.notificationEndpointTypes).sort()) {
menuItems.push({
text: config.name,
iconCls: 'fa fa-fw ' + (config.iconCls || 'fa-bell-o'),
handler: () => me.controller.openEditWindow(endpointType),
});
}
Ext.apply(me, {
tbar: [
{
text: gettext('Add'),
menu: menuItems,
},
{
xtype: 'proxmoxButton',
text: gettext('Modify'),
handler: 'openEditForSelectedItem',
enableFn: rec => rec.data.name !== 'mail-to-root',
disabled: true,
},
{
xtype: 'proxmoxStdRemoveButton',
callback: 'reload',
enableFn: rec => rec.data.name !== 'mail-to-root',
getUrl: function(rec) {
if (rec.data.type === 'group') {
return `${me.baseUrl}/groups/${rec.getId()}`;
}
return `${me.baseUrl}/endpoints/${rec.data.type}/${rec.getId()}`;
},
},
'-',
{
xtype: 'proxmoxButton',
text: gettext('Test'),
handler: 'testEndpoint',
disabled: true,
},
],
});
me.callParent();
me.store.rstore.proxy.setUrl(`/api2/json/${me.baseUrl}/targets`);
},
});

View File

@ -0,0 +1,130 @@
Ext.define('Proxmox.panel.SendmailEditPanel', {
extend: 'Proxmox.panel.InputPanel',
xtype: 'pmxSendmailEditPanel',
mixins: ['Proxmox.Mixin.CBind'],
type: 'sendmail',
mailValidator: function() {
let mailto_user = this.down(`[name=mailto-user]`);
let mailto = this.down(`[name=mailto]`);
if (!mailto_user.getValue()?.length && !mailto.getValue()) {
return gettext('Either mailto or mailto-user must be set');
}
return true;
},
items: [
{
xtype: 'pmxDisplayEditField',
name: 'name',
cbind: {
value: '{name}',
editable: '{isCreate}',
},
fieldLabel: gettext('Endpoint Name'),
allowBlank: false,
},
{
xtype: 'pmxUserSelector',
name: 'mailto-user',
reference: 'mailto-user',
multiSelect: true,
allowBlank: true,
editable: false,
skipEmptyText: true,
fieldLabel: gettext('User(s)'),
cbind: {
deleteEmpty: '{!isCreate}',
},
validator: function() {
return this.up('pmxSendmailEditPanel').mailValidator();
},
listConfig: {
width: 600,
columns: [
{
header: gettext('User'),
sortable: true,
dataIndex: 'userid',
renderer: Ext.String.htmlEncode,
flex: 1,
},
{
header: gettext('E-Mail'),
sortable: true,
dataIndex: 'email',
renderer: Ext.String.htmlEncode,
flex: 1,
},
{
header: gettext('Comment'),
sortable: false,
dataIndex: 'comment',
renderer: Ext.String.htmlEncode,
flex: 1,
},
],
},
},
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Additional Recipient(s)'),
name: 'mailto',
reference: 'mailto',
allowBlank: true,
cbind: {
deleteEmpty: '{!isCreate}',
},
autoEl: {
tag: 'div',
'data-qtip': gettext(
'Multiple recipients must be separated by spaces, commas or semicolons',
),
},
validator: function() {
return this.up('pmxSendmailEditPanel').mailValidator();
},
},
{
xtype: 'proxmoxtextfield',
name: 'comment',
fieldLabel: gettext('Comment'),
cbind: {
deleteEmpty: '{!isCreate}',
},
},
],
advancedItems: [
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Author'),
name: 'author',
allowBlank: true,
emptyText: gettext('Proxmox VE'),
cbind: {
deleteEmpty: '{!isCreate}',
},
},
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('From Address'),
name: 'from-address',
allowBlank: true,
emptyText: gettext('Defaults to datacenter configuration, or root@$hostname'),
cbind: {
deleteEmpty: '{!isCreate}',
},
},
],
onGetValues: (values) => {
if (values.mailto) {
values.mailto = values.mailto.split(/[\s,;]+/);
}
return values;
},
});

View File

@ -0,0 +1,52 @@
Ext.define('Proxmox.window.EndpointEditBase', {
extend: 'Proxmox.window.Edit',
isAdd: true,
fieldDefaults: {
labelWidth: 120,
},
width: 700,
initComponent: function() {
let me = this;
me.isCreate = !me.name;
if (!me.baseUrl) {
throw "baseUrl not set";
}
me.url = `/api2/extjs${me.baseUrl}/endpoints/${me.type}`;
if (me.isCreate) {
me.method = 'POST';
} else {
me.url += `/${me.name}`;
me.method = 'PUT';
}
let endpointConfig = Proxmox.Schema.notificationEndpointTypes[me.type];
if (!endpointConfig) {
throw 'unknown endpoint type';
}
me.subject = endpointConfig.name;
Ext.apply(me, {
items: [{
name: me.name,
xtype: endpointConfig.ipanel,
isCreate: me.isCreate,
type: me.type,
}],
});
me.callParent();
if (!me.isCreate) {
me.load();
}
},
});