mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-07 05:17:48 +08:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cabe830f55 | ||
![]() |
78af5daec3 | ||
![]() |
6c76913f71 | ||
![]() |
5a0d1bcb6e | ||
![]() |
37232faa07 | ||
![]() |
4d9c81ef96 | ||
![]() |
b0d87f60ae |
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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>
|
||||
);
|
||||
|
@@ -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");
|
||||
|
@@ -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");
|
||||
|
@@ -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 => {
|
||||
|
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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} />
|
||||
|
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
});
|
||||
|
@@ -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) => {
|
||||
|
102
web/src/i18n.js
102
web/src/i18n.js
@@ -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;
|
||||
|
@@ -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>)
|
||||
|
Reference in New Issue
Block a user