diff --git a/Cargo.lock b/Cargo.lock
index 26dfe243..3173b306 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -592,6 +592,11 @@ dependencies = [
"unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "if_chain"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "indexmap"
version = "1.0.1"
@@ -994,7 +999,11 @@ dependencies = [
"rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?rev=13ca47ef73be86cef9caca30c516e4e95f3051ce)",
"rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)",
"rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "validator 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "validator_derive 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webfinger 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1183,6 +1192,18 @@ dependencies = [
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "regex"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "regex-syntax"
version = "0.5.5"
@@ -1191,6 +1212,14 @@ dependencies = [
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "regex-syntax"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "relay"
version = "0.1.1"
@@ -1926,6 +1955,32 @@ dependencies = [
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "validator"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "validator_derive"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "validator 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "vcpkg"
version = "0.2.3"
@@ -2064,6 +2119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum hyper 0.11.25 (registry+https://github.com/rust-lang/crates.io-index)" = "549dbb86397490ce69d908425b9beebc85bbaad25157d67479d4995bb56fdf9a"
"checksum hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a5aa51f6ae9842239b0fac14af5f22123b8432b4cc774a44ff059fcba0f675ca"
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
+"checksum if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "61bb90bdd39e3af69b0172dfc6130f6cd6332bf040fbb9bdd4401d37adbd48b8"
"checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum isatty 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a118a53ba42790ef25c82bb481ecf36e2da892646cccd361e69a6bb881e19398"
@@ -2128,7 +2184,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2"
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
"checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb"
+"checksum regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13c93d55961981ba9226a213b385216f83ab43bd6ac53ab16b2eeb47e337cf4e"
"checksum regex-syntax 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bd90079345f4a4c3409214734ae220fd773c6f2e8a543d07370c6c1c369cfbfb"
+"checksum regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05b06a75f5217880fc5e905952a42750bf44787e56a6c6d6852ed0992f5e1d54"
"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a"
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
"checksum reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "241faa9a8ca28a03cbbb9815a5d085f271d4c0168a19181f106aa93240c22ddb"
@@ -2210,6 +2268,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum utf-8 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1262dfab4c30d5cb7c07026be00ee343a6cf5027fdc0104a9160f354e5db75c"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
+"checksum validator 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4a8c44fecf027a477e70a86cd7f4863410adf120ca2cb13408cb099057b8e2d0"
+"checksum validator_derive 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "708ee89305635499f793d0e2dd9d0b1b5d00daba90fdfb1392b87c7279521fab"
"checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
diff --git a/Cargo.toml b/Cargo.toml
index 85261db3..75522b0c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,19 +10,23 @@ failure = "0.1"
gettext-rs = "0.4"
heck = "0.3.0"
rpassword = "2.0"
+serde = "1.0"
+serde_derive = "1.0"
serde_json = "1.0"
+validator = "0.7"
+validator_derive = "0.7"
webfinger = "0.2"
[dependencies.diesel]
features = ["postgres", "r2d2", "chrono"]
version = "*"
-[dependencies.plume-models]
-path = "plume-models"
-
[dependencies.plume-common]
path = "plume-common"
+[dependencies.plume-models]
+path = "plume-models"
+
[dependencies.rocket]
git = "https://github.com/SergioBenitez/Rocket"
rev = "df7111143e466c18d1f56377a8d9530a5a306aba"
@@ -45,4 +49,4 @@ git = "https://github.com/BaptisteGelez/rocket_i18n"
rev = "5b4225d5bed5769482dc926a7e6d6b79f1217be6"
[workspace]
-members = ['plume-models', 'plume-common']
+members = ["plume-models", "plume-common"]
diff --git a/migrations/2018-04-22-093322_create_instances/up.sql b/migrations/2018-04-22-093322_create_instances/up.sql
index e6689b0f..46fd4a3c 100644
--- a/migrations/2018-04-22-093322_create_instances/up.sql
+++ b/migrations/2018-04-22-093322_create_instances/up.sql
@@ -1,4 +1,4 @@
--- Your SQL goes here
+l-- Your SQL goes here
CREATE TABLE instances (
id SERIAL PRIMARY KEY,
local_domain VARCHAR NOT NULL,
diff --git a/po/plume.pot b/po/plume.pot
index e1599c75..ccb727e3 100644
--- a/po/plume.pot
+++ b/po/plume.pot
@@ -281,3 +281,37 @@ msgstr ""
msgid "Your comment"
msgstr ""
+
+msgid "Unknown error"
+msgstr ""
+
+msgid "Invalid name"
+msgstr ""
+
+msgid "A blog with the same name already exists."
+msgstr ""
+
+msgid "Your comment can't be empty"
+msgstr ""
+
+msgid "A post with the same title already exists."
+msgstr ""
+
+msgid "We need an email or a username to identify you"
+msgstr ""
+
+msgid "Your password should be at least 8 characters long"
+msgstr ""
+
+msgid "Passwords are not matching"
+msgstr ""
+
+msgid "Username can't be empty"
+msgstr ""
+
+msgid "Invalid email"
+msgstr ""
+
+msgid "Password should be at least 8 characters long"
+msgstr ""
+
diff --git a/src/main.rs b/src/main.rs
index 6d55a5dc..215e2101 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,8 +15,14 @@ extern crate rocket_contrib;
extern crate rocket_csrf;
extern crate rocket_i18n;
extern crate rpassword;
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
#[macro_use]
extern crate serde_json;
+extern crate validator;
+#[macro_use]
+extern crate validator_derive;
extern crate webfinger;
use rocket_contrib::Template;
diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs
index 1e627439..9a6fcb8c 100644
--- a/src/routes/blogs.rs
+++ b/src/routes/blogs.rs
@@ -5,6 +5,8 @@ use rocket::{
};
use rocket_contrib::Template;
use serde_json;
+use std::{collections::HashMap, borrow::Cow};
+use validator::{Validate, ValidationError, ValidationErrors};
use plume_common::activity_pub::ActivityStream;
use plume_common::utils;
@@ -40,7 +42,9 @@ fn activity_details(name: String, conn: DbConn) -> ActivityStream {
#[get("/blogs/new")]
fn new(user: User) -> Template {
Template::render("blogs/new", json!({
- "account": user
+ "account": user,
+ "errors": null,
+ "form": null
}))
}
@@ -49,21 +53,41 @@ fn new_auth() -> Flash{
utils::requires_login("You need to be logged in order to create a new blog", uri!(new))
}
-#[derive(FromForm)]
+#[derive(FromForm, Validate, Serialize)]
struct NewBlogForm {
+ #[validate(custom(function = "valid_slug", message = "Invalid name"))]
pub title: String
}
+fn valid_slug(title: &str) -> Result<(), ValidationError> {
+ let slug = utils::make_actor_id(title.to_string());
+ if slug.len() == 0 {
+ Err(ValidationError::new("empty_slug"))
+ } else {
+ Ok(())
+ }
+}
+
#[post("/blogs/new", data = "")]
-fn create(conn: DbConn, data: LenientForm, user: User) -> Redirect {
+fn create(conn: DbConn, data: LenientForm, user: User) -> Result {
let form = data.get();
let slug = utils::make_actor_id(form.title.to_string());
- if Blog::find_local(&*conn, slug.clone()).is_some() || slug.len() == 0 {
- Redirect::to(uri!(new))
- } else {
+ let mut errors = match form.validate() {
+ Ok(_) => ValidationErrors::new(),
+ Err(e) => e
+ };
+ if let Some(_) = Blog::find_local(&*conn, slug.clone()) {
+ errors.add("title", ValidationError {
+ code: Cow::from("existing_slug"),
+ message: Some(Cow::from("A blog with the same name already exists.")),
+ params: HashMap::new()
+ });
+ }
+
+ if errors.is_empty() {
let blog = Blog::insert(&*conn, NewBlog::new_local(
- slug.to_string(),
+ slug.clone(),
form.title.to_string(),
String::from(""),
Instance::local_id(&*conn)
@@ -76,7 +100,14 @@ fn create(conn: DbConn, data: LenientForm, user: User) -> Redirect
is_owner: true
});
- Redirect::to(uri!(details: name = slug))
+ Ok(Redirect::to(uri!(details: name = slug.clone())))
+ } else {
+ println!("{:?}", errors);
+ Err(Template::render("blogs/new", json!({
+ "account": user,
+ "errors": errors.inner(),
+ "form": form
+ })))
}
}
diff --git a/src/routes/comments.rs b/src/routes/comments.rs
index ee5112bb..d08998ed 100644
--- a/src/routes/comments.rs
+++ b/src/routes/comments.rs
@@ -2,7 +2,9 @@ use rocket::{
request::LenientForm,
response::Redirect
};
+use rocket_contrib::Template;
use serde_json;
+use validator::Validate;
use plume_common::activity_pub::broadcast;
use plume_models::{
@@ -15,30 +17,52 @@ use plume_models::{
};
use inbox::Inbox;
-#[derive(FromForm, Debug)]
+#[derive(FromForm, Debug, Validate)]
struct NewCommentForm {
pub responding_to: Option,
+ #[validate(length(min = "1", message = "Your comment can't be empty"))]
pub content: String
}
#[post("/~///comment", data = "")]
-fn create(blog_name: String, slug: String, data: LenientForm, user: User, conn: DbConn) -> Redirect {
+fn create(blog_name: String, slug: String, data: LenientForm, user: User, conn: DbConn) -> Result {
let blog = Blog::find_by_fqn(&*conn, blog_name.clone()).unwrap();
let post = Post::find_by_slug(&*conn, slug.clone(), blog.id).unwrap();
let form = data.get();
- println!("form: {:?}", form);
+ form.validate()
+ .map(|_| {
+ let (new_comment, id) = NewComment::build()
+ .content(form.content.clone())
+ .in_response_to_id(form.responding_to.clone())
+ .post(post.clone())
+ .author(user.clone())
+ .create(&*conn);
- let (new_comment, id) = NewComment::build()
- .content(form.content.clone())
- .in_response_to_id(form.responding_to.clone())
- .post(post)
- .author(user.clone())
- .create(&*conn);
+ let instance = Instance::get_local(&*conn).unwrap();
+ instance.received(&*conn, serde_json::to_value(new_comment.clone()).expect("JSON serialization error"))
+ .expect("We are not compatible with ourselve: local broadcast failed (new comment)");
+ broadcast(&user, new_comment, user.get_followers(&*conn));
- let instance = Instance::get_local(&*conn).unwrap();
- instance.received(&*conn, serde_json::to_value(new_comment.clone()).expect("JSON serialization error"))
- .expect("We are not compatible with ourselve: local broadcast failed (new comment)");
- broadcast(&user, new_comment, user.get_followers(&*conn));
+ Redirect::to(format!("/~/{}/{}/#comment-{}", blog_name, slug, id))
+ })
+ .map_err(|errors| {
+ // TODO: de-duplicate this code
+ let comments = Comment::list_by_post(&*conn, post.id);
- Redirect::to(format!("/~/{}/{}/#comment-{}", blog_name, slug, id))
+ Template::render("posts/details", json!({
+ "author": post.get_authors(&*conn)[0].to_json(&*conn),
+ "post": post,
+ "blog": blog,
+ "comments": comments.into_iter().map(|c| c.to_json(&*conn)).collect::>(),
+ "n_likes": post.get_likes(&*conn).len(),
+ "has_liked": user.has_liked(&*conn, &post),
+ "n_reshares": post.get_reshares(&*conn).len(),
+ "has_reshared": user.has_reshared(&*conn, &post),
+ "account": user,
+ "date": &post.creation_date.timestamp(),
+ "previous": form.responding_to.map(|r| Comment::get(&*conn, r).expect("Error retrieving previous comment").to_json(&*conn)),
+ "user_fqn": user.get_fqn(&*conn),
+ "errors": errors
+ }))
+ })
}
diff --git a/src/routes/posts.rs b/src/routes/posts.rs
index 9a518b25..30051042 100644
--- a/src/routes/posts.rs
+++ b/src/routes/posts.rs
@@ -4,6 +4,8 @@ use rocket::request::LenientForm;
use rocket::response::{Redirect, Flash};
use rocket_contrib::Template;
use serde_json;
+use std::{collections::HashMap, borrow::Cow};
+use validator::{Validate, ValidationError, ValidationErrors};
use plume_common::activity_pub::{broadcast, ActivityStream};
use plume_common::utils;
@@ -76,29 +78,54 @@ fn new(blog: String, user: User, conn: DbConn) -> Template {
}))
} else {
Template::render("posts/new", json!({
- "account": user
+ "account": user,
+ "errors": null,
+ "form": null
}))
}
}
-#[derive(FromForm)]
+#[derive(FromForm, Validate, Serialize)]
struct NewPostForm {
+ #[validate(custom(function = "valid_slug", message = "Invalid title"))]
pub title: String,
pub content: String,
pub license: String
}
+fn valid_slug(title: &str) -> Result<(), ValidationError> {
+ let slug = title.to_string().to_kebab_case();
+ if slug.len() == 0 {
+ Err(ValidationError::new("empty_slug"))
+ } else if slug == "new" {
+ Err(ValidationError::new("invalid_slug"))
+ } else {
+ Ok(())
+ }
+}
+
#[post("/~//new", data = "")]
-fn create(blog_name: String, data: LenientForm, user: User, conn: DbConn) -> Redirect {
+fn create(blog_name: String, data: LenientForm, user: User, conn: DbConn) -> Result {
let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap();
let form = data.get();
let slug = form.title.to_string().to_kebab_case();
+
+ let mut errors = match form.validate() {
+ Ok(_) => ValidationErrors::new(),
+ Err(e) => e
+ };
+ if let Some(_) = Post::find_by_slug(&*conn, slug.clone(), blog.id) {
+ errors.add("title", ValidationError {
+ code: Cow::from("existing_slug"),
+ message: Some(Cow::from("A post with the same title already exists.")),
+ params: HashMap::new()
+ });
+ }
- if !user.is_author_in(&*conn, blog.clone()) {
- Redirect::to(uri!(super::blogs::details: name = blog_name))
- } else {
- if slug == "new" || Post::find_by_slug(&*conn, slug.clone(), blog.id).is_some() {
- Redirect::to(uri!(new: blog = blog_name))
+ if errors.is_empty() {
+ if !user.is_author_in(&*conn, blog.clone()) {
+ // actually it's not "Ok"…
+ Ok(Redirect::to(uri!(super::blogs::details: name = blog_name)))
} else {
let (content, mentions) = utils::md_to_html(form.content.to_string().as_ref());
@@ -124,7 +151,13 @@ fn create(blog_name: String, data: LenientForm, user: User, conn: D
let act = post.create_activity(&*conn);
broadcast(&user, act, user.get_followers(&*conn));
- Redirect::to(uri!(details: blog = blog_name, slug = slug))
+ Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug)))
}
+ } else {
+ Err(Template::render("posts/new", json!({
+ "account": user,
+ "errors": errors.inner(),
+ "form": form
+ })))
}
}
diff --git a/src/routes/session.rs b/src/routes/session.rs
index c99548ba..fd79f057 100644
--- a/src/routes/session.rs
+++ b/src/routes/session.rs
@@ -1,10 +1,10 @@
-use gettextrs::gettext;
use rocket::{
http::{Cookie, Cookies, uri::Uri},
- response::{Redirect, status::NotFound},
+ response::Redirect,
request::{LenientForm,FlashMessage}
};
use rocket_contrib::Template;
+use validator::{Validate, ValidationError, ValidationErrors};
use plume_models::{
db_conn::DbConn,
@@ -14,7 +14,9 @@ use plume_models::{
#[get("/login")]
fn new(user: Option) -> Template {
Template::render("session/login", json!({
- "account": user
+ "account": user,
+ "errors": null,
+ "form": null
}))
}
@@ -27,40 +29,50 @@ struct Message {
fn new_message(user: Option, message: Message) -> Template {
Template::render("session/login", json!({
"account": user,
- "message": message.m
+ "message": message.m,
+ "errors": null,
+ "form": null
}))
}
-#[derive(FromForm)]
+#[derive(FromForm, Validate, Serialize)]
struct LoginForm {
+ #[validate(length(min = "1", message = "We need an email or a username to identify you"))]
email_or_name: String,
+ #[validate(length(min = "8", message = "Your password should be at least 8 characters long"))]
password: String
}
#[post("/login", data = "")]
-fn create(conn: DbConn, data: LenientForm, flash: Option, mut cookies: Cookies) -> Result> {
+fn create(conn: DbConn, data: LenientForm, flash: Option, mut cookies: Cookies) -> Result {
let form = data.get();
- let user = match User::find_by_email(&*conn, form.email_or_name.to_string()) {
- Some(usr) => Ok(usr),
- None => match User::find_local(&*conn, form.email_or_name.to_string()) {
- Some(usr) => Ok(usr),
- None => Err(gettext("Invalid username or password"))
- }
+ let user = User::find_by_email(&*conn, form.email_or_name.to_string())
+ .map(|u| Ok(u))
+ .unwrap_or_else(|| User::find_local(&*conn, form.email_or_name.to_string()).map(|u| Ok(u)).unwrap_or(Err(())));
+
+ let mut errors = match form.validate() {
+ Ok(_) => ValidationErrors::new(),
+ Err(e) => e
};
- match user {
- Ok(usr) => {
- if usr.auth(form.password.to_string()) {
- cookies.add_private(Cookie::new(AUTH_COOKIE, usr.id.to_string()));
- Ok(Redirect::to(Uri::new(flash
- .and_then(|f| if f.name() == "callback" { Some(f.msg().to_owned()) } else { None })
- .unwrap_or("/".to_owned()))
- ))
- } else {
- Err(NotFound(gettext("Invalid username or password")))
- }
- },
- Err(e) => Err(NotFound(String::from(e)))
+ if let Err(_) = user.clone() {
+ errors.add("email_or_name", ValidationError::new("invalid_login"))
+ } else if !user.clone().expect("User not found").auth(form.password.clone()) {
+ errors.add("email_or_name", ValidationError::new("invalid_login"))
+ }
+
+ if errors.is_empty() {
+ cookies.add_private(Cookie::new(AUTH_COOKIE, user.unwrap().id.to_string()));
+ Ok(Redirect::to(Uri::new(flash
+ .and_then(|f| if f.name() == "callback" { Some(f.msg().to_owned()) } else { None })
+ .unwrap_or("/".to_owned()))
+ ))
+ } else {
+ Err(Template::render("session/login", json!({
+ "account": user,
+ "errors": errors.inner(),
+ "form": form
+ })))
}
}
diff --git a/src/routes/user.rs b/src/routes/user.rs
index 3e47b8fc..1b0f42e9 100644
--- a/src/routes/user.rs
+++ b/src/routes/user.rs
@@ -7,6 +7,7 @@ use rocket::{request::LenientForm,
};
use rocket_contrib::Template;
use serde_json;
+use validator::{Validate, ValidationError};
use plume_common::activity_pub::{
ActivityStream, broadcast, Id, IntoId,
@@ -120,7 +121,9 @@ fn activity_details(name: String, conn: DbConn) -> ActivityStream
#[get("/users/new")]
fn new(user: Option) -> Template {
Template::render("users/new", json!({
- "account": user
+ "account": user,
+ "errors": null,
+ "form": null
}))
}
@@ -157,40 +160,49 @@ fn update(_name: String, conn: DbConn, user: User, data: LenientForm) -> Result {
- let form = data.get();
-
- if form.username.clone().len() < 1 {
- Err(String::from("Username is required"))
- } else if form.email.clone().len() < 1 {
- Err(String::from("Email is required"))
- } else if form.password.clone().len() < 8 {
- Err(String::from("Password should be at least 8 characters long"))
- } else if form.password == form.password_confirmation {
- NewUser::new_local(
- &*conn,
- form.username.to_string(),
- form.username.to_string(),
- false,
- String::from(""),
- form.email.to_string(),
- User::hash_pass(form.password.to_string())
- ).update_boxes(&*conn);
- Ok(Redirect::to(uri!(super::session::new)))
+fn passwords_match(form: &NewUserForm) -> Result<(), ValidationError> {
+ if form.password != form.password_confirmation {
+ Err(ValidationError::new("password_match"))
} else {
- Err(String::from("Passwords don't match"))
+ Ok(())
}
}
+#[post("/users/new", data = "")]
+fn create(conn: DbConn, data: LenientForm) -> Result {
+ let form = data.get();
+ form.validate()
+ .map(|_| {
+ NewUser::new_local(
+ &*conn,
+ form.username.to_string(),
+ form.username.to_string(),
+ false,
+ String::from(""),
+ form.email.to_string(),
+ User::hash_pass(form.password.to_string())
+ ).update_boxes(&*conn);
+ Redirect::to(uri!(super::session::new))
+ })
+ .map_err(|e| Template::render("users/new", json!({
+ "errors": e.inner(),
+ "form": form
+ })))
+}
+
#[get("/@//outbox")]
fn outbox(name: String, conn: DbConn) -> ActivityStream {
let user = User::find_local(&*conn, name).unwrap();
diff --git a/templates/blogs/new.html.tera b/templates/blogs/new.html.tera
index 0dd78775..9c1f12ec 100644
--- a/templates/blogs/new.html.tera
+++ b/templates/blogs/new.html.tera
@@ -1,4 +1,5 @@
{% extends "base" %}
+{% import "macros" as macros %}
{% block title %}
{{ "New blog" | _ }}
@@ -7,8 +8,8 @@
{% block content %}
{{ "Create a blog" | _ }}
{% endblock content %}
diff --git a/templates/macros.html.tera b/templates/macros.html.tera
index 6a10d1ee..c12a6799 100644
--- a/templates/macros.html.tera
+++ b/templates/macros.html.tera
@@ -21,3 +21,12 @@
{% endmacro post_card %}
+{% macro input(name, label, errors, form, type="text", props="") %}
+
+ {% if errors is defined and errors[name] %}
+ {% for err in errors[name] %}
+ {{ err.message | default(value="Unknown error") | _ }}
+ {% endfor %}
+ {% endif %}
+
+{% endmacro input %}
diff --git a/templates/posts/new.html.tera b/templates/posts/new.html.tera
index 4cf457be..335e2396 100644
--- a/templates/posts/new.html.tera
+++ b/templates/posts/new.html.tera
@@ -1,4 +1,5 @@
{% extends "base" %}
+{% import "macros" as macros %}
{% block title %}
{{ "New post" | _ }}
@@ -7,11 +8,17 @@
{% block content %}
{{ "Create a post" | _ }}
diff --git a/templates/session/login.html.tera b/templates/session/login.html.tera
index 4486bf3d..bbee8c98 100644
--- a/templates/session/login.html.tera
+++ b/templates/session/login.html.tera
@@ -1,4 +1,5 @@
{% extends "base" %}
+{% import "macros" as macros %}
{% block title %}
{{ "Login" | _ }}
@@ -10,11 +11,8 @@
{{ message }}
{% endif %}
diff --git a/templates/users/new.html.tera b/templates/users/new.html.tera
index edf328c0..b0aef4e1 100644
--- a/templates/users/new.html.tera
+++ b/templates/users/new.html.tera
@@ -1,4 +1,5 @@
{% extends "base" %}
+{% import "macros" as macros %}
{% block title %}
{{ "New Account" | _ }}
@@ -7,17 +8,10 @@
{% block content %}
{{ "Create an account" | _ }}