Compare commits
No commits in common. "9ad50fd4f0628c84b90026a44d5761d884dd5e92" and "cb49ba0e8d6b82e2cfa43938eb5bd6850810edbd" have entirely different histories.
9ad50fd4f0
...
cb49ba0e8d
@ -1 +0,0 @@
|
||||
DATABASE_URL=sqlite://./db.sqlite
|
3
backend/.gitignore
vendored
3
backend/.gitignore
vendored
@ -1,4 +1 @@
|
||||
/target
|
||||
db.sqlite
|
||||
.env
|
||||
|
||||
|
1469
backend/Cargo.lock
generated
1469
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,12 +4,5 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
argon2 = "0.5.3"
|
||||
env_logger = "0.11.6"
|
||||
log = "0.4.22"
|
||||
rocket = { version = "0.5.1", features = ["json", "secrets"] }
|
||||
rocket_db_pools = { version = "0.2.0", features = ["sqlx_sqlite"] }
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.134"
|
||||
sqlx = { version = "0.7", features = ["macros", "migrate"] }
|
||||
rocket = "0.5.1"
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
[default.databases.main]
|
||||
url = "./db.sqlite"
|
||||
|
@ -1,2 +0,0 @@
|
||||
-- Add migration script here
|
||||
DROP TABLE monke;
|
@ -1,6 +0,0 @@
|
||||
-- Add migration script here
|
||||
CREATE TABLE monke (
|
||||
monke_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
monke_name TEXT NOT NULL,
|
||||
monke_password TEXT NOT NULL
|
||||
);
|
@ -1,67 +1,12 @@
|
||||
use rocket::{
|
||||
fairing,
|
||||
http::Status,
|
||||
request::{FromRequest, Outcome},
|
||||
Request, Rocket,
|
||||
};
|
||||
use rocket_db_pools::sqlx::{self};
|
||||
use rocket_db_pools::Database;
|
||||
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
mod modules;
|
||||
|
||||
// so i was trying out claude.ai, and as a joke i talked to it as monke,
|
||||
// and it answered like monke. so i thought it would be funny to,
|
||||
// instead of plain, boring "User", just use "Monke" :D
|
||||
|
||||
/// Stores info about a Monke
|
||||
struct Monke {
|
||||
pub monke_id: usize,
|
||||
pub monke_name: String,
|
||||
}
|
||||
|
||||
#[derive(Database)]
|
||||
#[database("main")]
|
||||
pub struct MainDb(sqlx::SqlitePool);
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for Monke {
|
||||
type Error = ();
|
||||
|
||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
// monke get cookies from request
|
||||
let cookies = request.cookies();
|
||||
|
||||
// monke look for auth cookie
|
||||
match cookies.get_private("monke_session") {
|
||||
Some(cookie) => {
|
||||
let auth_db = request.rocket().state::<MainDb>().unwrap();
|
||||
|
||||
// in real app, monke validate token here
|
||||
if cookie.value().is_empty() {
|
||||
Outcome::Error((Status::Unauthorized, ()))
|
||||
} else {
|
||||
Outcome::Success(Monke {
|
||||
monke_id: cookie.value().parse().unwrap(),
|
||||
monke_name: "Test".into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
None => Outcome::Error((Status::Unauthorized, ())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
fn index() -> &'static str {
|
||||
"Hello, world!"
|
||||
}
|
||||
|
||||
#[launch]
|
||||
async fn rocket() -> _ {
|
||||
rocket::build()
|
||||
.attach(MainDb::init())
|
||||
.mount("/api", routes![index, modules::auth::login])
|
||||
fn rocket() -> _ {
|
||||
rocket::build().mount("/", routes![index])
|
||||
}
|
||||
|
@ -1,83 +0,0 @@
|
||||
use argon2::{password_hash, Argon2, PasswordHash, PasswordVerifier};
|
||||
use rocket::{
|
||||
http::{CookieJar, Status},
|
||||
serde::json::Json,
|
||||
};
|
||||
use rocket_db_pools::Connection;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::MainDb;
|
||||
|
||||
const COOKIE_INDEX: &'static str = "monke_secret";
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LoginCredentials {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
type ApiResponse = Json<Result<(), String>>;
|
||||
|
||||
#[post("/login", data = "<credentials>")]
|
||||
pub async fn login(
|
||||
credentials: Json<LoginCredentials>,
|
||||
mut db: Connection<MainDb>,
|
||||
cookies: &CookieJar<'_>,
|
||||
) -> (Status, ApiResponse) {
|
||||
// check them db
|
||||
let result = sqlx::query!(
|
||||
"SELECT * FROM monke WHERE monke_name=?",
|
||||
credentials.username
|
||||
)
|
||||
.fetch_one(&mut **db)
|
||||
.await;
|
||||
|
||||
let result = match result {
|
||||
Ok(r) => r,
|
||||
Err(sqlx::Error::RowNotFound) => {
|
||||
return (Status::BadRequest, Json(Err("Invalid credentials".into())));
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("error fetching db: {:?}", e);
|
||||
|
||||
return (
|
||||
Status::InternalServerError,
|
||||
Json(Err("Server error".into())),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// check them credentials
|
||||
let parsed_hash = match PasswordHash::new(&result.monke_password) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
log::error!("argon2 hash parse error: {:?}", e);
|
||||
return (
|
||||
Status::InternalServerError,
|
||||
Json(Err(String::from("Server error"))),
|
||||
);
|
||||
}
|
||||
};
|
||||
match Argon2::default().verify_password(credentials.password.as_bytes(), &parsed_hash) {
|
||||
Ok(_) => {}
|
||||
Err(password_hash::Error::Password) => {
|
||||
return (
|
||||
Status::BadRequest,
|
||||
Json(Err(String::from("Invalid credentials"))),
|
||||
)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("error verifying argon2 password: {:?}", e);
|
||||
return (
|
||||
Status::InternalServerError,
|
||||
Json(Err(String::from("Server error"))),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// create cookie
|
||||
cookies.add_private((COOKIE_INDEX, result.monke_id.to_string()));
|
||||
|
||||
// send ok
|
||||
return (Status::Ok, Json(Ok(())));
|
||||
}
|
@ -1 +0,0 @@
|
||||
pub mod auth;
|
Loading…
Reference in New Issue
Block a user