Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/app/public/static/locales/en_US/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,14 @@
"transmission_method": "Transmission Method",
"smtp_label": "SMTP",
"ses_label": "SES(AWS)",
"oauth2_label": "OAuth 2.0 (Google Workspace)",
"oauth2_description": "Configure OAuth 2.0 authentication for sending emails using Google Workspace. You need to create OAuth 2.0 credentials in Google Cloud Console and obtain a refresh token.",
"oauth2_user": "Email Address",
"oauth2_user_help": "The email address of the authorized Google account",
"oauth2_client_id": "Client ID",
"oauth2_client_secret": "Client Secret",
"oauth2_refresh_token": "Refresh Token",
"oauth2_refresh_token_help": "The refresh token obtained from OAuth 2.0 authorization flow",
"send_test_email": "Send a test-email",
"success_to_send_test_email": "Success to send a test-email",
"smtp_settings": "SMTP settings",
Expand Down
8 changes: 8 additions & 0 deletions apps/app/public/static/locales/fr_FR/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,14 @@
"transmission_method": "Mode",
"smtp_label": "SMTP",
"ses_label": "SES(AWS)",
"oauth2_label": "OAuth 2.0 (Google Workspace)",
"oauth2_description": "Configurez l'authentification OAuth 2.0 pour envoyer des courriels en utilisant Google Workspace. Vous devez créer des identifiants OAuth 2.0 dans la console Google Cloud et obtenir un jeton de rafraîchissement.",
"oauth2_user": "Adresse courriel",
"oauth2_user_help": "L'adresse courriel du compte Google autorisé",
"oauth2_client_id": "ID client",
"oauth2_client_secret": "Secret client",
"oauth2_refresh_token": "Jeton de rafraîchissement",
"oauth2_refresh_token_help": "Le jeton de rafraîchissement obtenu à partir du flux d'autorisation OAuth 2.0",
"send_test_email": "Courriel d'essai",
"success_to_send_test_email": "Courriel d'essai envoyé",
"smtp_settings": "Configuration SMTP",
Expand Down
8 changes: 8 additions & 0 deletions apps/app/public/static/locales/ja_JP/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,14 @@
"transmission_method": "送信方法",
"smtp_label": "SMTP",
"ses_label": "SES(AWS)",
"oauth2_label": "OAuth 2.0 (Google Workspace)",
"oauth2_description": "Google Workspaceを使用してメールを送信するためのOAuth 2.0認証を設定します。Google Cloud ConsoleでOAuth 2.0認証情報を作成し、リフレッシュトークンを取得する必要があります。",
"oauth2_user": "メールアドレス",
"oauth2_user_help": "認証されたGoogleアカウントのメールアドレス",
"oauth2_client_id": "クライアントID",
"oauth2_client_secret": "クライアントシークレット",
"oauth2_refresh_token": "リフレッシュトークン",
"oauth2_refresh_token_help": "OAuth 2.0認証フローから取得したリフレッシュトークン",
"send_test_email": "テストメールを送信",
"success_to_send_test_email": "テストメールを送信しました。",
"smtp_settings": "SMTP設定",
Expand Down
8 changes: 8 additions & 0 deletions apps/app/public/static/locales/ko_KR/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,14 @@
"transmission_method": "전송 방식",
"smtp_label": "SMTP",
"ses_label": "SES(AWS)",
"oauth2_label": "OAuth 2.0 (Google Workspace)",
"oauth2_description": "Google Workspace를 사용하여 이메일을 보내기 위한 OAuth 2.0 인증을 구성합니다. Google Cloud Console에서 OAuth 2.0 자격 증명을 생성하고 갱신 토큰을 얻어야 합니다.",
"oauth2_user": "이메일 주소",
"oauth2_user_help": "인증된 Google 계정의 이메일 주소",
"oauth2_client_id": "클라이언트 ID",
"oauth2_client_secret": "클라이언트 시크릿",
"oauth2_refresh_token": "갱신 토큰",
"oauth2_refresh_token_help": "OAuth 2.0 인증 흐름에서 얻은 갱신 토큰",
"send_test_email": "테스트 이메일 전송",
"success_to_send_test_email": "테스트 이메일 전송 성공",
"smtp_settings": "SMTP 설정",
Expand Down
8 changes: 8 additions & 0 deletions apps/app/public/static/locales/zh_CN/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,14 @@
"transmission_method": "传送方法",
"smtp_label": "SMTP",
"ses_label": "SES(AWS)",
"oauth2_label": "OAuth 2.0 (Google Workspace)",
"oauth2_description": "配置 OAuth 2.0 身份验证以使用 Google Workspace 发送电子邮件。您需要在 Google Cloud Console 中创建 OAuth 2.0 凭据并获取刷新令牌。",
"oauth2_user": "电子邮件地址",
"oauth2_user_help": "已授权的 Google 帐户的电子邮件地址",
"oauth2_client_id": "客户端 ID",
"oauth2_client_secret": "客户端密钥",
"oauth2_refresh_token": "刷新令牌",
"oauth2_refresh_token_help": "从 OAuth 2.0 授权流程获得的刷新令牌",
"from_e-mail_address": "邮件发出地址",
"send_test_email": "发送测试邮件",
"success_to_send_test_email": "成功发送了一封测试邮件",
Expand Down
20 changes: 18 additions & 2 deletions apps/app/src/client/components/Admin/App/MailSetting.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useCallback, useEffect } from 'react';
import { useCallback, useEffect } from 'react';
import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form';

