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 vform.CountryCode = mfaProps.CountryCode
} }
provider, err = application.GetSmsProvider(vform.Method) provider, err = application.GetSmsProvider(vform.Method, vform.CountryCode)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@ -38,7 +38,7 @@ func (application *Application) GetProviderByCategory(category string) (*Provide
return nil, nil 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) providers, err := GetProviders(application.Organization)
if err != nil { if err != nil {
return nil, err return nil, err
@ -54,7 +54,15 @@ func (application *Application) GetProviderByCategoryAndRule(category string, me
} }
for _, providerItem := range application.Providers { 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 { if provider, ok := m[providerItem.Name]; ok {
return provider, nil return provider, nil
} }
@ -65,11 +73,11 @@ func (application *Application) GetProviderByCategoryAndRule(category string, me
} }
func (application *Application) GetEmailProvider(method string) (*Provider, error) { 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) { func (application *Application) GetSmsProvider(method string, countryCode string) (*Provider, error) {
return application.GetProviderByCategoryAndRule("SMS", method) return application.GetProviderByCategoryAndRule("SMS", method, countryCode)
} }
func (application *Application) GetStorageProvider() (*Provider, error) { func (application *Application) GetStorageProvider() (*Provider, error) {

View File

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

View File

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

View File

@ -13,11 +13,14 @@
// limitations under the License. // limitations under the License.
import {Select} from "antd"; import {Select} from "antd";
import i18next from "i18next";
import * as Setting from "../../Setting"; import * as Setting from "../../Setting";
import React from "react"; import React from "react";
const {Option} = Select;
export const CountryCodeSelect = (props) => { export const CountryCodeSelect = (props) => {
const {onChange, style, disabled, initValue} = props; const {onChange, style, disabled, initValue, mode} = props;
const countryCodes = props.countryCodes ?? []; const countryCodes = props.countryCodes ?? [];
const [value, setValue] = React.useState(""); const [value, setValue] = React.useState("");
@ -42,11 +45,19 @@ export const CountryCodeSelect = (props) => {
style={style} style={style}
disabled={disabled} disabled={disabled}
value={value} value={value}
mode={mode}
dropdownMatchSelectWidth={false} dropdownMatchSelectWidth={false}
optionLabelProp={"label"} optionLabelProp={"label"}
onChange={handleOnChange} onChange={handleOnChange}
filterOption={(input, option) => (option?.text ?? "").toLowerCase().includes(input.toLowerCase())} 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)) Setting.getCountryCodeData(countryCodes).map((country) => Setting.getCountryCodeOption(country))
} }

View File

@ -15,6 +15,7 @@
import React from "react"; import React from "react";
import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons"; import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Input, Row, Select, Switch, Table, Tooltip} from "antd"; import {Button, Col, Input, Row, Select, Switch, Table, Tooltip} from "antd";
import {CountryCodeSelect} from "../common/select/CountryCodeSelect";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import i18next from "i18next"; import i18next from "i18next";
import * as Provider from "../auth/Provider"; import * as Provider from "../auth/Provider";
@ -29,6 +30,10 @@ class ProviderTable extends React.Component {
}; };
} }
getUserOrganization() {
return this.props.application?.organizationObj;
}
updateTable(table) { updateTable(table) {
this.props.onUpdateTable(table); this.props.onUpdateTable(table);
} }
@ -109,6 +114,30 @@ class ProviderTable extends React.Component {
return Provider.getProviderLogoWidget(provider); 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"), title: i18next.t("provider:Can signup"),
dataIndex: "canSignUp", dataIndex: "canSignUp",