organize
This commit is contained in:
parent
a5a75ce2ac
commit
a93704f998
13
src/jwt.rs
Normal file
13
src/jwt.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use jsonwebtoken::EncodingKey;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, Hash)]
|
||||||
|
pub struct Claims {
|
||||||
|
pub id: i32,
|
||||||
|
pub username: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_jwt_secret() -> EncodingKey {
|
||||||
|
let secret = std::env::var("SAAPI_JWT_SECRET").unwrap_or("Sdb\\y5PP`,hmG+98".into());
|
||||||
|
EncodingKey::from_secret(secret.as_bytes())
|
||||||
|
}
|
||||||
159
src/main.rs
159
src/main.rs
@ -1,27 +1,21 @@
|
|||||||
|
mod events;
|
||||||
|
mod jwt;
|
||||||
|
mod user;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::State,
|
|
||||||
http::StatusCode,
|
|
||||||
response::IntoResponse,
|
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
Json, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use jsonwebtoken::{EncodingKey, Header};
|
use jsonwebtoken::EncodingKey;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::json;
|
|
||||||
use sqlx::postgres::PgPoolOptions;
|
use sqlx::postgres::PgPoolOptions;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use user::{signin, signup};
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug, Hash)]
|
|
||||||
struct Claims {
|
|
||||||
id: i32,
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
pub struct AppState {
|
||||||
db_pool: PgPool,
|
pub db_pool: PgPool,
|
||||||
jwt_key: EncodingKey,
|
pub jwt_key: EncodingKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -34,9 +28,9 @@ async fn main() {
|
|||||||
let db_url = std::env::var("DATABASE_URL")
|
let db_url = std::env::var("DATABASE_URL")
|
||||||
.expect("Set `DATABASE_URL` to the url of the postgres database.");
|
.expect("Set `DATABASE_URL` to the url of the postgres database.");
|
||||||
|
|
||||||
let jwt_secret = std::env::var("SAAPI_JWT_SECRET").unwrap_or("Sdb\\y5PP`,hmG+98".into());
|
let jwt_key = jwt::get_jwt_secret();
|
||||||
|
|
||||||
let pool = PgPoolOptions::new()
|
let db_pool = PgPoolOptions::new()
|
||||||
.max_connections(50)
|
.max_connections(50)
|
||||||
.connect(&db_url)
|
.connect(&db_url)
|
||||||
.await
|
.await
|
||||||
@ -44,15 +38,10 @@ async fn main() {
|
|||||||
|
|
||||||
// build our application with a route
|
// build our application with a route
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
// `GET /` goes to `root`
|
|
||||||
.route("/", get(root))
|
.route("/", get(root))
|
||||||
// `POST /users` goes to `create_user`
|
|
||||||
.route("/user/signup", post(signup))
|
.route("/user/signup", post(signup))
|
||||||
.route("/user/signin", post(signin))
|
.route("/user/signin", post(signin))
|
||||||
.with_state(AppState {
|
.with_state(AppState { db_pool, jwt_key });
|
||||||
db_pool: pool,
|
|
||||||
jwt_key: EncodingKey::from_secret(jwt_secret.as_bytes()),
|
|
||||||
});
|
|
||||||
|
|
||||||
// run our app with hyper
|
// run our app with hyper
|
||||||
// `axum::Server` is a re-export of `hyper::Server`
|
// `axum::Server` is a re-export of `hyper::Server`
|
||||||
@ -68,125 +57,3 @@ async fn main() {
|
|||||||
async fn root() -> &'static str {
|
async fn root() -> &'static str {
|
||||||
"Hello, World!"
|
"Hello, World!"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn signup(
|
|
||||||
State(app_state): State<AppState>,
|
|
||||||
Json(signup): Json<Signup>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
// insert your application logic here
|
|
||||||
let pass_hash = sha256::digest(&*signup.password);
|
|
||||||
|
|
||||||
let result = sqlx::query!(
|
|
||||||
r#"
|
|
||||||
INSERT INTO users (username, password)
|
|
||||||
VALUES ( $1, $2 )
|
|
||||||
RETURNING id;
|
|
||||||
"#,
|
|
||||||
signup.username,
|
|
||||||
pass_hash.as_bytes(),
|
|
||||||
)
|
|
||||||
.fetch_one(&app_state.db_pool)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(record) => (StatusCode::CREATED, Json(json!({"id": record.id}))),
|
|
||||||
Err(err) => {
|
|
||||||
// See if we get an error for a duplicate usename
|
|
||||||
if let sqlx::Error::Database(database_err) = &err {
|
|
||||||
if let Some(err_code) = database_err.code() {
|
|
||||||
if err_code == "23505" {
|
|
||||||
return (
|
|
||||||
StatusCode::BAD_REQUEST,
|
|
||||||
Json(json!({
|
|
||||||
"error": format!("username '{}' is taken.", signup.username)
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
Json(json!({
|
|
||||||
"error": format!("Unknown error signing up: {}", err)
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn signin(
|
|
||||||
State(app_state): State<AppState>,
|
|
||||||
Json(signin): Json<Signin>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let pass_hash = sha256::digest(&*signin.password);
|
|
||||||
|
|
||||||
let result = sqlx::query!(
|
|
||||||
r#"
|
|
||||||
SELECT id, username
|
|
||||||
FROM users
|
|
||||||
WHERE username = $1 AND password = $2
|
|
||||||
"#,
|
|
||||||
signin.username,
|
|
||||||
pass_hash.as_bytes(),
|
|
||||||
)
|
|
||||||
.fetch_optional(&app_state.db_pool)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(Some(user)) => {
|
|
||||||
let claims = Claims {
|
|
||||||
id: user.id,
|
|
||||||
username: user.username,
|
|
||||||
};
|
|
||||||
let token = match jsonwebtoken::encode(&Header::default(), &claims, &app_state.jwt_key)
|
|
||||||
{
|
|
||||||
Ok(token) => token,
|
|
||||||
Err(err) => {
|
|
||||||
return (
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
Json(json!({
|
|
||||||
"error": format!("Unknown error signing in when creating JWT: {}", err)
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (StatusCode::OK, Json(json!({ "data": token })));
|
|
||||||
}
|
|
||||||
Ok(None) => {
|
|
||||||
return (
|
|
||||||
StatusCode::UNAUTHORIZED,
|
|
||||||
Json(json!({
|
|
||||||
"error": format!("Incorrect username or password")
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
return (
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
Json(json!({
|
|
||||||
"error": format!("Unknown error signing in: {}", err)
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
struct Signup {
|
|
||||||
username: String,
|
|
||||||
password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
struct Signin {
|
|
||||||
username: String,
|
|
||||||
password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
// the output to our `create_user` handler
|
|
||||||
#[derive(Serialize, sqlx::FromRow)]
|
|
||||||
struct User {
|
|
||||||
id: u64,
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|||||||
126
src/user.rs
126
src/user.rs
@ -1,9 +1,127 @@
|
|||||||
use rocket::Route;
|
use axum::{extract::State, http::StatusCode, response::IntoResponse, Json};
|
||||||
|
use jsonwebtoken::Header;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
use crate::{jwt::Claims, AppState};
|
||||||
routes![
|
|
||||||
|
|
||||||
]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct Signup {
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct Signin {
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, sqlx::FromRow)]
|
||||||
|
struct User {
|
||||||
|
id: u64,
|
||||||
|
username: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn signup(
|
||||||
|
State(app_state): State<AppState>,
|
||||||
|
Json(signup): Json<Signup>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
// insert your application logic here
|
||||||
|
let pass_hash = sha256::digest(&*signup.password);
|
||||||
|
|
||||||
|
let result = sqlx::query!(
|
||||||
|
r#"
|
||||||
|
INSERT INTO users (username, password)
|
||||||
|
VALUES ( $1, $2 )
|
||||||
|
RETURNING id;
|
||||||
|
"#,
|
||||||
|
signup.username,
|
||||||
|
pass_hash.as_bytes(),
|
||||||
|
)
|
||||||
|
.fetch_one(&app_state.db_pool)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(record) => (StatusCode::CREATED, Json(json!({"id": record.id}))),
|
||||||
|
Err(err) => {
|
||||||
|
// See if we get an error for a duplicate usename
|
||||||
|
if let sqlx::Error::Database(database_err) = &err {
|
||||||
|
if let Some(err_code) = database_err.code() {
|
||||||
|
if err_code == "23505" {
|
||||||
|
return (
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Json(json!({
|
||||||
|
"error": format!("username '{}' is taken.", signup.username)
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
Json(json!({
|
||||||
|
"error": format!("Unknown error signing up: {}", err)
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn signin(
|
||||||
|
State(app_state): State<AppState>,
|
||||||
|
Json(signin): Json<Signin>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let pass_hash = sha256::digest(&*signin.password);
|
||||||
|
|
||||||
|
let result = sqlx::query!(
|
||||||
|
r#"
|
||||||
|
SELECT id, username
|
||||||
|
FROM users
|
||||||
|
WHERE username = $1 AND password = $2
|
||||||
|
"#,
|
||||||
|
signin.username,
|
||||||
|
pass_hash.as_bytes(),
|
||||||
|
)
|
||||||
|
.fetch_optional(&app_state.db_pool)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(Some(user)) => {
|
||||||
|
let claims = Claims {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
};
|
||||||
|
let token = match jsonwebtoken::encode(&Header::default(), &claims, &app_state.jwt_key)
|
||||||
|
{
|
||||||
|
Ok(token) => token,
|
||||||
|
Err(err) => {
|
||||||
|
return (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
Json(json!({
|
||||||
|
"error": format!("Unknown error signing in when creating JWT: {}", err)
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (StatusCode::OK, Json(json!({ "data": token })));
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
return (
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
Json(json!({
|
||||||
|
"error": format!("Incorrect username or password")
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
Json(json!({
|
||||||
|
"error": format!("Unknown error signing in: {}", err)
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user