fix crash on first startup due to making admin account, and allow creation of new users through a ui
This commit is contained in:
parent
d6c418a899
commit
8e1d34595a
91
src/main.rs
91
src/main.rs
|
@ -1,4 +1,5 @@
|
||||||
use rocket::fairing::AdHoc;
|
use rocket::fairing::AdHoc;
|
||||||
|
use rocket::fs::FileServer;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::response::content::{self, RawHtml};
|
use rocket::response::content::{self, RawHtml};
|
||||||
use rocket::{Build, Request, Rocket};
|
use rocket::{Build, Request, Rocket};
|
||||||
|
@ -15,29 +16,18 @@ use rocket_db_pools::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::serde::{json::Json, Deserialize};
|
use rocket::serde::{json::Json, Deserialize};
|
||||||
use rocket::{
|
use rocket::http::CookieJar;
|
||||||
fs::FileServer,
|
|
||||||
http::CookieJar,
|
|
||||||
tokio::time::{sleep, Duration},
|
|
||||||
};
|
|
||||||
|
|
||||||
use fossil::tables::{Db, Post, User};
|
use fossil::tables::{Db, Post, User};
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/login")]
|
||||||
fn hello() -> RawHtml<String> {
|
fn login_page() -> RawHtml<String> {
|
||||||
content::RawHtml(fs::read_to_string("/srv/web/index.html").unwrap())
|
content::RawHtml(fs::read_to_string("/srv/web/login.html").unwrap())
|
||||||
// format!("hi!!!")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/book/<card>/<isbn>")]
|
#[get("/createuser")]
|
||||||
fn get_book(card: String, isbn: String) -> String {
|
fn createuser_page() -> RawHtml<String> {
|
||||||
format!("You're checking out the book {isbn}, with the account {card}.")
|
content::RawHtml(fs::read_to_string("/srv/web/createuser.html").unwrap())
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/delay/<time>")]
|
|
||||||
async fn delay(time: u64) -> String {
|
|
||||||
sleep(Duration::from_secs(time)).await;
|
|
||||||
format!("waited for {time} seconds")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -49,9 +39,7 @@ struct LoginInfo {
|
||||||
|
|
||||||
#[post("/createuser", data = "<info>")]
|
#[post("/createuser", data = "<info>")]
|
||||||
async fn createuser(
|
async fn createuser(
|
||||||
// this is so fucking jank but it works !!!
|
mut db: Connection<Db>,
|
||||||
db: Connection<Db>,
|
|
||||||
db2: Connection<Db>,
|
|
||||||
info: Json<LoginInfo>,
|
info: Json<LoginInfo>,
|
||||||
cookies: &CookieJar<'_>,
|
cookies: &CookieJar<'_>,
|
||||||
) -> &'static str {
|
) -> &'static str {
|
||||||
|
@ -59,22 +47,35 @@ async fn createuser(
|
||||||
match token.is_some() {
|
match token.is_some() {
|
||||||
true => "You're already logged in. Log out before trying to create a new account.",
|
true => "You're already logged in. Log out before trying to create a new account.",
|
||||||
false => {
|
false => {
|
||||||
if User::get_by_username(db, &info.username).await.is_some() {
|
match User::get_by_username(&mut db, &info.username).await {
|
||||||
"Username already taken. Please try again."
|
Some(_) => "Username already taken. Please try again.",
|
||||||
} else {
|
None => {
|
||||||
User::create(db2, &info.username, &info.password).await;
|
User::create(&mut db, &info.username, &info.password).await;
|
||||||
"Account created."
|
match User::get_by_username(&mut db, &info.username).await {
|
||||||
|
Some(user) => match user.set_new_token(&mut db).await {
|
||||||
|
Ok(t) => {
|
||||||
|
cookies.add_private(("token", t));
|
||||||
|
"Your account has been created and you've been automatically logged in."
|
||||||
|
},
|
||||||
|
Err(why) => {
|
||||||
|
eprintln!("{why:?}");
|
||||||
|
"Couldn't log you in. Your account has been created, though."
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None => "Something went really wrong. I don't know what."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/account")]
|
#[get("/account")]
|
||||||
async fn account(db: Connection<Db>, cookies: &CookieJar<'_>) -> String {
|
async fn account(mut db: Connection<Db>, cookies: &CookieJar<'_>) -> String {
|
||||||
let token = cookies.get_private("token");
|
let token = cookies.get_private("token");
|
||||||
match token {
|
match token {
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
match User::get_by_token(db, t.to_string().split('=').collect::<Vec<&str>>()[1].to_string() /*GOD I LOVE RUST*/).await {
|
match User::get_by_token(&mut db, t.to_string().split('=').collect::<Vec<&str>>()[1].to_string() /*GOD I LOVE RUST*/).await {
|
||||||
Some(user) => format!("Username: {}\nAdmin: {}\nMake Posts: {}\nComment: {}", user.username, user.admin, user.make_posts, user.comment),
|
Some(user) => format!("Username: {}\nAdmin: {}\nMake Posts: {}\nComment: {}", user.username, user.admin, user.make_posts, user.comment),
|
||||||
None => "User doesn't exist.".to_string()
|
None => "User doesn't exist.".to_string()
|
||||||
}
|
}
|
||||||
|
@ -86,20 +87,20 @@ async fn account(db: Connection<Db>, cookies: &CookieJar<'_>) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/login", data = "<info>")]
|
#[post("/login", data = "<info>")]
|
||||||
async fn login(db: Connection<Db>, db2: Connection<Db>, info: Json<LoginInfo>, cookies: &CookieJar<'_>) -> String {
|
async fn login(mut db: Connection<Db>, info: Json<LoginInfo>, cookies: &CookieJar<'_>) -> String {
|
||||||
let token = cookies.get_private("token");
|
let token = cookies.get_private("token");
|
||||||
match token {
|
match token {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
"already logged in".to_string()
|
"already logged in".to_string()
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
match User::get_by_username(db, &info.username).await {
|
match User::get_by_username(&mut db, &info.username).await {
|
||||||
Some(user) => {
|
Some(user) => {
|
||||||
if user.password == sha256::digest(&info.password) {
|
if user.password == sha256::digest(&info.password) {
|
||||||
match user.token {
|
match user.token {
|
||||||
Some(t) => {cookies.add_private(("token", t)); "Logged in".to_string()},
|
Some(t) => {cookies.add_private(("token", t)); "Logged in".to_string()},
|
||||||
None => {
|
None => {
|
||||||
match user.set_new_token(db2).await {
|
match user.set_new_token(&mut db).await {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
cookies.add_private(("token", t));
|
cookies.add_private(("token", t));
|
||||||
"logged in".to_string()
|
"logged in".to_string()
|
||||||
|
@ -129,26 +130,6 @@ async fn logout(cookies: &CookieJar<'_>) -> &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/dbtest/<id>")]
|
|
||||||
async fn dbtest(db: Connection<Db>, id: i32) -> Option<String> {
|
|
||||||
let post = Post::get(db, id).await;
|
|
||||||
Some(format!(
|
|
||||||
"ID: {}\nTitle: {}\nBody: {}\nPublished: {}",
|
|
||||||
post.id, post.title, post.body, post.published
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/dbcreate/<title>/<body>/<published>")]
|
|
||||||
async fn dbcreate(
|
|
||||||
db: Connection<Db>,
|
|
||||||
title: String,
|
|
||||||
body: String,
|
|
||||||
published: bool,
|
|
||||||
) -> &'static str {
|
|
||||||
Post::create(db, title, body, published).await;
|
|
||||||
"created!"
|
|
||||||
}
|
|
||||||
|
|
||||||
#[catch(default)]
|
#[catch(default)]
|
||||||
fn default_catcher(status: Status, _: &Request) -> RawHtml<String> {
|
fn default_catcher(status: Status, _: &Request) -> RawHtml<String> {
|
||||||
content::RawHtml(
|
content::RawHtml(
|
||||||
|
@ -199,9 +180,7 @@ async fn migrate(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
let username = std::env::var("ADMIN_USERNAME").expect("make ADMIN_USERNAME env var");
|
let username = std::env::var("ADMIN_USERNAME").expect("make ADMIN_USERNAME env var");
|
||||||
let password = sha256::digest(std::env::var("ADMIN_PASSWORD").expect("make ADMIN_PASSWORD env var"));
|
let password = sha256::digest(std::env::var("ADMIN_PASSWORD").expect("make ADMIN_PASSWORD env var"));
|
||||||
|
|
||||||
eprintln!("{username}\n{password}");
|
conn.execute(sqlx::query(
|
||||||
|
|
||||||
conn.fetch_one(sqlx::query(
|
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO users (username, password, admin)
|
INSERT INTO users (username, password, admin)
|
||||||
VALUES ($1, $2, true);
|
VALUES ($1, $2, true);
|
||||||
|
@ -226,10 +205,10 @@ async fn main() {
|
||||||
.attach(AdHoc::on_ignite("DB Migrations", migrate))
|
.attach(AdHoc::on_ignite("DB Migrations", migrate))
|
||||||
.mount(
|
.mount(
|
||||||
"/",
|
"/",
|
||||||
routes![hello, get_book, delay, login, logout, dbtest, dbcreate, createuser, account],
|
routes![login_page, login, logout, createuser, createuser_page, account],
|
||||||
)
|
)
|
||||||
|
.mount("/css", FileServer::from("/srv/web/css"))
|
||||||
.register("/", catchers![default_catcher])
|
.register("/", catchers![default_catcher])
|
||||||
.mount("/login", FileServer::from("/srv/web"))
|
|
||||||
.launch()
|
.launch()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -70,9 +70,9 @@ pub enum UserRole {
|
||||||
Comment,
|
Comment,
|
||||||
}
|
}
|
||||||
impl User {
|
impl User {
|
||||||
pub async fn create(mut db: Connection<Db>, username: &String, password: &String) {
|
pub async fn create(db: &mut Connection<Db>, username: &String, password: &String) {
|
||||||
match db
|
match db
|
||||||
.fetch_all(
|
.execute(
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO users (username, password)
|
INSERT INTO users (username, password)
|
||||||
|
@ -90,7 +90,7 @@ impl User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_by_id(mut db: Connection<Db>, id: i32) -> Option<User> {
|
pub async fn get_by_id(db: &mut Connection<Db>, id: i32) -> Option<User> {
|
||||||
match db
|
match db
|
||||||
.fetch_one(sqlx::query("SELECT * FROM users WHERE id = $1;").bind(id))
|
.fetch_one(sqlx::query("SELECT * FROM users WHERE id = $1;").bind(id))
|
||||||
.await
|
.await
|
||||||
|
@ -107,7 +107,7 @@ impl User {
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_by_token(mut db: Connection<Db>, token: String) -> Option<User> {
|
pub async fn get_by_token(db: &mut Connection<Db>, token: String) -> Option<User> {
|
||||||
match db
|
match db
|
||||||
.fetch_one(sqlx::query("SELECT * FROM users WHERE token = $1;").bind(token))
|
.fetch_one(sqlx::query("SELECT * FROM users WHERE token = $1;").bind(token))
|
||||||
.await
|
.await
|
||||||
|
@ -124,7 +124,7 @@ impl User {
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_by_username(mut db: Connection<Db>, username: &String) -> Option<User> {
|
pub async fn get_by_username(db: &mut Connection<Db>, username: &String) -> Option<User> {
|
||||||
match db
|
match db
|
||||||
.fetch_one(sqlx::query("SELECT * FROM users WHERE username = $1;").bind(username))
|
.fetch_one(sqlx::query("SELECT * FROM users WHERE username = $1;").bind(username))
|
||||||
.await
|
.await
|
||||||
|
@ -141,14 +141,14 @@ impl User {
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn set_new_token(&self, mut db: Connection<Db>) -> Result<String, String> {
|
pub async fn set_new_token(&self, db: &mut Connection<Db>) -> Result<String, String> {
|
||||||
let token_end = rand_hc::Hc128Rng::from_entropy().next_u64();
|
let token_end = rand_hc::Hc128Rng::from_entropy().next_u64();
|
||||||
let token_start = sha256::digest(&self.username);
|
let token_start = sha256::digest(&self.username);
|
||||||
|
|
||||||
let token = sha256::digest(format!("{token_start}-{token_end}"));
|
let token = sha256::digest(format!("{token_start}-{token_end}"));
|
||||||
|
|
||||||
match db
|
match db
|
||||||
.fetch_one(
|
.execute(
|
||||||
sqlx::query("UPDATE users SET token = $1 WHERE id = $2")
|
sqlx::query("UPDATE users SET token = $1 WHERE id = $2")
|
||||||
.bind(&token)
|
.bind(&token)
|
||||||
.bind(self.id),
|
.bind(self.id),
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>create user</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="/createuser/css/style.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
create user
|
||||||
|
<form action="/createuser" method="post" class="login-form" id="login-form">
|
||||||
|
<div class="form-test">
|
||||||
|
<label for="name">Username: </label>
|
||||||
|
<input type="text" name="username" id="name" required />
|
||||||
|
</div>
|
||||||
|
<div class="form-test">
|
||||||
|
<label for="pass">Password: </label>
|
||||||
|
<input type="text" name="password" id="password" required />
|
||||||
|
</div>
|
||||||
|
<div class="form-test">
|
||||||
|
<input type="submit" value="Create user" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<button id="logout-button">Log Out</button>
|
||||||
|
</body>
|
||||||
|
<script defer>
|
||||||
|
document.getElementById("login-form").addEventListener("submit", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const username = event.target.username.value;
|
||||||
|
const password = event.target.password.value;
|
||||||
|
|
||||||
|
const token = await fetch("/createuser", {
|
||||||
|
method: "POST",
|
||||||
|
header: {"Content-Type": "application/json"},
|
||||||
|
body: JSON.stringify({username, password})
|
||||||
|
})
|
||||||
|
alert(await token.text());
|
||||||
|
});
|
||||||
|
document.getElementById("logout-button").addEventListener("click", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const loggedout = await fetch("/logout", {
|
||||||
|
method: "POST"
|
||||||
|
});
|
||||||
|
alert(await loggedout.text());
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -26,7 +26,6 @@
|
||||||
<button id="logout-button">Log Out</button>
|
<button id="logout-button">Log Out</button>
|
||||||
</body>
|
</body>
|
||||||
<script defer>
|
<script defer>
|
||||||
console.log("FUCK YEAH JAVASCRIPT TIME BABYYYYYYY");
|
|
||||||
document.getElementById("login-form").addEventListener("submit", async (event) => {
|
document.getElementById("login-form").addEventListener("submit", async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const username = event.target.username.value;
|
const username = event.target.username.value;
|
||||||
|
@ -37,14 +36,14 @@
|
||||||
header: {"Content-Type": "application/json"},
|
header: {"Content-Type": "application/json"},
|
||||||
body: JSON.stringify({username, password})
|
body: JSON.stringify({username, password})
|
||||||
})
|
})
|
||||||
console.log(await token.text());
|
alert(await token.text());
|
||||||
});
|
});
|
||||||
document.getElementById("logout-button").addEventListener("click", async (event) => {
|
document.getElementById("logout-button").addEventListener("click", async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const loggedout = await fetch("/logout", {
|
const loggedout = await fetch("/logout", {
|
||||||
method: "POST"
|
method: "POST"
|
||||||
});
|
});
|
||||||
console.log(await loggedout.text());
|
alert(await loggedout.text());
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue