Merge branch 'account' into develop
This commit is contained in:
@@ -29,6 +29,7 @@ iron = "0.6"
|
||||
bodyparser = "0.8"
|
||||
persistent = "0.4"
|
||||
router = "0.6"
|
||||
mount = "0.4"
|
||||
cookie = "0.12"
|
||||
crossbeam-channel = "0.3"
|
||||
ws = "0.8"
|
||||
|
||||
+100
-26
@@ -8,7 +8,7 @@ use serde_cbor::{from_slice};
|
||||
|
||||
use postgres::transaction::Transaction;
|
||||
|
||||
use net::MnmlHttpError;
|
||||
use http::MnmlHttpError;
|
||||
use names::{name as generate_name};
|
||||
use construct::{Construct, construct_recover, construct_spawn};
|
||||
use instance::{Instance, instance_delete};
|
||||
@@ -29,6 +29,23 @@ pub struct Account {
|
||||
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> {
|
||||
let query = "
|
||||
SELECT id, name, balance, subscribed
|
||||
@@ -42,13 +59,23 @@ pub fn select(db: &Db, id: Uuid) -> Result<Account, Error> {
|
||||
let row = result.iter().next()
|
||||
.ok_or(format_err!("account not found {:?}", id))?;
|
||||
|
||||
let db_balance: i64 = row.get(2);
|
||||
let balance = u32::try_from(db_balance)
|
||||
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
|
||||
Account::try_from(row)
|
||||
}
|
||||
|
||||
let subscribed: bool = row.get(3);
|
||||
pub fn select_name(db: &Db, name: &String) -> Result<Account, Error> {
|
||||
let query = "
|
||||
SELECT id, name, balance, subscribed
|
||||
FROM accounts
|
||||
WHERE name = $1;
|
||||
";
|
||||
|
||||
Ok(Account { id, name: row.get(1), balance, subscribed })
|
||||
let result = db
|
||||
.query(query, &[&name])?;
|
||||
|
||||
let row = result.iter().next()
|
||||
.ok_or(format_err!("account not found name={:?}", name))?;
|
||||
|
||||
Account::try_from(row)
|
||||
}
|
||||
|
||||
pub fn from_token(db: &Db, token: String) -> Result<Account, Error> {
|
||||
@@ -65,15 +92,7 @@ pub fn from_token(db: &Db, token: String) -> Result<Account, Error> {
|
||||
let row = result.iter().next()
|
||||
.ok_or(err_msg("invalid token"))?;
|
||||
|
||||
let id: Uuid = row.get(0);
|
||||
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 })
|
||||
Account::try_from(row)
|
||||
}
|
||||
|
||||
pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<Account, MnmlHttpError> {
|
||||
@@ -101,23 +120,16 @@ 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 name: String = row.get(2);
|
||||
let db_balance: i64 = row.get(3);
|
||||
let subscribed: bool = row.get(4);
|
||||
|
||||
if !verify(password, &hash)? {
|
||||
return Err(MnmlHttpError::PasswordNotMatch);
|
||||
}
|
||||
|
||||
let balance = u32::try_from(db_balance)
|
||||
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
|
||||
|
||||
Ok(Account { id, name, balance, subscribed })
|
||||
Account::try_from(row)
|
||||
.or(Err(MnmlHttpError::ServerError))
|
||||
}
|
||||
|
||||
pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result<String, Error> {
|
||||
pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result<String, MnmlHttpError> {
|
||||
let mut rng = thread_rng();
|
||||
let token: String = iter::repeat(())
|
||||
.map(|()| rng.sample(Alphanumeric))
|
||||
@@ -136,11 +148,73 @@ pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result<String, Error> {
|
||||
.query(query, &[&token, &id])?;
|
||||
|
||||
result.iter().next()
|
||||
.ok_or(format_err!("account not updated {:?}", id))?;
|
||||
.ok_or(MnmlHttpError::Unauthorized)?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
pub fn set_password(tx: &mut Transaction, id: Uuid, current: &String, password: &String) -> Result<String, MnmlHttpError> {
|
||||
if password.len() < PASSWORD_MIN_LEN {
|
||||
return Err(MnmlHttpError::PasswordUnacceptable);
|
||||
}
|
||||
|
||||
let query = "
|
||||
SELECT id, password
|
||||
FROM accounts
|
||||
WHERE id = $1
|
||||
";
|
||||
|
||||
let result = tx
|
||||
.query(query, &[&id])?;
|
||||
|
||||
let row = match result.iter().next() {
|
||||
Some(row) => row,
|
||||
None => {
|
||||
let mut rng = thread_rng();
|
||||
let garbage: String = iter::repeat(())
|
||||
.map(|()| rng.sample(Alphanumeric))
|
||||
.take(64)
|
||||
.collect();
|
||||
|
||||
// verify garbage to prevent timing attacks
|
||||
verify(garbage.clone(), &garbage).ok();
|
||||
return Err(MnmlHttpError::AccountNotFound);
|
||||
},
|
||||
};
|
||||
|
||||
let id: Uuid = row.get(0);
|
||||
let db_pw: String = row.get(1);
|
||||
|
||||
if !verify(current, &db_pw)? {
|
||||
return Err(MnmlHttpError::PasswordNotMatch);
|
||||
}
|
||||
|
||||
let rounds = 8;
|
||||
let password = hash(&password, rounds)?;
|
||||
|
||||
let query = "
|
||||
UPDATE accounts
|
||||
SET password = $1, updated_at = now()
|
||||
WHERE id = $2
|
||||
RETURNING id, name;
|
||||
";
|
||||
|
||||
let result = tx
|
||||
.query(query, &[&password, &id])?;
|
||||
|
||||
let row = match result.iter().next() {
|
||||
Some(row) => row,
|
||||
None => return Err(MnmlHttpError::DbError),
|
||||
};
|
||||
|
||||
let name: String = row.get(1);
|
||||
|
||||
info!("password updated name={:?} id={:?}", name, id);
|
||||
|
||||
new_token(tx, id)
|
||||
}
|
||||
|
||||
|
||||
pub fn credit(tx: &mut Transaction, id: Uuid, credits: i64) -> Result<String, Error> {
|
||||
let query = "
|
||||
UPDATE accounts
|
||||
|
||||
@@ -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 account::Account;
|
||||
use pg::Db;
|
||||
|
||||
use construct::{Construct};
|
||||
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);
|
||||
}
|
||||
|
||||
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> {
|
||||
let query = "
|
||||
SELECT data, id
|
||||
|
||||
@@ -9,15 +9,17 @@ use iron::mime::Mime;
|
||||
use iron::{typemap, BeforeMiddleware,AfterMiddleware};
|
||||
use persistent::Read;
|
||||
use router::Router;
|
||||
use mount::{Mount};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use acp;
|
||||
use account;
|
||||
use pg::PgPool;
|
||||
use payments::{stripe};
|
||||
|
||||
pub const TOKEN_HEADER: &str = "x-auth-token";
|
||||
pub const AUTH_CLEAR: &str =
|
||||
"x-auth-token=; HttpOnly; SameSite=Strict; Max-Age=-1;";
|
||||
"x-auth-token=; HttpOnly; SameSite=Strict; Path=/; Max-Age=-1;";
|
||||
|
||||
#[derive(Clone, Copy, Fail, Debug, Serialize, Deserialize)]
|
||||
pub enum MnmlHttpError {
|
||||
@@ -30,6 +32,8 @@ pub enum MnmlHttpError {
|
||||
Unauthorized,
|
||||
#[fail(display="bad request")]
|
||||
BadRequest,
|
||||
#[fail(display="not found")]
|
||||
NotFound,
|
||||
#[fail(display="account name taken or invalid")]
|
||||
AccountNameNotProvided,
|
||||
#[fail(display="account name not provided")]
|
||||
@@ -58,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 {
|
||||
fn from(_err: failure::Error) -> Self {
|
||||
fn from(err: failure::Error) -> Self {
|
||||
warn!("{:?}", err);
|
||||
MnmlHttpError::ServerError
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct JsonResponse {
|
||||
response: Option<String>,
|
||||
success: bool,
|
||||
error_message: Option<String>
|
||||
#[serde(rename_all(serialize = "lowercase"))]
|
||||
pub enum Json {
|
||||
Error(String),
|
||||
Message(String),
|
||||
}
|
||||
|
||||
impl JsonResponse {
|
||||
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 {
|
||||
pub fn json_response(status: status::Status, response: Json) -> Response {
|
||||
let content_type = "application/json".parse::<Mime>().unwrap();
|
||||
let msg = match status {
|
||||
status::Ok => JsonResponse::success(message),
|
||||
_ => JsonResponse::error(message)
|
||||
};
|
||||
let msg_out = serde_json::to_string(&msg).unwrap();
|
||||
return Response::with((content_type, status, msg_out));
|
||||
let json = serde_json::to_string(&response).unwrap();
|
||||
return Response::with((content_type, status, json));
|
||||
}
|
||||
|
||||
pub fn json_object(status: status::Status, object: String) -> Response {
|
||||
let content_type = "application/json".parse::<Mime>().unwrap();
|
||||
return Response::with((content_type, status, object));
|
||||
}
|
||||
|
||||
impl From<MnmlHttpError> for IronError {
|
||||
fn from(m_err: MnmlHttpError) -> Self {
|
||||
let (err, res) = match m_err {
|
||||
let (err, status) = match m_err {
|
||||
MnmlHttpError::ServerError |
|
||||
MnmlHttpError::DbError => (m_err.compat(), status::InternalServerError),
|
||||
|
||||
@@ -107,8 +109,12 @@ impl From<MnmlHttpError> for IronError {
|
||||
MnmlHttpError::InvalidCode |
|
||||
MnmlHttpError::TokenDoesNotMatch |
|
||||
MnmlHttpError::Unauthorized => (m_err.compat(), status::Unauthorized),
|
||||
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +169,15 @@ fn token_res(token: String) -> Response {
|
||||
let v = Cookie::build(TOKEN_HEADER, token)
|
||||
.http_only(true)
|
||||
.same_site(SameSite::Strict)
|
||||
.path("/")
|
||||
.max_age(Duration::weeks(1)) // 1 week aligns with db set
|
||||
.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()]));
|
||||
|
||||
return res;
|
||||
@@ -220,7 +231,7 @@ fn login(req: &mut Request) -> IronResult<Response> {
|
||||
|
||||
match account::login(&mut tx, ¶ms.name, ¶ms.password) {
|
||||
Ok(a) => {
|
||||
let token = account::new_token(&mut tx, a.id).or(Err(MnmlHttpError::ServerError))?;
|
||||
let token = account::new_token(&mut tx, a.id)?;
|
||||
tx.commit().or(Err(MnmlHttpError::ServerError))?;
|
||||
Ok(token_res(token))
|
||||
},
|
||||
@@ -238,11 +249,11 @@ fn logout(req: &mut Request) -> IronResult<Response> {
|
||||
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
||||
let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?;
|
||||
|
||||
account::new_token(&mut tx, a.id).or(Err(MnmlHttpError::Unauthorized))?;
|
||||
account::new_token(&mut tx, a.id)?;
|
||||
|
||||
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()]));
|
||||
Ok(res)
|
||||
|
||||
@@ -251,6 +262,33 @@ fn logout(req: &mut Request) -> IronResult<Response> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Deserialize)]
|
||||
struct SetPassword {
|
||||
current: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
fn set_password(req: &mut Request) -> IronResult<Response> {
|
||||
let state = req.get::<Read<State>>().unwrap();
|
||||
let params = match req.get::<bodyparser::Struct<SetPassword>>() {
|
||||
Ok(Some(b)) => b,
|
||||
_ => return Err(IronError::from(MnmlHttpError::BadRequest)),
|
||||
};
|
||||
|
||||
match req.extensions.get::<account::Account>() {
|
||||
Some(a) => {
|
||||
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
||||
let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?;
|
||||
|
||||
let token = account::set_password(&mut tx, a.id, ¶ms.current, ¶ms.password)?;
|
||||
|
||||
tx.commit().or(Err(MnmlHttpError::ServerError))?;
|
||||
|
||||
Ok(token_res(token))
|
||||
},
|
||||
None => Err(IronError::from(MnmlHttpError::Unauthorized)),
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_BODY_LENGTH: usize = 1024 * 1024 * 10;
|
||||
|
||||
@@ -261,18 +299,33 @@ pub struct State {
|
||||
|
||||
impl Key for State { type Value = State; }
|
||||
|
||||
pub fn start(pool: PgPool) {
|
||||
fn account_mount() -> Router {
|
||||
let mut router = Router::new();
|
||||
|
||||
// auth
|
||||
router.post("/api/login", login, "login");
|
||||
router.post("/api/logout", logout, "logout");
|
||||
router.post("/api/register", register, "register");
|
||||
router.post("login", login, "login");
|
||||
router.post("logout", logout, "logout");
|
||||
router.post("register", register, "register");
|
||||
router.post("password", set_password, "set_password");
|
||||
router.post("email", logout, "email");
|
||||
|
||||
// payments
|
||||
router.post("/api/payments/stripe", stripe, "stripe");
|
||||
router
|
||||
}
|
||||
|
||||
let mut chain = Chain::new(router);
|
||||
fn payment_mount() -> Router {
|
||||
let mut router = Router::new();
|
||||
router.post("stripe", stripe, "stripe");
|
||||
|
||||
router
|
||||
}
|
||||
|
||||
pub fn start(pool: PgPool) {
|
||||
let mut mounts = Mount::new();
|
||||
|
||||
mounts.mount("/api/account/", account_mount());
|
||||
mounts.mount("/api/payments/", payment_mount());
|
||||
mounts.mount("/api/acp/", acp::acp_mount());
|
||||
|
||||
let mut chain = Chain::new(mounts);
|
||||
chain.link(Read::<State>::both(State { pool }));
|
||||
chain.link_before(Read::<bodyparser::MaxBodyLength>::one(MAX_BODY_LENGTH));
|
||||
chain.link_before(AuthMiddleware);
|
||||
@@ -396,9 +396,11 @@ impl Instance {
|
||||
|
||||
// if you don't win, you lose
|
||||
// ties can happen if both players forfeit
|
||||
// in this case we just finish the game and
|
||||
// dock them 10k mmr
|
||||
let winner_id = match game.winner() {
|
||||
Some(w) => w.id,
|
||||
None => Uuid::nil(),
|
||||
None => return Ok(self.finish()),
|
||||
};
|
||||
|
||||
for player in game.players.iter() {
|
||||
|
||||
+4
-2
@@ -23,12 +23,14 @@ extern crate iron;
|
||||
extern crate bodyparser;
|
||||
extern crate persistent;
|
||||
extern crate router;
|
||||
extern crate mount;
|
||||
extern crate cookie;
|
||||
|
||||
extern crate ws;
|
||||
extern crate crossbeam_channel;
|
||||
|
||||
mod account;
|
||||
mod acp;
|
||||
mod construct;
|
||||
mod effect;
|
||||
mod game;
|
||||
@@ -38,7 +40,7 @@ mod img;
|
||||
mod mob;
|
||||
mod mtx;
|
||||
mod names;
|
||||
mod net;
|
||||
mod http;
|
||||
mod payments;
|
||||
mod pg;
|
||||
mod player;
|
||||
@@ -98,7 +100,7 @@ fn main() {
|
||||
|
||||
let pg_pool = pool.clone();
|
||||
|
||||
spawn(move || net::start(http_pool));
|
||||
spawn(move || http::start(http_pool));
|
||||
spawn(move || warden.listen());
|
||||
spawn(move || warden::upkeep_tick(warden_tick_tx));
|
||||
spawn(move || pg::listen(pg_pool, pg_events_tx));
|
||||
|
||||
+9
-3
@@ -1,7 +1,7 @@
|
||||
use rand::prelude::*;
|
||||
use rand::{thread_rng};
|
||||
|
||||
const FIRSTS: [&'static str; 47] = [
|
||||
const FIRSTS: [&'static str; 50] = [
|
||||
"artificial",
|
||||
"ambient",
|
||||
"borean",
|
||||
@@ -20,10 +20,13 @@ const FIRSTS: [&'static str; 47] = [
|
||||
"fierce",
|
||||
"fossilised",
|
||||
"frozen",
|
||||
"gravitational",
|
||||
"jovian",
|
||||
"inverted",
|
||||
"leafy",
|
||||
"lurking",
|
||||
"limitless",
|
||||
"magnetic",
|
||||
"metallic",
|
||||
"mossy",
|
||||
"mighty",
|
||||
@@ -37,6 +40,7 @@ const FIRSTS: [&'static str; 47] = [
|
||||
"oxygenated",
|
||||
"oscillating",
|
||||
"ossified",
|
||||
"orbiting",
|
||||
"piscine",
|
||||
"purified",
|
||||
"recalcitrant",
|
||||
@@ -46,18 +50,18 @@ const FIRSTS: [&'static str; 47] = [
|
||||
"supercooled",
|
||||
"subsonic",
|
||||
"synthetic",
|
||||
"sweet",
|
||||
"terrestrial",
|
||||
"weary",
|
||||
];
|
||||
|
||||
const LASTS: [&'static str; 52] = [
|
||||
const LASTS: [&'static str; 55] = [
|
||||
"artifact",
|
||||
"assembly",
|
||||
"carbon",
|
||||
"console",
|
||||
"construct",
|
||||
"craft",
|
||||
"core",
|
||||
"design",
|
||||
"drone",
|
||||
"distortion",
|
||||
@@ -77,6 +81,8 @@ const LASTS: [&'static str; 52] = [
|
||||
"lifeform",
|
||||
"landmass",
|
||||
"lens",
|
||||
"mantle",
|
||||
"magnetism",
|
||||
"mechanism",
|
||||
"mountain",
|
||||
"nectar",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::io::Read;
|
||||
|
||||
use net::State;
|
||||
use http::State;
|
||||
use iron::prelude::*;
|
||||
use iron::response::HttpResponse;
|
||||
use iron::status;
|
||||
@@ -14,7 +14,7 @@ use failure::err_msg;
|
||||
|
||||
use stripe::{Event, EventObject, CheckoutSession, SubscriptionStatus};
|
||||
|
||||
use net::{MnmlHttpError};
|
||||
use http::{MnmlHttpError};
|
||||
use pg::{PgPool};
|
||||
use account;
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ use pg::{Db};
|
||||
use pg::{PgPool};
|
||||
use skill::{Skill, dev_resolve, Resolutions};
|
||||
use vbox::{vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_reclaim, vbox_unequip};
|
||||
use net::{AUTH_CLEAR, TOKEN_HEADER};
|
||||
use http::{AUTH_CLEAR, TOKEN_HEADER};
|
||||
|
||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||
pub enum RpcMessage {
|
||||
|
||||
Reference in New Issue
Block a user