zh-CN support & SSO login support
This commit is contained in:
parent
c6b6e54617
commit
786598b4eb
@ -24,6 +24,7 @@
|
||||
"dependencies": {
|
||||
"papaparse": "^5.2.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"ra-language-chinese": "^2.0.10",
|
||||
"ra-language-german": "^2.1.2",
|
||||
"react": "^16.13.1",
|
||||
"react-admin": "^3.10.0",
|
||||
|
@ -16,11 +16,13 @@ import { ImportFeature } from "./components/ImportFeature";
|
||||
import { Route } from "react-router-dom";
|
||||
import germanMessages from "./i18n/de";
|
||||
import englishMessages from "./i18n/en";
|
||||
import chineseMessages from "./i18n/zh";
|
||||
|
||||
// TODO: Can we use lazy loading together with browser locale?
|
||||
const messages = {
|
||||
de: germanMessages,
|
||||
en: englishMessages,
|
||||
zh: chineseMessages
|
||||
};
|
||||
const i18nProvider = polyglotI18nProvider(
|
||||
locale => (messages[locale] ? messages[locale] : messages.en),
|
||||
|
@ -78,10 +78,47 @@ const LoginPage = ({ theme }) => {
|
||||
const login = useLogin();
|
||||
const notify = useNotify();
|
||||
const [loading, setLoading] = useState(false);
|
||||
var locale = useLocale();
|
||||
const [ssoBaseUrl, setSSOBaseUrl] = useState("");
|
||||
let locale = useLocale();
|
||||
const setLocale = useSetLocale();
|
||||
const translate = useTranslate();
|
||||
const base_url = localStorage.getItem("base_url");
|
||||
const tokenReg = /\?loginToken=([a-zA-Z0-9_-]+)(?:#\/)?/;
|
||||
const retToken = tokenReg.exec(window.location.href);
|
||||
const ssoToken = localStorage.getItem("sso_ret_token");
|
||||
|
||||
if (retToken) {
|
||||
console.log('SSO token is', retToken[1]);
|
||||
localStorage.setItem("sso_ret_token", retToken[1]);
|
||||
console.log('SSO token saved. Reloading the page to prevent loging again.');
|
||||
window.location.href = window.location.origin; // prevent further requests
|
||||
} else if (ssoToken) {
|
||||
const baseUrl = localStorage.getItem("sso_base_url");
|
||||
localStorage.removeItem("sso_base_url");
|
||||
localStorage.removeItem("sso_ret_token");
|
||||
if (baseUrl) {
|
||||
const auth = {
|
||||
base_url: baseUrl,
|
||||
username: null,
|
||||
password: null,
|
||||
loginToken: ssoToken,
|
||||
};
|
||||
console.log('Base URL is:', baseUrl);
|
||||
console.log('SSO Token is:', ssoToken);
|
||||
console.log('Let\'s try token login...');
|
||||
login(auth)
|
||||
.catch(error => {
|
||||
alert(
|
||||
typeof error === "string"
|
||||
? error
|
||||
: typeof error === "undefined" || !error.message
|
||||
? "ra.auth.sign_in_error"
|
||||
: error.message
|
||||
);
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const renderInput = ({
|
||||
meta: { touched, error } = {},
|
||||
@ -134,6 +171,12 @@ const LoginPage = ({ theme }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleSSO = () => {
|
||||
localStorage.setItem("sso_base_url", ssoBaseUrl);
|
||||
const ssoFullUrl = `${ssoBaseUrl}/_matrix/client/r0/login/sso/redirect?redirectUrl=${encodeURIComponent(window.location.href)}`;
|
||||
window.location.href = ssoFullUrl;
|
||||
};
|
||||
|
||||
const extractHomeServer = username => {
|
||||
const usernameRegex = /@[a-zA-Z0-9._=\-/]+:([a-zA-Z0-9\-.]+\.[a-zA-Z]+)/;
|
||||
if (!username) return null;
|
||||
@ -185,6 +228,26 @@ const LoginPage = ({ theme }) => {
|
||||
.catch(_ => {
|
||||
setServerVersion("");
|
||||
});
|
||||
|
||||
// setSSOUrl
|
||||
const authMethodUrl = `${formData.base_url}/_matrix/client/r0/login`;
|
||||
let supportSSO = false;
|
||||
fetchUtils
|
||||
.fetchJson(authMethodUrl, { method: "GET" })
|
||||
.then(({ json }) => {
|
||||
json.flows.forEach(f => {
|
||||
if (f.type === 'm.login.sso') {
|
||||
setSSOBaseUrl(formData.base_url);
|
||||
supportSSO = true;
|
||||
}
|
||||
});
|
||||
if (!supportSSO) {
|
||||
setSSOBaseUrl("");
|
||||
}
|
||||
})
|
||||
.catch(_ => {
|
||||
setSSOBaseUrl("");
|
||||
});
|
||||
},
|
||||
[formData.base_url]
|
||||
);
|
||||
@ -255,6 +318,7 @@ const LoginPage = ({ theme }) => {
|
||||
>
|
||||
<MenuItem value="de">Deutsch</MenuItem>
|
||||
<MenuItem value="en">English</MenuItem>
|
||||
<MenuItem value="zh">简体中文</MenuItem>
|
||||
</Select>
|
||||
</div>
|
||||
<FormDataConsumer>
|
||||
@ -273,6 +337,17 @@ const LoginPage = ({ theme }) => {
|
||||
{loading && <CircularProgress size={25} thickness={2} />}
|
||||
{translate("ra.auth.sign_in")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleSSO}
|
||||
disabled={loading || ssoBaseUrl === ""}
|
||||
className={classes.button}
|
||||
fullWidth
|
||||
>
|
||||
{loading && <CircularProgress size={25} thickness={2} />}
|
||||
{translate("synapseadmin.auth.sso_sign_in")}
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
<Notification />
|
||||
|
@ -10,6 +10,7 @@ export default {
|
||||
username_error: "Bitte vollständigen Nutzernamen angeben: '@user:domain'",
|
||||
protocol_error: "Die URL muss mit 'http://' oder 'https://' beginnen",
|
||||
url_error: "Keine gültige Matrix Server URL",
|
||||
sso_sign_in: "Anmeldung mit SSO",
|
||||
},
|
||||
users: {
|
||||
invalid_user_id:
|
||||
|
@ -10,6 +10,7 @@ export default {
|
||||
username_error: "Please enter fully qualified user ID: '@user:domain'",
|
||||
protocol_error: "URL has to start with 'http://' or 'https://'",
|
||||
url_error: "Not a valid Matrix server URL",
|
||||
sso_sign_in: "Sign in with SSO",
|
||||
},
|
||||
users: {
|
||||
invalid_user_id:
|
||||
|
276
src/i18n/zh.js
Normal file
276
src/i18n/zh.js
Normal file
@ -0,0 +1,276 @@
|
||||
import chineseMessages from "ra-language-chinese";
|
||||
|
||||
export default {
|
||||
...chineseMessages,
|
||||
synapseadmin: {
|
||||
auth: {
|
||||
base_url: "服务器 URL",
|
||||
welcome: "欢迎来到 Synapse-admin",
|
||||
server_version: "Synapse 版本",
|
||||
username_error: "请输入完整有效的用户 ID: '@user:domain'",
|
||||
protocol_error: "URL 需要以'http://'或'https://'作为起始",
|
||||
url_error: "不是一个有效的 Matrix 服务器地址",
|
||||
sso_sign_in: "使用 SSO 登录",
|
||||
},
|
||||
users: {
|
||||
invalid_user_id:
|
||||
"必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver",
|
||||
},
|
||||
rooms: {
|
||||
tabs: {
|
||||
basic: "基本",
|
||||
members: "成员",
|
||||
detail: "细节",
|
||||
permission: "权限",
|
||||
},
|
||||
delete: {
|
||||
title: "删除房间",
|
||||
message:
|
||||
"您确定要删除这个房间吗?该操作无法被撤销。这个房间里所有的消息和分享的媒体都将被从服务器上删除!",
|
||||
},
|
||||
},
|
||||
reports: { tabs: { basic: "基本", detail: "细节" } },
|
||||
},
|
||||
import_users: {
|
||||
error: {
|
||||
at_entry: "在条目 %{entry}: %{message}",
|
||||
error: "错误",
|
||||
required_field: "需要的值 '%{field}' 未被设置。",
|
||||
invalid_value:
|
||||
"第 %{row} 行出现无效值。 '%{field}' 只可以是 'true' 或 'false'。",
|
||||
unreasonably_big:
|
||||
"拒绝加载过大的文件: %{size} MB",
|
||||
already_in_progress: "一个导入进程已经在运行中",
|
||||
id_exits: "ID %{id} 已经存在",
|
||||
},
|
||||
title: "通过 CSV 导入用户",
|
||||
goToPdf: "转到 PDF",
|
||||
cards: {
|
||||
importstats: {
|
||||
header: "导入用户",
|
||||
users_total:
|
||||
"%{smart_count} 用户在 CSV 文件中 |||| %{smart_count} 用户在 CSV 文件中",
|
||||
guest_count: "%{smart_count} 访客 |||| %{smart_count} 访客",
|
||||
admin_count: "%{smart_count} 管理员 |||| %{smart_count} 管理员",
|
||||
},
|
||||
conflicts: {
|
||||
header: "冲突处理策略",
|
||||
mode: {
|
||||
stop: "在冲突处停止",
|
||||
skip: "显示错误并跳过冲突",
|
||||
},
|
||||
},
|
||||
ids: {
|
||||
header: "IDs",
|
||||
all_ids_present: "每条记录的 ID",
|
||||
count_ids_present:
|
||||
"%{smart_count} 个含 ID 的记录 |||| %{smart_count} 个含 ID 的记录",
|
||||
mode: {
|
||||
ignore: "忽略 CSV 中的 ID 并创建新的",
|
||||
update: "更新已经存在的记录",
|
||||
},
|
||||
},
|
||||
passwords: {
|
||||
header: "密码",
|
||||
all_passwords_present: "每条记录的密码",
|
||||
count_passwords_present:
|
||||
"%{smart_count} 个含密码的记录 |||| %{smart_count} 个含密码的记录",
|
||||
use_passwords: "使用 CSV 中标记的密码",
|
||||
},
|
||||
upload: {
|
||||
header: "导入 CSV 文件",
|
||||
explanation:
|
||||
"在这里,你可以上传一个用逗号分隔的文件,用于创建或更新用户。该文件必须包括 'id' 和 'displayname' 字段。你可以在这里下载并修改一个示例文件:",
|
||||
},
|
||||
startImport: {
|
||||
simulate_only: "模拟模式",
|
||||
run_import: "导入",
|
||||
},
|
||||
results: {
|
||||
header: "导入结果",
|
||||
total:
|
||||
"共计 %{smart_count} 条记录 |||| 共计 %{smart_count} 条记录",
|
||||
successful: "%{smart_count} 条记录导入成功",
|
||||
skipped: "跳过 %{smart_count} 条记录",
|
||||
download_skipped: "下载跳过的记录",
|
||||
with_error:
|
||||
"%{smart_count} 条记录出现错误 ||| %{smart_count} 条记录出现错误",
|
||||
simulated_only: "只是一次模拟运行",
|
||||
},
|
||||
},
|
||||
},
|
||||
resources: {
|
||||
users: {
|
||||
backtolist: "回到列表",
|
||||
name: "用户",
|
||||
email: "邮箱",
|
||||
msisdn: "电话",
|
||||
threepid: "邮箱 / 电话",
|
||||
fields: {
|
||||
avatar: "邮箱",
|
||||
id: "用户 ID",
|
||||
name: "用户名",
|
||||
is_guest: "访客",
|
||||
admin: "服务器管理员",
|
||||
deactivated: "被禁用",
|
||||
guests: "显示访客",
|
||||
show_deactivated: "显示被禁用的账户",
|
||||
user_id: "搜索用户",
|
||||
displayname: "显示名字",
|
||||
password: "密码",
|
||||
avatar_url: "头像 URL",
|
||||
avatar_src: "头像",
|
||||
medium: "Medium",
|
||||
threepids: "3PIDs",
|
||||
address: "地址",
|
||||
creation_ts_ms: "创建时间戳",
|
||||
consent_version: "协议版本",
|
||||
},
|
||||
helper: {
|
||||
deactivate: "您必须提供一串密码来激活账户。",
|
||||
erase: "将用户标记为根据 GDPR 的要求抹除了",
|
||||
},
|
||||
action: {
|
||||
erase: "抹除用户信息",
|
||||
},
|
||||
},
|
||||
rooms: {
|
||||
name: "房间",
|
||||
fields: {
|
||||
room_id: "房间 ID",
|
||||
name: "房间名",
|
||||
canonical_alias: "别名",
|
||||
joined_members: "成员",
|
||||
joined_local_members: "本地成员",
|
||||
state_events: "状态事件",
|
||||
version: "版本",
|
||||
is_encrypted: "已经加密",
|
||||
encryption: "加密",
|
||||
federatable: "可联合的",
|
||||
public: "公开",
|
||||
creator: "创建者",
|
||||
join_rules: "加入规则",
|
||||
guest_access: "访客访问",
|
||||
history_visibility: "历史可见性",
|
||||
},
|
||||
enums: {
|
||||
join_rules: {
|
||||
public: "公开",
|
||||
knock: "申请",
|
||||
invite: "邀请",
|
||||
private: "私有",
|
||||
},
|
||||
guest_access: {
|
||||
can_join: "访客可以加入",
|
||||
forbidden: "访客不可加入",
|
||||
},
|
||||
history_visibility: {
|
||||
invited: "自从被邀请",
|
||||
joined: "自从加入",
|
||||
shared: "自从分享",
|
||||
world_readable: "任何人",
|
||||
},
|
||||
unencrypted: "未加密",
|
||||
},
|
||||
},
|
||||
reports: {
|
||||
name: "报告事件",
|
||||
fields: {
|
||||
id: "ID",
|
||||
received_ts: "报告时间",
|
||||
user_id: "报告者",
|
||||
name: "房间名",
|
||||
score: "分数",
|
||||
reason: "原因",
|
||||
event_id: "事件 ID",
|
||||
event_json: {
|
||||
origin: "原始服务器",
|
||||
origin_server_ts: "发送时间",
|
||||
type: "事件类型",
|
||||
content: {
|
||||
msgtype: "内容类型",
|
||||
body: "内容",
|
||||
format: "格式",
|
||||
formatted_body: "格式化的数据",
|
||||
algorithm: "算法",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
connections: {
|
||||
name: "连接",
|
||||
fields: {
|
||||
last_seen: "日期",
|
||||
ip: "IP 地址",
|
||||
user_agent: "用户代理 (UA)",
|
||||
},
|
||||
},
|
||||
devices: {
|
||||
name: "设备",
|
||||
fields: {
|
||||
device_id: "设备 ID",
|
||||
display_name: "设备名",
|
||||
last_seen_ts: "时间戳",
|
||||
last_seen_ip: "IP 地址",
|
||||
},
|
||||
action: {
|
||||
erase: {
|
||||
title: "移除 %{id}",
|
||||
content: '您确定要移除设备 "%{name}"?',
|
||||
success: "设备移除成功。",
|
||||
failure: "出现了一个错误。",
|
||||
},
|
||||
},
|
||||
},
|
||||
users_media: {
|
||||
name: "媒体文件",
|
||||
fields: {
|
||||
media_id: "媒体文件 ID",
|
||||
media_length: "长度",
|
||||
media_type: "类型",
|
||||
upload_name: "文件名",
|
||||
quarantined_by: "被隔离",
|
||||
safe_from_quarantine: "取消隔离",
|
||||
created_ts: "创建",
|
||||
last_access_ts: "上一次访问",
|
||||
},
|
||||
},
|
||||
pushers: {
|
||||
name: "发布者",
|
||||
fields: {
|
||||
app: "App",
|
||||
app_display_name: "App 名称",
|
||||
app_id: "App ID",
|
||||
device_display_name: "设备显示名",
|
||||
kind: "类型",
|
||||
lang: "语言",
|
||||
profile_tag: "数据标签",
|
||||
pushkey: "Pushkey",
|
||||
data: { url: "URL" },
|
||||
},
|
||||
},
|
||||
servernotices: {
|
||||
name: "服务器提示",
|
||||
send: "发送服务器提示",
|
||||
fields: {
|
||||
body: "信息",
|
||||
},
|
||||
action: {
|
||||
send: "发送提示",
|
||||
send_success: "服务器提示发送成功。",
|
||||
send_failure: "出现了一个错误。",
|
||||
},
|
||||
helper: {
|
||||
send:
|
||||
'向选中的用户发送服务器提示。服务器配置中的 "服务器提示(Server Notices)" 选项需要被设置为启用。',
|
||||
},
|
||||
},
|
||||
user_media_statistics: {
|
||||
name: "用户的媒体文件",
|
||||
fields: {
|
||||
media_count: "媒体文件统计",
|
||||
media_length: "媒体文件长度",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
@ -2,17 +2,33 @@ import { fetchUtils } from "react-admin";
|
||||
|
||||
const authProvider = {
|
||||
// called when the user attempts to log in
|
||||
login: ({ base_url, username, password }) => {
|
||||
console.log("login ");
|
||||
const options = {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
type: "m.login.password",
|
||||
user: username,
|
||||
password: password,
|
||||
initial_device_display_name: "Synapse Admin",
|
||||
}),
|
||||
};
|
||||
login: ({ base_url, username, password, loginToken }) => {
|
||||
console.log("login to ", base_url);
|
||||
console.log("login token ", loginToken);
|
||||
let options;
|
||||
if (username && password) {
|
||||
options = {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
type: "m.login.password",
|
||||
user: username,
|
||||
password: password,
|
||||
initial_device_display_name: "Synapse Admin",
|
||||
}),
|
||||
};
|
||||
} else if (loginToken) {
|
||||
options = {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
type: "m.login.token",
|
||||
token: loginToken,
|
||||
initial_device_display_name: "Synapse Admin",
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
// Invalid request
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// use the base_url from login instead of the well_known entry from the
|
||||
// server, since the admin might want to access the admin API via some
|
||||
|
@ -9300,6 +9300,11 @@ ra-i18n-polyglot@^3.10.1:
|
||||
node-polyglot "^2.2.2"
|
||||
ra-core "^3.10.1"
|
||||
|
||||
ra-language-chinese@^2.0.10:
|
||||
version "2.0.10"
|
||||
resolved "https://registry.yarnpkg.com/ra-language-chinese/-/ra-language-chinese-2.0.10.tgz#7c51b4d13cd6cf62cf8b4e945e489ac85bdc0e7f"
|
||||
integrity sha512-k+X6XdkBEZnmpKIJZj9Lb77Lj8LCmterilJTj2ovp3i8/H/dLo9IujASfjFypjHnVUpN7Y63LT19kgPrS6+row==
|
||||
|
||||
ra-language-english@^3.10.1:
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/ra-language-english/-/ra-language-english-3.10.1.tgz#d9006ed02962366d1b7221b1a1b33c4e31c985b8"
|
||||
|
Loading…
Reference in New Issue
Block a user