2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-03 04:00:57 +00:00

feat: more mail sending backends and plugability (#9608)

* [FR] Improve Email handeling
Fixes #7950

* extend implementation of email thread and message models

* add missing args

* add unit test

* increase test coverage

* make key not necessary

* do not consider in coverage

* add email apis

* Add email admin

* fix email configuration check

* improve rendering

* squash migrations

* add config value overview

* log if mails were send

* add additional headers

* fix api unit test

* fix url resolving

* add InvenTree specific task to issue mails
required to extend sending options (prio, reply to)

* use internal sending task to keep telemetry cleaner

* add prio handling

* add plugin handling

* add setting

* factor plugin method out

* add typing

* move function

* bump version

* fix import path

* add a test for the test endpoint

* fix checking logic

* Add anymail sending / tracking handling

* add more ordering fields to api

* remove unneeded assingment

* add basic docs

* handle incoming emails with anymail

* Add inbox handling
Closes https://github.com/inventree/InvenTree/issues/7951

* add list of supported ESPs

* add better error transparency when sending fails

* add missing migration

* combine migrations back down

* fix todos

* fix qc export

* fix missing model props

* add tests

* ensure things are passed as a list

* fix list formatting

* fix deps

* move tests

* add testing with anymail

* allow handling of priority and headers

* add test for events

* add test for inbound messages

* rename variable

* increase coverage

* fix format

* add setting doc

* fix link

* rename fnc

* disable pro test

* make messages clearer

* fix doc syntax

* fix assign

* fix test

* revert test disablement

* add enum

* disable check for now

* try changing test around

* add incoming mail processing

* fix import

* add docs

* Fix mail.md

* bump deps

* fix api version
This commit is contained in:
Matthias Mair
2025-06-20 03:49:02 +02:00
committed by GitHub
parent 797b5f57b0
commit 45daef8442
41 changed files with 1463 additions and 38 deletions

View File

@ -235,5 +235,8 @@ export enum ApiEndpoints {
error_report_list = 'error-report/',
project_code_list = 'project-code/',
custom_unit_list = 'units/',
notes_image_upload = 'notes-image-upload/'
notes_image_upload = 'notes-image-upload/',
email_list = 'admin/email/',
email_test = 'admin/email/test/',
config_list = 'admin/config/'
}

View File

@ -0,0 +1,41 @@
import { Code, Text } from '@mantine/core';
import { ApiEndpoints, apiUrl } from '@lib/index';
import { Trans } from '@lingui/react/macro';
import { useQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
import { api } from '../../App';
export function ConfigValueList({ keys }: Readonly<{ keys: string[] }>) {
const { data, isLoading } = useQuery({
queryKey: ['config'],
queryFn: async () => {
return api.get(apiUrl(ApiEndpoints.config_list)).then((res) => {
return res.data;
});
}
});
const totalData = useMemo(() => {
if (!data) return [];
return keys.map((key) => {
return {
key: key,
value: data.find((item: any) => item.key === key)
};
});
}, [isLoading, data, keys]);
return (
<span>
{totalData.map((vals) => (
<Text key={vals.key}>
<Trans>
<Code>{vals.key}</Code> is set via {vals.value?.source} and was last
set {vals.value.accessed}
</Trans>
</Text>
))}
</span>
);
}

View File

@ -0,0 +1,42 @@
import { t } from '@lingui/core/macro';
import { Accordion } from '@mantine/core';
import { StylishText } from '../../../../components/items/StylishText';
import { ConfigValueList } from '../../../../components/settings/ConfigValueList';
import { EmailTable } from '../../../../tables/settings/EmailTable';
export default function UserManagementPanel() {
return (
<Accordion multiple defaultValue={['emails']}>
<Accordion.Item value='emails' key='emails'>
<Accordion.Control>
<StylishText size='lg'>{t`Email Messages`}</StylishText>
</Accordion.Control>
<Accordion.Panel>
<EmailTable />
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item value='settings' key='settings'>
<Accordion.Control>
<StylishText size='lg'>{t`Settings`}</StylishText>
</Accordion.Control>
<Accordion.Panel>
<ConfigValueList
key='email_settings'
keys={[
'INVENTREE_EMAIL_BACKEND',
'INVENTREE_EMAIL_HOST',
'INVENTREE_EMAIL_PORT',
'INVENTREE_EMAIL_USERNAME',
'INVENTREE_EMAIL_PASSWORD',
'INVENTREE_EMAIL_PREFIX',
'INVENTREE_EMAIL_TLS',
'INVENTREE_EMAIL_SSL',
'INVENTREE_EMAIL_SENDER'
]}
/>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
);
}

View File

@ -10,6 +10,7 @@ import {
IconFileUpload,
IconList,
IconListDetails,
IconMail,
IconPackages,
IconPlugConnected,
IconQrcode,
@ -44,6 +45,10 @@ const UserManagementPanel = Loadable(
lazy(() => import('./UserManagementPanel'))
);
const EmailManagementPanel = Loadable(
lazy(() => import('./EmailManagementPanel'))
);
const TaskManagementPanel = Loadable(
lazy(() => import('./TaskManagementPanel'))
);
@ -112,6 +117,13 @@ export default function AdminCenter() {
content: <UserManagementPanel />,
hidden: !user.hasViewRole(UserRoles.admin)
},
{
name: 'email',
label: t`Email Settings`,
icon: <IconMail />,
content: <EmailManagementPanel />,
hidden: !user.isSuperuser()
},
{
name: 'import',
label: t`Data Import`,

View File

@ -63,6 +63,7 @@ export default function PluginManagementPanel() {
'ENABLE_PLUGINS_URL',
'ENABLE_PLUGINS_NAVIGATION',
'ENABLE_PLUGINS_APP',
'ENABLE_PLUGINS_MAILS',
'PLUGIN_ON_STARTUP',
'PLUGIN_UPDATE_CHECK'
]}

View File

@ -0,0 +1,109 @@
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { apiUrl } from '@lib/functions/Api';
import { t } from '@lingui/core/macro';
import { IconTestPipe } from '@tabler/icons-react';
import { useMemo } from 'react';
import { ActionButton } from '../../components/buttons/ActionButton';
import { useCreateApiFormModal } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable';
import { DateColumn } from '../ColumnRenderers';
import { InvenTreeTable } from '../InvenTreeTable';
export function EmailTable() {
const sendTestMail = useCreateApiFormModal({
url: ApiEndpoints.email_test,
title: t`Send Test Email`,
fields: { email: {} },
successMessage: t`Email sent successfully`,
onFormSuccess: (data: any) => {
table.refreshTable();
}
});
const tableActions = useMemo(() => {
return [
<ActionButton
icon={<IconTestPipe />}
key={'test'}
tooltip={t`Send Test Email`}
onClick={() => sendTestMail.open()}
/>
];
}, []);
const table = useTable('emails', 'id');
const tableColumns = useMemo(() => {
return [
{
accessor: 'subject',
title: t`Subject`,
sortable: true
},
{
accessor: 'to',
title: t`To`,
sortable: true
},
{
accessor: 'sender',
title: t`Sender`,
sortable: true
},
{
accessor: 'status',
title: t`Status`,
sortable: true,
render: (record: any) => {
switch (record.status) {
case 'A':
return t`Announced`;
case 'S':
return t`Sent`;
case 'F':
return t`Failed`;
case 'D':
return t`Delivered`;
case 'R':
return t`Read`;
case 'C':
return t`Confirmed`;
}
return '-';
},
switchable: true
},
{
accessor: 'direction',
title: t`Direction`,
sortable: true,
render: (record: any) => {
return record.direction === 'incoming' ? t`Incoming` : t`Outgoing`;
},
switchable: true
},
DateColumn({
accessor: 'timestamp',
title: t`Timestamp`,
sortable: true,
switchable: true
})
];
}, []);
return (
<>
{sendTestMail.modal}
<InvenTreeTable
tableState={table}
url={apiUrl(ApiEndpoints.email_list)}
columns={tableColumns}
props={{
enableSearch: true,
enableColumnSwitching: true,
tableActions: tableActions
}}
/>
</>
);
}