Skip to content

Commit 1aaf58a

Browse files
committed
feat: add auth routes
1 parent c49d6bf commit 1aaf58a

File tree

9 files changed

+114
-10
lines changed

9 files changed

+114
-10
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-- This file should undo anything in `up.sql`
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
ALTER TABLE snacks
2+
ADD COLUMN user_id INTEGER;
3+
4+
INSERT INTO users (username, password_hash, role, created_at, updated_at)
5+
SELECT 'admin',
6+
'$2a$12$K6O5bFK6.O4V6csO94BFN.yvMkv4pOXxYH.rolz4.Y4.pxpwB6fvC',
7+
'admin',
8+
CURRENT_TIMESTAMP,
9+
CURRENT_TIMESTAMP
10+
WHERE NOT EXISTS (
11+
SELECT 1 FROM users WHERE username = 'admin'
12+
) RETURNING id;
13+
14+
UPDATE snacks
15+
SET user_id = (SELECT id FROM users WHERE username = 'admin')
16+
WHERE user_id IS NULL;
17+
18+
ALTER TABLE snacks
19+
ALTER COLUMN user_id SET NOT NULL;
20+
21+
ALTER TABLE snacks
22+
ADD CONSTRAINT fk_snacks_user
23+
FOREIGN KEY (user_id)
24+
REFERENCES users(id)
25+
ON DELETE CASCADE;
26+
27+
CREATE INDEX idx_snacks_user_id ON snacks(user_id);

src/catchers/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ pub fn unauthorized(req: &Request) -> Json<ErrorResponse> {
1818
}
1919

