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::{
Database,
LeaderBoardTest

View File

@ -13,6 +13,7 @@
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
use rocket::serde::Serialize;
/// Contains the database connection pool
pub struct Database(SqlitePool);
/// 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
/// 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!("
INSERT INTO Users (username, password)
VALUES (?1, ?2)",
username, password
INSERT INTO Users (username, password, secret)
VALUES (?1, ?2, ?3)",
username, password, secret
).execute(&self.0).await?;
Ok(())
@ -87,12 +88,12 @@ impl Database {
let user = sqlx::query!("
SELECT user_id, secret
FROM Users
WHERE username=:username AND password=:password",
WHERE username=? AND password=?",
username, password
).fetch_all(&self.0).await?;
).fetch_one(&self.0).await?;
let user_id = user[0].user_id.unwrap() as u32;
let secret = user[0].secret.clone();
let user_id = user.user_id.unwrap() as u32;
let secret = user.secret.clone();
Ok(Some((user_id, secret)))
}
@ -104,10 +105,12 @@ impl Database {
SELECT test_type, test_length, test_time, test_seed, quote_id, wpm, accuracy
FROM tests
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
).fetch_all(&self.0).await?;
println!("{}", tests.len());
let user_tests = tests.iter()
.map(|test| Test {
test_type: test.test_type.clone(),

View File

@ -3,6 +3,8 @@ use rocket::{
State
};
use rand::{ distributions::Alphanumeric, Rng };
use crate::typing::sql::{ Database, Test };
/// Struct representing the user
@ -18,9 +20,15 @@ pub struct User<'r> {
/// Acessible from http://url/api/create_user
#[post("/create_user", data = "<user>")]
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}"); }
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.
/// Accessible from http://url/api/login
#[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 {
Err(why) => {
println!("A database error occured during login for {username}, {why}");
None
Json(None)
}
Ok(user) => {
match user {
None => None,
Some(user) => { Some(Json(LoginResponse { user_id: user.0, secret: user.1 })) }
None => Json(None),
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)]
#[serde(crate = "rocket::serde")]
struct LoginResponse {
pub struct LoginResponse {
user_id: u32,
secret: String,
}

View File

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

View File

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