import AdminAppContainer from '~/client/services/AdminAppContainer';
import { toastError, toastSuccess } from '~/client/util/toastr';

import { withUnstatedContainers } from '../../UnstatedUtils';
import { OAuth2Setting } from './OAuth2Setting';
import { SesSetting } from './SesSetting';
import { SmtpSetting } from './SmtpSetting';

Expand All @@ -17,7 +18,7 @@ const MailSetting = (props: Props) => {
const { t } = useTranslation(['admin', 'commons']);
const { adminAppContainer } = props;

const transmissionMethods = ['smtp', 'ses'];
const transmissionMethods = ['smtp', 'ses', 'oauth2'];

const { register, handleSubmit, reset, watch } = useForm();

Expand All @@ -38,6 +39,10 @@ const MailSetting = (props: Props) => {
smtpPassword: adminAppContainer.state.smtpPassword || '',
sesAccessKeyId: adminAppContainer.state.sesAccessKeyId || '',
sesSecretAccessKey: adminAppContainer.state.sesSecretAccessKey || '',
oauth2ClientId: adminAppContainer.state.oauth2ClientId || '',
oauth2ClientSecret: adminAppContainer.state.oauth2ClientSecret || '',
oauth2RefreshToken: adminAppContainer.state.oauth2RefreshToken || '',
oauth2User: adminAppContainer.state.oauth2User || '',
});
}, [
adminAppContainer.state.fromAddress,
Expand All @@ -48,6 +53,10 @@ const MailSetting = (props: Props) => {
adminAppContainer.state.smtpPassword,
adminAppContainer.state.sesAccessKeyId,
adminAppContainer.state.sesSecretAccessKey,
adminAppContainer.state.oauth2ClientId,
adminAppContainer.state.oauth2ClientSecret,
adminAppContainer.state.oauth2RefreshToken,
adminAppContainer.state.oauth2User,
reset,
]);

Expand All @@ -64,6 +73,10 @@ const MailSetting = (props: Props) => {
adminAppContainer.changeSmtpPassword(data.smtpPassword),
adminAppContainer.changeSesAccessKeyId(data.sesAccessKeyId),
adminAppContainer.changeSesSecretAccessKey(data.sesSecretAccessKey),
adminAppContainer.changeOAuth2ClientId(data.oauth2ClientId),
adminAppContainer.changeOAuth2ClientSecret(data.oauth2ClientSecret),
adminAppContainer.changeOAuth2RefreshToken(data.oauth2RefreshToken),
adminAppContainer.changeOAuth2User(data.oauth2User),
]);

