完成登陆的前端,修改api
This commit is contained in:
@@ -28,6 +28,11 @@
|
|||||||
|
|
||||||
`num+` 指符合版本号 `num` 的最新版本及后续所有版本。`最新版` 以 stable 分支为准。
|
`num+` 指符合版本号 `num` 的最新版本及后续所有版本。`最新版` 以 stable 分支为准。
|
||||||
|
|
||||||
|
## 接口
|
||||||
|
|
||||||
|
+ /login/...
|
||||||
|
+ /api/v1/...
|
||||||
|
|
||||||
## 问题反馈
|
## 问题反馈
|
||||||
|
|
||||||
对 新T树洞 网页版的 bug 反馈请在后端仓库提交 Issue。
|
对 新T树洞 网页版的 bug 反馈请在后端仓库提交 Issue。
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
"pressure": "^2.1.2",
|
"pressure": "^2.1.2",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-dom": "^16.13.0",
|
"react-dom": "^16.13.0",
|
||||||
"react-google-recaptcha-v3": "^1.5.2",
|
|
||||||
"react-scripts": "^3.4.1",
|
"react-scripts": "^3.4.1",
|
||||||
"react-timeago": "^4.4.0",
|
"react-timeago": "^4.4.0",
|
||||||
"typescript": "^4.0.2"
|
"typescript": "^4.0.2"
|
||||||
|
|||||||
@@ -26,6 +26,6 @@
|
|||||||
<title>新T树洞</title>
|
<title>新T树洞</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root">开启javascript,或刷新重试</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
35
src/App.js
35
src/App.js
@@ -33,12 +33,6 @@ class App extends Component {
|
|||||||
this.show_sidebar_bound = this.show_sidebar.bind(this);
|
this.show_sidebar_bound = this.show_sidebar.bind(this);
|
||||||
this.set_mode_bound = this.set_mode.bind(this);
|
this.set_mode_bound = this.set_mode.bind(this);
|
||||||
this.on_pressure_bound = this.on_pressure.bind(this);
|
this.on_pressure_bound = this.on_pressure.bind(this);
|
||||||
// a silly self-deceptive approach to ban guests, enough to fool those muggles
|
|
||||||
// document cookie 'pku_ip_flag=yes'
|
|
||||||
this.inthu_flag =
|
|
||||||
window[atob('ZG9jdW1lbnQ')][atob('Y29va2ll')].indexOf(
|
|
||||||
atob('dGh1X2lwX2ZsYWc9eWVz'),
|
|
||||||
) !== -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static is_darkmode() {
|
static is_darkmode() {
|
||||||
@@ -102,6 +96,15 @@ class App extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
let arg = window.location.search;
|
||||||
|
console.log(arg);
|
||||||
|
if (arg.startsWith('?token=')) {
|
||||||
|
localStorage['TOKEN'] = arg.substr(7);
|
||||||
|
window.location.search = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<TokenCtx.Provider
|
<TokenCtx.Provider
|
||||||
@@ -129,20 +132,18 @@ class App extends Component {
|
|||||||
{!token.value && (
|
{!token.value && (
|
||||||
<div className="flow-item-row aux-margin">
|
<div className="flow-item-row aux-margin">
|
||||||
<div className="box box-tip">
|
<div className="box box-tip">
|
||||||
<p>
|
<LoginPopup token_callback={token.set_value}>
|
||||||
<LoginPopup token_callback={token.set_value}>
|
{(do_popup) => (
|
||||||
{(do_popup) => (
|
<a onClick={do_popup}>
|
||||||
<a onClick={do_popup}>
|
<span className="icon icon-login" />
|
||||||
<span className="icon icon-login" />
|
登录到 新T树洞
|
||||||
登录到 新T树洞
|
</a>
|
||||||
</a>
|
)}
|
||||||
)}
|
</LoginPopup>
|
||||||
</LoginPopup>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.inthu_flag || token.value ? (
|
{token.value ? (
|
||||||
<Flow
|
<Flow
|
||||||
key={this.state.flow_render_key}
|
key={this.state.flow_render_key}
|
||||||
show_sidebar={this.show_sidebar_bound}
|
show_sidebar={this.show_sidebar_bound}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { Component, PureComponent } from 'react';
|
import React, { Component, PureComponent } from 'react';
|
||||||
import { format_time, Time, TitleLine } from './infrastructure/widgets';
|
import { format_time, Time, TitleLine } from './infrastructure/widgets';
|
||||||
import { THUHOLE_API_ROOT } from './flows_api';
|
|
||||||
|
|
||||||
import HtmlToReact from 'html-to-react';
|
import HtmlToReact from 'html-to-react';
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ import renderMd from './Markdown';
|
|||||||
|
|
||||||
export { format_time, Time, TitleLine };
|
export { format_time, Time, TitleLine };
|
||||||
|
|
||||||
export const API_BASE = THUHOLE_API_ROOT + 'services/thuhole';
|
export const API_BASE = '/api/v1/';
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
|
// https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
|
||||||
function escape_regex(string) {
|
function escape_regex(string) {
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import { API } from './flows_api';
|
|||||||
|
|
||||||
const IMAGE_BASE = 'https://thimg.yecdn.com/';
|
const IMAGE_BASE = 'https://thimg.yecdn.com/';
|
||||||
const IMAGE_BAK_BASE = 'https://img2.thuhole.com/';
|
const IMAGE_BAK_BASE = 'https://img2.thuhole.com/';
|
||||||
// const AUDIO_BASE=THUHOLE_API_ROOT+'services/thuhole/audios/';
|
|
||||||
|
|
||||||
const CLICKABLE_TAGS = { a: true, audio: true };
|
const CLICKABLE_TAGS = { a: true, audio: true };
|
||||||
const PREVIEW_REPLY_COUNT = 10;
|
const PREVIEW_REPLY_COUNT = 10;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { THUHOLE_API_ROOT, get_json, API_VERSION_PARAM } from './flows_api';
|
|
||||||
import { Time } from './Common';
|
import { Time } from './Common';
|
||||||
|
|
||||||
export class MessageViewer extends PureComponent {
|
export class MessageViewer extends PureComponent {
|
||||||
@@ -23,8 +22,7 @@ export class MessageViewer extends PureComponent {
|
|||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
fetch(
|
fetch(
|
||||||
THUHOLE_API_ROOT +
|
'/api/v1/system_msg?user_token=' +
|
||||||
'api_xmcp/hole/system_msg?user_token=' +
|
|
||||||
encodeURIComponent(this.props.token) +
|
encodeURIComponent(this.props.token) +
|
||||||
API_VERSION_PARAM(),
|
API_VERSION_PARAM(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class ControlBar extends PureComponent {
|
|||||||
className="no-underline control-btn"
|
className="no-underline control-btn"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.show_sidebar(
|
this.props.show_sidebar(
|
||||||
'新T树洞',
|
'新T树洞',
|
||||||
<InfoSidebar show_sidebar={this.props.show_sidebar} />,
|
<InfoSidebar show_sidebar={this.props.show_sidebar} />,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@@ -173,12 +173,12 @@ export function Title(props) {
|
|||||||
<span
|
<span
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
props.show_sidebar(
|
props.show_sidebar(
|
||||||
'新T树洞',
|
'新T树洞',
|
||||||
<InfoSidebar show_sidebar={props.show_sidebar} />,
|
<InfoSidebar show_sidebar={props.show_sidebar} />,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
新T树洞
|
新T树洞
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.login-form button {
|
.login-form button {
|
||||||
width: 6rem;
|
min-width: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reply-form {
|
.reply-form {
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import fixOrientation from 'fix-orientation';
|
|||||||
import copy from 'copy-to-clipboard';
|
import copy from 'copy-to-clipboard';
|
||||||
import { cache } from './cache';
|
import { cache } from './cache';
|
||||||
import {
|
import {
|
||||||
API_VERSION_PARAM,
|
|
||||||
THUHOLE_API_ROOT,
|
|
||||||
API,
|
API,
|
||||||
get_json,
|
get_json,
|
||||||
token_param,
|
token_param,
|
||||||
@@ -107,7 +105,7 @@ export function InfoSidebar(props) {
|
|||||||
开源
|
开源
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
新T树洞 网页版基于
|
新T树洞 网页版基于
|
||||||
<a
|
<a
|
||||||
href="https://github.com/pkuhelper-web/webhole"
|
href="https://github.com/pkuhelper-web/webhole"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -130,71 +128,6 @@ export function InfoSidebar(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResetUsertokenWidget extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
loading_status: 'done',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
do_reset() {
|
|
||||||
if (
|
|
||||||
window.confirm(
|
|
||||||
'您正在重置 UserToken!\n您的账号将会在【所有设备】上注销,您需要手动重新登录!',
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
let uid = window.prompt(
|
|
||||||
'您正在重置 UserToken!\n请输入您的学号以确认身份:',
|
|
||||||
);
|
|
||||||
if (uid)
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
loading_status: 'loading',
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
fetch(THUHOLE_API_ROOT + 'api_xmcp/hole/reset_usertoken', {
|
|
||||||
method: 'post',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
user_token: this.props.token,
|
|
||||||
uid: uid,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then(get_json)
|
|
||||||
.then((json) => {
|
|
||||||
if (json.error) throw new Error(json.error);
|
|
||||||
else alert('重置成功!您需要在所有设备上重新登录。');
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'done',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
alert('重置失败:' + e);
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'done',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.state.loading_status === 'done')
|
|
||||||
return <a onClick={this.do_reset.bind(this)}>重置</a>;
|
|
||||||
else if (this.state.loading_status === 'loading')
|
|
||||||
return (
|
|
||||||
<a>
|
|
||||||
<span className="icon icon-loading" />
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LoginForm extends Component {
|
export class LoginForm extends Component {
|
||||||
copy_token(token) {
|
copy_token(token) {
|
||||||
if (copy(token)) alert('复制成功!\n请一定不要泄露哦');
|
if (copy(token)) alert('复制成功!\n请一定不要泄露哦');
|
||||||
@@ -242,9 +175,7 @@ export class LoginForm extends Component {
|
|||||||
复制 User Token
|
复制 User Token
|
||||||
</a>
|
</a>
|
||||||
<br />
|
<br />
|
||||||
复制 User Token
|
User Token仅用于开发bot,切勿告知他人。若怀疑被盗号请刷新Token。
|
||||||
可以在新设备登录,切勿告知他人。若怀疑被盗号请重置Token。
|
|
||||||
{/*,若怀疑被盗号请尽快 <ResetUsertokenWidget token={token.value} />*/}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { get_json, API_VERSION_PARAM } from './infrastructure/functions';
|
import { get_json, API_VERSION_PARAM } from './infrastructure/functions';
|
||||||
import { THUHOLE_API_ROOT } from './infrastructure/const';
|
|
||||||
import { API_BASE } from './Common';
|
import { API_BASE } from './Common';
|
||||||
import { cache } from './cache';
|
import { cache } from './cache';
|
||||||
|
|
||||||
export { THUHOLE_API_ROOT, API_VERSION_PARAM };
|
|
||||||
|
|
||||||
export function token_param(token) {
|
export function token_param(token) {
|
||||||
return API_VERSION_PARAM() + (token ? '&user_token=' + token : '');
|
return API_VERSION_PARAM() + (token ? '&user_token=' + token : '');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
// export const THUHOLE_API_ROOT='//localhost:5001/';
|
|
||||||
export const THUHOLE_API_ROOT = 'https://thuhole.com/'
|
|
||||||
@@ -241,7 +241,7 @@ a.app-switcher-item, .app-switcher-item a {
|
|||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
}
|
}
|
||||||
.thuhole-login-popup button {
|
.thuhole-login-popup button {
|
||||||
width: 6rem;
|
min-width: 6rem;
|
||||||
color: black;
|
color: black;
|
||||||
background-color: rgba(235,235,235,.5);
|
background-color: rgba(235,235,235,.5);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@@ -299,3 +299,15 @@ a.app-switcher-item, .app-switcher-item a {
|
|||||||
.time-str {
|
.time-str {
|
||||||
color: #999999;
|
color: #999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.button {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
-moz-appearance: button;
|
||||||
|
appearance: button;
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
|
color: initial;
|
||||||
|
|
||||||
|
min-width: 6em;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,16 +17,8 @@ import appicon_course_survey from './appicon/course_survey.png';
|
|||||||
import appicon_dropdown from './appicon/dropdown.png';
|
import appicon_dropdown from './appicon/dropdown.png';
|
||||||
import appicon_dropdown_rev from './appicon/dropdown_rev.png';
|
import appicon_dropdown_rev from './appicon/dropdown_rev.png';
|
||||||
import appicon_homepage from './appicon/homepage.png';
|
import appicon_homepage from './appicon/homepage.png';
|
||||||
import {THUHOLE_API_ROOT} from './const';
|
|
||||||
import {get_json, API_VERSION_PARAM} from './functions';
|
import {get_json, API_VERSION_PARAM} from './functions';
|
||||||
|
|
||||||
import {
|
|
||||||
GoogleReCaptchaProvider,
|
|
||||||
GoogleReCaptcha
|
|
||||||
} from 'react-google-recaptcha-v3';
|
|
||||||
|
|
||||||
const LOGIN_POPUP_ANCHOR_ID='pkuhelper_login_popup_anchor';
|
|
||||||
|
|
||||||
function pad2(x) {
|
function pad2(x) {
|
||||||
return x<10 ? '0'+x : ''+x;
|
return x<10 ? '0'+x : ''+x;
|
||||||
}
|
}
|
||||||
@@ -66,365 +58,48 @@ export function GlobalTitle(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const FALLBACK_APPS={
|
|
||||||
// id, text, url, icon_normal, icon_hover, new_tab
|
|
||||||
bar: [
|
|
||||||
['hole', '树洞', '/hole', appicon_hole, null, false],
|
|
||||||
['imasugu', '教室', '/spare_classroom', appicon_imasugu, appicon_imasugu_rev, false],
|
|
||||||
['syllabus', '课表', '/syllabus', appicon_syllabus, null, false],
|
|
||||||
['score', '成绩', '/my_score', appicon_score, null, false],
|
|
||||||
],
|
|
||||||
dropdown: [
|
|
||||||
['course_survey', '课程测评', 'https://courses.pinzhixiaoyuan.com/', appicon_course_survey, null, true],
|
|
||||||
['homepage', '客户端', '/', appicon_homepage, null, true],
|
|
||||||
],
|
|
||||||
fix: {},
|
|
||||||
};
|
|
||||||
// const SWITCHER_DATA_VER='switcher_2';
|
|
||||||
// const SWITCHER_DATA_URL=THUHOLE_API_ROOT+'web_static/appswitcher_items.json';
|
|
||||||
|
|
||||||
// export class AppSwitcher extends Component {
|
|
||||||
// constructor(props) {
|
|
||||||
// super(props);
|
|
||||||
// this.state={
|
|
||||||
// apps: this.get_apps_from_localstorage(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// get_apps_from_localstorage() {
|
|
||||||
// let ret=FALLBACK_APPS;
|
|
||||||
// if(localStorage['APPSWITCHER_ITEMS'])
|
|
||||||
// try {
|
|
||||||
// let content=JSON.parse(localStorage['APPSWITCHER_ITEMS'])[SWITCHER_DATA_VER];
|
|
||||||
// if(!content || !content.bar)
|
|
||||||
// throw new Error('content is empty');
|
|
||||||
//
|
|
||||||
// ret=content;
|
|
||||||
// } catch(e) {
|
|
||||||
// console.error('load appswitcher items from localstorage failed');
|
|
||||||
// console.trace(e);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return ret;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// check_fix() {
|
|
||||||
// if(this.state.apps && this.state.apps.fix && this.state.apps.fix[this.props.appid])
|
|
||||||
// setTimeout(()=>{
|
|
||||||
// window.HOTFIX_CONTEXT={
|
|
||||||
// build_info: process.env.REACT_APP_BUILD_INFO || '---',
|
|
||||||
// build_env: process.env.NODE_ENV,
|
|
||||||
// };
|
|
||||||
// eval(this.state.apps.fix[this.props.appid]);
|
|
||||||
// },1); // make it async so failures won't be critical
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// componentDidMount() {
|
|
||||||
// this.check_fix();
|
|
||||||
// setTimeout(()=>{
|
|
||||||
// fetch(SWITCHER_DATA_URL)
|
|
||||||
// .then((res)=>{
|
|
||||||
// if(!res.ok) throw Error(`网络错误 ${res.status} ${res.statusText}`);
|
|
||||||
// return res.text();
|
|
||||||
// })
|
|
||||||
// .then((txt)=>{
|
|
||||||
// if(txt!==localStorage['APPSWITCHER_ITEMS']) {
|
|
||||||
// console.log('loaded new appswitcher items',txt);
|
|
||||||
// localStorage['APPSWITCHER_ITEMS']=txt;
|
|
||||||
//
|
|
||||||
// this.setState({
|
|
||||||
// apps: this.get_apps_from_localstorage(),
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// console.log('appswitcher items unchanged');
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .catch((e)=>{
|
|
||||||
// console.error('loading appswitcher items failed');
|
|
||||||
// console.trace(e);
|
|
||||||
// });
|
|
||||||
// },500);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// componentDidUpdate(prevProps, prevState) {
|
|
||||||
// if(this.state.apps!==prevState.apps)
|
|
||||||
// this.check_fix();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// render() {
|
|
||||||
// let cur_id=this.props.appid;
|
|
||||||
//
|
|
||||||
// function app_elem([id,title,url,icon_normal,icon_hover,new_tab],no_class=false,ref=null) {
|
|
||||||
// return (
|
|
||||||
// <a ref={ref} key={id} className={no_class ? null : ('app-switcher-item'+(id===cur_id ? ' app-switcher-item-current' : ''))}
|
|
||||||
// href={url} target={new_tab ? '_blank' : '_self'}>
|
|
||||||
// {!!icon_normal && [
|
|
||||||
// <img key="normal" src={icon_normal} className="app-switcher-logo-normal" />,
|
|
||||||
// <img key="hover" src={icon_hover||icon_normal} className="app-switcher-logo-hover" />
|
|
||||||
// ]}
|
|
||||||
// <span>{title}</span>
|
|
||||||
// </a>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let dropdown_cur_app=null;
|
|
||||||
// this.state.apps.dropdown.forEach((app)=>{
|
|
||||||
// if(app[0]===cur_id)
|
|
||||||
// dropdown_cur_app=app;
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// //console.log(JSON.stringify(this.state.apps));
|
|
||||||
//
|
|
||||||
// return (
|
|
||||||
// <div className="app-switcher">
|
|
||||||
// <span className="app-switcher-desc app-switcher-left">PKUHelper</span>
|
|
||||||
// {this.state.apps.bar.map((app)=>
|
|
||||||
// app_elem(app)
|
|
||||||
// )}
|
|
||||||
// {!!this.state.apps.dropdown.length &&
|
|
||||||
// <div className={
|
|
||||||
// 'app-switcher-item app-switcher-dropdown '
|
|
||||||
// +(dropdown_cur_app ? ' app-switcher-item-current' : '')
|
|
||||||
// }>
|
|
||||||
// <p className="app-switcher-dropdown-title">
|
|
||||||
// {!!dropdown_cur_app ?
|
|
||||||
// app_elem((()=>{
|
|
||||||
// let [id,title,_url,icon_normal,icon_hover,_new_tab]=dropdown_cur_app;
|
|
||||||
// return [id,title+'▾',null,icon_normal,icon_hover,false];
|
|
||||||
// })(),true) :
|
|
||||||
// app_elem(['-placeholder-elem','更多▾',null,appicon_dropdown,appicon_dropdown_rev,false],true)
|
|
||||||
// }
|
|
||||||
// </p>
|
|
||||||
// {this.state.apps.dropdown.map((app)=>{
|
|
||||||
// let ref=React.createRef();
|
|
||||||
// return (
|
|
||||||
// <p key={app[0]} className="app-switcher-dropdown-item" onClick={(e)=>{
|
|
||||||
// if(!e.target.closest('a') && ref.current)
|
|
||||||
// ref.current.click();
|
|
||||||
// }}>
|
|
||||||
// {app_elem(app,true,ref)}
|
|
||||||
// </p>
|
|
||||||
// );
|
|
||||||
// })}
|
|
||||||
// </div>
|
|
||||||
// }
|
|
||||||
// <span className="app-switcher-desc app-switcher-right">网页版</span>
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
class LoginPopupSelf extends Component {
|
class LoginPopupSelf extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state={
|
this.state={
|
||||||
loading_status: 'idle',
|
loading_status: 'idle',
|
||||||
recaptcha_verified: false
|
|
||||||
// excluded_scopes: [],
|
|
||||||
};
|
|
||||||
this.username_ref=React.createRef();
|
|
||||||
this.password_ref=React.createRef();
|
|
||||||
this.input_token_ref=React.createRef();
|
|
||||||
|
|
||||||
this.popup_anchor=document.getElementById(LOGIN_POPUP_ANCHOR_ID);
|
|
||||||
if(!this.popup_anchor) {
|
|
||||||
this.popup_anchor=document.createElement('div');
|
|
||||||
this.popup_anchor.id=LOGIN_POPUP_ANCHOR_ID;
|
|
||||||
document.body.appendChild(this.popup_anchor);
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
do_sendcode(type) {
|
render() {
|
||||||
if(!this.state.recaptcha_verified) {
|
|
||||||
alert("reCAPTCHA风控系统正在评估您的浏览器安全状态,请稍后重试。")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if(this.state.loading_status==='loading')
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.setState({
|
return (
|
||||||
loading_status: 'loading',
|
|
||||||
},()=>{
|
|
||||||
fetch(
|
|
||||||
THUHOLE_API_ROOT+'api_xmcp/login/send_code'
|
|
||||||
+'?user='+encodeURIComponent(this.username_ref.current.value)
|
|
||||||
+'&code_type='+encodeURIComponent(type)
|
|
||||||
+"&recaptcha_token="+localStorage["recaptcha"]
|
|
||||||
+API_VERSION_PARAM(), {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
excluded_scopes: [],
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(get_json)
|
|
||||||
.then((json)=>{
|
|
||||||
console.log(json);
|
|
||||||
if(!json.success)
|
|
||||||
throw new Error(JSON.stringify(json));
|
|
||||||
|
|
||||||
alert(json.msg);
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'done',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((e)=>{
|
|
||||||
console.error(e);
|
|
||||||
alert('发送失败\n'+e);
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'done',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
do_login(set_token) {
|
|
||||||
if(this.state.loading_status==='loading')
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'loading',
|
|
||||||
},()=>{
|
|
||||||
fetch(
|
|
||||||
THUHOLE_API_ROOT+'api_xmcp/login/login'
|
|
||||||
+'?user='+encodeURIComponent(this.username_ref.current.value)
|
|
||||||
+'&valid_code='+encodeURIComponent(this.password_ref.current.value)
|
|
||||||
+API_VERSION_PARAM(), {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
excluded_scopes: [],
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(get_json)
|
|
||||||
.then((json)=>{
|
|
||||||
if(json.code!==0) {
|
|
||||||
if(json.msg) throw new Error(json.msg);
|
|
||||||
throw new Error(JSON.stringify(json));
|
|
||||||
}
|
|
||||||
|
|
||||||
set_token(json.user_token);
|
|
||||||
alert(`登录成功`);
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'done',
|
|
||||||
});
|
|
||||||
this.props.on_close();
|
|
||||||
})
|
|
||||||
.catch((e)=>{
|
|
||||||
console.error(e);
|
|
||||||
alert('登录失败\n'+e);
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'done',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
do_input_token(set_token) {
|
|
||||||
if(this.state.loading_status==='loading')
|
|
||||||
return;
|
|
||||||
|
|
||||||
let token=this.input_token_ref.current.value;
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'loading',
|
|
||||||
},()=>{
|
|
||||||
fetch(THUHOLE_API_ROOT+'api_xmcp/hole/system_msg?user_token='+encodeURIComponent(token)+API_VERSION_PARAM())
|
|
||||||
.then((res)=>res.json())
|
|
||||||
.then((json)=>{
|
|
||||||
if(json.error)
|
|
||||||
throw new Error(json.error);
|
|
||||||
if(json.result.length===0)
|
|
||||||
throw new Error('result check failed');
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'done',
|
|
||||||
});
|
|
||||||
set_token(token);
|
|
||||||
this.props.on_close();
|
|
||||||
})
|
|
||||||
.catch((e)=>{
|
|
||||||
alert('Token检验失败\n'+e);
|
|
||||||
this.setState({
|
|
||||||
loading_status: 'done',
|
|
||||||
});
|
|
||||||
console.error(e);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// perm_alert() {
|
|
||||||
// alert('如果你不需要 PKU Helper 的某项功能,可以取消相应权限。\n其中【状态信息】包括你的网费、校园卡余额等。\n该设置应用到你的【所有】设备,取消后如需再次启用相应功能需要重新登录。');
|
|
||||||
// }
|
|
||||||
|
|
||||||
render() {
|
|
||||||
// let PERM_SCOPES=[
|
|
||||||
// ['score','成绩查询'],
|
|
||||||
// ['syllabus','课表查询'],
|
|
||||||
// ['my_info','状态信息'],
|
|
||||||
// ];
|
|
||||||
|
|
||||||
return ReactDOM.createPortal(
|
|
||||||
<GoogleReCaptchaProvider reCaptchaKey={"6Leq0a0ZAAAAAHEStocsqtJfKEs9APB0LdgzTNfZ"} useRecaptchaNet={true}>
|
|
||||||
<GoogleReCaptcha onVerify={(token) => {
|
|
||||||
this.setState({
|
|
||||||
recaptcha_verified: true,
|
|
||||||
});
|
|
||||||
localStorage["recaptcha"] = token
|
|
||||||
}} />
|
|
||||||
<div>
|
<div>
|
||||||
<div className="thuhole-login-popup-shadow" />
|
<div className="thuhole-login-popup-shadow" />
|
||||||
<div className="thuhole-login-popup">
|
<div className="thuhole-login-popup">
|
||||||
<p>
|
<p>
|
||||||
<b>接收验证码来登录 T大树洞</b>
|
<b>通过第三方验证登陆T大树洞</b>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label>
|
<a className="button" href="/login/request?p=cs" target="_blank"
|
||||||
邮箱
|
>
|
||||||
<input ref={this.username_ref} type="email" autoFocus={true} defaultValue="@mails.tsinghua.edu.cn" />
|
闭社
|
||||||
</label>
|
</a>
|
||||||
<span className="thuhole-login-type">
|
</p>
|
||||||
{/*<a onClick={(e)=>this.do_sendcode('sms')}>*/}
|
<p>
|
||||||
{/* 短信 */}
|
<button type="button" disabled
|
||||||
{/*</a>*/}
|
>
|
||||||
{/*/*/}
|
T大树洞
|
||||||
<a onClick={(e)=>this.do_sendcode('mail')}>
|
</button>
|
||||||
发送邮件
|
</p>
|
||||||
</a>
|
<p>
|
||||||
</span>
|
<button type="button" disabled
|
||||||
</p>
|
>
|
||||||
<p>
|
未名bbs
|
||||||
<label>
|
</button>
|
||||||
验证码
|
</p>
|
||||||
<input ref={this.password_ref} type="tel" />
|
<p>
|
||||||
</label>
|
<button type="button" disabled
|
||||||
<button type="button" disabled={this.state.loading_status==='loading'}
|
>
|
||||||
onClick={(e)=>this.do_login(this.props.token_callback)}>
|
清华统一身份认证
|
||||||
登录
|
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<hr />
|
<hr />
|
||||||
<p>
|
|
||||||
<b>从其他设备导入登录状态</b>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<input ref={this.input_token_ref} placeholder="User Token" />
|
|
||||||
<button type="button" disabled={this.state.loading_status==='loading'}
|
|
||||||
onClick={(e)=>this.do_input_token(this.props.token_callback)}>
|
|
||||||
导入
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
<hr />
|
|
||||||
<p style={{fontSize:11}}>
|
|
||||||
This site is protected by reCAPTCHA and the Google <a
|
|
||||||
href="https://policies.google.com/privacy">Privacy Policy</a> and <a
|
|
||||||
href="https://policies.google.com/terms">Terms of Service</a> apply.
|
|
||||||
</p>
|
|
||||||
<p>
|
<p>
|
||||||
<button onClick={this.props.on_close}>
|
<button onClick={this.props.on_close}>
|
||||||
取消
|
取消
|
||||||
@@ -432,8 +107,6 @@ class LoginPopupSelf extends Component {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</GoogleReCaptchaProvider>,
|
|
||||||
this.popup_anchor,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
yarn.lock
18
yarn.lock
@@ -1110,7 +1110,7 @@
|
|||||||
|
|
||||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
|
||||||
version "7.11.2"
|
version "7.11.2"
|
||||||
resolved "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.11.2.tgz?cache=0&sync_timestamp=1596637820375&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
|
resolved "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
|
||||||
integrity sha1-9UnBPHVMxAuHZEufqfCaapX+BzY=
|
integrity sha1-9UnBPHVMxAuHZEufqfCaapX+BzY=
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
@@ -5167,13 +5167,6 @@ hmac-drbg@^1.0.0:
|
|||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
minimalistic-crypto-utils "^1.0.1"
|
minimalistic-crypto-utils "^1.0.1"
|
||||||
|
|
||||||
hoist-non-react-statics@3.3.2:
|
|
||||||
version "3.3.2"
|
|
||||||
resolved "https://registry.npm.taobao.org/hoist-non-react-statics/download/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
|
||||||
integrity sha1-7OCsr3HWLClpwuxZ/v9CpLGoW0U=
|
|
||||||
dependencies:
|
|
||||||
react-is "^16.7.0"
|
|
||||||
|
|
||||||
hosted-git-info@^2.1.4:
|
hosted-git-info@^2.1.4:
|
||||||
version "2.8.8"
|
version "2.8.8"
|
||||||
resolved "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.8.tgz?cache=0&sync_timestamp=1594427993800&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhosted-git-info%2Fdownload%2Fhosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
resolved "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.8.tgz?cache=0&sync_timestamp=1594427993800&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhosted-git-info%2Fdownload%2Fhosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
||||||
@@ -8820,14 +8813,7 @@ react-error-overlay@^6.0.7:
|
|||||||
resolved "https://registry.npm.taobao.org/react-error-overlay/download/react-error-overlay-6.0.7.tgz?cache=0&sync_timestamp=1596670540895&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-error-overlay%2Fdownload%2Freact-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
|
resolved "https://registry.npm.taobao.org/react-error-overlay/download/react-error-overlay-6.0.7.tgz?cache=0&sync_timestamp=1596670540895&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-error-overlay%2Fdownload%2Freact-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
|
||||||
integrity sha1-Hc+0WatnHVP2YKmRUTyy8KBVMQg=
|
integrity sha1-Hc+0WatnHVP2YKmRUTyy8KBVMQg=
|
||||||
|
|
||||||
react-google-recaptcha-v3@^1.5.2:
|
react-is@^16.8.1, react-is@^16.8.4:
|
||||||
version "1.5.2"
|
|
||||||
resolved "https://registry.npm.taobao.org/react-google-recaptcha-v3/download/react-google-recaptcha-v3-1.5.2.tgz#0a20cb133270bd4fbd90a641937142e45d0ff9ae"
|
|
||||||
integrity sha1-CiDLEzJwvU+9kKZBk3FC5F0P+a4=
|
|
||||||
dependencies:
|
|
||||||
hoist-non-react-statics "3.3.2"
|
|
||||||
|
|
||||||
react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
|
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.npm.taobao.org/react-is/download/react-is-16.13.1.tgz?cache=0&sync_timestamp=1598612913326&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-is%2Fdownload%2Freact-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.npm.taobao.org/react-is/download/react-is-16.13.1.tgz?cache=0&sync_timestamp=1598612913326&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-is%2Fdownload%2Freact-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
integrity sha1-eJcppNw23imZ3BVt1sHZwYzqVqQ=
|
integrity sha1-eJcppNw23imZ3BVt1sHZwYzqVqQ=
|
||||||
|
|||||||
Reference in New Issue
Block a user