2020
#[catch(404)]
21-
pub fn not_found(req: &Request) -> Json<ErrorResponse> {
21+
pub fn not_found(_req: &Request) -> Json<ErrorResponse> {
2222
Json(ErrorResponse {
2323
status: 404,
2424
message: "Page not found".to_string(),
2525
})
2626
}
2727

2828
#[catch(500)]
29-
pub fn internal_server_error(req: &Request) -> Json<ErrorResponse> {
29+
pub fn internal_server_error(_req: &Request) -> Json<ErrorResponse> {
3030
Json(ErrorResponse {
3131
status: 500,
3232
message: "Internal Server Error".to_string(),

src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod catchers;
88
mod db;
99
mod schema;
1010

11+
use crate::routes::auth::{login, register};
1112
use crate::routes::snack::{create_snack, delete_snack, list_snacks, update_snack};
1213
use dotenv::dotenv;
1314
use rocket::*;
@@ -28,7 +29,7 @@ fn index() -> &'static str {
2829
fn rocket() -> _ {
2930
dotenv().ok();
3031

31-
rocket::build().mount("/", routes![index, create_snack, list_snacks, update_snack, delete_snack]).register("/", catchers![catchers::unauthorized, catchers::not_found,
32+
rocket::build().mount("/", routes![index, create_snack, list_snacks, update_snack, delete_snack, register, login]).register("/", catchers![catchers::unauthorized, catchers::not_found,
3233
catchers::internal_server_error])
3334
}
3435

src/models/snack.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::models::user::User;
12
use chrono::NaiveDateTime;
23
use diesel::prelude::*;
34
use rust_decimal::Decimal;
@@ -16,6 +17,8 @@ pub struct Snack {
1617
pub image_url: String,
1718
pub created_at: NaiveDateTime,
1819
pub updated_at: NaiveDateTime,
20+
pub user_id: i32,
21+
1922
}
2023

2124
#[derive(Insertable, Deserialize)]

src/models/user.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use chrono::NaiveDateTime;
2-
use diesel::{Insertable, Queryable};
3-
use serde::{Deserialize, Serialize};
4-
5-
#[derive(Queryable, Serialize)]
2+
use diesel::{Identifiable, Insertable, Queryable};
3+
use serde::Serialize;
4+
#[derive(Queryable, Serialize, Identifiable)]
5+
#[diesel(table_name = crate::schema::users)]
66
pub struct User {
77
pub id: i32,
88
pub username: String,
@@ -12,7 +12,8 @@ pub struct User {
1212
pub updated_at: NaiveDateTime,
1313
}
1414

15-
#[derive(Insertable, Deserialize)]
15+
#[derive(Insertable)]
16+
#[diesel(table_name = crate::schema::users)]
1617
pub struct NewUser {
1718
pub username: String,
1819
pub password_hash: String,

src/routes/auth.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use crate::auth::user::Claims;
2+
use crate::db;
3+
use crate::models::user::{NewUser, User};
4+
use crate::schema::users::dsl::*;
5+
use bcrypt::{hash, verify, DEFAULT_COST};
6+
use diesel::prelude::*;
7+
use jsonwebtoken::{encode, EncodingKey, Header};
8+
use rocket::http::Status;
9+
use rocket::serde::json::Json;
10+
use serde::{Deserialize, Serialize};
11+
#[derive(Deserialize)]
12+
pub struct RegisterInfo {
13+
username: String,
14+
password: String,
15+
}
16+
17+
#[derive(Deserialize)]
18+
pub struct LoginInfo {
19+
username: String,
20+
password: String,
21+
}
22+
#[derive(Serialize)]
23+
pub struct TokenResponse {
24+
token: String,
25+
}
26+
27+
#[post("/register", data = "<info>")]
28+
pub fn register(info: Json<RegisterInfo>) -> Result<Json<User>, Status> {
29+
let conn = &mut db::establish_connection();
30+
let hashed_password = hash(&info.password, DEFAULT_COST)
31+
.map_err(|_| Status::InternalServerError)?;
32+
33+
let new_user = NewUser {
34+
username: info.username.clone(),
35+
password_hash: hashed_password,
36+
role: "user".to_string(),
37+
};
38+
39+
diesel::insert_into(users)
40+
.values(new_user)
41+
.get_result::<User>(conn)
42+
.map(Json)
43+
.map_err(|_| Status::InternalServerError)
44+
}
45+
46+
#[post("/login", data = "<info>")]
47+
pub fn login(info: Json<LoginInfo>) -> Result<Json<TokenResponse>, Status> {
48+
let conn = &mut db::establish_connection();
49+
let user = users
50+
.filter(username.eq(&info.username))
51+
.first::<User>(conn)
52+
.map_err(|_| Status::Unauthorized)?;
53+
if verify(&info.password, &user.password_hash).map_err(|_| Status::InternalServerError)? {
54+
let claims = Claims {
55+
sub: user.id,
56+
role: user.role.clone(),
57+
exp: 10000000000,
58+
};
59+
let token = encode(
60+
&Header::default(),
61+
&claims,
62+
&EncodingKey::from_secret("SECRET".as_ref()),
63+
)
64+
.map_err(|_| Status::InternalServerError)?;
65+
Ok(Json(TokenResponse { token }))
66+
} else {
67+
Err(Status::Unauthorized)
68+
}
69+
}

src/routes/snack.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use crate::auth::ApiKey;
21
use crate::db;
32
use crate::models::snack::{NewSnack, Snack};
4-
use crate::schema::snacks::dsl::{id, snacks};
3+
use crate::schema::snacks::dsl::snacks;
54
use diesel::prelude::*;
65
use rocket::http::Status;
76
use rocket::serde::json::Json;

src/schema.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ diesel::table! {
99
image_url -> Varchar,
1010
created_at -> Timestamp,
1111
updated_at -> Timestamp,
12+
user_id -> Int4,
1213
}
1314
}
1415

@@ -23,6 +24,8 @@ diesel::table! {
2324
}
2425
}
2526

27+
diesel::joinable!(snacks -> users (user_id));
28+
2629
diesel::allow_tables_to_appear_in_same_query!(
2730
snacks,
2831
users,

0 commit comments

Comments
 (0)