await adminAppContainer.updateMailSettingHandler();
Expand Down Expand Up @@ -149,6 +162,9 @@ const MailSetting = (props: Props) => {
{currentTransmissionMethod === 'ses' && (
<SesSetting register={register} />
)}
{currentTransmissionMethod === 'oauth2' && (
<OAuth2Setting register={register} />
)}

<div className="row my-3">
<div className="col-md-3"></div>
Expand Down
116 changes: 116 additions & 0 deletions apps/app/src/client/components/Admin/App/OAuth2Setting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { useTranslation } from 'next-i18next';
import type { UseFormRegister } from 'react-hook-form';

import AdminAppContainer from '~/client/services/AdminAppContainer';

import { withUnstatedContainers } from '../../UnstatedUtils';

type Props = {
adminAppContainer?: AdminAppContainer;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
register: UseFormRegister<any>;
};

const OAuth2Setting = (props: Props): JSX.Element => {
const { t } = useTranslation();
const { register } = props;

return (
<div id="mail-oauth2" className="tab-pane active">
<div className="row mb-3">
<div className="col-md-12">
<div className="alert alert-info">
<span className="material-symbols-outlined">info</span>{' '}
{t('admin:app_setting.oauth2_description')}
</div>
</div>
</div>

<div className="row">
<label
className="text-start text-md-end col-md-3 col-form-label"
htmlFor="admin-oauth2-user"
>
{t('admin:app_setting.oauth2_user')}
</label>
<div className="col-md-6">
<input
className="form-control"
type="email"
id="admin-oauth2-user"
placeholder="[email protected]"
{...register('oauth2User')}
/>
<small className="form-text text-muted">
{t('admin:app_setting.oauth2_user_help')}
</small>
</div>
</div>

<div className="row">
<label
className="text-start text-md-end col-md-3 col-form-label"
htmlFor="admin-oauth2-client-id"
>
{t('admin:app_setting.oauth2_client_id')}
</label>
<div className="col-md-6">
<input
className="form-control"
type="text"
id="admin-oauth2-client-id"
{...register('oauth2ClientId')}
/>
</div>
</div>

<div className="row">
<label
className="text-start text-md-end col-md-3 col-form-label"
htmlFor="admin-oauth2-client-secret"
>
{t('admin:app_setting.oauth2_client_secret')}
</label>
<div className="col-md-6">
<input
className="form-control"
type="password"
id="admin-oauth2-client-secret"
{...register('oauth2ClientSecret')}
/>
</div>
</div>

<div className="row">
<label
className="text-start text-md-end col-md-3 col-form-label"
htmlFor="admin-oauth2-refresh-token"
>
{t('admin:app_setting.oauth2_refresh_token')}
</label>
<div className="col-md-6">
<input
className="form-control"
type="password"
id="admin-oauth2-refresh-token"
{...register('oauth2RefreshToken')}
/>
<small className="form-text text-muted">
{t('admin:app_setting.oauth2_refresh_token_help')}
</small>
</div>
</div>
</div>
);
};

export { OAuth2Setting };

/**
* Wrapper component for using unstated
*/
const OAuth2SettingWrapper = withUnstatedContainers(OAuth2Setting, [
AdminAppContainer,
]);

export default OAuth2SettingWrapper;
60 changes: 60 additions & 0 deletions apps/app/src/client/services/AdminAppContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export default class AdminAppContainer extends Container {
sesAccessKeyId: '',
sesSecretAccessKey: '',

oauth2ClientId: '',
oauth2ClientSecret: '',
oauth2RefreshToken: '',
oauth2User: '',

isMaintenanceMode: false,
};
}
Expand Down Expand Up @@ -78,6 +83,11 @@ export default class AdminAppContainer extends Container {
sesAccessKeyId: appSettingsParams.sesAccessKeyId,
sesSecretAccessKey: appSettingsParams.sesSecretAccessKey,

oauth2ClientId: appSettingsParams.oauth2ClientId,
oauth2ClientSecret: appSettingsParams.oauth2ClientSecret,
oauth2RefreshToken: appSettingsParams.oauth2RefreshToken,
oauth2User: appSettingsParams.oauth2User,

isMaintenanceMode: appSettingsParams.isMaintenanceMode,
});
}
Expand Down Expand Up @@ -187,6 +197,34 @@ export default class AdminAppContainer extends Container {
this.setState({ sesSecretAccessKey });
}

/**
* Change oauth2ClientId
*/
changeOAuth2ClientId(oauth2ClientId) {
this.setState({ oauth2ClientId });
}

/**
* Change oauth2ClientSecret
*/
changeOAuth2ClientSecret(oauth2ClientSecret) {
this.setState({ oauth2ClientSecret });
}

/**
* Change oauth2RefreshToken
*/
changeOAuth2RefreshToken(oauth2RefreshToken) {
this.setState({ oauth2RefreshToken });
}

/**
* Change oauth2User
*/
changeOAuth2User(oauth2User) {
this.setState({ oauth2User });
}

/**
* Update app setting
* @memberOf AdminAppContainer
Expand Down Expand Up @@ -226,6 +264,9 @@ export default class AdminAppContainer extends Container {
if (this.state.transmissionMethod === 'smtp') {
return this.updateSmtpSetting();
}
if (this.state.transmissionMethod === 'oauth2') {
return this.updateOAuth2Setting();
}
return this.updateSesSetting();
}

Expand Down Expand Up @@ -265,6 +306,25 @@ export default class AdminAppContainer extends Container {
return mailSettingParams;
}

/**
* Update OAuth 2.0 setting
* @memberOf AdminAppContainer
* @return {Array} Appearance
*/
async updateOAuth2Setting() {
const response = await apiv3Put('/app-settings/oauth2-setting', {
fromAddress: this.state.fromAddress,
transmissionMethod: this.state.transmissionMethod,
oauth2ClientId: this.state.oauth2ClientId,
oauth2ClientSecret: this.state.oauth2ClientSecret,
oauth2RefreshToken: this.state.oauth2RefreshToken,
oauth2User: this.state.oauth2User,
});
const { mailSettingParams } = response.data;
this.setState({ isMailerSetup: mailSettingParams.isMailerSetup });
return mailSettingParams;
}

/**
* send test e-mail
* @memberOf AdminAppContainer
Expand Down
3 changes: 3 additions & 0 deletions apps/app/src/interfaces/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const ACTION_ADMIN_APP_SETTINGS_UPDATE = 'ADMIN_APP_SETTING_UPDATE';
const ACTION_ADMIN_SITE_URL_UPDATE = 'ADMIN_SITE_URL_UPDATE';
const ACTION_ADMIN_MAIL_SMTP_UPDATE = 'ADMIN_MAIL_SMTP_UPDATE';
const ACTION_ADMIN_MAIL_SES_UPDATE = 'ADMIN_MAIL_SES_UPDATE';
const ACTION_ADMIN_MAIL_OAUTH2_UPDATE = 'ADMIN_MAIL_OAUTH2_UPDATE';
const ACTION_ADMIN_MAIL_TEST_SUBMIT = 'ADMIN_MAIL_TEST_SUBMIT';
const ACTION_ADMIN_FILE_UPLOAD_CONFIG_UPDATE =
'ADMIN_FILE_UPLOAD_CONFIG_UPDATE';
Expand Down Expand Up @@ -281,6 +282,7 @@ export const SupportedAction = {
ACTION_ADMIN_SITE_URL_UPDATE,
ACTION_ADMIN_MAIL_SMTP_UPDATE,
ACTION_ADMIN_MAIL_SES_UPDATE,
ACTION_ADMIN_MAIL_OAUTH2_UPDATE,
ACTION_ADMIN_MAIL_TEST_SUBMIT,
ACTION_ADMIN_FILE_UPLOAD_CONFIG_UPDATE,
ACTION_ADMIN_PAGE_BULK_EXPORT_SETTINGS_UPDATE,
Expand Down Expand Up @@ -472,6 +474,7 @@ export const LargeActionGroup = {
ACTION_ADMIN_SITE_URL_UPDATE,
ACTION_ADMIN_MAIL_SMTP_UPDATE,
ACTION_ADMIN_MAIL_SES_UPDATE,
ACTION_ADMIN_MAIL_OAUTH2_UPDATE,
ACTION_ADMIN_MAIL_TEST_SUBMIT,
ACTION_ADMIN_FILE_UPLOAD_CONFIG_UPDATE,
ACTION_ADMIN_PAGE_BULK_EXPORT_SETTINGS_UPDATE,
Expand Down
Loading
Loading