Интеграция appInfo с API

This commit is contained in:
SleepWalker 2016-02-23 07:57:16 +02:00
parent aa422cb4f2
commit 404684b8d9
6 changed files with 146 additions and 15 deletions

View File

@ -0,0 +1,42 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { oAuthValidate } from 'components/auth/actions';
class OAuthInit extends Component {
static displayName = 'OAuthInit';
static propTypes = {
query: PropTypes.shape({
client_id: PropTypes.string.isRequired,
redirect_uri: PropTypes.string.isRequired,
response_type: PropTypes.string.isRequired,
scope: PropTypes.string.isRequired,
state: PropTypes.string
}),
validate: PropTypes.func.isRequired
};
componentWillMount() {
const {query} = this.props;
this.props.validate({
clientId: query.client_id,
redirectUrl: query.redirect_uri,
responseType: query.response_type,
scope: query.scope,
state: query.state
});
}
render() {
return <span />;
}
}
export default connect((state) => ({
query: state.routing.location.query
}), {
validate: oAuthValidate
})(OAuthInit);

View File

@ -107,3 +107,45 @@ export function logout() {
dispatch(routeActions.push('/login'));
};
}
// TODO: move to oAuth actions?
// test request: /oauth?client_id=ely&redirect_uri=http%3A%2F%2Fely.by&response_type=code&scope=minecraft_server_session
export function oAuthValidate({clientId, redirectUrl, responseType, scope, state}) {
return (dispatch) =>
request.get(
'/api/oauth/validate',
{
client_id: clientId,
redirect_uri: redirectUrl,
response_type: responseType,
scope,
state
}
)
.then((resp) => {
dispatch(setClient(resp.client));
dispatch(routeActions.push('/oauth/permissions'));
})
.catch((resp = {}) => { // TODO
if (resp.statusCode === 400 && resp.error === 'invalid_request') {
alert(`Invalid request (${resp.parameter} required).`);
}
if (resp.statusCode === 401 && resp.error === 'invalid_client') {
alert('Can not find application you are trying to authorize.');
}
if (resp.statusCode === 400 && resp.error === 'unsupported_response_type') {
alert(`Invalid response type '${resp.parameter}'.`);
}
if (resp.statusCode === 400 && resp.error === 'invalid_scope') {
alert(`Invalid scope '${resp.parameter}'.`);
}
});
}
export const SET_CLIENT = 'set_client';
export function setClient({id, name, description}) {
return {
type: SET_CLIENT,
payload: {id, name, description}
};
}

View File

@ -1,9 +1,10 @@
import { combineReducers } from 'redux';
import { ERROR } from './actions';
import { ERROR, SET_CLIENT } from './actions';
export default combineReducers({
error
error,
client
});
function error(
@ -21,3 +22,20 @@ function error(
return state;
}
}
function client(
state = null,
{type, payload = {}}
) {
switch (type) {
case SET_CLIENT:
return {
id: payload.id,
name: payload.name,
description: payload.description
};
default:
return state;
}
}

View File

@ -1,29 +1,34 @@
import React, { Component } from 'react';
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import AppInfo from 'components/auth/AppInfo';
import PanelTransition from 'components/auth/PanelTransition';
import styles from './auth.scss';
export default class AuthPage extends Component {
class AuthPage extends Component {
static displayName = 'AuthPage';
static propTypes = {
client: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
description: PropTypes.string.isRequired
})
};
state = {
isSidebarHidden: false
};
render() {
var {isSidebarHidden} = this.state;
var appInfo = {
name: 'TLauncher',
description: `Лучший альтернативный лаунчер для Minecraft с большим количеством версий и их модификаций, а также возмоностью входа как с лицензионным аккаунтом, так и без него.`
};
const {isSidebarHidden} = this.state;
const {client} = this.props;
return (
<div>
<div className={isSidebarHidden ? styles.hiddenSidebar : styles.sidebar}>
<AppInfo {...appInfo} onGoToAuth={this.onGoToAuth} />
<AppInfo {...client} onGoToAuth={this.onGoToAuth} />
</div>
<div className={styles.content}>
<PanelTransition {...this.props} />
@ -38,3 +43,8 @@ export default class AuthPage extends Component {
});
};
}
export default connect((state) => ({
client: state.auth.client
}))(AuthPage);

View File

@ -5,6 +5,7 @@ import RootPage from 'pages/root/RootPage';
import IndexPage from 'pages/index/IndexPage';
import AuthPage from 'pages/auth/AuthPage';
import OAuthInit from 'components/auth/OAuthInit';
import Register from 'components/auth/Register';
import Login from 'components/auth/Login';
import Permissions from 'components/auth/Permissions';
@ -47,10 +48,10 @@ export default function routesFactory(store) {
<Route path="/register" components={new Register()} />
<Route path="/activation" components={new Activation()} />
<Route path="/oauth/permissions" components={new Permissions()} onEnter={checkAuth} />
<Route path="/oauth/:id" component={Permissions} />
<Route path="/password-change" components={new PasswordChange()} />
</Route>
<Route path="oauth" component={OAuthInit} />
<Route path="logout" component={Logout} />
</Route>
);

View File

@ -2,7 +2,7 @@ function serialize(data) {
return Object.keys(data)
.map(
(keyName) =>
[keyName, data[keyName]]
[keyName, typeof data[keyName] === 'undefined' ? '' : data[keyName]]
.map(encodeURIComponent)
.join('=')
)
@ -10,6 +10,9 @@ function serialize(data) {
;
}
const toJSON = (resp) => resp.json();
const handleResponse = (resp) => Promise[resp.success ? 'resolve' : 'reject'](resp);
export default {
post(url, data) {
return fetch(url, {
@ -20,8 +23,23 @@ export default {
},
body: serialize(data)
})
.then((resp) => resp.json())
.then((resp) => Promise[resp.success ? 'resolve' : 'reject'](resp))
.then(toJSON)
.then(handleResponse)
;
},
get(url, data) {
if (typeof data === 'object') {
const separator = url.indexOf('?') === -1 ? '?' : '&';
url += separator + serialize(data);
}
return fetch(url, {
headers: {
Accept: 'application/json'
}
})
.then(toJSON)
.then(handleResponse)
;
}
};