mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
fix(email): test saved SMTP config and add TLS modes (#3388)
This commit is contained in:
1 parent
45b78a13c2
commit
cca8227ca6
66 files changed
+2464
-319
No files matched your search
@@ -41,6 +41,7 @@ import {
|
||||
ServicesStep,
|
||||
SignInMethodStep,
|
||||
ThemeStep,
|
||||
toSmtpTlsMode,
|
||||
WelcomeStep,
|
||||
} from '@app/features/app/components/setup/SetupWizardSteps';
|
||||
import {resolveAppShellBranding} from '@app/features/app/state/AppShellBranding';
|
||||
@@ -80,6 +81,7 @@ import {
|
||||
TagStyles,
|
||||
} from '@fluxer/constants/src/AccountIdentityConstants';
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import type {SmtpTlsMode} from '@fluxer/constants/src/SmtpConstants';
|
||||
import {type ThemeType, ThemeTypes} from '@fluxer/constants/src/UserConstants';
|
||||
import type {InstanceConfigResponse} from '@fluxer/schema/src/domains/admin/AdminSchemas';
|
||||
import type {MessageDescriptor} from '@lingui/core';
|
||||
@@ -199,7 +201,7 @@ const DEFAULT_INTEGRATION_DRAFT: ServiceIntegrationDraft = {
|
||||
smtpPort: '587',
|
||||
smtpUsername: '',
|
||||
smtpPassword: '',
|
||||
smtpSecure: true,
|
||||
smtpTlsMode: 'auto',
|
||||
blueskyMode: 'later',
|
||||
blueskyEnabled: true,
|
||||
blueskyClientName: '',
|
||||
@@ -345,7 +347,7 @@ function buildIntegrationsPatch(draft: ServiceIntegrationDraft, accountIdentity:
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
secure: boolean;
|
||||
tls_mode: SmtpTlsMode;
|
||||
};
|
||||
};
|
||||
bluesky?: {
|
||||
@@ -365,6 +367,7 @@ function buildIntegrationsPatch(draft: ServiceIntegrationDraft, accountIdentity:
|
||||
integrations.youtube = {api_key: draft.youtubeApiKey.trim()};
|
||||
}
|
||||
if (draft.emailMode === 'configure' && accountIdentity === AccountIdentityModes.EMAIL) {
|
||||
const smtpPort = parsePort(draft.smtpPort) ?? 587;
|
||||
integrations.email = {
|
||||
enabled: draft.emailEnabled,
|
||||
provider: 'smtp',
|
||||
@@ -372,10 +375,10 @@ function buildIntegrationsPatch(draft: ServiceIntegrationDraft, accountIdentity:
|
||||
from_name: draft.emailFromName.trim(),
|
||||
smtp: {
|
||||
host: draft.smtpHost.trim(),
|
||||
port: parsePort(draft.smtpPort) ?? 587,
|
||||
port: smtpPort,
|
||||
username: draft.smtpUsername.trim(),
|
||||
password: draft.smtpPassword.trim(),
|
||||
secure: draft.smtpSecure,
|
||||
tls_mode: toSmtpTlsMode(draft.smtpTlsMode, smtpPort),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -599,7 +602,7 @@ export const SelfHostedSetupWizardGate = observer(() => {
|
||||
smtpHost: next.integrations.email.smtp.host ?? current.smtpHost,
|
||||
smtpPort: next.integrations.email.smtp.port ? String(next.integrations.email.smtp.port) : current.smtpPort,
|
||||
smtpUsername: next.integrations.email.smtp.username ?? current.smtpUsername,
|
||||
smtpSecure: next.integrations.email.smtp.secure ?? current.smtpSecure,
|
||||
smtpTlsMode: next.integrations.email.smtp.tls_mode ?? current.smtpTlsMode,
|
||||
blueskyEnabled: next.integrations.bluesky.effective_enabled || next.integrations.bluesky.enabled !== false,
|
||||
blueskyClientName: next.integrations.bluesky.client_name ?? current.blueskyClientName,
|
||||
blueskyClientUri: next.integrations.bluesky.client_uri ?? current.blueskyClientUri,
|
||||
@@ -864,7 +867,7 @@ export const SelfHostedSetupWizardGate = observer(() => {
|
||||
port,
|
||||
username: integrationDraft.smtpUsername.trim(),
|
||||
password: integrationDraft.smtpPassword,
|
||||
secure: integrationDraft.smtpSecure,
|
||||
tls_mode: toSmtpTlsMode(integrationDraft.smtpTlsMode, port),
|
||||
});
|
||||
setSmtpTestResult(result.ok ? 'ok' : (result.error ?? 'failed'));
|
||||
} catch (error) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
type TagStyle,
|
||||
TagStyles,
|
||||
} from '@fluxer/constants/src/AccountIdentityConstants';
|
||||
import {resolveSmtpTlsMode, type SmtpTlsMode, SmtpTlsModes} from '@fluxer/constants/src/SmtpConstants';
|
||||
import type {ThemeType} from '@fluxer/constants/src/UserConstants';
|
||||
import type {MessageDescriptor} from '@lingui/core';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
@@ -1046,7 +1047,7 @@ export interface ServiceIntegrationDraft {
|
||||
smtpPort: string;
|
||||
smtpUsername: string;
|
||||
smtpPassword: string;
|
||||
smtpSecure: boolean;
|
||||
smtpTlsMode: SmtpTlsModeChoice;
|
||||
blueskyMode: IntegrationSetupMode;
|
||||
blueskyEnabled: boolean;
|
||||
blueskyClientName: string;
|
||||
@@ -1123,9 +1124,49 @@ const SMTP_HOST_LABEL_DESCRIPTOR = msg({message: 'SMTP host', comment: 'Label fo
|
||||
const SMTP_PORT_LABEL_DESCRIPTOR = msg({message: 'Port', comment: 'Label for SMTP port input.'});
|
||||
const SMTP_USERNAME_LABEL_DESCRIPTOR = msg({message: 'Username', comment: 'Label for SMTP username input.'});
|
||||
const SMTP_PASSWORD_LABEL_DESCRIPTOR = msg({message: 'Password', comment: 'Label for SMTP password input.'});
|
||||
const SMTP_SECURE_LABEL_DESCRIPTOR = msg({
|
||||
message: 'Use TLS',
|
||||
comment: 'Label for SMTP TLS toggle.',
|
||||
const SMTP_TLS_MODE_LABEL_DESCRIPTOR = msg({
|
||||
message: 'Encryption',
|
||||
comment: 'Label for the choice of how the SMTP connection is encrypted.',
|
||||
});
|
||||
const SMTP_TLS_MODE_AUTO_NAME_DESCRIPTOR = msg({
|
||||
message: 'Automatic',
|
||||
comment: 'SMTP encryption option that picks the mode from the port.',
|
||||
});
|
||||
const SMTP_TLS_MODE_AUTO_DESC_DESCRIPTOR = msg({
|
||||
message: 'Implicit TLS on port 465, required STARTTLS on any other port.',
|
||||
comment: 'Description of the automatic SMTP encryption option.',
|
||||
});
|
||||
const SMTP_TLS_MODE_IMPLICIT_NAME_DESCRIPTOR = msg({
|
||||
message: 'Implicit TLS',
|
||||
comment: 'SMTP encryption option where the connection starts encrypted.',
|
||||
});
|
||||
const SMTP_TLS_MODE_IMPLICIT_DESC_DESCRIPTOR = msg({
|
||||
message: 'The connection is encrypted from the start. Usually port 465.',
|
||||
comment: 'Description of the implicit TLS SMTP encryption option.',
|
||||
});
|
||||
const SMTP_TLS_MODE_STARTTLS_NAME_DESCRIPTOR = msg({
|
||||
message: 'STARTTLS',
|
||||
comment: 'SMTP encryption option where a plain connection must upgrade with STARTTLS.',
|
||||
});
|
||||
const SMTP_TLS_MODE_STARTTLS_DESC_DESCRIPTOR = msg({
|
||||
message: 'The connection must upgrade to TLS before signing in. Usually port 587.',
|
||||
comment: 'Description of the required STARTTLS SMTP encryption option.',
|
||||
});
|
||||
const SMTP_TLS_MODE_OPPORTUNISTIC_NAME_DESCRIPTOR = msg({
|
||||
message: 'STARTTLS when offered',
|
||||
comment: 'SMTP encryption option where STARTTLS is used only if the server offers it.',
|
||||
});
|
||||
const SMTP_TLS_MODE_OPPORTUNISTIC_DESC_DESCRIPTOR = msg({
|
||||
message: 'The connection upgrades to TLS only if the server offers it.',
|
||||
comment: 'Description of the opportunistic STARTTLS SMTP encryption option.',
|
||||
});
|
||||
const SMTP_TLS_MODE_NONE_NAME_DESCRIPTOR = msg({
|
||||
message: 'None',
|
||||
comment: 'SMTP encryption option that never uses TLS.',
|
||||
});
|
||||
const SMTP_TLS_MODE_NONE_DESC_DESCRIPTOR = msg({
|
||||
message: 'The connection is never encrypted. Only for a mail server on a network you trust.',
|
||||
comment: 'Description of the SMTP encryption option that never uses TLS.',
|
||||
});
|
||||
const SMTP_TEST_DESCRIPTOR = msg({
|
||||
message: 'Test SMTP',
|
||||
@@ -1172,6 +1213,44 @@ const BLUESKY_PRIVATE_KEY_LABEL_DESCRIPTOR = msg({
|
||||
comment: 'Label for Bluesky OAuth private key input.',
|
||||
});
|
||||
|
||||
export type SmtpTlsModeChoice = SmtpTlsMode | 'auto';
|
||||
|
||||
export function toSmtpTlsMode(choice: SmtpTlsModeChoice, port: number): SmtpTlsMode {
|
||||
return choice === 'auto' ? resolveSmtpTlsMode(null, port) : choice;
|
||||
}
|
||||
|
||||
function buildSmtpTlsModeOptions(
|
||||
i18n: ReturnType<typeof useLingui>['i18n'],
|
||||
): ReadonlyArray<RadioOption<SmtpTlsModeChoice>> {
|
||||
return [
|
||||
{
|
||||
value: 'auto',
|
||||
name: i18n._(SMTP_TLS_MODE_AUTO_NAME_DESCRIPTOR),
|
||||
desc: i18n._(SMTP_TLS_MODE_AUTO_DESC_DESCRIPTOR),
|
||||
},
|
||||
{
|
||||
value: SmtpTlsModes.IMPLICIT,
|
||||
name: i18n._(SMTP_TLS_MODE_IMPLICIT_NAME_DESCRIPTOR),
|
||||
desc: i18n._(SMTP_TLS_MODE_IMPLICIT_DESC_DESCRIPTOR),
|
||||
},
|
||||
{
|
||||
value: SmtpTlsModes.STARTTLS,
|
||||
name: i18n._(SMTP_TLS_MODE_STARTTLS_NAME_DESCRIPTOR),
|
||||
desc: i18n._(SMTP_TLS_MODE_STARTTLS_DESC_DESCRIPTOR),
|
||||
},
|
||||
{
|
||||
value: SmtpTlsModes.OPPORTUNISTIC,
|
||||
name: i18n._(SMTP_TLS_MODE_OPPORTUNISTIC_NAME_DESCRIPTOR),
|
||||
desc: i18n._(SMTP_TLS_MODE_OPPORTUNISTIC_DESC_DESCRIPTOR),
|
||||
},
|
||||
{
|
||||
value: SmtpTlsModes.NONE,
|
||||
name: i18n._(SMTP_TLS_MODE_NONE_NAME_DESCRIPTOR),
|
||||
desc: i18n._(SMTP_TLS_MODE_NONE_DESC_DESCRIPTOR),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function buildIntegrationModeOptions(
|
||||
i18n: ReturnType<typeof useLingui>['i18n'],
|
||||
): ReadonlyArray<RadioOption<IntegrationSetupMode>> {
|
||||
@@ -1490,12 +1569,19 @@ export const IntegrationStep = observer(
|
||||
disabled={disabled}
|
||||
data-flx="app.setup.setup-wizard-steps.integration-step.input.draft-change.password--2"
|
||||
/>
|
||||
<Switch
|
||||
label={i18n._(SMTP_SECURE_LABEL_DESCRIPTOR)}
|
||||
value={draft.smtpSecure}
|
||||
onChange={(value) => onDraftChange({smtpSecure: value})}
|
||||
<div
|
||||
className={styles.fieldLabel}
|
||||
data-flx="app.setup.setup-wizard-steps.integration-step.smtp-tls-mode-label"
|
||||
>
|
||||
{i18n._(SMTP_TLS_MODE_LABEL_DESCRIPTOR)}
|
||||
</div>
|
||||
<RadioGroup
|
||||
options={buildSmtpTlsModeOptions(i18n)}
|
||||
value={draft.smtpTlsMode}
|
||||
onChange={(value) => onDraftChange({smtpTlsMode: value})}
|
||||
disabled={disabled}
|
||||
data-flx="app.setup.setup-wizard-steps.integration-step.switch.draft-change--2"
|
||||
aria-label={i18n._(SMTP_TLS_MODE_LABEL_DESCRIPTOR)}
|
||||
data-flx="app.setup.setup-wizard-steps.integration-step.radio-group.smtp-tls-mode"
|
||||
/>
|
||||
<div
|
||||
className={styles.integrationActionRow}
|
||||
|
||||
Reference in new issue
Block a user