User accounts, DAO trait, registration.
Not very thorough, but it does work.
This commit is contained in:
parent
881518cc8f
commit
0530011138
|
@ -79,6 +79,18 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
|
@ -126,12 +138,29 @@ version = "0.12.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
|
@ -208,6 +237,12 @@ dependencies = [
|
|||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.11.3"
|
||||
|
@ -224,6 +259,17 @@ dependencies = [
|
|||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.7.0"
|
||||
|
@ -1086,6 +1132,18 @@ dependencies = [
|
|||
"unicode-xid 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.18"
|
||||
|
@ -1246,8 +1304,11 @@ name = "tenebrous-sheets"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
"log 0.4.11",
|
||||
"rand",
|
||||
"rocket",
|
||||
"rocket_contrib",
|
||||
"rust-argon2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
|
|
@ -12,6 +12,9 @@ serde_derive = "1.0"
|
|||
serde_json = "1.0"
|
||||
diesel = "1.4"
|
||||
thiserror = "1.0"
|
||||
rust-argon2 = "0.8"
|
||||
log = "0.4"
|
||||
rand = "0.7"
|
||||
rocket = { version= "0.4.6", features = ["private-cookies"] }
|
||||
|
||||
[dependencies.rocket_contrib]
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE users;
|
|
@ -0,0 +1,5 @@
|
|||
CREATE TABLE users (
|
||||
id INTEGER NOT NULL PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
password TEXT NOT NULL
|
||||
);
|
71
src/db.rs
71
src/db.rs
|
@ -1,39 +1,74 @@
|
|||
use crate::models::characters::{CharacterEntry, NewCharacter};
|
||||
use crate::models::users::{NewUser, User};
|
||||
use diesel::prelude::*;
|
||||
use diesel::SqliteConnection;
|
||||
|
||||
#[database("tenebrous_db")]
|
||||
pub(crate) struct TenebrousDbConn(diesel::SqliteConnection);
|
||||
pub(crate) struct TenebrousDbConn(SqliteConnection);
|
||||
|
||||
pub(crate) fn load_character_list(
|
||||
conn: TenebrousDbConn,
|
||||
for_user_id: i32,
|
||||
) -> QueryResult<Vec<CharacterEntry>> {
|
||||
use crate::schema::characters::dsl::*;
|
||||
characters.filter(user_id.eq(for_user_id)).load(&*conn)
|
||||
pub(crate) trait Dao {
|
||||
fn load_user_by_id(&self, id: i32) -> QueryResult<Option<User>>;
|
||||
fn load_user(&self, for_username: &str) -> QueryResult<Option<User>>;
|
||||
|
||||
fn insert_user(&self, new_user: &NewUser) -> QueryResult<User>;
|
||||
|
||||
fn load_character_list(&self, for_user_id: i32) -> QueryResult<Vec<CharacterEntry>>;
|
||||
|
||||
fn load_character(&self, character_id: i32) -> QueryResult<Option<CharacterEntry>>;
|
||||
|
||||
fn insert_character(&self, new_character: &NewCharacter) -> QueryResult<()>;
|
||||
}
|
||||
|
||||
pub(crate) fn load_character(
|
||||
conn: TenebrousDbConn,
|
||||
character_id: i32,
|
||||
) -> QueryResult<Option<CharacterEntry>> {
|
||||
impl Dao for TenebrousDbConn {
|
||||
fn load_user_by_id(&self, user_id: i32) -> QueryResult<Option<User>> {
|
||||
use crate::schema::users::dsl::*;
|
||||
|
||||
users.filter(id.eq(user_id)).first(&self.0).optional()
|
||||
}
|
||||
|
||||
fn load_user(&self, for_username: &str) -> QueryResult<Option<User>> {
|
||||
use crate::schema::users::dsl::*;
|
||||
|
||||
users
|
||||
.filter(username.eq(for_username))
|
||||
.first(&self.0)
|
||||
.optional()
|
||||
}
|
||||
|
||||
fn insert_user(&self, new_user: &NewUser) -> QueryResult<User> {
|
||||
use crate::schema::users;
|
||||
|
||||
diesel::insert_into(users::table)
|
||||
.values(new_user)
|
||||
.execute(&self.0)?;
|
||||
|
||||
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<CharacterEntry>> {
|
||||
use crate::schema::characters::dsl::*;
|
||||
characters.filter(user_id.eq(for_user_id)).load(&self.0)
|
||||
}
|
||||
|
||||
fn load_character(&self, character_id: i32) -> QueryResult<Option<CharacterEntry>> {
|
||||
use crate::schema::characters::dsl::*;
|
||||
|
||||
characters
|
||||
.filter(id.eq(character_id))
|
||||
.limit(1)
|
||||
.first(&*conn)
|
||||
.first(&self.0)
|
||||
.optional()
|
||||
}
|
||||
|
||||
pub(crate) fn insert_character(
|
||||
conn: TenebrousDbConn,
|
||||
new_character: &NewCharacter,
|
||||
) -> QueryResult<()> {
|
||||
fn insert_character(&self, new_character: &NewCharacter) -> QueryResult<()> {
|
||||
use crate::schema::characters;
|
||||
|
||||
diesel::insert_into(characters::table)
|
||||
.values(new_character)
|
||||
.execute(&*conn)?;
|
||||
.execute(&self.0)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,10 @@ pub enum Error {
|
|||
QueryError(#[from] diesel::result::Error),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("internal eror")]
|
||||
pub struct SensitiveError(Error);
|
||||
|
||||
impl Error {
|
||||
fn is_sensitive(&self) -> bool {
|
||||
use Error::*;
|
||||
|
@ -33,6 +37,7 @@ impl Error {
|
|||
|
||||
impl<'r> Responder<'r> for Error {
|
||||
fn respond_to(self, req: &Request) -> response::Result<'r> {
|
||||
log::error!("error: {0}", self.to_string());
|
||||
//Hide sensitive error information
|
||||
let message: String = if self.is_sensitive() {
|
||||
"internal error".into()
|
||||
|
@ -53,3 +58,22 @@ 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,44 +1,2 @@
|
|||
use rocket::http::RawStr;
|
||||
use rocket::outcome::IntoOutcome;
|
||||
use rocket::request::{self, FromParam, FromRequest, Request};
|
||||
use serde_derive::Serialize;
|
||||
|
||||
pub mod characters;
|
||||
|
||||
#[derive(Eq, PartialEq, Serialize, Debug)]
|
||||
pub struct User {
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for User {
|
||||
type Error = !;
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<User, !> {
|
||||
request
|
||||
.cookies()
|
||||
.get_private("user_id")
|
||||
.and_then(|cookie| cookie.value().parse().ok())
|
||||
.map(|id|
|
||||
//TODO load from db
|
||||
User {
|
||||
id: id,
|
||||
username: "somebody".to_string(),
|
||||
})
|
||||
.or_forward(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> FromParam<'r> for User {
|
||||
type Error = &'r str;
|
||||
|
||||
fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
|
||||
let username: String = param.url_decode().or(Err("Invalid character ID"))?;
|
||||
|
||||
//TODO load from DB
|
||||
Ok(User {
|
||||
id: 1,
|
||||
username: username,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub mod users;
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
use crate::db::{Dao, TenebrousDbConn};
|
||||
use crate::schema::users;
|
||||
use argon2::{self, Config, Error as ArgonError};
|
||||
use rand::Rng;
|
||||
use rocket::outcome::IntoOutcome;
|
||||
use rocket::request::{self, FromRequest, Request};
|
||||
use serde_derive::Serialize;
|
||||
|
||||
pub(crate) fn hash_password(raw_password: &str) -> Result<String, ArgonError> {
|
||||
let salt = rand::thread_rng().gen::<[u8; 16]>();
|
||||
let config = Config::default();
|
||||
argon2::hash_encoded(raw_password.as_bytes(), &salt, &config)
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Serialize, Debug, Queryable)]
|
||||
pub struct User {
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn verify_password(&self, raw_password: &str) -> bool {
|
||||
argon2::verify_encoded(&self.password, raw_password.as_bytes()).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for &'a User {
|
||||
type Error = !;
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, !> {
|
||||
let user: &Option<User> = request.local_cache(|| {
|
||||
let attempt_load_user = |id| -> Option<User> {
|
||||
TenebrousDbConn::from_request(request)
|
||||
.map(|conn| conn.load_user_by_id(id).ok().flatten())
|
||||
.succeeded()
|
||||
.flatten()
|
||||
};
|
||||
|
||||
request
|
||||
.cookies()
|
||||
.get_private("user_id")
|
||||
.and_then(|cookie| cookie.value().parse().ok())
|
||||
.and_then(attempt_load_user)
|
||||
});
|
||||
|
||||
user.as_ref().or_forward(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[table_name = "users"]
|
||||
pub struct NewUser<'a> {
|
||||
pub username: &'a str,
|
||||
pub password: &'a str,
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
use crate::models::User;
|
||||
use crate::db::{Dao, TenebrousDbConn};
|
||||
use crate::models::users::{self, NewUser, User};
|
||||
use log::error;
|
||||
use rocket::http::{Cookie, Cookies};
|
||||
use rocket::request::{FlashMessage, Form};
|
||||
use rocket::response::{Flash, Redirect};
|
||||
|
@ -6,7 +8,14 @@ use rocket_contrib::templates::Template;
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub(crate) fn routes() -> Vec<rocket::Route> {
|
||||
routes![login, logout, logged_in_user, login_page]
|
||||
routes![
|
||||
login,
|
||||
logout,
|
||||
logged_in_user,
|
||||
login_page,
|
||||
register_page,
|
||||
register
|
||||
]
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
|
@ -15,27 +24,58 @@ struct Login {
|
|||
password: String,
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
struct Registration {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
fn add_login_cookie(cookies: &mut Cookies, user: &User) {
|
||||
cookies.add_private(Cookie::new("user_id", user.id.to_string()));
|
||||
}
|
||||
|
||||
fn remove_login_cookie(cookies: &mut Cookies) {
|
||||
cookies.remove_private(Cookie::named("user_id"));
|
||||
}
|
||||
|
||||
fn login_error_redirect<S: AsRef<str>>(message: S) -> Flash<Redirect> {
|
||||
Flash::error(Redirect::to(uri!(login_page)), message.as_ref())
|
||||
}
|
||||
|
||||
fn registration_error_redirect<S: AsRef<str>>(message: S) -> Flash<Redirect> {
|
||||
Flash::error(Redirect::to(uri!(register_page)), message.as_ref())
|
||||
}
|
||||
|
||||
#[post("/login", data = "<login>")]
|
||||
fn login(mut cookies: Cookies, login: Form<Login>) -> Result<Redirect, Flash<Redirect>> {
|
||||
if login.username == "test" && login.password == "test" {
|
||||
cookies.add_private(Cookie::new("user_id", 1.to_string()));
|
||||
fn login(
|
||||
mut cookies: Cookies,
|
||||
login: Form<Login>,
|
||||
conn: TenebrousDbConn,
|
||||
) -> Result<Redirect, Flash<Redirect>> {
|
||||
let user = conn
|
||||
.load_user(&login.username)
|
||||
.map_err(|e| {
|
||||
error!("login - error loading user user: {}", e);
|
||||
login_error_redirect("Internal error.")
|
||||
})?
|
||||
.ok_or_else(|| login_error_redirect("Invalid username or password."))?;
|
||||
|
||||
if user.verify_password(&login.password) {
|
||||
add_login_cookie(&mut cookies, &user);
|
||||
Ok(Redirect::to(uri!(super::root::index)))
|
||||
} else {
|
||||
Err(Flash::error(
|
||||
Redirect::to(uri!(login_page)),
|
||||
"Invalid username orpassword.",
|
||||
))
|
||||
Err(login_error_redirect("Invalid username or password."))
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/logout")]
|
||||
fn logout(mut cookies: Cookies) -> Flash<Redirect> {
|
||||
cookies.remove_private(Cookie::named("user_id"));
|
||||
remove_login_cookie(&mut cookies);
|
||||
Flash::success(Redirect::to(uri!(login_page)), "Successfully logged out.")
|
||||
}
|
||||
|
||||
#[get("/login")]
|
||||
fn logged_in_user(_user: User) -> Redirect {
|
||||
fn logged_in_user(_user: &User) -> Redirect {
|
||||
Redirect::to(uri!(super::root::index))
|
||||
}
|
||||
|
||||
|
@ -48,3 +88,50 @@ fn login_page(flash: Option<FlashMessage>) -> Template {
|
|||
|
||||
Template::render("login", &context)
|
||||
}
|
||||
|
||||
#[get("/register")]
|
||||
fn register_page(flash: Option<FlashMessage>) -> Template {
|
||||
let mut context = HashMap::new();
|
||||
if let Some(ref msg) = flash {
|
||||
context.insert("flash", msg.msg());
|
||||
}
|
||||
|
||||
Template::render("registration", &context)
|
||||
}
|
||||
|
||||
#[post("/register", data = "<registration>")]
|
||||
fn register(
|
||||
mut cookies: Cookies,
|
||||
registration: Form<Registration>,
|
||||
conn: TenebrousDbConn,
|
||||
) -> Result<Redirect, Flash<Redirect>> {
|
||||
let existing_user = conn.load_user(®istration.username).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() {
|
||||
return Err(registration_error_redirect(format!(
|
||||
"The username {} is already taken.",
|
||||
registration.username
|
||||
)));
|
||||
}
|
||||
|
||||
let hashed_pw = users::hash_password(®istration.password).map_err(|e| {
|
||||
error!("registration - password hashing error: {}", e);
|
||||
registration_error_redirect("There was an error attempting to register.")
|
||||
})?;
|
||||
|
||||
let user = NewUser {
|
||||
username: ®istration.username,
|
||||
password: &hashed_pw,
|
||||
};
|
||||
|
||||
let user = conn.insert_user(&user).map_err(|e| {
|
||||
error!("registration - could not insert user: {}", e);
|
||||
registration_error_redirect("There was an error completing registration.")
|
||||
})?;
|
||||
|
||||
add_login_cookie(&mut cookies, &user);
|
||||
Ok(Redirect::to(uri!(super::root::index)))
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::db::{self, TenebrousDbConn};
|
||||
use crate::db::{Dao, TenebrousDbConn};
|
||||
use crate::errors::Error;
|
||||
use crate::models::{
|
||||
characters::{CharacterEntry, NewCharacter},
|
||||
User,
|
||||
users::User,
|
||||
};
|
||||
use rocket::response::Redirect;
|
||||
use rocket_contrib::templates::Template;
|
||||
|
@ -21,20 +21,20 @@ pub(crate) fn routes() -> Vec<rocket::Route> {
|
|||
//TODO make private -- currently is referenced in homepage route.
|
||||
//or move to common place.
|
||||
#[derive(Serialize)]
|
||||
pub struct TemplateContext {
|
||||
pub struct TemplateContext<'a> {
|
||||
pub characters: Vec<CharacterEntry>,
|
||||
pub user: User,
|
||||
pub user: &'a User,
|
||||
}
|
||||
|
||||
//TODO should return result based on whether or not character is publicly viewable.
|
||||
#[get("/<user>/<character_id>")]
|
||||
#[get("/<username>/<character_id>")]
|
||||
fn view_character(
|
||||
character_id: i32,
|
||||
user: Option<User>,
|
||||
username: String,
|
||||
conn: TenebrousDbConn,
|
||||
) -> Result<Template, Error> {
|
||||
let user = user.ok_or(Error::NotFound)?;
|
||||
let character = db::load_character(conn, character_id)?.ok_or(Error::NotFound)?;
|
||||
let user = conn.load_user(&username)?.ok_or(Error::NotFound)?;
|
||||
let character = conn.load_character(character_id)?.ok_or(Error::NotFound)?;
|
||||
|
||||
let mut context = HashMap::new();
|
||||
context.insert("name", character.name);
|
||||
|
@ -43,7 +43,7 @@ fn view_character(
|
|||
}
|
||||
|
||||
#[get("/new")]
|
||||
fn new_character(logged_in_user: User, conn: TenebrousDbConn) -> Result<Template, Error> {
|
||||
fn new_character(logged_in_user: &User, conn: TenebrousDbConn) -> Result<Template, Error> {
|
||||
let context = HashMap::<String, String>::new();
|
||||
Ok(Template::render("new_character", context))
|
||||
}
|
||||
|
@ -56,15 +56,15 @@ fn new_character_not_logged_in() -> Redirect {
|
|||
#[get("/<owner>/<character_id>/edit")]
|
||||
fn edit_character(
|
||||
character_id: i32,
|
||||
owner: Option<User>,
|
||||
logged_in_user: Option<User>,
|
||||
owner: String,
|
||||
logged_in_user: Option<&User>,
|
||||
conn: TenebrousDbConn,
|
||||
) -> Result<Template, Error> {
|
||||
let owner = owner.ok_or(Error::NotFound)?;
|
||||
let owner = conn.load_user(&owner)?.ok_or(Error::NotFound)?;
|
||||
let logged_in_user = logged_in_user.ok_or(Error::NotLoggedIn)?;
|
||||
let character = db::load_character(conn, character_id)?.ok_or(Error::NotFound)?;
|
||||
let character = conn.load_character(character_id)?.ok_or(Error::NotFound)?;
|
||||
|
||||
if logged_in_user != owner {
|
||||
if logged_in_user != &owner {
|
||||
return Err(Error::NoPermission);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::db;
|
||||
use crate::db::TenebrousDbConn;
|
||||
use crate::db::{Dao, TenebrousDbConn};
|
||||
use crate::errors::Error;
|
||||
use crate::models::{characters::CharacterEntry, User};
|
||||
use crate::models::{characters::CharacterEntry, users::User};
|
||||
use rocket::response::Redirect;
|
||||
use rocket_contrib::templates::Template;
|
||||
|
||||
|
@ -10,9 +9,9 @@ pub fn routes() -> Vec<rocket::Route> {
|
|||
}
|
||||
|
||||
#[get("/")]
|
||||
fn user_index(user: User, conn: TenebrousDbConn) -> Result<Template, Error> {
|
||||
fn user_index(user: &User, conn: TenebrousDbConn) -> Result<Template, Error> {
|
||||
use crate::routes::characters::TemplateContext;
|
||||
let characters = db::load_character_list(conn, user.id)?;
|
||||
let characters = conn.load_character_list(user.id)?;
|
||||
|
||||
let context = TemplateContext {
|
||||
characters: characters,
|
||||
|
|
|
@ -7,3 +7,16 @@ table! {
|
|||
character_data -> Nullable<Binary>,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
users (id) {
|
||||
id -> Integer,
|
||||
username -> Text,
|
||||
password -> Text,
|
||||
}
|
||||
}
|
||||
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
characters,
|
||||
users,
|
||||
);
|
||||
|
|
|
@ -13,5 +13,11 @@
|
|||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<form action="/logout" method="post">
|
||||
<input type="submit" value="Logout" />
|
||||
</form>
|
||||
</p>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<input type="text" name="username" id="username" value="" />
|
||||
<label for="password">password</label>
|
||||
<input type="password" name="password" id="password" value="" />
|
||||
<p><input type="submit" value="login"></p>
|
||||
<p><input type="submit" value="Login"></p>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
{% extends "base" %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<h1>Registration</h1>
|
||||
|
||||
{% if flash %}
|
||||
<p>Error: {{ flash }}</p>
|
||||
{% endif %}
|
||||
|
||||
<p>Please register with a username and password.</p>
|
||||
<form action="/register" method="post" accept-charset="utf-8">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input id="username" name="username" type="text" value="" />
|
||||
|
||||
<label for="password">Password:</label>
|
||||
<input id="password" name="password" type="password" value="" />
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Register" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
Loading…
Reference in New Issue