Compare commits

..

7 Commits

Author SHA1 Message Date
DacongDA
cabe830f55 feat: use dynamic import to load web3Auth (#2757)
* feat: use dynamic import to load web3Auth and success reduce the size of signin page to 720KB when web3 idp disabled

* feat: avoid frequent import in OAuthWidget.js which may cause e2e test EPIPE error

* feat: remove import may cause e2e error

* feat: remove import may cause e2e error

* feat: remove bug may cause e2e error

* feat: try use chrome in ci/cd instead of electron to solve e2e error
2024-02-28 15:58:04 +08:00
DacongDA
78af5daec3 feat: use resourcesToBackend to load i18n files (#2755) 2024-02-28 01:43:55 +08:00
Lénaïc Grolleau
6c76913f71 fix: Set default value for email and SMS rule to all instead of none (#2754) 2024-02-28 01:28:59 +08:00
Yang Luo
5a0d1bcb6e Support login by user ID 2024-02-28 01:28:24 +08:00
Yang Luo
37232faa07 feat: fix bug for missing SMS and Email provider in application 2024-02-27 22:54:35 +08:00
Yang Luo
4d9c81ef96 Fix broken error messages 2024-02-27 22:48:33 +08:00
DacongDA
b0d87f60ae feat: use lazy load to load management pages (#2752) 2024-02-27 22:31:02 +08:00
14 changed files with 135 additions and 114 deletions

View File

@@ -108,6 +108,7 @@ jobs:
working-directory: ./web
- uses: cypress-io/github-action@v5
with:
browser: chrome
start: yarn start
wait-on: 'http://localhost:7001'
wait-on-timeout: 210

View File

@@ -54,7 +54,7 @@ func (application *Application) GetProviderByCategoryAndRule(category string, me
}
for _, providerItem := range application.Providers {
if providerItem.Rule == method || providerItem.Rule == "all" {
if providerItem.Rule == method || (providerItem.Rule == "all" || providerItem.Rule == "" || providerItem.Rule == "None") {
if provider, ok := m[providerItem.Name]; ok {
return provider, nil
}

View File

@@ -77,6 +77,12 @@ func GetUserByFields(organization string, field string) (*User, error) {
return user, err
}
// check user ID
user, err = GetUserByField(organization, "id", field)
if user != nil || err != nil {
return user, err
}
// check ID card
user, err = GetUserByField(organization, "id_card", field)
if user != nil || err != nil {

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import React, {Component} from "react";
import React, {Component, Suspense, lazy} from "react";
import "./App.less";
import {Helmet} from "react-helmet";
import * as Setting from "./Setting";
@@ -30,7 +30,7 @@ import AuthCallback from "./auth/AuthCallback";
import SamlCallback from "./auth/SamlCallback";
import i18next from "i18next";
import {withTranslation} from "react-i18next";
import ManagementPage from "./ManagementPage";
const ManagementPage = lazy(() => import("./ManagementPage"));
const {Footer, Content} = Layout;
import {setTwoToneColor} from "@ant-design/icons";
@@ -366,47 +366,49 @@ class App extends Component {
<FloatButton.BackTop />
<CustomGithubCorner />
{
<Layout id="parent-area">
<ManagementPage
account={this.state.account}
uri={this.state.uri}
themeData={this.state.themeData}
themeAlgorithm={this.state.themeAlgorithm}
selectedMenuKey={this.state.selectedMenuKey}
requiredEnableMfa={this.state.requiredEnableMfa}
menuVisible={this.state.menuVisible}
logo={this.state.logo}
onChangeTheme={this.setTheme}
onClick = {this.onClick}
onfinish={() => {
this.setState({requiredEnableMfa: false});
}}
openAiAssistant={() => {
this.setState({
isAiAssistantOpen: true,
});
}}
setLogoAndThemeAlgorithm={(nextThemeAlgorithm) => {
this.setState({
themeAlgorithm: nextThemeAlgorithm,
logo: this.getLogo(nextThemeAlgorithm),
});
localStorage.setItem("themeAlgorithm", JSON.stringify(nextThemeAlgorithm));
}}
setLogoutState={() => {
this.setState({
account: null,
themeAlgorithm: ["default"],
});
}}
/>
{
this.renderFooter()
}
{
this.renderAiAssistant()
}
</Layout>
<Suspense fallback={<div>loading</div>}>
<Layout id="parent-area">
<ManagementPage
account={this.state.account}
uri={this.state.uri}
themeData={this.state.themeData}
themeAlgorithm={this.state.themeAlgorithm}
selectedMenuKey={this.state.selectedMenuKey}
requiredEnableMfa={this.state.requiredEnableMfa}
menuVisible={this.state.menuVisible}
logo={this.state.logo}
onChangeTheme={this.setTheme}
onClick = {this.onClick}
onfinish={() => {
this.setState({requiredEnableMfa: false});
}}
openAiAssistant={() => {
this.setState({
isAiAssistantOpen: true,
});
}}
setLogoAndThemeAlgorithm={(nextThemeAlgorithm) => {
this.setState({
themeAlgorithm: nextThemeAlgorithm,
logo: this.getLogo(nextThemeAlgorithm),
});
localStorage.setItem("themeAlgorithm", JSON.stringify(nextThemeAlgorithm));
}}
setLogoutState={() => {
this.setState({
account: null,
themeAlgorithm: ["default"],
});
}}
/>
{
this.renderFooter()
}
{
this.renderAiAssistant()
}
</Layout>
</Suspense>
}
</React.Fragment>
);

View File

@@ -91,7 +91,7 @@ class PermissionListPage extends BaseListPage {
const {pagination} = this.state;
this.fetch({pagination});
} else {
Setting.showMessage("error", `Users failed to upload: ${res.msg}`);
Setting.showMessage("error", `${i18next.t("general:Failed to sync")}: ${res.msg}`);
}
} else if (status === "error") {
Setting.showMessage("error", "File failed to upload");

View File

@@ -83,7 +83,7 @@ class RoleListPage extends BaseListPage {
const {pagination} = this.state;
this.fetch({pagination});
} else {
Setting.showMessage("error", `Users failed to upload: ${res.msg}`);
Setting.showMessage("error", `${i18next.t("general:Failed to sync")}: ${res.msg}`);
}
} else if (status === "error") {
Setting.showMessage("error", "File failed to upload");

View File

@@ -454,7 +454,7 @@ class SyncerEditPage extends React.Component {
Setting.showMessage("success", i18next.t("syncer:Connect successfully"));
} else {
this.setState({testDbLoading: false});
Setting.showMessage("error", i18next.t("syncer:Failed to connect") + ": " + res.msg);
Setting.showMessage("error", `${i18next.t("syncer:Failed to connect")}: ${res.msg}`);
}
})
.catch(error => {

View File

@@ -17,7 +17,6 @@ import i18next from "i18next";
import * as Provider from "./Provider";
import {getProviderLogoURL} from "../Setting";
import {GithubLoginButton, GoogleLoginButton} from "react-social-login-buttons";
import {authViaMetaMask, authViaWeb3Onboard} from "./Web3Auth";
import QqLoginButton from "./QqLoginButton";
import FacebookLoginButton from "./FacebookLoginButton";
import WeiboLoginButton from "./WeiboLoginButton";
@@ -124,9 +123,17 @@ function goToSamlUrl(provider, location) {
export function goToWeb3Url(application, provider, method) {
if (provider.type === "MetaMask") {
authViaMetaMask(application, provider, method);
import("./Web3Auth")
.then(module => {
const authViaMetaMask = module.authViaMetaMask;
authViaMetaMask(application, provider, method);
});
} else if (provider.type === "Web3Onboard") {
authViaWeb3Onboard(application, provider, method);
import("./Web3Auth")
.then(module => {
const authViaWeb3Onboard = module.authViaWeb3Onboard;
authViaWeb3Onboard(application, provider, method);
});
}
}

View File

@@ -17,6 +17,10 @@ import LoginPage from "./LoginPage";
import {authConfig} from "./Auth";
class SelfLoginPage extends React.Component {
constructor(props) {
super(props);
import("../ManagementPage");
}
render() {
return (
<LoginPage type={"login"} mode={"signin"} applicationName={authConfig.appName} {...this.props} />

View File

@@ -223,7 +223,7 @@ class SignupPage extends React.Component {
Setting.goToLinkSoft(this, this.getResultPath(application, values));
}
} else {
Setting.showMessage("error", i18next.t(`signup:${res.msg}`));
Setting.showMessage("error", res.msg);
}
});
}

View File

@@ -147,7 +147,7 @@ export function sendCode(captchaType, captchaToken, clientSecret, method, countr
Setting.showMessage("success", i18next.t("user:Verification code sent"));
return true;
} else {
Setting.showMessage("error", i18next.t("user:" + res.msg));
Setting.showMessage("error", res.msg);
return false;
}
});

View File

@@ -20,7 +20,6 @@ import * as Setting from "../Setting";
import * as Provider from "../auth/Provider";
import * as AuthBackend from "../auth/AuthBackend";
import {goToWeb3Url} from "../auth/ProviderButton";
import {delWeb3AuthToken} from "../auth/Web3Auth";
import AccountAvatar from "../account/AccountAvatar";
class OAuthWidget extends React.Component {
@@ -98,7 +97,22 @@ class OAuthWidget extends React.Component {
user: this.props.user,
};
if (providerType === "MetaMask" || providerType === "Web3Onboard") {
delWeb3AuthToken(linkedValue);
import("../auth/Web3Auth")
.then(module => {
const delWeb3AuthToken = module.delWeb3AuthToken;
delWeb3AuthToken(linkedValue);
AuthBackend.unlink(body)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", "Unlinked successfully");
this.unlinked();
} else {
Setting.showMessage("error", `Failed to unlink: ${res.msg}`);
}
});
});
return;
}
AuthBackend.unlink(body)
.then((res) => {

View File

@@ -13,57 +13,34 @@
// limitations under the License.
import i18n from "i18next";
import en from "./locales/en/data.json";
import zh from "./locales/zh/data.json";
import es from "./locales/es/data.json";
import fr from "./locales/fr/data.json";
import de from "./locales/de/data.json";
import id from "./locales/id/data.json";
import ja from "./locales/ja/data.json";
import ko from "./locales/ko/data.json";
import ru from "./locales/ru/data.json";
import vi from "./locales/vi/data.json";
import pt from "./locales/pt/data.json";
import it from "./locales/it/data.json";
import ms from "./locales/ms/data.json";
import tr from "./locales/tr/data.json";
import ar from "./locales/ar/data.json";
import he from "./locales/he/data.json";
import nl from "./locales/nl/data.json";
import pl from "./locales/pl/data.json";
import fi from "./locales/fi/data.json";
import sv from "./locales/sv/data.json";
import uk from "./locales/uk/data.json";
import kk from "./locales/kk/data.json";
import fa from "./locales/fa/data.json";
import * as Conf from "./Conf";
import {initReactI18next} from "react-i18next";
import en from "./locales/en/data.json";
const resources = {
en: en,
zh: zh,
es: es,
fr: fr,
de: de,
id: id,
ja: ja,
ko: ko,
ru: ru,
vi: vi,
pt: pt,
it: it,
ms: ms,
tr: tr,
ar: ar,
he: he,
nl: nl,
pl: pl,
fi: fi,
sv: sv,
uk: uk,
kk: kk,
fa: fa,
};
const resourcesToBackend = (res) => ({
type: "backend",
init(services, backendOptions, i18nextOptions) {/* use services and options */},
read(language, namespace, callback) {
if (typeof res === "function") {
if (res.length < 3) {
try {
const r = res(language, namespace);
if (r && typeof r.then === "function") {
r.then((data) => callback(null, (data && data.default) || data)).catch(callback);
} else {
callback(null, r);
}
} catch (err) {
callback(err);
}
return;
}
res(language, namespace, callback);
return;
}
callback(null, res && res[language] && res[language][namespace]);
},
});
function initLanguage() {
let language = localStorage.getItem("language");
@@ -157,18 +134,23 @@ function initLanguage() {
return language;
}
i18n.use(initReactI18next).init({
lng: initLanguage(),
i18n.use(resourcesToBackend(async(language, namespace) => {
const res = await import(`./locales/${language}/data.json`);
return res.default[namespace];
}
))
.use(initReactI18next)
.init({
lng: initLanguage(),
ns: Object.keys(en),
fallbackLng: "en",
resources: resources,
keySeparator: false,
interpolation: {
escapeValue: true,
},
// debug: true,
saveMissing: true,
});
keySeparator: false,
interpolation: {
escapeValue: true,
},
// debug: true,
saveMissing: true,
});
export default i18n;

View File

@@ -76,6 +76,11 @@ class ProviderTable extends React.Component {
this.updateField(table, index, "name", value);
const provider = Setting.getArrayItem(this.props.providers, "name", value);
this.updateField(table, index, "provider", provider);
// If the provider is email or SMS, set the rule to "all" instead of the default "None"
if (provider.category === "Email" || provider.category === "SMS") {
this.updateField(table, index, "rule", "all");
}
}} >
{
Setting.getDeduplicatedArray(this.props.providers, table, "name").map((provider, index) => <Option key={index} value={provider.name}>{provider.name}</Option>)