fk axios
This commit is contained in:
parent
f68a12eab8
commit
6fe9b52d00
@ -10,8 +10,6 @@
|
|||||||
<meta name="author" content="ntr@smokestack.io">
|
<meta name="author" content="ntr@smokestack.io">
|
||||||
<link rel="stylesheet" href="./node_modules/izitoast/dist/css/iziToast.min.css"></script>
|
<link rel="stylesheet" href="./node_modules/izitoast/dist/css/iziToast.min.css"></script>
|
||||||
<link href="https://fonts.googleapis.com/css?family=Jura" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Jura" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="assets/styles/normalize.css">
|
|
||||||
<link rel="stylesheet" href="assets/styles/skeleton.css">
|
|
||||||
</head>
|
</head>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
require('./../client/assets/styles/normalize.css');
|
||||||
|
require('./../client/assets/styles/skeleton.css');
|
||||||
|
|
||||||
require('./../client/assets/styles/styles.less');
|
require('./../client/assets/styles/styles.less');
|
||||||
require('./../client/assets/styles/menu.less');
|
require('./../client/assets/styles/menu.less');
|
||||||
require('./../client/assets/styles/nav.less');
|
require('./../client/assets/styles/nav.less');
|
||||||
|
|||||||
@ -15,7 +15,6 @@
|
|||||||
"anime": "^0.1.2",
|
"anime": "^0.1.2",
|
||||||
"animejs": "^3.0.1",
|
"animejs": "^3.0.1",
|
||||||
"async": "^2.6.2",
|
"async": "^2.6.2",
|
||||||
"axios": "^0.19.0",
|
|
||||||
"borc": "^2.0.3",
|
"borc": "^2.0.3",
|
||||||
"docco": "^0.7.0",
|
"docco": "^0.7.0",
|
||||||
"izitoast": "^1.4.0",
|
"izitoast": "^1.4.0",
|
||||||
|
|||||||
38
acp/src/acp.game.list.jsx
Normal file
38
acp/src/acp.game.list.jsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const preact = require('preact');
|
||||||
|
const { Component } = require('preact');
|
||||||
|
const { connect } = require('preact-redux');
|
||||||
|
const linkState = require('linkstate').default;
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
const actions = require('./actions');
|
||||||
|
|
||||||
|
const addState = connect(
|
||||||
|
function receiveState(state) {
|
||||||
|
const {
|
||||||
|
games,
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
return {
|
||||||
|
games
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
function AcpGameList(args) {
|
||||||
|
const {
|
||||||
|
games,
|
||||||
|
} = args;
|
||||||
|
|
||||||
|
if (!games) return false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{games.map((g, i) => <tr key={i}><td>{JSON.stringify(g)}</td></tr>)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addState(AcpGameList);
|
||||||
@ -7,7 +7,7 @@ const { createStore, combineReducers } = require('redux');
|
|||||||
const reducers = require('./reducers');
|
const reducers = require('./reducers');
|
||||||
const actions = require('./actions');
|
const actions = require('./actions');
|
||||||
|
|
||||||
const Users = require('./acp.users');
|
const Main = require('./acp.main');
|
||||||
|
|
||||||
// Redux Store
|
// Redux Store
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
@ -22,9 +22,8 @@ document.fonts.load('16pt "Jura"').then(() => {
|
|||||||
<nav>
|
<nav>
|
||||||
<h1>acp</h1>
|
<h1>acp</h1>
|
||||||
<hr/>
|
<hr/>
|
||||||
<button>users</button>
|
|
||||||
</nav>
|
</nav>
|
||||||
<Users />
|
<Main />
|
||||||
<aside></aside>
|
<aside></aside>
|
||||||
</div>
|
</div>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|||||||
162
acp/src/acp.main.jsx
Normal file
162
acp/src/acp.main.jsx
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
const preact = require('preact');
|
||||||
|
const { Component } = require('preact');
|
||||||
|
const { connect } = require('preact-redux');
|
||||||
|
const linkState = require('linkstate').default;
|
||||||
|
|
||||||
|
const actions = require('./actions');
|
||||||
|
const { postData, errorToast } = require('./../../client/src/utils');
|
||||||
|
|
||||||
|
const AcpGameList = require('./acp.game.list');
|
||||||
|
const AcpUser = require('./acp.user');
|
||||||
|
|
||||||
|
const addState = connect(
|
||||||
|
function receiveState(state) {
|
||||||
|
const {
|
||||||
|
account,
|
||||||
|
user,
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
return {
|
||||||
|
account, user,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
function receiveDispatch(dispatch) {
|
||||||
|
function setUser(user) {
|
||||||
|
dispatch(actions.setUser(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGames(list) {
|
||||||
|
dispatch(actions.setGames(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
setUser,
|
||||||
|
setGames,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
class AcpMain extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
account: {},
|
||||||
|
name: null,
|
||||||
|
id: null,
|
||||||
|
msg: '',
|
||||||
|
user: null,
|
||||||
|
games: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(args, state) {
|
||||||
|
const {
|
||||||
|
setGames,
|
||||||
|
setUser,
|
||||||
|
} = args;
|
||||||
|
|
||||||
|
const {
|
||||||
|
msg,
|
||||||
|
name,
|
||||||
|
id,
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
const getUser = () => {
|
||||||
|
this.setState({ msg: null });
|
||||||
|
postData('/acp/user', { id, name })
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) return this.setState({ msg: data.error });
|
||||||
|
setUser(data);
|
||||||
|
})
|
||||||
|
.catch(error => errorToast(error));
|
||||||
|
};
|
||||||
|
|
||||||
|
const gameList = () => {
|
||||||
|
this.setState({ msg: null });
|
||||||
|
postData('/acp/game/list', { number: 20 })
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) return this.setState({ msg: data.error });
|
||||||
|
console.log(data);
|
||||||
|
setGames(data.data);
|
||||||
|
})
|
||||||
|
.catch(error => errorToast(error));
|
||||||
|
};
|
||||||
|
|
||||||
|
const gameOpen = () => {
|
||||||
|
this.setState({ msg: null });
|
||||||
|
postData('/acp/game/open')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) return this.setState({ msg: data.error });
|
||||||
|
console.log(data);
|
||||||
|
setGames(data);
|
||||||
|
})
|
||||||
|
.catch(error => errorToast(error));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main class='menu'>
|
||||||
|
<div class="top">
|
||||||
|
<div>{msg}</div>
|
||||||
|
<AcpUser />
|
||||||
|
<AcpGameList />
|
||||||
|
</div>
|
||||||
|
<div class="bottom acp list">
|
||||||
|
<div>
|
||||||
|
<label for="current">Username:</label>
|
||||||
|
<input
|
||||||
|
class="login-input"
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={this.state.name}
|
||||||
|
onInput={linkState(this, 'name')}
|
||||||
|
placeholder="name"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
class="login-input"
|
||||||
|
type="text"
|
||||||
|
name="userid"
|
||||||
|
value={this.state.id}
|
||||||
|
onInput={linkState(this, 'id')}
|
||||||
|
placeholder="id"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={getUser}>
|
||||||
|
Search
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="current">Game:</label>
|
||||||
|
<input
|
||||||
|
class="login-input"
|
||||||
|
type="text"
|
||||||
|
name="userid"
|
||||||
|
value={this.state.id}
|
||||||
|
onInput={linkState(this, 'id')}
|
||||||
|
placeholder="id"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={getUser}>
|
||||||
|
Search
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={gameList}>
|
||||||
|
Last 20
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={gameOpen}>
|
||||||
|
Open
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addState(AcpMain);
|
||||||
44
acp/src/acp.user.jsx
Normal file
44
acp/src/acp.user.jsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const preact = require('preact');
|
||||||
|
const { Component } = require('preact');
|
||||||
|
const { connect } = require('preact-redux');
|
||||||
|
const linkState = require('linkstate').default;
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
const actions = require('./actions');
|
||||||
|
|
||||||
|
const addState = connect(
|
||||||
|
function receiveState(state) {
|
||||||
|
const {
|
||||||
|
user,
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
return {
|
||||||
|
user
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
function AcpGameList(args) {
|
||||||
|
const {
|
||||||
|
user,
|
||||||
|
} = args;
|
||||||
|
|
||||||
|
if (!user) return false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{user.name}</h1>
|
||||||
|
<dl>
|
||||||
|
<dt>Id</dt>
|
||||||
|
<dd>{user.id}</dd>
|
||||||
|
<dt>Credits</dt>
|
||||||
|
<dd>{user.balance}</dd>
|
||||||
|
<dt>Subscribed</dt>
|
||||||
|
<dd>{user.subscribed.toString()}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addState(AcpGameList);
|
||||||
@ -1,119 +0,0 @@
|
|||||||
const preact = require('preact');
|
|
||||||
const { Component } = require('preact');
|
|
||||||
const { connect } = require('preact-redux');
|
|
||||||
const linkState = require('linkstate').default;
|
|
||||||
|
|
||||||
const axios = require('axios');
|
|
||||||
|
|
||||||
const actions = require('./actions');
|
|
||||||
|
|
||||||
const addState = connect(
|
|
||||||
function receiveState(state) {
|
|
||||||
const {
|
|
||||||
account,
|
|
||||||
user,
|
|
||||||
} = state;
|
|
||||||
|
|
||||||
return {
|
|
||||||
account, user,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
function receiveDispatch(dispatch) {
|
|
||||||
function setUser(user) {
|
|
||||||
dispatch(actions.setUser(user));
|
|
||||||
}
|
|
||||||
|
|
||||||
return { setUser };
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
class AccountStatus extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
account: {},
|
|
||||||
name: null,
|
|
||||||
id: null,
|
|
||||||
msg: '',
|
|
||||||
user: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
render(args, state) {
|
|
||||||
const {
|
|
||||||
msg,
|
|
||||||
name,
|
|
||||||
id,
|
|
||||||
user,
|
|
||||||
} = state;
|
|
||||||
|
|
||||||
console.log(user);
|
|
||||||
|
|
||||||
const getUser = () => {
|
|
||||||
this.setState({ msg: null });
|
|
||||||
axios.post('/api/acp/user', { id, name })
|
|
||||||
.then(response => {
|
|
||||||
console.log(response);
|
|
||||||
this.setState({ user: JSON.parse(response.data.response) });
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error(error);
|
|
||||||
this.setState({ msg: error.message });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const userEl = user
|
|
||||||
? (
|
|
||||||
<div>
|
|
||||||
<h1>{user.name}</h1>
|
|
||||||
<dl>
|
|
||||||
<dt>Id</dt>
|
|
||||||
<dd>{user.id}</dd>
|
|
||||||
<dt>Credits</dt>
|
|
||||||
<dd>{user.balance}</dd>
|
|
||||||
<dt>Subscribed</dt>
|
|
||||||
<dd>{user.subscribed.toString()}</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main class='menu'>
|
|
||||||
<div class="top">
|
|
||||||
<div>{msg}</div>
|
|
||||||
{userEl}
|
|
||||||
</div>
|
|
||||||
<div class="bottom acp">
|
|
||||||
<div>
|
|
||||||
<label for="current">Username:</label>
|
|
||||||
<input
|
|
||||||
class="login-input"
|
|
||||||
type="text"
|
|
||||||
name="name"
|
|
||||||
value={this.state.name}
|
|
||||||
onInput={linkState(this, 'name')}
|
|
||||||
placeholder="name"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
class="login-input"
|
|
||||||
type="text"
|
|
||||||
name="userid"
|
|
||||||
value={this.state.id}
|
|
||||||
onInput={linkState(this, 'id')}
|
|
||||||
placeholder="id"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={getUser}>
|
|
||||||
Search
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = addState(AccountStatus);
|
|
||||||
@ -1,2 +1,3 @@
|
|||||||
export const setAccount = value => ({ type: 'SET_ACCOUNT', value });
|
export const setAccount = value => ({ type: 'SET_ACCOUNT', value });
|
||||||
export const setUser = value => ({ type: 'SET_USER', value });
|
export const setUser = value => ({ type: 'SET_USER', value });
|
||||||
|
export const setGames = value => ({ type: 'SET_GAMES', value });
|
||||||
|
|||||||
@ -13,4 +13,5 @@ function createReducer(defaultState, actionType) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
account: createReducer(null, 'SET_ACCOUNT'),
|
account: createReducer(null, 'SET_ACCOUNT'),
|
||||||
user: createReducer(null, 'SET_USER'),
|
user: createReducer(null, 'SET_USER'),
|
||||||
|
games: createReducer([], 'SET_GAMES'),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -89,12 +89,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#mnml.acp, .acp {
|
#mnml.acp {
|
||||||
user-select: text;
|
user-select: text;
|
||||||
-moz-user-select: text;
|
-moz-user-select: text;
|
||||||
-webkit-user-select: text;
|
-webkit-user-select: text;
|
||||||
-ms-user-select: text;
|
-ms-user-select: text;
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -204,23 +204,6 @@ function postData(url = '/', data = {}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getData(url = '/', data = {}) {
|
|
||||||
// Default options are marked with *
|
|
||||||
return fetch(`/api${url}`, {
|
|
||||||
method: 'GET', // *GET, POST, PUT, DELETE, etc.
|
|
||||||
// mode: 'no-cors', // no-cors, cors, *same-origin
|
|
||||||
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
|
||||||
credentials: 'include', // include, same-origin, *omit
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
redirect: 'error', // manual, *follow, error
|
|
||||||
// referrer: ', // no-referrer, *client
|
|
||||||
body: JSON.stringify(data), // body data type must match "Content-Type" header
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function errorToast(message) {
|
function errorToast(message) {
|
||||||
toast.error({
|
toast.error({
|
||||||
position: 'topRight',
|
position: 'topRight',
|
||||||
|
|||||||
@ -29,6 +29,23 @@ pub struct Account {
|
|||||||
pub subscribed: bool,
|
pub subscribed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<postgres::rows::Row<'a>> for Account {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(row: postgres::rows::Row) -> Result<Self, Error> {
|
||||||
|
let id: Uuid = row.get("id");
|
||||||
|
|
||||||
|
let db_balance: i64 = row.get("balance");
|
||||||
|
let balance = u32::try_from(db_balance)
|
||||||
|
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
|
||||||
|
|
||||||
|
let subscribed: bool = row.get("subscribed");
|
||||||
|
let name: String = row.get("name");
|
||||||
|
|
||||||
|
Ok(Account { id, name, balance, subscribed })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn select(db: &Db, id: Uuid) -> Result<Account, Error> {
|
pub fn select(db: &Db, id: Uuid) -> Result<Account, Error> {
|
||||||
let query = "
|
let query = "
|
||||||
SELECT id, name, balance, subscribed
|
SELECT id, name, balance, subscribed
|
||||||
@ -42,13 +59,7 @@ pub fn select(db: &Db, id: Uuid) -> Result<Account, Error> {
|
|||||||
let row = result.iter().next()
|
let row = result.iter().next()
|
||||||
.ok_or(format_err!("account not found {:?}", id))?;
|
.ok_or(format_err!("account not found {:?}", id))?;
|
||||||
|
|
||||||
let db_balance: i64 = row.get(2);
|
Account::try_from(row)
|
||||||
let balance = u32::try_from(db_balance)
|
|
||||||
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
|
|
||||||
|
|
||||||
let subscribed: bool = row.get(3);
|
|
||||||
|
|
||||||
Ok(Account { id, name: row.get(1), balance, subscribed })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_name(db: &Db, name: &String) -> Result<Account, Error> {
|
pub fn select_name(db: &Db, name: &String) -> Result<Account, Error> {
|
||||||
@ -64,14 +75,7 @@ pub fn select_name(db: &Db, name: &String) -> Result<Account, Error> {
|
|||||||
let row = result.iter().next()
|
let row = result.iter().next()
|
||||||
.ok_or(format_err!("account not found name={:?}", name))?;
|
.ok_or(format_err!("account not found name={:?}", name))?;
|
||||||
|
|
||||||
let id: Uuid = row.get(0);
|
Account::try_from(row)
|
||||||
let db_balance: i64 = row.get(2);
|
|
||||||
let balance = u32::try_from(db_balance)
|
|
||||||
.or(Err(format_err!("user {:?} has unparsable balance {:?}", name, db_balance)))?;
|
|
||||||
|
|
||||||
let subscribed: bool = row.get(3);
|
|
||||||
|
|
||||||
Ok(Account { id, name: row.get(1), balance, subscribed })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_token(db: &Db, token: String) -> Result<Account, Error> {
|
pub fn from_token(db: &Db, token: String) -> Result<Account, Error> {
|
||||||
@ -88,15 +92,7 @@ pub fn from_token(db: &Db, token: String) -> Result<Account, Error> {
|
|||||||
let row = result.iter().next()
|
let row = result.iter().next()
|
||||||
.ok_or(err_msg("invalid token"))?;
|
.ok_or(err_msg("invalid token"))?;
|
||||||
|
|
||||||
let id: Uuid = row.get(0);
|
Account::try_from(row)
|
||||||
let name: String = row.get(1);
|
|
||||||
let subscribed: bool = row.get(2);
|
|
||||||
let db_balance: i64 = row.get(3);
|
|
||||||
|
|
||||||
let balance = u32::try_from(db_balance)
|
|
||||||
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
|
|
||||||
|
|
||||||
Ok(Account { id, name, balance, subscribed })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<Account, MnmlHttpError> {
|
pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<Account, MnmlHttpError> {
|
||||||
@ -124,20 +120,13 @@ pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<A
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let id: Uuid = row.get(0);
|
|
||||||
let hash: String = row.get(1);
|
let hash: String = row.get(1);
|
||||||
let name: String = row.get(2);
|
|
||||||
let db_balance: i64 = row.get(3);
|
|
||||||
let subscribed: bool = row.get(4);
|
|
||||||
|
|
||||||
if !verify(password, &hash)? {
|
if !verify(password, &hash)? {
|
||||||
return Err(MnmlHttpError::PasswordNotMatch);
|
return Err(MnmlHttpError::PasswordNotMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
let balance = u32::try_from(db_balance)
|
Account::try_from(row)
|
||||||
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
|
.or(Err(MnmlHttpError::ServerError))
|
||||||
|
|
||||||
Ok(Account { id, name, balance, subscribed })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result<String, MnmlHttpError> {
|
pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result<String, MnmlHttpError> {
|
||||||
|
|||||||
125
server/src/acp.rs
Normal file
125
server/src/acp.rs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
use iron::prelude::*;
|
||||||
|
use iron::status;
|
||||||
|
|
||||||
|
use iron::{BeforeMiddleware};
|
||||||
|
use persistent::Read;
|
||||||
|
use router::Router;
|
||||||
|
|
||||||
|
use serde::{Deserialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use account;
|
||||||
|
use game;
|
||||||
|
|
||||||
|
use http::{State, MnmlHttpError, json_object};
|
||||||
|
|
||||||
|
struct AcpMiddleware;
|
||||||
|
impl BeforeMiddleware for AcpMiddleware {
|
||||||
|
fn before(&self, req: &mut Request) -> IronResult<()> {
|
||||||
|
match req.extensions.get::<account::Account>() {
|
||||||
|
Some(a) => {
|
||||||
|
if ["ntr", "mashy"].contains(&a.name.to_ascii_lowercase().as_ref()) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(MnmlHttpError::Unauthorized.into());
|
||||||
|
},
|
||||||
|
None => Err(MnmlHttpError::Unauthorized.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Deserialize)]
|
||||||
|
struct GetUser {
|
||||||
|
name: Option<String>,
|
||||||
|
id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn acp_user(req: &mut Request) -> IronResult<Response> {
|
||||||
|
let state = req.get::<Read<State>>().unwrap();
|
||||||
|
let params = match req.get::<bodyparser::Struct<GetUser>>() {
|
||||||
|
Ok(Some(b)) => b,
|
||||||
|
_ => return Err(MnmlHttpError::BadRequest.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
|
||||||
|
let user = match params.id {
|
||||||
|
Some(id) => account::select(&db, id)
|
||||||
|
.or(Err(MnmlHttpError::NotFound))?,
|
||||||
|
|
||||||
|
None => match params.name {
|
||||||
|
Some(n) => account::select_name(&db, &n)
|
||||||
|
.or(Err(MnmlHttpError::NotFound))?,
|
||||||
|
None => return Err(MnmlHttpError::BadRequest.into()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(json_object(status::Ok, serde_json::to_string(&user).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Deserialize)]
|
||||||
|
struct GetGame {
|
||||||
|
id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn acp_game(req: &mut Request) -> IronResult<Response> {
|
||||||
|
let state = req.get::<Read<State>>().unwrap();
|
||||||
|
let params = match req.get::<bodyparser::Struct<GetGame>>() {
|
||||||
|
Ok(Some(b)) => b,
|
||||||
|
_ => return Err(MnmlHttpError::BadRequest.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
|
||||||
|
let game = game::select(&db, params.id)
|
||||||
|
.or(Err(MnmlHttpError::NotFound))?;
|
||||||
|
|
||||||
|
Ok(json_object(status::Ok, serde_json::to_string(&game).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Deserialize)]
|
||||||
|
struct GameList {
|
||||||
|
number: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn game_list(req: &mut Request) -> IronResult<Response> {
|
||||||
|
let state = req.get::<Read<State>>().unwrap();
|
||||||
|
let params = match req.get::<bodyparser::Struct<GameList>>() {
|
||||||
|
Ok(Some(b)) => b,
|
||||||
|
_ => return Err(MnmlHttpError::BadRequest.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
|
||||||
|
let list = game::list(&db, params.number)
|
||||||
|
.or(Err(MnmlHttpError::ServerError))?;
|
||||||
|
|
||||||
|
Ok(json_object(status::Ok, serde_json::to_string(&list).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn game_open(req: &mut Request) -> IronResult<Response> {
|
||||||
|
let state = req.get::<Read<State>>().unwrap();
|
||||||
|
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
|
||||||
|
let list = game::games_need_upkeep(&mut tx)
|
||||||
|
.or(Err(MnmlHttpError::ServerError))?;
|
||||||
|
|
||||||
|
tx.commit()
|
||||||
|
.or(Err(MnmlHttpError::ServerError))?;
|
||||||
|
|
||||||
|
Ok(json_object(status::Ok, serde_json::to_string(&list).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acp_mount() -> Chain {
|
||||||
|
let mut router = Router::new();
|
||||||
|
router.post("user", acp_user, "acp_user");
|
||||||
|
router.post("game", acp_game, "acp_game");
|
||||||
|
router.post("game/list", game_list, "acp_game_list");
|
||||||
|
router.post("game/open", game_open, "acp_game_open");
|
||||||
|
|
||||||
|
let mut chain = Chain::new(router);
|
||||||
|
chain.link_before(AcpMiddleware);
|
||||||
|
chain
|
||||||
|
}
|
||||||
@ -12,6 +12,7 @@ use failure::Error;
|
|||||||
use failure::err_msg;
|
use failure::err_msg;
|
||||||
|
|
||||||
use account::Account;
|
use account::Account;
|
||||||
|
use pg::Db;
|
||||||
|
|
||||||
use construct::{Construct};
|
use construct::{Construct};
|
||||||
use skill::{Skill, Cast, Resolution, Event, resolution_steps};
|
use skill::{Skill, Cast, Resolution, Event, resolution_steps};
|
||||||
@ -655,6 +656,55 @@ pub fn game_get(tx: &mut Transaction, id: Uuid) -> Result<Game, Error> {
|
|||||||
return Ok(game);
|
return Ok(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select(db: &Db, id: Uuid) -> Result<Game, Error> {
|
||||||
|
let query = "
|
||||||
|
SELECT *
|
||||||
|
FROM games
|
||||||
|
WHERE id = $1;
|
||||||
|
";
|
||||||
|
|
||||||
|
let result = db
|
||||||
|
.query(query, &[&id])?;
|
||||||
|
|
||||||
|
let returned = match result.iter().next() {
|
||||||
|
Some(row) => row,
|
||||||
|
None => return Err(err_msg("game not found")),
|
||||||
|
};
|
||||||
|
|
||||||
|
// tells from_slice to cast into a construct
|
||||||
|
let game_bytes: Vec<u8> = returned.get("data");
|
||||||
|
let game = from_slice::<Game>(&game_bytes)?;
|
||||||
|
|
||||||
|
return Ok(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list(db: &Db, number: u32) -> Result<Vec<Game>, Error> {
|
||||||
|
let query = "
|
||||||
|
SELECT data
|
||||||
|
FROM games
|
||||||
|
ORDER BY created_at
|
||||||
|
LIMIT $1;
|
||||||
|
";
|
||||||
|
|
||||||
|
let result = db
|
||||||
|
.query(query, &[&number])?;
|
||||||
|
|
||||||
|
let mut list = vec![];
|
||||||
|
|
||||||
|
for row in result.into_iter() {
|
||||||
|
let bytes: Vec<u8> = row.get(0);
|
||||||
|
|
||||||
|
match from_slice::<Game>(&bytes) {
|
||||||
|
Ok(i) => list.push(i),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("{:?}", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn games_need_upkeep(tx: &mut Transaction) -> Result<Vec<Game>, Error> {
|
pub fn games_need_upkeep(tx: &mut Transaction) -> Result<Vec<Game>, Error> {
|
||||||
let query = "
|
let query = "
|
||||||
SELECT data, id
|
SELECT data, id
|
||||||
|
|||||||
@ -11,8 +11,8 @@ use persistent::Read;
|
|||||||
use router::Router;
|
use router::Router;
|
||||||
use mount::{Mount};
|
use mount::{Mount};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
|
use acp;
|
||||||
use account;
|
use account;
|
||||||
use pg::PgPool;
|
use pg::PgPool;
|
||||||
use payments::{stripe};
|
use payments::{stripe};
|
||||||
@ -62,42 +62,40 @@ impl From<postgres::Error> for MnmlHttpError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<r2d2::Error> for MnmlHttpError {
|
||||||
|
fn from(_err: r2d2::Error) -> Self {
|
||||||
|
MnmlHttpError::DbError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<failure::Error> for MnmlHttpError {
|
impl From<failure::Error> for MnmlHttpError {
|
||||||
fn from(_err: failure::Error) -> Self {
|
fn from(err: failure::Error) -> Self {
|
||||||
|
warn!("{:?}", err);
|
||||||
MnmlHttpError::ServerError
|
MnmlHttpError::ServerError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct JsonResponse {
|
#[serde(rename_all(serialize = "lowercase"))]
|
||||||
response: Option<String>,
|
pub enum Json {
|
||||||
success: bool,
|
Error(String),
|
||||||
error_message: Option<String>
|
Message(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JsonResponse {
|
pub fn json_response(status: status::Status, response: Json) -> Response {
|
||||||
fn success(response: String) -> Self {
|
|
||||||
JsonResponse { response: Some(response), success: true, error_message: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn error(msg: String) -> Self {
|
|
||||||
JsonResponse { response: None, success: false, error_message: Some(msg) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iron_response(status: status::Status, message: String) -> Response {
|
|
||||||
let content_type = "application/json".parse::<Mime>().unwrap();
|
let content_type = "application/json".parse::<Mime>().unwrap();
|
||||||
let msg = match status {
|
let json = serde_json::to_string(&response).unwrap();
|
||||||
status::Ok => JsonResponse::success(message),
|
return Response::with((content_type, status, json));
|
||||||
_ => JsonResponse::error(message)
|
}
|
||||||
};
|
|
||||||
let msg_out = serde_json::to_string(&msg).unwrap();
|
pub fn json_object(status: status::Status, object: String) -> Response {
|
||||||
return Response::with((content_type, status, msg_out));
|
let content_type = "application/json".parse::<Mime>().unwrap();
|
||||||
|
return Response::with((content_type, status, object));
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MnmlHttpError> for IronError {
|
impl From<MnmlHttpError> for IronError {
|
||||||
fn from(m_err: MnmlHttpError) -> Self {
|
fn from(m_err: MnmlHttpError) -> Self {
|
||||||
let (err, res) = match m_err {
|
let (err, status) = match m_err {
|
||||||
MnmlHttpError::ServerError |
|
MnmlHttpError::ServerError |
|
||||||
MnmlHttpError::DbError => (m_err.compat(), status::InternalServerError),
|
MnmlHttpError::DbError => (m_err.compat(), status::InternalServerError),
|
||||||
|
|
||||||
@ -114,7 +112,9 @@ impl From<MnmlHttpError> for IronError {
|
|||||||
|
|
||||||
MnmlHttpError::NotFound => (m_err.compat(), status::NotFound),
|
MnmlHttpError::NotFound => (m_err.compat(), status::NotFound),
|
||||||
};
|
};
|
||||||
IronError { error: Box::new(err), response: iron_response(res, m_err.to_string()) }
|
|
||||||
|
let response = json_response(status, Json::Error(m_err.to_string()));
|
||||||
|
IronError { error: Box::new(err), response }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +173,11 @@ fn token_res(token: String) -> Response {
|
|||||||
.max_age(Duration::weeks(1)) // 1 week aligns with db set
|
.max_age(Duration::weeks(1)) // 1 week aligns with db set
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
let mut res = iron_response(status::Ok, "token_res".to_string());
|
let mut res = json_response(
|
||||||
|
status::Ok,
|
||||||
|
Json::Message("authenticated".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
res.headers.set(SetCookie(vec![v.to_string()]));
|
res.headers.set(SetCookie(vec![v.to_string()]));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -249,7 +253,7 @@ fn logout(req: &mut Request) -> IronResult<Response> {
|
|||||||
|
|
||||||
tx.commit().or(Err(MnmlHttpError::ServerError))?;
|
tx.commit().or(Err(MnmlHttpError::ServerError))?;
|
||||||
|
|
||||||
let mut res = iron_response(status::Ok, "logout".to_string());
|
let mut res = json_response(status::Ok, Json::Message("logged out".to_string()));
|
||||||
res.headers.set(SetCookie(vec![AUTH_CLEAR.to_string()]));
|
res.headers.set(SetCookie(vec![AUTH_CLEAR.to_string()]));
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
|
||||||
@ -314,68 +318,12 @@ fn payment_mount() -> Router {
|
|||||||
router
|
router
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AcpMiddleware;
|
|
||||||
impl BeforeMiddleware for AcpMiddleware {
|
|
||||||
fn before(&self, req: &mut Request) -> IronResult<()> {
|
|
||||||
match req.extensions.get::<account::Account>() {
|
|
||||||
Some(a) => {
|
|
||||||
if ["ntr", "mashy"].contains(&a.name.to_ascii_lowercase().as_ref()) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Err(IronError::from(MnmlHttpError::Unauthorized));
|
|
||||||
},
|
|
||||||
None => Err(IronError::from(MnmlHttpError::Unauthorized)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug,Clone,Deserialize)]
|
|
||||||
struct GetUser {
|
|
||||||
name: Option<String>,
|
|
||||||
id: Option<Uuid>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn acp_user(req: &mut Request) -> IronResult<Response> {
|
|
||||||
let state = req.get::<Read<State>>().unwrap();
|
|
||||||
let params = match req.get::<bodyparser::Struct<GetUser>>() {
|
|
||||||
Ok(Some(b)) => b,
|
|
||||||
_ => return Err(IronError::from(MnmlHttpError::BadRequest)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
|
||||||
|
|
||||||
println!("{:?}", params);
|
|
||||||
|
|
||||||
let user = match params.id {
|
|
||||||
Some(id) => account::select(&db, id)
|
|
||||||
.or(Err(MnmlHttpError::NotFound))?,
|
|
||||||
|
|
||||||
None => match params.name {
|
|
||||||
Some(n) => account::select_name(&db, &n)
|
|
||||||
.or(Err(MnmlHttpError::NotFound))?,
|
|
||||||
None => return Err(IronError::from(MnmlHttpError::BadRequest)),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(iron_response(status::Ok, serde_json::to_string(&user).unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn acp_mount() -> Chain {
|
|
||||||
let mut router = Router::new();
|
|
||||||
router.post("user", acp_user, "acp_user");
|
|
||||||
|
|
||||||
let mut chain = Chain::new(router);
|
|
||||||
chain.link_before(AcpMiddleware);
|
|
||||||
chain
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(pool: PgPool) {
|
pub fn start(pool: PgPool) {
|
||||||
let mut mounts = Mount::new();
|
let mut mounts = Mount::new();
|
||||||
|
|
||||||
mounts.mount("/api/account/", account_mount());
|
mounts.mount("/api/account/", account_mount());
|
||||||
mounts.mount("/api/payments/", payment_mount());
|
mounts.mount("/api/payments/", payment_mount());
|
||||||
mounts.mount("/api/acp/", acp_mount());
|
mounts.mount("/api/acp/", acp::acp_mount());
|
||||||
|
|
||||||
let mut chain = Chain::new(mounts);
|
let mut chain = Chain::new(mounts);
|
||||||
chain.link(Read::<State>::both(State { pool }));
|
chain.link(Read::<State>::both(State { pool }));
|
||||||
|
|||||||
@ -30,6 +30,7 @@ extern crate ws;
|
|||||||
extern crate crossbeam_channel;
|
extern crate crossbeam_channel;
|
||||||
|
|
||||||
mod account;
|
mod account;
|
||||||
|
mod acp;
|
||||||
mod construct;
|
mod construct;
|
||||||
mod effect;
|
mod effect;
|
||||||
mod game;
|
mod game;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user