@plutonhq/core-frontend 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/@types/index.js +4 -1
- package/dist-lib/@types/index.js.map +1 -1
- package/dist-lib/@types/settings.d.ts +14 -0
- package/dist-lib/@types/settings.d.ts.map +1 -1
- package/dist-lib/@types/settings.js +34 -0
- package/dist-lib/@types/settings.js.map +1 -0
- package/dist-lib/components/Plan/PlanSettings/PlanNotificationSettings.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanNotificationSettings.js +148 -90
- package/dist-lib/components/Plan/PlanSettings/PlanNotificationSettings.js.map +1 -1
- package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.d.ts.map +1 -1
- package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.js +52 -47
- package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.js.map +1 -1
- package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.module.scss.js +12 -6
- package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.module.scss.js.map +1 -1
- package/dist-lib/components/Settings/IntegrationSettings/NtfySettings.d.ts +9 -0
- package/dist-lib/components/Settings/IntegrationSettings/NtfySettings.d.ts.map +1 -0
- package/dist-lib/components/Settings/IntegrationSettings/NtfySettings.js +79 -0
- package/dist-lib/components/Settings/IntegrationSettings/NtfySettings.js.map +1 -0
- package/dist-lib/components/Settings/IntegrationSettings/SMTPSettings.d.ts +4 -3
- package/dist-lib/components/Settings/IntegrationSettings/SMTPSettings.d.ts.map +1 -1
- package/dist-lib/components/Settings/IntegrationSettings/SMTPSettings.js +37 -35
- package/dist-lib/components/Settings/IntegrationSettings/SMTPSettings.js.map +1 -1
- package/dist-lib/components/Settings/IntegrationSettings/ValidateEmailIntegration.d.ts +10 -0
- package/dist-lib/components/Settings/IntegrationSettings/ValidateEmailIntegration.d.ts.map +1 -0
- package/dist-lib/components/Settings/IntegrationSettings/ValidateEmailIntegration.js +49 -0
- package/dist-lib/components/Settings/IntegrationSettings/ValidateEmailIntegration.js.map +1 -0
- package/dist-lib/components/Storage/EditStorage/EditStorage.js +10 -10
- package/dist-lib/components/Storage/EditStorage/EditStorage.js.map +1 -1
- package/dist-lib/components/index.d.ts +2 -0
- package/dist-lib/components/index.d.ts.map +1 -1
- package/dist-lib/components.js +58 -54
- package/dist-lib/components.js.map +1 -1
- package/dist-lib/services/settings.d.ts +3 -2
- package/dist-lib/services/settings.d.ts.map +1 -1
- package/dist-lib/services/settings.js +0 -1
- package/dist-lib/services/settings.js.map +1 -1
- package/dist-lib/styles/core-frontend.css +1 -1
- package/package.json +1 -1
- package/src/@types/settings.ts +43 -0
- package/src/components/Plan/PlanSettings/PlanNotificationSettings.tsx +65 -0
- package/src/components/Settings/IntegrationSettings/IntegrationSettings.module.scss +16 -0
- package/src/components/Settings/IntegrationSettings/IntegrationSettings.tsx +45 -42
- package/src/components/Settings/IntegrationSettings/NtfySettings.tsx +106 -0
- package/src/components/Settings/IntegrationSettings/SMTPSettings.tsx +28 -19
- package/src/components/Settings/IntegrationSettings/ValidateEmailIntegration.tsx +58 -0
- package/src/components/Storage/EditStorage/EditStorage.tsx +1 -1
- package/src/components/index.ts +2 -0
- package/src/services/settings.ts +2 -2
- /package/dist-lib/providers/{azureBlob.png → azureblob.png} +0 -0
- /package/dist-lib/providers/{azureFiles.png → azurefiles.png} +0 -0
- /package/dist-lib/providers/{files.png → filescom.png} +0 -0
- /package/dist-lib/providers/{oracle.png → oracleobjectstorage.png} +0 -0
- /package/dist-lib/providers/{proton.png → protondrive.png} +0 -0
package/package.json
CHANGED
package/src/@types/settings.ts
CHANGED
|
@@ -18,3 +18,46 @@ export interface LogItem {
|
|
|
18
18
|
deviceId?: string;
|
|
19
19
|
msg: string;
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
export interface NtfySettingsType {
|
|
23
|
+
authType: string;
|
|
24
|
+
authToken: string;
|
|
25
|
+
connected: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface IntegrationSettings {
|
|
29
|
+
smtp?: SmtpSettingsType;
|
|
30
|
+
ntfy?: NtfySettingsType;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type IntegrationTypes = 'smtp' | 'sendgrid' | 'mailgun' | 'brevo' | 'resend' | 'awsSes' | 'ntfy';
|
|
34
|
+
export const INTEGRATIONS_AVAILABLE: Record<IntegrationTypes, { name: string; required: string[] }> = {
|
|
35
|
+
smtp: {
|
|
36
|
+
name: 'SMTP',
|
|
37
|
+
required: ['server', 'port', 'username', 'password', 'senderEmail'],
|
|
38
|
+
},
|
|
39
|
+
sendgrid: {
|
|
40
|
+
name: 'SendGrid',
|
|
41
|
+
required: ['apiKey', 'senderEmail'],
|
|
42
|
+
},
|
|
43
|
+
mailgun: {
|
|
44
|
+
name: 'Mailgun',
|
|
45
|
+
required: ['apiKey', 'domain', 'senderEmail'],
|
|
46
|
+
},
|
|
47
|
+
brevo: {
|
|
48
|
+
name: 'Brevo',
|
|
49
|
+
required: ['apiKey', 'senderEmail'],
|
|
50
|
+
},
|
|
51
|
+
resend: {
|
|
52
|
+
name: 'Resend',
|
|
53
|
+
required: ['apiKey', 'senderEmail'],
|
|
54
|
+
},
|
|
55
|
+
awsSes: {
|
|
56
|
+
name: 'AWS SES',
|
|
57
|
+
required: ['accessKeyId', 'secretAccessKey', 'region', 'senderEmail'],
|
|
58
|
+
},
|
|
59
|
+
ntfy: {
|
|
60
|
+
name: 'Ntfy',
|
|
61
|
+
required: ['authType', 'authToken'],
|
|
62
|
+
},
|
|
63
|
+
} as const;
|
|
@@ -28,6 +28,7 @@ const PlanNotificationSettings = ({
|
|
|
28
28
|
}: PlanNotificationSettingsProps) => {
|
|
29
29
|
const hasConnectedIntegrations = types.length > 0;
|
|
30
30
|
const defaultEmail = !hasConnectedIntegrations && admin_email ? admin_email : '';
|
|
31
|
+
const hasNtfyConnected = types.includes('ntfy');
|
|
31
32
|
|
|
32
33
|
const updateNotificationEmails = (emails: string) => {
|
|
33
34
|
onUpdate({ ...notificationSettings, email: { ...notificationSettings.email, emails } });
|
|
@@ -203,6 +204,70 @@ const PlanNotificationSettings = ({
|
|
|
203
204
|
</div>
|
|
204
205
|
)}
|
|
205
206
|
</div>
|
|
207
|
+
|
|
208
|
+
{/* Ntfy Push Notification */}
|
|
209
|
+
<div className={classes.notificationSettingsSection}>
|
|
210
|
+
<div className={`${classes.field} ${classes.notificationToggle}`}>
|
|
211
|
+
<Icon type="ntfy" size={14} />
|
|
212
|
+
<Toggle
|
|
213
|
+
label="Enable Ntfy Push Notifications"
|
|
214
|
+
fieldValue={notificationSettings?.push?.enabled || false}
|
|
215
|
+
onUpdate={(val: boolean) => onUpdate({ ...notificationSettings, push: { ...notificationSettings.push, enabled: val } })}
|
|
216
|
+
hint={`Send me Push Notifications on Backup failure or success.`}
|
|
217
|
+
inline={true}
|
|
218
|
+
/>
|
|
219
|
+
</div>
|
|
220
|
+
{notificationSettings.push?.enabled &&
|
|
221
|
+
(hasNtfyConnected ? (
|
|
222
|
+
<div className={classes.notificationSettings}>
|
|
223
|
+
{!isSync && (
|
|
224
|
+
<div className={classes.field}>
|
|
225
|
+
<Select
|
|
226
|
+
label="Send Notification On"
|
|
227
|
+
fieldValue={notificationSettings?.push?.case || 'failure'}
|
|
228
|
+
options={caseOptions}
|
|
229
|
+
onUpdate={(val: string) =>
|
|
230
|
+
onUpdate({
|
|
231
|
+
...notificationSettings,
|
|
232
|
+
push: { ...notificationSettings.push, case: val as PlanNotification['push']['case'] },
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
inline={true}
|
|
236
|
+
/>
|
|
237
|
+
</div>
|
|
238
|
+
)}
|
|
239
|
+
<div className={classes.field}>
|
|
240
|
+
<Input
|
|
241
|
+
label="Ntfy Push URL"
|
|
242
|
+
fieldValue={notificationSettings?.push?.url || ''}
|
|
243
|
+
onUpdate={(val) => onUpdate({ ...notificationSettings, push: { ...notificationSettings.push, url: val } })}
|
|
244
|
+
placeholder="https://ntfy.sh/mytopic"
|
|
245
|
+
inline={true}
|
|
246
|
+
required={true}
|
|
247
|
+
full={true}
|
|
248
|
+
error={!notificationSettings?.push?.url ? 'Required' : undefined}
|
|
249
|
+
/>
|
|
250
|
+
</div>
|
|
251
|
+
<div className={classes.field}>
|
|
252
|
+
<Input
|
|
253
|
+
label="Tags"
|
|
254
|
+
fieldValue={notificationSettings?.push?.tags || ''}
|
|
255
|
+
onUpdate={(val) => onUpdate({ ...notificationSettings, push: { ...notificationSettings.push, tags: val } })}
|
|
256
|
+
placeholder="warning, daily-backup"
|
|
257
|
+
inline={true}
|
|
258
|
+
full={true}
|
|
259
|
+
/>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
) : (
|
|
263
|
+
<div className={classes.notificationSettings}>
|
|
264
|
+
<div className={classes.fieldNotice}>
|
|
265
|
+
⚠️ Ntfy Auth Token is not configured. Set it up in <NavLink to={`/settings?t=integration`}>Settings</NavLink> to enable Ntfy
|
|
266
|
+
Push Notifications.
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
))}
|
|
270
|
+
</div>
|
|
206
271
|
</>
|
|
207
272
|
);
|
|
208
273
|
};
|
|
@@ -36,3 +36,19 @@
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
@media only screen and (max-width: 768px) {
|
|
41
|
+
.integrations {
|
|
42
|
+
.field {
|
|
43
|
+
width: 100%;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
.integrationSelect {
|
|
47
|
+
width: 100%;
|
|
48
|
+
margin-bottom: 30px;
|
|
49
|
+
|
|
50
|
+
:global([class*='_dropdown_']) {
|
|
51
|
+
width: 100%;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import classes from './IntegrationSettings.module.scss';
|
|
3
|
-
import Input from '../../common/form/Input/Input';
|
|
4
|
-
import ActionModal from '../../common/ActionModal/ActionModal';
|
|
5
|
-
import { useValidateIntegration } from '../../../services/settings';
|
|
6
3
|
import SMTPSettings from './SMTPSettings';
|
|
4
|
+
import NtfySettings from './NtfySettings';
|
|
5
|
+
import Icon from '../../common/Icon/Icon';
|
|
6
|
+
import Select from '../../common/form/Select/Select';
|
|
7
|
+
import { isMobile } from '../../../utils';
|
|
7
8
|
|
|
8
9
|
interface IntegrationSettingsProps {
|
|
9
10
|
settingsID: number;
|
|
@@ -12,12 +13,9 @@ interface IntegrationSettingsProps {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const IntegrationSettings = ({ settingsID, settings, onUpdate }: IntegrationSettingsProps) => {
|
|
15
|
-
const [
|
|
16
|
-
const [showEmailTestModal, setShowEmailTestModal] = useState<'' | 'smtp'>('');
|
|
16
|
+
const [tab, setTab] = useState<'smtp' | 'ntfy'>('smtp');
|
|
17
17
|
const integrationSettings = settings?.integration || {};
|
|
18
|
-
const { smtp } = integrationSettings || {};
|
|
19
|
-
|
|
20
|
-
const validationMutation = useValidateIntegration();
|
|
18
|
+
const { smtp, ntfy } = integrationSettings || {};
|
|
21
19
|
|
|
22
20
|
const onIntegrationUpdate = (key: string, intSettings: Record<string, any>) => {
|
|
23
21
|
console.log('onIntegrationUpdate :', key, intSettings);
|
|
@@ -26,42 +24,47 @@ const IntegrationSettings = ({ settingsID, settings, onUpdate }: IntegrationSett
|
|
|
26
24
|
|
|
27
25
|
return (
|
|
28
26
|
<div className={classes.integrations}>
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
message={
|
|
40
|
-
<>
|
|
41
|
-
<Input
|
|
42
|
-
label="Send Test Email to this email"
|
|
43
|
-
full={true}
|
|
44
|
-
fieldValue={testEmail}
|
|
45
|
-
onUpdate={(val) => setTestEmail(val)}
|
|
46
|
-
type="email"
|
|
47
|
-
placeholder="test@test.com"
|
|
48
|
-
/>
|
|
49
|
-
</>
|
|
50
|
-
}
|
|
51
|
-
errorMessage={validationMutation.error?.message}
|
|
52
|
-
successMessage={validationMutation.isSuccess ? 'Test email sent. Integration validated successfully.' : ''}
|
|
53
|
-
closeModal={() => setShowEmailTestModal('')}
|
|
54
|
-
width="400px"
|
|
55
|
-
secondaryAction={{ title: 'Close', action: () => setShowEmailTestModal('') }}
|
|
56
|
-
primaryAction={{
|
|
57
|
-
title: `Send Test Email`,
|
|
58
|
-
type: 'default',
|
|
59
|
-
icon: 'email',
|
|
60
|
-
isPending: validationMutation.isPending,
|
|
61
|
-
action: () => validationMutation.mutate({ settingsID, type: 'smtp', settings: settings, test: { email: testEmail } }),
|
|
62
|
-
}}
|
|
27
|
+
{isMobile() ? (
|
|
28
|
+
<Select
|
|
29
|
+
customClasses={classes.integrationSelect}
|
|
30
|
+
options={[
|
|
31
|
+
{ label: 'SMTP', value: 'smtp', icon: 'email' },
|
|
32
|
+
{ label: 'Ntfy', value: 'ntfy', icon: 'ntfy' },
|
|
33
|
+
]}
|
|
34
|
+
fieldValue={tab}
|
|
35
|
+
full={true}
|
|
36
|
+
onUpdate={(val) => setTab(val as 'smtp' | 'ntfy')}
|
|
63
37
|
/>
|
|
38
|
+
) : (
|
|
39
|
+
<>
|
|
40
|
+
<ul className={classes.tabs}>
|
|
41
|
+
<li className={`${tab === 'smtp' ? classes.tabActive : ''}`} onClick={() => setTab('smtp')}>
|
|
42
|
+
<Icon type="email" size={14} /> SMTP
|
|
43
|
+
{smtp?.connected && <Icon type="check-circle-filled" size={12} />}
|
|
44
|
+
</li>
|
|
45
|
+
<li className={`${tab === 'ntfy' ? classes.tabActive : ''}`} onClick={() => setTab('ntfy')}>
|
|
46
|
+
<Icon type="ntfy" size={14} /> Ntfy
|
|
47
|
+
{ntfy?.connected && <Icon type="check-circle-filled" size={12} />}
|
|
48
|
+
</li>
|
|
49
|
+
</ul>
|
|
50
|
+
</>
|
|
64
51
|
)}
|
|
52
|
+
<div>
|
|
53
|
+
{tab === 'smtp' && (
|
|
54
|
+
<SMTPSettings
|
|
55
|
+
settingsID={settingsID}
|
|
56
|
+
settings={integrationSettings}
|
|
57
|
+
onUpdate={(iSettings) => onIntegrationUpdate('smtp', iSettings)}
|
|
58
|
+
/>
|
|
59
|
+
)}
|
|
60
|
+
{tab === 'ntfy' && (
|
|
61
|
+
<NtfySettings
|
|
62
|
+
settingsID={settingsID}
|
|
63
|
+
settings={integrationSettings}
|
|
64
|
+
onUpdate={(iSettings) => onIntegrationUpdate('ntfy', iSettings)}
|
|
65
|
+
/>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
65
68
|
</div>
|
|
66
69
|
);
|
|
67
70
|
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import classes from './IntegrationSettings.module.scss';
|
|
3
|
+
import { IntegrationSettings, NtfySettingsType } from '../../../@types';
|
|
4
|
+
import Icon from '../../common/Icon/Icon';
|
|
5
|
+
import Select from '../../common/form/Select/Select';
|
|
6
|
+
import Input from '../../common/form/Input/Input';
|
|
7
|
+
import ActionModal from '../../common/ActionModal/ActionModal';
|
|
8
|
+
import { useValidateIntegration } from '../../../services';
|
|
9
|
+
import PasswordField from '../../common/form/PasswordField/PasswordField';
|
|
10
|
+
|
|
11
|
+
interface NtfySettingsProps {
|
|
12
|
+
settingsID: number;
|
|
13
|
+
settings: IntegrationSettings;
|
|
14
|
+
onUpdate: (settings: NtfySettingsType) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const NtfySettings = ({ settingsID, settings, onUpdate }: NtfySettingsProps) => {
|
|
18
|
+
const [ntfySettings, setNtfySettings] = useState<NtfySettingsType>(settings?.ntfy || { authType: 'token', authToken: '', connected: false });
|
|
19
|
+
const [errorFields, setErrorFields] = useState<{ authToken: string }>({ authToken: '' });
|
|
20
|
+
const [showTestModal, setShowTestModal] = useState(false);
|
|
21
|
+
const [testUrl, setTestUrl] = useState('');
|
|
22
|
+
const validationMutation = useValidateIntegration();
|
|
23
|
+
|
|
24
|
+
const authType = ntfySettings.authType || 'token';
|
|
25
|
+
|
|
26
|
+
const updateNtfySettings = (updated: NtfySettingsType) => {
|
|
27
|
+
setNtfySettings(updated);
|
|
28
|
+
onUpdate(updated);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const validateSettings = (e: React.FormEvent) => {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
|
|
34
|
+
const newErrors = { authToken: '' };
|
|
35
|
+
|
|
36
|
+
if (!ntfySettings?.authToken) {
|
|
37
|
+
newErrors.authToken = 'Required';
|
|
38
|
+
}
|
|
39
|
+
setErrorFields(newErrors);
|
|
40
|
+
const hasErrors = Object.values(newErrors).some((error) => error !== '');
|
|
41
|
+
if (!hasErrors) {
|
|
42
|
+
setShowTestModal(true);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div>
|
|
48
|
+
<div className={classes.field}>
|
|
49
|
+
<Select
|
|
50
|
+
label="Auth Type*"
|
|
51
|
+
fieldValue={authType}
|
|
52
|
+
options={[{ label: 'Token', value: 'token' }]}
|
|
53
|
+
onUpdate={(val) => updateNtfySettings({ ...ntfySettings, authType: val })}
|
|
54
|
+
inline={true}
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
<div className={classes.field}>
|
|
58
|
+
<PasswordField
|
|
59
|
+
label="Auth Token*"
|
|
60
|
+
fieldValue={ntfySettings.authToken}
|
|
61
|
+
onUpdate={(val) => updateNtfySettings({ ...ntfySettings, authToken: val })}
|
|
62
|
+
error={errorFields?.authToken}
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
<div className={classes.field}>
|
|
66
|
+
<button className={classes.validateBtn} onClick={validateSettings} type="button">
|
|
67
|
+
<Icon type="check" size={10} /> {ntfySettings.connected ? 'Re-validate Ntfy' : 'Validate Ntfy'}
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
{showTestModal && (
|
|
71
|
+
<ActionModal
|
|
72
|
+
title={`Test Ntfy Integration`}
|
|
73
|
+
message={
|
|
74
|
+
<>
|
|
75
|
+
<Input
|
|
76
|
+
label="Send Test Notification to this topic"
|
|
77
|
+
full={true}
|
|
78
|
+
inline={false}
|
|
79
|
+
fieldValue={testUrl}
|
|
80
|
+
onUpdate={(val) => setTestUrl(val)}
|
|
81
|
+
type="text"
|
|
82
|
+
placeholder="test/topic url. Eg: https://ntfy.sh/testtopic"
|
|
83
|
+
/>
|
|
84
|
+
</>
|
|
85
|
+
}
|
|
86
|
+
errorMessage={validationMutation.error?.message}
|
|
87
|
+
successMessage={validationMutation.isSuccess ? 'Test notification sent. Integration validated successfully.' : ''}
|
|
88
|
+
closeModal={() => setShowTestModal(false)}
|
|
89
|
+
width="400px"
|
|
90
|
+
secondaryAction={{ title: 'Close', action: () => setShowTestModal(false) }}
|
|
91
|
+
primaryAction={{
|
|
92
|
+
title: `Send Test Notification`,
|
|
93
|
+
type: 'default',
|
|
94
|
+
icon: 'send',
|
|
95
|
+
isPending: validationMutation.isPending,
|
|
96
|
+
action: () =>
|
|
97
|
+
testUrl &&
|
|
98
|
+
validationMutation.mutate({ settingsID, type: 'ntfy', settings: { ...settings, ntfy: ntfySettings }, test: { url: testUrl } }),
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export default NtfySettings;
|
|
@@ -3,42 +3,47 @@ import Input from '../../common/form/Input/Input';
|
|
|
3
3
|
import Icon from '../../common/Icon/Icon';
|
|
4
4
|
import { isValidEmail } from '../../../utils/helpers';
|
|
5
5
|
import { SmtpSettingsType } from '../../../@types/settings';
|
|
6
|
+
import { IntegrationSettings } from '../../../@types';
|
|
6
7
|
import classes from './IntegrationSettings.module.scss';
|
|
8
|
+
import ValidateEmailIntegration from './ValidateEmailIntegration';
|
|
7
9
|
|
|
8
10
|
interface SMTPSettingsProps {
|
|
9
|
-
|
|
11
|
+
settingsID: number;
|
|
12
|
+
settings: IntegrationSettings;
|
|
10
13
|
onUpdate: (settings: SmtpSettingsType) => void;
|
|
11
|
-
showTestModal: (type: 'smtp') => void;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
const SMTPSettings = ({ settings, onUpdate
|
|
16
|
+
const SMTPSettings = ({ settingsID, settings, onUpdate }: SMTPSettingsProps) => {
|
|
17
|
+
const [showTestModal, setShowTestModal] = useState(false);
|
|
15
18
|
const [errorFields, setErrorFields] = useState<{ server: string; port: string; senderEmail: string }>({
|
|
16
19
|
server: '',
|
|
17
20
|
port: '',
|
|
18
21
|
senderEmail: '',
|
|
19
22
|
});
|
|
20
23
|
|
|
24
|
+
const smtpSettings = settings?.smtp || { server: '', port: 587, senderEmail: '', username: '', password: '', connected: false };
|
|
25
|
+
|
|
21
26
|
const validateSettings = (e: React.FormEvent) => {
|
|
22
27
|
e.preventDefault();
|
|
23
28
|
|
|
24
29
|
const newErrors = { server: '', port: '', senderEmail: '' };
|
|
25
30
|
|
|
26
|
-
if (!
|
|
31
|
+
if (!smtpSettings?.server) {
|
|
27
32
|
newErrors.server = 'Server is required';
|
|
28
33
|
}
|
|
29
|
-
if (!
|
|
34
|
+
if (!smtpSettings?.port) {
|
|
30
35
|
newErrors.port = 'Port is required';
|
|
31
36
|
}
|
|
32
|
-
if (!
|
|
37
|
+
if (!smtpSettings?.senderEmail) {
|
|
33
38
|
newErrors.senderEmail = 'Sender Email is required';
|
|
34
|
-
} else if (!isValidEmail(
|
|
39
|
+
} else if (!isValidEmail(smtpSettings.senderEmail)) {
|
|
35
40
|
newErrors.senderEmail = 'Invalid email';
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
setErrorFields(newErrors);
|
|
39
44
|
const hasErrors = Object.values(newErrors).some((error) => error !== '');
|
|
40
45
|
if (!hasErrors) {
|
|
41
|
-
|
|
46
|
+
setShowTestModal(true);
|
|
42
47
|
}
|
|
43
48
|
};
|
|
44
49
|
|
|
@@ -47,16 +52,16 @@ const SMTPSettings = ({ settings, onUpdate, showTestModal }: SMTPSettingsProps)
|
|
|
47
52
|
<div className={classes.field}>
|
|
48
53
|
<Input
|
|
49
54
|
label="SMTP Server*"
|
|
50
|
-
fieldValue={(
|
|
51
|
-
onUpdate={(val) => onUpdate({ ...
|
|
55
|
+
fieldValue={(smtpSettings?.server || '') as string}
|
|
56
|
+
onUpdate={(val) => onUpdate({ ...smtpSettings, server: val })}
|
|
52
57
|
error={errorFields?.server}
|
|
53
58
|
/>
|
|
54
59
|
</div>
|
|
55
60
|
<div className={classes.field}>
|
|
56
61
|
<Input
|
|
57
62
|
label="SMTP PORT*"
|
|
58
|
-
fieldValue={(
|
|
59
|
-
onUpdate={(val) => onUpdate({ ...
|
|
63
|
+
fieldValue={(smtpSettings?.port || '') as string}
|
|
64
|
+
onUpdate={(val) => onUpdate({ ...smtpSettings, port: parseInt(val, 10) })}
|
|
60
65
|
error={errorFields?.port}
|
|
61
66
|
/>
|
|
62
67
|
</div>
|
|
@@ -64,32 +69,36 @@ const SMTPSettings = ({ settings, onUpdate, showTestModal }: SMTPSettingsProps)
|
|
|
64
69
|
<Input
|
|
65
70
|
label="Sender Email*"
|
|
66
71
|
type="email"
|
|
67
|
-
fieldValue={(
|
|
68
|
-
onUpdate={(val) => onUpdate({ ...
|
|
72
|
+
fieldValue={(smtpSettings?.senderEmail || '') as string}
|
|
73
|
+
onUpdate={(val) => onUpdate({ ...smtpSettings, senderEmail: val })}
|
|
69
74
|
error={errorFields?.senderEmail}
|
|
70
75
|
/>
|
|
71
76
|
</div>
|
|
72
77
|
<div className={classes.field}>
|
|
73
78
|
<Input
|
|
74
79
|
label="SMTP Username"
|
|
75
|
-
fieldValue={(
|
|
76
|
-
onUpdate={(val) => onUpdate({ ...
|
|
80
|
+
fieldValue={(smtpSettings?.username || '') as string}
|
|
81
|
+
onUpdate={(val) => onUpdate({ ...smtpSettings, username: val })}
|
|
77
82
|
/>
|
|
78
83
|
</div>
|
|
79
84
|
<div className={classes.field}>
|
|
80
85
|
<Input
|
|
81
86
|
label="SMTP Password"
|
|
82
|
-
fieldValue={(
|
|
87
|
+
fieldValue={(smtpSettings?.password || '') as string}
|
|
83
88
|
type="password"
|
|
84
|
-
onUpdate={(val) => onUpdate({ ...
|
|
89
|
+
onUpdate={(val) => onUpdate({ ...smtpSettings, password: val })}
|
|
85
90
|
/>
|
|
86
91
|
</div>
|
|
87
92
|
|
|
88
93
|
<div className={classes.field}>
|
|
89
94
|
<button className={classes.validateBtn} onClick={validateSettings} type="button">
|
|
90
|
-
<Icon type="check" size={10} /> {
|
|
95
|
+
<Icon type="check" size={10} /> {smtpSettings?.connected ? 'Re-validate SMTP' : 'Validate SMTP'}
|
|
91
96
|
</button>
|
|
92
97
|
</div>
|
|
98
|
+
|
|
99
|
+
{showTestModal && (
|
|
100
|
+
<ValidateEmailIntegration settingsID={settingsID} settings={settings} integrationType="smtp" onClose={() => setShowTestModal(false)} />
|
|
101
|
+
)}
|
|
93
102
|
</div>
|
|
94
103
|
);
|
|
95
104
|
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import Input from '../../common/form/Input/Input';
|
|
3
|
+
import ActionModal from '../../common/ActionModal/ActionModal';
|
|
4
|
+
import { useValidateIntegration } from '../../../services';
|
|
5
|
+
import { INTEGRATIONS_AVAILABLE, IntegrationSettings, IntegrationTypes } from '../../../@types';
|
|
6
|
+
|
|
7
|
+
interface ValidateEmailIntegrationProps {
|
|
8
|
+
settingsID: number;
|
|
9
|
+
settings: IntegrationSettings;
|
|
10
|
+
integrationType: IntegrationTypes;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const ValidateEmailIntegration = ({ settingsID, settings, integrationType, onClose }: ValidateEmailIntegrationProps) => {
|
|
15
|
+
const [testEmail, setTestEmail] = useState('');
|
|
16
|
+
const validationMutation = useValidateIntegration();
|
|
17
|
+
const integrationName = INTEGRATIONS_AVAILABLE[integrationType as IntegrationTypes].name;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<ActionModal
|
|
21
|
+
title={`Test ${integrationName} Integration`}
|
|
22
|
+
message={
|
|
23
|
+
<>
|
|
24
|
+
<Input
|
|
25
|
+
label="Send Test Email to this email"
|
|
26
|
+
full={true}
|
|
27
|
+
inline={false}
|
|
28
|
+
fieldValue={testEmail}
|
|
29
|
+
onUpdate={(val) => setTestEmail(val)}
|
|
30
|
+
type="email"
|
|
31
|
+
placeholder="test@test.com"
|
|
32
|
+
/>
|
|
33
|
+
</>
|
|
34
|
+
}
|
|
35
|
+
errorMessage={validationMutation.error?.message}
|
|
36
|
+
successMessage={validationMutation.isSuccess ? `Test email sent. ${integrationName} validated successfully.` : ''}
|
|
37
|
+
closeModal={onClose}
|
|
38
|
+
width="400px"
|
|
39
|
+
secondaryAction={{ title: 'Close', action: onClose }}
|
|
40
|
+
primaryAction={{
|
|
41
|
+
title: 'Send Test Email',
|
|
42
|
+
type: 'default',
|
|
43
|
+
icon: 'email',
|
|
44
|
+
isPending: validationMutation.isPending,
|
|
45
|
+
action: () =>
|
|
46
|
+
testEmail &&
|
|
47
|
+
validationMutation.mutate({
|
|
48
|
+
settingsID,
|
|
49
|
+
type: integrationType,
|
|
50
|
+
settings: { ...settings },
|
|
51
|
+
test: { email: testEmail },
|
|
52
|
+
}),
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default ValidateEmailIntegration;
|
|
@@ -39,7 +39,7 @@ const EditStorage = ({ close, storage }: EditStorageProps) => {
|
|
|
39
39
|
storage.storageFields.forEach((field: storageOptionField) => {
|
|
40
40
|
if (field.required) {
|
|
41
41
|
(groupedFields.required as storageOptionField[]).push(field);
|
|
42
|
-
} else {
|
|
42
|
+
} else if (!field.authFieldType) {
|
|
43
43
|
(groupedFields.optional as storageOptionField[]).push(field);
|
|
44
44
|
}
|
|
45
45
|
});
|
package/src/components/index.ts
CHANGED
|
@@ -113,6 +113,8 @@ export { default as AppLogs } from './Settings/AppLogs/AppLogs';
|
|
|
113
113
|
export { default as GeneralSettings } from './Settings/GeneralSettings/GeneralSettings';
|
|
114
114
|
export { default as IntegrationSettings } from './Settings/IntegrationSettings/IntegrationSettings';
|
|
115
115
|
export { default as SMTPSettings } from './Settings/IntegrationSettings/SMTPSettings';
|
|
116
|
+
export { default as NtfySettings } from './Settings/IntegrationSettings/NtfySettings';
|
|
117
|
+
export { default as ValidateEmailIntegration } from './Settings/IntegrationSettings/ValidateEmailIntegration';
|
|
116
118
|
export { default as TwoFactorSetup } from './Settings/TwoFactorSetup/TwoFactorSetup';
|
|
117
119
|
|
|
118
120
|
// Skeleton components
|
package/src/services/settings.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
|
2
2
|
import { experimental_createQueryPersister } from '@tanstack/react-query-persist-client';
|
|
3
3
|
import { API_URL } from '../utils/constants';
|
|
4
4
|
import { useNavigate } from 'react-router';
|
|
5
|
+
import { IntegrationSettings } from '../@types';
|
|
5
6
|
|
|
6
7
|
// ============== Settings API ==============
|
|
7
8
|
|
|
@@ -136,10 +137,9 @@ export function useGetDownloadAppLogs() {
|
|
|
136
137
|
export async function validateIntegration(updatePayload: {
|
|
137
138
|
settingsID: number;
|
|
138
139
|
type: string;
|
|
139
|
-
settings:
|
|
140
|
+
settings: IntegrationSettings;
|
|
140
141
|
test: Record<string, string | boolean | number>;
|
|
141
142
|
}) {
|
|
142
|
-
console.log('updatePayload :', updatePayload);
|
|
143
143
|
const header = new Headers({ 'Content-Type': 'application/json', Accept: 'application/json' });
|
|
144
144
|
const res = await fetch(`${API_URL}/settings/integration/validate`, {
|
|
145
145
|
method: 'POST',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|