Migrate to Rocket 0.5.
Rocket 0.5 is a major uprade, rewriting most of Rocket to be async. Required many changes through the code, especially the database layer. The new Rocket async database calls require Futures with 'static lifetimes. General: - Move to stable rust. - Most of codebase is now async. - Rocket migrations (e.g. Cookies to CookieJar). Database: - Switched to owned data (&str -> String) for inserts because of the 'static lifetime requirement on Rocket's DB future. - All database methods now asynchronous. Pages: - Changed various routes to async. - Needed to add clone calls to some places because we need to use owned data multiple times (registration).
This commit is contained in:
parent
4024f32621
commit
8c8ed4f6ef
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
|
@ -20,10 +20,16 @@ thiserror = "1.0"
|
||||||
rust-argon2 = "0.8"
|
rust-argon2 = "0.8"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
rocket = { version= "0.4.6", features = ["private-cookies"] }
|
futures = "0.3"
|
||||||
strum = { version = "0.20", features = ["derive"] }
|
strum = { version = "0.20", features = ["derive"] }
|
||||||
|
|
||||||
|
[dependencies.rocket]
|
||||||
|
git = "https://github.com/SergioBenitez/Rocket"
|
||||||
|
branch = "master"
|
||||||
|
features = ["secrets"]
|
||||||
|
|
||||||
[dependencies.rocket_contrib]
|
[dependencies.rocket_contrib]
|
||||||
version = "0.4.6"
|
git = "https://github.com/SergioBenitez/Rocket"
|
||||||
|
branch = "master"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [ "tera_templates", "diesel_sqlite_pool", "serve" ]
|
features = [ "tera_templates", "diesel_sqlite_pool", "serve" ]
|
|
@ -7,5 +7,5 @@ write_timeout = 5
|
||||||
log = "normal"
|
log = "normal"
|
||||||
limits = { forms = 32768 }
|
limits = { forms = 32768 }
|
||||||
|
|
||||||
[development.databases]
|
[global.databases]
|
||||||
tenebrous_db = { url = "./tenebrous.sqlite" }
|
tenebrous_db = { url = "./tenebrous.sqlite" }
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
nightly
|
stable
|
||||||
|
|
98
src/db.rs
98
src/db.rs
|
@ -2,23 +2,27 @@ use crate::models::characters::{Character, NewCharacter, StrippedCharacter};
|
||||||
use crate::models::users::{NewUser, User};
|
use crate::models::users::{NewUser, User};
|
||||||
use crate::schema::characters;
|
use crate::schema::characters;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::SqliteConnection;
|
use rocket_contrib::databases::diesel;
|
||||||
|
|
||||||
#[database("tenebrous_db")]
|
#[database("tenebrous_db")]
|
||||||
pub(crate) struct TenebrousDbConn(SqliteConnection);
|
pub(crate) struct TenebrousDbConn(SqliteConnection);
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
pub(crate) trait Dao {
|
pub(crate) trait Dao {
|
||||||
fn load_user_by_id(&self, id: i32) -> QueryResult<Option<User>>;
|
async fn load_user_by_id(&self, id: i32) -> QueryResult<Option<User>>;
|
||||||
|
|
||||||
fn load_user(&self, for_username: &str) -> QueryResult<Option<User>>;
|
async fn load_user(&self, for_username: String) -> QueryResult<Option<User>>;
|
||||||
|
|
||||||
fn insert_user(&self, new_user: &NewUser) -> QueryResult<User>;
|
//async fn insert_user<'a>(&self, new_user: &'a NewUser<'a>) -> QueryResult<User>;
|
||||||
|
|
||||||
fn load_character_list(&self, for_user_id: i32) -> QueryResult<Vec<StrippedCharacter>>;
|
async fn insert_user(&self, new_user: NewUser) -> QueryResult<User>;
|
||||||
|
|
||||||
fn load_character(&self, character_id: i32) -> QueryResult<Option<Character>>;
|
async fn load_character_list(&self, for_user_id: i32) -> QueryResult<Vec<StrippedCharacter>>;
|
||||||
|
|
||||||
fn insert_character(&self, new_character: NewCharacter) -> QueryResult<()>;
|
async fn load_character(&self, character_id: i32) -> QueryResult<Option<Character>>;
|
||||||
|
|
||||||
|
//async fn insert_character<'a>(&self, new_character: NewCharacter<'a>) -> QueryResult<()>;
|
||||||
|
async fn insert_character(&self, new_character: NewCharacter) -> QueryResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type StrippedCharacterColumns = (
|
type StrippedCharacterColumns = (
|
||||||
|
@ -39,55 +43,67 @@ const STRIPPED_CHARACTER_COLUMNS: StrippedCharacterColumns = (
|
||||||
characters::data_version,
|
characters::data_version,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
impl Dao for TenebrousDbConn {
|
impl Dao for TenebrousDbConn {
|
||||||
fn load_user_by_id(&self, user_id: i32) -> QueryResult<Option<User>> {
|
async fn load_user_by_id(&self, user_id: i32) -> QueryResult<Option<User>> {
|
||||||
use crate::schema::users::dsl::*;
|
use crate::schema::users::dsl::*;
|
||||||
users.filter(id.eq(user_id)).first(&self.0).optional()
|
self.run(move |conn| users.filter(id.eq(user_id)).first(conn).optional())
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_user(&self, for_username: &str) -> QueryResult<Option<User>> {
|
async fn load_user(&self, for_username: String) -> QueryResult<Option<User>> {
|
||||||
use crate::schema::users::dsl::*;
|
use crate::schema::users::dsl::*;
|
||||||
|
|
||||||
users
|
self.run(move |conn| {
|
||||||
.filter(username.eq(for_username))
|
users
|
||||||
.first(&self.0)
|
.filter(username.eq(for_username))
|
||||||
.optional()
|
.first(conn)
|
||||||
|
.optional()
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_user(&self, new_user: &NewUser) -> QueryResult<User> {
|
async fn insert_user(&self, new_user: NewUser) -> QueryResult<User> {
|
||||||
use crate::schema::users;
|
self.run(move |conn| {
|
||||||
|
diesel::insert_into(users).values(&new_user).execute(conn)?;
|
||||||
|
|
||||||
diesel::insert_into(users::table)
|
use crate::schema::users::dsl::*;
|
||||||
.values(new_user)
|
users.filter(username.eq(new_user.username)).first(conn)
|
||||||
.execute(&self.0)?;
|
})
|
||||||
|
.await
|
||||||
use crate::schema::users::dsl::*;
|
|
||||||
Ok(users
|
|
||||||
.filter(username.eq(new_user.username))
|
|
||||||
.first(&self.0)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_character_list(&self, for_user_id: i32) -> QueryResult<Vec<StrippedCharacter>> {
|
async fn load_character_list(&self, for_user_id: i32) -> QueryResult<Vec<StrippedCharacter>> {
|
||||||
use crate::schema::characters::dsl::*;
|
|
||||||
characters
|
|
||||||
.filter(user_id.eq(for_user_id))
|
|
||||||
.select(STRIPPED_CHARACTER_COLUMNS)
|
|
||||||
.load(&self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_character(&self, character_id: i32) -> QueryResult<Option<Character>> {
|
|
||||||
use crate::schema::characters::dsl::*;
|
use crate::schema::characters::dsl::*;
|
||||||
|
|
||||||
characters
|
self.run(move |conn| {
|
||||||
.filter(id.eq(character_id))
|
characters
|
||||||
.first(&self.0)
|
.filter(user_id.eq(for_user_id))
|
||||||
.optional()
|
.select(STRIPPED_CHARACTER_COLUMNS)
|
||||||
|
.load(conn)
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_character(&self, new_character: NewCharacter) -> QueryResult<()> {
|
async fn load_character(&self, character_id: i32) -> QueryResult<Option<Character>> {
|
||||||
diesel::insert_into(characters::table)
|
use crate::schema::characters::dsl::*;
|
||||||
.values(new_character)
|
|
||||||
.execute(&self.0)?;
|
self.run(move |conn| {
|
||||||
|
characters
|
||||||
|
.filter(id.eq(character_id))
|
||||||
|
.first(conn)
|
||||||
|
.optional()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn insert_character(&self, new_character: NewCharacter) -> QueryResult<()> {
|
||||||
|
self.run(|conn| {
|
||||||
|
diesel::insert_into(characters::table)
|
||||||
|
.values(new_character)
|
||||||
|
.execute(conn)
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,6 @@ pub enum Error {
|
||||||
DeserializationError(#[from] prost::DecodeError),
|
DeserializationError(#[from] prost::DecodeError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
#[error("internal eror")]
|
|
||||||
pub struct SensitiveError(Error);
|
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
fn is_sensitive(&self) -> bool {
|
fn is_sensitive(&self) -> bool {
|
||||||
use Error::*;
|
use Error::*;
|
||||||
|
@ -41,8 +37,9 @@ impl Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Responder<'r> for Error {
|
#[rocket::async_trait]
|
||||||
fn respond_to(self, req: &Request) -> response::Result<'r> {
|
impl<'r> Responder<'r, 'static> for Error {
|
||||||
|
fn respond_to(self, req: &Request) -> response::Result<'static> {
|
||||||
log::error!("error: {0}", self.to_string());
|
log::error!("error: {0}", self.to_string());
|
||||||
//Hide sensitive error information
|
//Hide sensitive error information
|
||||||
let message: String = if self.is_sensitive() {
|
let message: String = if self.is_sensitive() {
|
||||||
|
@ -64,22 +61,3 @@ impl<'r> Responder<'r> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Responder<'r> for SensitiveError {
|
|
||||||
fn respond_to(self, req: &Request) -> response::Result<'r> {
|
|
||||||
log::error!("sensitive error: {0}", self.0.to_string());
|
|
||||||
let message: String = self.to_string();
|
|
||||||
|
|
||||||
let mut context = HashMap::new();
|
|
||||||
context.insert("message", message);
|
|
||||||
let resp = Template::render("error", context).respond_to(req)?;
|
|
||||||
|
|
||||||
use Error::*;
|
|
||||||
match self.0 {
|
|
||||||
NotFound => status::NotFound(resp).respond_to(req),
|
|
||||||
NotLoggedIn => status::Forbidden(Some(resp)).respond_to(req),
|
|
||||||
NoPermission => status::Forbidden(Some(resp)).respond_to(req),
|
|
||||||
_ => status::Custom(Status::InternalServerError, resp).respond_to(req),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![feature(proc_macro_hygiene, decl_macro, never_type)]
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
|
@ -24,7 +22,8 @@ pub mod models;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
|
||||||
fn main() {
|
#[rocket::main]
|
||||||
|
async fn main() -> Result<(), rocket::error::Error> {
|
||||||
let root_routes: Vec<rocket::Route> = {
|
let root_routes: Vec<rocket::Route> = {
|
||||||
routes::root::routes()
|
routes::root::routes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -45,5 +44,6 @@ fn main() {
|
||||||
StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/proto")),
|
StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/proto")),
|
||||||
)
|
)
|
||||||
.register(catchers)
|
.register(catchers)
|
||||||
.launch();
|
.launch()
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,11 +155,11 @@ impl Visibility for StrippedCharacter {
|
||||||
/// names correspond to columns.
|
/// names correspond to columns.
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name = "characters"]
|
#[table_name = "characters"]
|
||||||
pub struct NewCharacter<'a> {
|
pub struct NewCharacter {
|
||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
pub viewable: bool,
|
pub viewable: bool,
|
||||||
pub character_name: &'a str,
|
pub character_name: String,
|
||||||
pub data_type: CharacterDataType,
|
pub data_type: CharacterDataType,
|
||||||
pub data_version: i32,
|
pub data_version: i32,
|
||||||
pub data: &'a [u8],
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,24 +25,29 @@ impl User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn attempt_load_user<'a>(db: &'a TenebrousDbConn, id: i32) -> Option<User> {
|
||||||
|
db.load_user_by_id(id).await.ok().flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
impl<'a, 'r> FromRequest<'a, 'r> for &'a User {
|
impl<'a, 'r> FromRequest<'a, 'r> for &'a User {
|
||||||
type Error = !;
|
type Error = ();
|
||||||
|
|
||||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, !> {
|
async fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, ()> {
|
||||||
let user: &Option<User> = request.local_cache(|| {
|
let db = try_outcome!(request.guard::<TenebrousDbConn>().await);
|
||||||
let attempt_load_user = |id| -> Option<User> {
|
let user_id: Option<i32> = request
|
||||||
TenebrousDbConn::from_request(request)
|
.cookies()
|
||||||
.map(|conn| conn.load_user_by_id(id).ok().flatten())
|
.get_private("user_id")
|
||||||
.succeeded()
|
.and_then(|cookie| cookie.value().parse().ok());
|
||||||
.flatten()
|
|
||||||
};
|
|
||||||
|
|
||||||
request
|
let user: &Option<User> = request
|
||||||
.cookies()
|
.local_cache_async(async {
|
||||||
.get_private("user_id")
|
match user_id {
|
||||||
.and_then(|cookie| cookie.value().parse().ok())
|
Some(id) => attempt_load_user(&db, id).await,
|
||||||
.and_then(attempt_load_user)
|
_ => None,
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
user.as_ref().or_forward(())
|
user.as_ref().or_forward(())
|
||||||
}
|
}
|
||||||
|
@ -50,7 +55,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for &'a User {
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name = "users"]
|
#[table_name = "users"]
|
||||||
pub struct NewUser<'a> {
|
pub struct NewUser {
|
||||||
pub username: &'a str,
|
pub username: String,
|
||||||
pub password: &'a str,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::db::{Dao, TenebrousDbConn};
|
use crate::db::{Dao, TenebrousDbConn};
|
||||||
use crate::models::users::{self, NewUser, User};
|
use crate::models::users::{self, NewUser, User};
|
||||||
use log::error;
|
use log::error;
|
||||||
use rocket::http::{Cookie, Cookies};
|
use rocket::http::{Cookie, CookieJar};
|
||||||
use rocket::request::{FlashMessage, Form};
|
use rocket::request::{FlashMessage, Form};
|
||||||
use rocket::response::{Flash, Redirect};
|
use rocket::response::{Flash, Redirect};
|
||||||
use rocket_contrib::templates::Template;
|
use rocket_contrib::templates::Template;
|
||||||
|
@ -30,11 +30,11 @@ struct Registration {
|
||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_login_cookie(cookies: &mut Cookies, user: &User) {
|
fn add_login_cookie(cookies: &CookieJar<'_>, user: &User) {
|
||||||
cookies.add_private(Cookie::new("user_id", user.id.to_string()));
|
cookies.add_private(Cookie::new("user_id", user.id.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_login_cookie(cookies: &mut Cookies) {
|
fn remove_login_cookie(cookies: &CookieJar<'_>) {
|
||||||
cookies.remove_private(Cookie::named("user_id"));
|
cookies.remove_private(Cookie::named("user_id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,13 +47,14 @@ fn registration_error_redirect<S: AsRef<str>>(message: S) -> Flash<Redirect> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/login", data = "<login>")]
|
#[post("/login", data = "<login>")]
|
||||||
fn login(
|
async fn login(
|
||||||
mut cookies: Cookies,
|
cookies: &CookieJar<'_>,
|
||||||
login: Form<Login>,
|
login: Form<Login>,
|
||||||
conn: TenebrousDbConn,
|
conn: TenebrousDbConn,
|
||||||
) -> Result<Redirect, Flash<Redirect>> {
|
) -> Result<Redirect, Flash<Redirect>> {
|
||||||
let user = conn
|
let user = conn
|
||||||
.load_user(&login.username)
|
.load_user(login.username.clone())
|
||||||
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!("login - error loading user user: {}", e);
|
error!("login - error loading user user: {}", e);
|
||||||
login_error_redirect("Internal error.")
|
login_error_redirect("Internal error.")
|
||||||
|
@ -61,7 +62,7 @@ fn login(
|
||||||
.ok_or_else(|| login_error_redirect("Invalid username or password."))?;
|
.ok_or_else(|| login_error_redirect("Invalid username or password."))?;
|
||||||
|
|
||||||
if user.verify_password(&login.password) {
|
if user.verify_password(&login.password) {
|
||||||
add_login_cookie(&mut cookies, &user);
|
add_login_cookie(cookies, &user);
|
||||||
Ok(Redirect::to(uri!(super::root::index)))
|
Ok(Redirect::to(uri!(super::root::index)))
|
||||||
} else {
|
} else {
|
||||||
Err(login_error_redirect("Invalid username or password."))
|
Err(login_error_redirect("Invalid username or password."))
|
||||||
|
@ -69,8 +70,8 @@ fn login(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/logout")]
|
#[post("/logout")]
|
||||||
fn logout(mut cookies: Cookies) -> Flash<Redirect> {
|
fn logout(cookies: &CookieJar<'_>) -> Flash<Redirect> {
|
||||||
remove_login_cookie(&mut cookies);
|
remove_login_cookie(cookies);
|
||||||
Flash::success(Redirect::to(uri!(login_page)), "Successfully logged out.")
|
Flash::success(Redirect::to(uri!(login_page)), "Successfully logged out.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,15 +101,18 @@ fn register_page(flash: Option<FlashMessage>) -> Template {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/register", data = "<registration>")]
|
#[post("/register", data = "<registration>")]
|
||||||
fn register(
|
async fn register(
|
||||||
mut cookies: Cookies,
|
mut cookies: &CookieJar<'_>,
|
||||||
registration: Form<Registration>,
|
registration: Form<Registration>,
|
||||||
conn: TenebrousDbConn,
|
conn: TenebrousDbConn,
|
||||||
) -> Result<Redirect, Flash<Redirect>> {
|
) -> Result<Redirect, Flash<Redirect>> {
|
||||||
let existing_user = conn.load_user(®istration.username).map_err(|e| {
|
let existing_user = conn
|
||||||
error!("registration - error loading existing user: {}", e);
|
.load_user(registration.username.clone())
|
||||||
registration_error_redirect("There was an error attempting to register.")
|
.await
|
||||||
})?;
|
.map_err(|e| {
|
||||||
|
error!("registration - error loading existing user: {}", e);
|
||||||
|
registration_error_redirect("There was an error attempting to register.")
|
||||||
|
})?;
|
||||||
|
|
||||||
if existing_user.is_some() {
|
if existing_user.is_some() {
|
||||||
return Err(registration_error_redirect(format!(
|
return Err(registration_error_redirect(format!(
|
||||||
|
@ -123,11 +127,11 @@ fn register(
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let user = NewUser {
|
let user = NewUser {
|
||||||
username: ®istration.username,
|
username: registration.username.clone(),
|
||||||
password: &hashed_pw,
|
password: hashed_pw,
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = conn.insert_user(&user).map_err(|e| {
|
let user = conn.insert_user(user).await.map_err(|e| {
|
||||||
error!("registration - could not insert user: {}", e);
|
error!("registration - could not insert user: {}", e);
|
||||||
registration_error_redirect("There was an error completing registration.")
|
registration_error_redirect("There was an error completing registration.")
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -45,16 +45,17 @@ fn view_character_template(user: &User, character: Character) -> Result<Template
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<username>/<character_id>")]
|
#[get("/<username>/<character_id>")]
|
||||||
fn view_character(
|
async fn view_character(
|
||||||
character_id: i32,
|
character_id: i32,
|
||||||
username: String,
|
username: String,
|
||||||
conn: TenebrousDbConn,
|
conn: TenebrousDbConn,
|
||||||
logged_in_user: Option<&User>,
|
logged_in_user: Option<&User>,
|
||||||
) -> Result<Template, Error> {
|
) -> Result<Template, Error> {
|
||||||
let user = &conn.load_user(&username)?.ok_or(Error::NotFound)?;
|
let user = &conn.load_user(username).await?.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
let character = conn
|
let character = conn
|
||||||
.load_character(character_id)?
|
.load_character(character_id)
|
||||||
|
.await?
|
||||||
.and_then(|c| c.as_visible_for(logged_in_user))
|
.and_then(|c| c.as_visible_for(logged_in_user))
|
||||||
.ok_or(Error::NotFound)?;
|
.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
|
@ -63,15 +64,19 @@ fn view_character(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<owner>/<character_id>/edit")]
|
#[get("/<owner>/<character_id>/edit")]
|
||||||
fn edit_character(
|
async fn edit_character(
|
||||||
character_id: i32,
|
character_id: i32,
|
||||||
owner: String,
|
owner: String,
|
||||||
logged_in_user: Option<&User>,
|
logged_in_user: Option<&User>,
|
||||||
conn: TenebrousDbConn,
|
conn: TenebrousDbConn,
|
||||||
) -> Result<Template, Error> {
|
) -> Result<Template, Error> {
|
||||||
let owner = conn.load_user(&owner)?.ok_or(Error::NotFound)?;
|
let owner = conn.load_user(owner).await?.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
let logged_in_user = logged_in_user.ok_or(Error::NotLoggedIn)?;
|
let logged_in_user = logged_in_user.ok_or(Error::NotLoggedIn)?;
|
||||||
let character = conn.load_character(character_id)?.ok_or(Error::NotFound)?;
|
let character = conn
|
||||||
|
.load_character(character_id)
|
||||||
|
.await?
|
||||||
|
.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
if logged_in_user != &owner {
|
if logged_in_user != &owner {
|
||||||
return Err(Error::NoPermission);
|
return Err(Error::NoPermission);
|
||||||
|
|
|
@ -5,7 +5,6 @@ use crate::models::{
|
||||||
convert::ValidationError,
|
convert::ValidationError,
|
||||||
users::User,
|
users::User,
|
||||||
};
|
};
|
||||||
use prost::bytes::BytesMut;
|
|
||||||
use rocket::{request::Form, response::Redirect};
|
use rocket::{request::Form, response::Redirect};
|
||||||
use rocket_contrib::templates::Template;
|
use rocket_contrib::templates::Template;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
@ -57,24 +56,24 @@ impl NewCharacterContext {
|
||||||
|
|
||||||
/// Create and insert a new character into the database. The form is
|
/// Create and insert a new character into the database. The form is
|
||||||
/// assumed to be successfully validated.
|
/// assumed to be successfully validated.
|
||||||
fn create_new_character(
|
async fn create_new_character(
|
||||||
form: &Form<NewCharacterForm>,
|
form: &Form<NewCharacterForm>,
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
conn: TenebrousDbConn,
|
conn: TenebrousDbConn,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let system: CharacterDataType = *form.system.as_ref().unwrap();
|
let system: CharacterDataType = *form.system.as_ref().unwrap();
|
||||||
let sheet: BytesMut = system.create_data()?;
|
let sheet: Vec<u8> = system.create_data()?.to_vec();
|
||||||
|
|
||||||
let insert = NewCharacter {
|
let insert = NewCharacter {
|
||||||
user_id: user_id,
|
user_id: user_id,
|
||||||
viewable: true,
|
viewable: true,
|
||||||
character_name: &form.name,
|
character_name: form.name.clone(),
|
||||||
data_type: system,
|
data_type: system,
|
||||||
data_version: 1,
|
data_version: 1,
|
||||||
data: &sheet,
|
data: sheet,
|
||||||
};
|
};
|
||||||
|
|
||||||
conn.insert_character(insert)?;
|
conn.insert_character(insert).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +94,7 @@ pub(super) fn new_character_page(_logged_in_user: &User) -> Result<Template, Err
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/new", data = "<form>")]
|
#[post("/new", data = "<form>")]
|
||||||
pub(super) fn new_character_submit(
|
pub(super) async fn new_character_submit(
|
||||||
form: Form<NewCharacterForm>,
|
form: Form<NewCharacterForm>,
|
||||||
logged_in_user: &User,
|
logged_in_user: &User,
|
||||||
conn: TenebrousDbConn,
|
conn: TenebrousDbConn,
|
||||||
|
@ -104,7 +103,7 @@ pub(super) fn new_character_submit(
|
||||||
return Err(render_error(&form, e.to_string().clone()));
|
return Err(render_error(&form, e.to_string().clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
match create_new_character(&form, logged_in_user.id, conn) {
|
match create_new_character(&form, logged_in_user.id, conn).await {
|
||||||
Ok(_) => Ok(crate::routes::common::redirect_to_index()),
|
Ok(_) => Ok(crate::routes::common::redirect_to_index()),
|
||||||
Err(e) => Err(render_error(&form, e.to_string().clone())),
|
Err(e) => Err(render_error(&form, e.to_string().clone())),
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,10 @@ pub struct UserHomeContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn user_index(user: &User, conn: TenebrousDbConn) -> Result<Template, Error> {
|
async fn user_index(user: &User, conn: TenebrousDbConn) -> Result<Template, Error> {
|
||||||
let characters: Vec<StrippedCharacter> = conn
|
let characters: Vec<StrippedCharacter> = conn
|
||||||
.load_character_list(user.id)?
|
.load_character_list(user.id)
|
||||||
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|c| c.as_visible_for(Some(user)))
|
.map(|c| c.as_visible_for(Some(user)))
|
||||||
.filter_map(|c| c)
|
.filter_map(|c| c)
|
||||||
|
|
Loading…
Reference in New Issue