diff --git a/README.md b/README.md index 0e5d92f..56df08d 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,11 @@ `num+` 指符合版本号 `num` 的最新版本及后续所有版本。`最新版` 以 stable 分支为准。 +## 接口 + ++ /login/... ++ /api/v1/... + ## 问题反馈 对 新T树洞 网页版的 bug 反馈请在后端仓库提交 Issue。 diff --git a/package.json b/package.json index 57f5b6d..3fed50e 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "pressure": "^2.1.2", "react": "^16.13.1", "react-dom": "^16.13.0", - "react-google-recaptcha-v3": "^1.5.2", "react-scripts": "^3.4.1", "react-timeago": "^4.4.0", "typescript": "^4.0.2" diff --git a/public/index.html b/public/index.html index a3525f6..8dcf3f8 100644 --- a/public/index.html +++ b/public/index.html @@ -26,6 +26,6 @@ 新T树洞 -
+
开启javascript,或刷新重试
diff --git a/src/App.js b/src/App.js index fbef56c..c3a7295 100644 --- a/src/App.js +++ b/src/App.js @@ -33,12 +33,6 @@ class App extends Component { this.show_sidebar_bound = this.show_sidebar.bind(this); this.set_mode_bound = this.set_mode.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() { @@ -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() { return (
-

- - {(do_popup) => ( - - -  登录到 新T树洞 - - )} - -

+ + {(do_popup) => ( + + +  登录到 新T树洞 + + )} +
)} - {this.inthu_flag || token.value ? ( + {token.value ? ( { fetch( - THUHOLE_API_ROOT + - 'api_xmcp/hole/system_msg?user_token=' + + '/api/v1/system_msg?user_token=' + encodeURIComponent(this.props.token) + API_VERSION_PARAM(), ) diff --git a/src/Title.js b/src/Title.js index 118c2ee..99b6205 100644 --- a/src/Title.js +++ b/src/Title.js @@ -126,7 +126,7 @@ class ControlBar extends PureComponent { className="no-underline control-btn" onClick={() => { this.props.show_sidebar( - '新T树洞', + '新T树洞', , ); }} @@ -173,12 +173,12 @@ export function Title(props) { props.show_sidebar( - '新T树洞', + '新T树洞', , ) } > - 新T树洞 + 新T树洞

diff --git a/src/UserAction.css b/src/UserAction.css index cc4a96f..36e95b0 100644 --- a/src/UserAction.css +++ b/src/UserAction.css @@ -3,7 +3,7 @@ text-align: center; } .login-form button { - width: 6rem; + min-width: 6rem; } .reply-form { @@ -108,4 +108,4 @@ .life-info-error a { --var-link-color: hsl(25,100%,45%); -} \ No newline at end of file +} diff --git a/src/UserAction.js b/src/UserAction.js index b126e77..9f17691 100644 --- a/src/UserAction.js +++ b/src/UserAction.js @@ -13,8 +13,6 @@ import fixOrientation from 'fix-orientation'; import copy from 'copy-to-clipboard'; import { cache } from './cache'; import { - API_VERSION_PARAM, - THUHOLE_API_ROOT, API, get_json, token_param, @@ -107,7 +105,7 @@ export function InfoSidebar(props) { 开源

- 新T树洞 网页版基于 + 新T树洞 网页版基于 { - 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 重置; - else if (this.state.loading_status === 'loading') - return ( - - - - ); - } -} - export class LoginForm extends Component { copy_token(token) { if (copy(token)) alert('复制成功!\n请一定不要泄露哦'); @@ -242,9 +175,7 @@ export class LoginForm extends Component { 复制 User Token
- 复制 User Token - 可以在新设备登录,切勿告知他人。若怀疑被盗号请重置Token。 - {/*,若怀疑被盗号请尽快 */} + User Token仅用于开发bot,切勿告知他人。若怀疑被盗号请刷新Token。

) : ( diff --git a/src/flows_api.js b/src/flows_api.js index e4d0f5c..9010902 100644 --- a/src/flows_api.js +++ b/src/flows_api.js @@ -1,10 +1,7 @@ import { get_json, API_VERSION_PARAM } from './infrastructure/functions'; -import { THUHOLE_API_ROOT } from './infrastructure/const'; import { API_BASE } from './Common'; import { cache } from './cache'; -export { THUHOLE_API_ROOT, API_VERSION_PARAM }; - export function token_param(token) { return API_VERSION_PARAM() + (token ? '&user_token=' + token : ''); } diff --git a/src/infrastructure/const.js b/src/infrastructure/const.js deleted file mode 100644 index b8bc1b1..0000000 --- a/src/infrastructure/const.js +++ /dev/null @@ -1,2 +0,0 @@ -// export const THUHOLE_API_ROOT='//localhost:5001/'; -export const THUHOLE_API_ROOT = 'https://thuhole.com/' diff --git a/src/infrastructure/widgets.css b/src/infrastructure/widgets.css index b439cca..5fc7ddf 100644 --- a/src/infrastructure/widgets.css +++ b/src/infrastructure/widgets.css @@ -241,7 +241,7 @@ a.app-switcher-item, .app-switcher-item a { line-height: 2em; } .thuhole-login-popup button { - width: 6rem; + min-width: 6rem; color: black; background-color: rgba(235,235,235,.5); border-radius: 5px; @@ -298,4 +298,16 @@ a.app-switcher-item, .app-switcher-item a { .time-str { color: #999999; -} \ No newline at end of file +} + +a.button { + -webkit-appearance: button; + -moz-appearance: button; + appearance: button; + + text-decoration: none; + color: initial; + + min-width: 6em; + font-size: 0.85em; +} diff --git a/src/infrastructure/widgets.js b/src/infrastructure/widgets.js index 2ca90fc..ede3044 100644 --- a/src/infrastructure/widgets.js +++ b/src/infrastructure/widgets.js @@ -17,16 +17,8 @@ import appicon_course_survey from './appicon/course_survey.png'; import appicon_dropdown from './appicon/dropdown.png'; import appicon_dropdown_rev from './appicon/dropdown_rev.png'; import appicon_homepage from './appicon/homepage.png'; -import {THUHOLE_API_ROOT} from './const'; 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) { 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 ( -// -// {!!icon_normal && [ -// , -// -// ]} -// {title} -// -// ); -// } -// -// 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 ( -//
-// PKUHelper -// {this.state.apps.bar.map((app)=> -// app_elem(app) -// )} -// {!!this.state.apps.dropdown.length && -//
-//

-// {!!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) -// } -//

-// {this.state.apps.dropdown.map((app)=>{ -// let ref=React.createRef(); -// return ( -//

{ -// if(!e.target.closest('a') && ref.current) -// ref.current.click(); -// }}> -// {app_elem(app,true,ref)} -//

-// ); -// })} -//
-// } -// 网页版 -//
-// ); -// } -// } - class LoginPopupSelf extends Component { constructor(props) { super(props); this.state={ 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) { - if(!this.state.recaptcha_verified) { - alert("reCAPTCHA风控系统正在评估您的浏览器安全状态,请稍后重试。") - return - } - if(this.state.loading_status==='loading') - return; - - this.setState({ - 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; + }; + + render() { - 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( - - { - this.setState({ - recaptcha_verified: true, - }); - localStorage["recaptcha"] = token - }} /> + return (

- 接收验证码来登录 T大树洞 + 通过第三方验证登陆T大树洞

-

- - - {/*this.do_sendcode('sms')}>*/} - {/*  短信 */} - {/**/} - {/*/*/} - this.do_sendcode('mail')}> -  发送邮件  - - -

-

- - -

-
-

- 从其他设备导入登录状态 -

-

- - +

+

+


-

- This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply. -

- , - this.popup_anchor, ); } } @@ -469,4 +142,4 @@ export class LoginPopup extends Component { ); } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 2a56b02..db75135 100644 --- a/yarn.lock +++ b/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": 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= dependencies: regenerator-runtime "^0.13.4" @@ -5167,13 +5167,6 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" 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: 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" @@ -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" integrity sha1-Hc+0WatnHVP2YKmRUTyy8KBVMQg= -react-google-recaptcha-v3@^1.5.2: - 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: +react-is@^16.8.1, react-is@^16.8.4: 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" integrity sha1-eJcppNw23imZ3BVt1sHZwYzqVqQ=