api fixes

This commit is contained in:
Arlo Filley 2023-09-04 21:01:22 +01:00
parent 5102606f51
commit 39aedf8fcc
8 changed files with 101 additions and 81 deletions

12
TODO.md Normal file
View File

@ -0,0 +1,12 @@
# Backend
-[x] Migrate to sqlx as backend database queryer
-[ ] Allow Private Accounts
-[ ] Secrets to authenicate that its the user
# Frontend
-[ ] Make js api asynchronous
-[ ] Hash passwords on front end so server plaintext password is never sent over the internet
-[ ] Use secret to uniquely identify
-[ ] Scrollbars
-[ ] Change Color Scheme
-[ ] Be able to view others accounts

Binary file not shown.

View File

@ -1 +0,0 @@
- Migrated to sqlx

View File

@ -1,4 +1,4 @@
use rocket::{ serde::json::Json, State, Data }; use rocket::{ serde::json::Json, State };
use crate::typing::sql::{ use crate::typing::sql::{
Database, Database,
LeaderBoardTest LeaderBoardTest

View File

@ -13,6 +13,7 @@
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions}; use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
use rocket::serde::Serialize; use rocket::serde::Serialize;
/// Contains the database connection pool
pub struct Database(SqlitePool); pub struct Database(SqlitePool);
/// gets a connection to the database and returns it as /// gets a connection to the database and returns it as
@ -71,11 +72,11 @@ impl Database {
/// takes a username and password and creates a database /// takes a username and password and creates a database
/// entry for a new user /// entry for a new user
pub async fn create_user(&self, username: &str, password: &str) -> Result<(), sqlx::Error> { pub async fn create_user(&self, username: &str, password: &str, secret: &str) -> Result<(), sqlx::Error> {
sqlx::query!(" sqlx::query!("
INSERT INTO Users (username, password) INSERT INTO Users (username, password, secret)
VALUES (?1, ?2)", VALUES (?1, ?2, ?3)",
username, password username, password, secret
).execute(&self.0).await?; ).execute(&self.0).await?;
Ok(()) Ok(())
@ -87,12 +88,12 @@ impl Database {
let user = sqlx::query!(" let user = sqlx::query!("
SELECT user_id, secret SELECT user_id, secret
FROM Users FROM Users
WHERE username=:username AND password=:password", WHERE username=? AND password=?",
username, password username, password
).fetch_all(&self.0).await?; ).fetch_one(&self.0).await?;
let user_id = user[0].user_id.unwrap() as u32; let user_id = user.user_id.unwrap() as u32;
let secret = user[0].secret.clone(); let secret = user.secret.clone();
Ok(Some((user_id, secret))) Ok(Some((user_id, secret)))
} }
@ -104,10 +105,12 @@ impl Database {
SELECT test_type, test_length, test_time, test_seed, quote_id, wpm, accuracy SELECT test_type, test_length, test_time, test_seed, quote_id, wpm, accuracy
FROM tests FROM tests
INNER JOIN users ON users.user_id = tests.user_id INNER JOIN users ON users.user_id = tests.user_id
WHERE users.user_id=:user_id AND users.secret=:secret", WHERE users.user_id=? AND users.secret=?",
user_id, secret user_id, secret
).fetch_all(&self.0).await?; ).fetch_all(&self.0).await?;
println!("{}", tests.len());
let user_tests = tests.iter() let user_tests = tests.iter()
.map(|test| Test { .map(|test| Test {
test_type: test.test_type.clone(), test_type: test.test_type.clone(),

View File

@ -3,6 +3,8 @@ use rocket::{
State State
}; };
use rand::{ distributions::Alphanumeric, Rng };
use crate::typing::sql::{ Database, Test }; use crate::typing::sql::{ Database, Test };
/// Struct representing the user /// Struct representing the user
@ -18,9 +20,15 @@ pub struct User<'r> {
/// Acessible from http://url/api/create_user /// Acessible from http://url/api/create_user
#[post("/create_user", data = "<user>")] #[post("/create_user", data = "<user>")]
pub async fn sign_up(user: Json<User<'_>>, database: &State<Database>) { pub async fn sign_up(user: Json<User<'_>>, database: &State<Database>) {
match database.create_user( user.username, &sha256::digest(user.password) ).await { let secret: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(50)
.map(char::from)
.collect();
match database.create_user( user.username, &sha256::digest(user.password), &secret ).await {
Err(why) => { println!("A database error occured during signup, {why}"); } Err(why) => { println!("A database error occured during signup, {why}"); }
Ok(()) => { println!("Succesfully Signed up user {}", user.username); } Ok(()) => { println!("Succesfully Signed up User: {}", user.username); }
} }
} }
@ -45,16 +53,16 @@ pub async fn get_tests(user_id: u32, secret: String, database: &State<Database>)
/// which can be used to identify their tests etc. /// which can be used to identify their tests etc.
/// Accessible from http://url/api/login /// Accessible from http://url/api/login
#[get("/login/<username>/<password>")] #[get("/login/<username>/<password>")]
pub async fn login(username: &str, password: &str, database: &State<Database>) -> Option<Json<LoginResponse>> { pub async fn login(username: &str, password: &str, database: &State<Database>) -> Json<Option<LoginResponse>> {
match database.find_user(username, &sha256::digest(password)).await { match database.find_user(username, &sha256::digest(password)).await {
Err(why) => { Err(why) => {
println!("A database error occured during login for {username}, {why}"); println!("A database error occured during login for {username}, {why}");
None Json(None)
} }
Ok(user) => { Ok(user) => {
match user { match user {
None => None, None => Json(None),
Some(user) => { Some(Json(LoginResponse { user_id: user.0, secret: user.1 })) } Some(user) => { Json(Some(LoginResponse { user_id: user.0, secret: user.1 })) }
} }
} }
} }
@ -62,7 +70,7 @@ pub async fn login(username: &str, password: &str, database: &State<Database>) -
#[derive(Serialize)] #[derive(Serialize)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
struct LoginResponse { pub struct LoginResponse {
user_id: u32, user_id: u32,
secret: String, secret: String,
} }

View File

@ -16,7 +16,8 @@
class API { class API {
constructor() { constructor() {
this.url = "https://arlofilley.com/api/"; // this.url = "https://arlofilley.com/api/";
this.url = "../api";
// this is the url of the server // this is the url of the server
// this may have to change later on // this may have to change later on
} }
@ -33,16 +34,7 @@ class API {
* @param {int} accuracy * @param {int} accuracy
* @param {int} userId * @param {int} userId
*/ */
postTest( postTest(pTestType, pTestLength, pTestTime, pTestSeed, pQuoteId, pWpm, pAccuracy, pUserId) {
pTestType,
pTestLength,
pTestTime,
pTestSeed,
pQuoteId,
pWpm,
pAccuracy,
pUserId
) {
const data = { const data = {
'test_type': pTestType, 'test_type': pTestType,
'test_length': pTestLength, 'test_length': pTestLength,
@ -57,7 +49,7 @@ class API {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open( xhr.open(
"POST", "POST",
this.url+"post_test" `${this.url}/post_test/`
); );
xhr.send( xhr.send(
@ -174,16 +166,7 @@ class API {
// there will be other tests here in later iterations but for now these tests should suffice // there will be other tests here in later iterations but for now these tests should suffice
this.postTest( this.postTest(testType, testLength, testTime, testSeed, quoteId, wpm, accuracy, userId);
testType,
testLength,
testTime,
testSeed,
quoteId,
wpm,
accuracy,
userId
);
} }
/** /**
@ -194,59 +177,73 @@ class API {
* @param {String} password * @param {String} password
* @returns * @returns
*/ */
createUser( createUser( username, password ) {
username, console.log( username, password );
password
) {
console.log(username, password);
const user = { const user = {
username: username, username: username,
password: password password: password
}; };
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open( xhr.open( "POST", `${this.url}/create_user/` );
"POST",
`${this.url}create_user/`
);
xhr.send( xhr.send( JSON.stringify(user) );
JSON.stringify(user)
);
xhr.onload = () => { xhr.onload = () => {
if (xhr.status === 500) { if (xhr.status === 500) {
alert("Sorry, looks like your username isn't unique"); alert("Sorry, looks like your username isn't unique");
console.error("Sorry, looks like your username isn't unique")
} else { } else {
this.login(username,password); this.login(username, password);
} }
}; };
} }
login(pUsername, pPassword) {
if (localStorage.userId === null || localStorage.userId === 0 || localStorage.userId === undefined) { /**
let xhr = new XMLHttpRequest(); * takes a validated name and password and sends
xhr.open('GET', `${this.url}login/${pUsername}/${pPassword}`); * a post request to make a user with the given
xhr.send(); * username and password
xhr.onload = () => { * @param {String} username
user.userId = Number(xhr.response); * @param {String} password
if (user.userId > 0) { * @param {boolean} initial
user.username = pUsername * @returns
localStorage.setItem("userId", user.userId); */
localStorage.setItem("username", pUsername); login(pUsername, pPassword, initial = false) {
localStorage.setItem("password", pPassword); // Variable Validation
} else { if (pUsername == undefined || pPassword == undefined) {
user.username = "no one"; return
user.password = "none";
user.userId = 0;
user.tests = [];
}
};
} else if (localStorage.userId > 0) {
user.userId = localStorage.userId;
user.username = localStorage.username;
user.password = localStorage.password;
} }
// If Local Storage has the information we need there is no need to make a request to the server
if (localStorage.getItem("username") == pUsername) {
user.userId = localStorage.getItem("userId");
user.secret = localStorage.getItem("secret");
user.username = localStorage.getItem("username");
return
}
let xhr = new XMLHttpRequest();
xhr.open('GET', `${this.url}/login/${pUsername}/${pPassword}`);
xhr.send();
xhr.onload = () => {
let response = JSON.parse(xhr.response);
// If there is an error with the login we need
if (xhr.response == null) {
alert("Error Logging in, maybe check your password");
return
}
user.userId = response.user_id;
user.username = pUsername
user.secret = response.secret;
localStorage.setItem("userId", user.userId);
localStorage.setItem("username", pUsername);
localStorage.setItem("secret", user.secret);
};
} }
logout() { logout() {
@ -265,8 +262,8 @@ class API {
return; return;
} }
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
let userId = Number(user.userId);
xhr.open('GET', `${this.url}get_user_tests/${userId}/`); xhr.open('GET', `${this.url}/get_tests/${user.userId}/${user.secret}`);
xhr.send(); xhr.send();
xhr.onload = () => { xhr.onload = () => {
user.tests = JSON.parse(xhr.response); user.tests = JSON.parse(xhr.response);
@ -275,7 +272,7 @@ class API {
getLeaderBoard() { getLeaderBoard() {
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.open('GET', `${this.url}leaderboard/`); xhr.open('GET', `${this.url}/leaderboard/`);
xhr.send(); xhr.send();
xhr.onload = () => { xhr.onload = () => {
user.leaderboard = JSON.parse(xhr.response); user.leaderboard = JSON.parse(xhr.response);
@ -284,7 +281,7 @@ class API {
getTest() { getTest() {
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.open('GET', `${this.url}new_test/`); xhr.open('GET', `${this.url}/new_test/`);
xhr.send(); xhr.send();
xhr.onload = () =>{ xhr.onload = () =>{
const effectiveWidth = (windowWidth - 200) / 13; const effectiveWidth = (windowWidth - 200) / 13;

View File

@ -16,8 +16,9 @@
class User { class User {
constructor() { constructor() {
this.username = "not logged in"; this.username = "not logged in";
this.password = "there";
this.userId = 0; this.userId = 0;
this.secret = "";
this.leaderboard; this.leaderboard;
this.time = 15; this.time = 15;