feat: support multiple SMS providers for different regions (#2914)

* feat: support using different sms provider for different region

* feat: add multiple support for select and remove log

* feat: revert change for countryCode in loginPage

* feat: revert change for countryCode in user_util.go

* feat: revert change for countryCode in auth.go

* Update application_item.go

* Update CountryCodeSelect.js

* Update ProviderTable.js

---------

Co-authored-by: Yang Luo <hsluoyz@qq.com>
This commit is contained in:
DacongDA
2024-05-01 00:40:47 +08:00
committed by GitHub
parent 199f1d4d10
commit 3d29e27d54
6 changed files with 57 additions and 8 deletions

View File

@ -295,7 +295,7 @@ func (c *ApiController) SendVerificationCode() {
vform.CountryCode = mfaProps.CountryCode
}
provider, err = application.GetSmsProvider(vform.Method)
provider, err = application.GetSmsProvider(vform.Method, vform.CountryCode)
if err != nil {
c.ResponseError(err.Error())
return

View File

@ -38,7 +38,7 @@ func (application *Application) GetProviderByCategory(category string) (*Provide
return nil, nil
}
func (application *Application) GetProviderByCategoryAndRule(category string, method string) (*Provider, error) {
func (application *Application) GetProviderByCategoryAndRule(category string, method string, countryCode string) (*Provider, error) {
providers, err := GetProviders(application.Organization)
if err != nil {
return nil, err
@ -54,7 +54,15 @@ func (application *Application) GetProviderByCategoryAndRule(category string, me
}
for _, providerItem := range application.Providers {
if providerItem.Rule == method || (providerItem.Rule == "all" || providerItem.Rule == "" || providerItem.Rule == "None") {
includeCode := false
if providerItem.CountryCode != nil {
for _, item := range providerItem.CountryCode {
if item == countryCode || item == "All" || item == "" {
includeCode = true
}
}
}
if (providerItem.Rule == method || (providerItem.Rule == "All" || providerItem.Rule == "" || providerItem.Rule == "None")) && includeCode {
if provider, ok := m[providerItem.Name]; ok {
return provider, nil
}
@ -65,11 +73,11 @@ func (application *Application) GetProviderByCategoryAndRule(category string, me
}
func (application *Application) GetEmailProvider(method string) (*Provider, error) {
return application.GetProviderByCategoryAndRule("Email", method)
return application.GetProviderByCategoryAndRule("Email", method, "All")
}
func (application *Application) GetSmsProvider(method string) (*Provider, error) {
return application.GetProviderByCategoryAndRule("SMS", method)
func (application *Application) GetSmsProvider(method string, countryCode string) (*Provider, error) {
return application.GetProviderByCategoryAndRule("SMS", method, countryCode)
}
func (application *Application) GetStorageProvider() (*Provider, error) {

View File

@ -21,6 +21,7 @@ type ProviderItem struct {
CanSignUp bool `json:"canSignUp"`
CanSignIn bool `json:"canSignIn"`
CanUnlink bool `json:"canUnlink"`
CountryCode []string `json:"countryCode"`
Prompted bool `json:"prompted"`
SignupGroup string `json:"signupGroup"`
Rule string `json:"rule"`

View File

@ -840,7 +840,7 @@ class LoginPage extends React.Component {
{application.displayName}
</span>
</a>
:
:
</div>
<br />
{

View File

@ -13,11 +13,14 @@
// limitations under the License.
import {Select} from "antd";
import i18next from "i18next";
import * as Setting from "../../Setting";
import React from "react";
const {Option} = Select;
export const CountryCodeSelect = (props) => {
const {onChange, style, disabled, initValue} = props;
const {onChange, style, disabled, initValue, mode} = props;
const countryCodes = props.countryCodes ?? [];
const [value, setValue] = React.useState("");
@ -42,11 +45,19 @@ export const CountryCodeSelect = (props) => {
style={style}
disabled={disabled}
value={value}
mode={mode}
dropdownMatchSelectWidth={false}
optionLabelProp={"label"}
onChange={handleOnChange}
filterOption={(input, option) => (option?.text ?? "").toLowerCase().includes(input.toLowerCase())}
>
{
props.hasDefault ? (<Option key={"All"} value={"All"} label={i18next.t("organization:All")} text={"organization:All"} >
<div style={{display: "flex", justifyContent: "space-between", marginRight: "10px"}}>
{i18next.t("organization:All")}
</div>
</Option>) : null
}
{
Setting.getCountryCodeData(countryCodes).map((country) => Setting.getCountryCodeOption(country))
}

View File

@ -15,6 +15,7 @@
import React from "react";
import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Input, Row, Select, Switch, Table, Tooltip} from "antd";
import {CountryCodeSelect} from "../common/select/CountryCodeSelect";
import * as Setting from "../Setting";
import i18next from "i18next";
import * as Provider from "../auth/Provider";
@ -29,6 +30,10 @@ class ProviderTable extends React.Component {
};
}
getUserOrganization() {
return this.props.application?.organizationObj;
}
updateTable(table) {
this.props.onUpdateTable(table);
}
@ -109,6 +114,30 @@ class ProviderTable extends React.Component {
return Provider.getProviderLogoWidget(provider);
},
},
{
title: i18next.t("user:Country/Region"),
dataIndex: "countryCode",
key: "countryCode",
width: "80px",
render: (text, record, index) => {
if (record.provider?.category !== "SMS") {
return null;
}
return (
<CountryCodeSelect
style={{width: "100%"}}
hasDefault={true}
mode={"multiple"}
initValue={text ? text : ["All"]}
onChange={(value) => {
this.updateField(table, index, "countryCode", value);
}}
countryCodes={this.getUserOrganization()?.countryCodes}
/>
);
},
},
{
title: i18next.t("provider:Can signup"),
dataIndex: "canSignUp",