new catchers for 404 errors
This commit is contained in:
parent
4a57128a27
commit
45e7887a7e
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"rust-analyzer.showUnlinkedFileNotification": false
|
||||
}
|
26
documentation/not_found.html
Normal file
26
documentation/not_found.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="github-markdown.css">
|
||||
</head>
|
||||
<body class="markdown-body">
|
||||
<style>
|
||||
.markdown-body {
|
||||
box-sizing: border-box;
|
||||
min-width: 200px;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
padding: 45px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.markdown-body {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<h1>The content you are looking for does not appear to be here...</h1>
|
||||
<a href="/api/documentation/"><button>main page</button></a>
|
||||
</body>
|
||||
</html>
|
12
public/Error/not_found.html
Normal file
12
public/Error/not_found.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Not Found</title>
|
||||
</head>
|
||||
<body >
|
||||
The content you are looking for does not appear to be here...
|
||||
<a href="/typing/"><button>main page</button></a>
|
||||
</body>
|
||||
</html>
|
@ -1,8 +1,5 @@
|
||||
use rocket::{ serde::json::Json, State };
|
||||
use crate::typing::sql::{
|
||||
Database,
|
||||
LeaderBoardTest
|
||||
};
|
||||
use crate::api::sql::{Database, LeaderBoardTest};
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
type LeaderBoardTests = Vec<LeaderBoardTest>;
|
||||
|
||||
@ -10,15 +7,14 @@ type LeaderBoardTests = Vec<LeaderBoardTest>;
|
||||
/// a json array
|
||||
/// Acessible from http://url/api/leaderboard
|
||||
#[get("/leaderboard")]
|
||||
pub async fn leaderboard( database: &State<Database> ) -> Option<Json<LeaderBoardTests>> {
|
||||
pub async fn leaderboard(database: &State<Database>) -> Option<Json<LeaderBoardTests>> {
|
||||
let leaderboard = match database.get_leaderboard(0).await {
|
||||
Err(why) => {
|
||||
Err(why) => {
|
||||
println!("Error getting leaderboard, {why}");
|
||||
return None
|
||||
return None;
|
||||
}
|
||||
Ok(leaderboard) => { leaderboard }
|
||||
Ok(leaderboard) => leaderboard,
|
||||
};
|
||||
|
||||
Some(Json(leaderboard))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub mod sql;
|
||||
pub mod user;
|
||||
pub mod leaderboard;
|
||||
pub mod test;
|
||||
pub mod sql;
|
||||
pub mod test;
|
||||
pub mod user;
|
@ -4,16 +4,16 @@
|
||||
//! it abstracts away the rusqlite necessary to perform
|
||||
//! these functions
|
||||
//! Author: Arlo Filley
|
||||
//!
|
||||
//! TODO:
|
||||
//!
|
||||
//! TODO:
|
||||
//! - put necessary structs into a different file
|
||||
//! - create structure for the input of post test
|
||||
//! - create structure for the input of post test
|
||||
|
||||
// Imports for json handling and rusqlite
|
||||
use sqlx::sqlite::{ SqlitePool, SqlitePoolOptions };
|
||||
use rocket::serde::{ Serialize, json::Json };
|
||||
use rocket::serde::{json::Json, Serialize};
|
||||
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
|
||||
|
||||
use crate::typing::test::PostTest;
|
||||
use crate::api::test::PostTest;
|
||||
|
||||
/// Contains the database connection pool
|
||||
pub struct Database(SqlitePool);
|
||||
@ -24,7 +24,7 @@ impl Database {
|
||||
pub async fn new() -> Result<Self, sqlx::Error> {
|
||||
let pool = SqlitePoolOptions::new()
|
||||
.max_connections(2)
|
||||
.connect("sqlite:/Users/arlo/code/cs_coursework/database/dev/database.sqlite")
|
||||
.connect("sqlite:/Users/arlo/Code/Projects/cs_coursework/database/database.sqlite")
|
||||
.await?;
|
||||
|
||||
Ok(Self(pool))
|
||||
@ -33,16 +33,20 @@ impl Database {
|
||||
/// Creates the necessary tables inside the database with
|
||||
/// correct normalised links between data for later querying
|
||||
pub async fn _new_database(&self) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("
|
||||
sqlx::query!(
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS Users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
secret TEXT NOT NULL
|
||||
)"
|
||||
).execute(&self.0).await?;
|
||||
)
|
||||
.execute(&self.0)
|
||||
.await?;
|
||||
|
||||
sqlx::query!("
|
||||
sqlx::query!(
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS Tests (
|
||||
test_id INTEGER PRIMARY KEY,
|
||||
test_type TEXT NOT NULL,
|
||||
@ -55,7 +59,9 @@ impl Database {
|
||||
user_id INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(user_id)
|
||||
)"
|
||||
).execute(&self.0).await?;
|
||||
)
|
||||
.execute(&self.0)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -64,15 +70,18 @@ impl Database {
|
||||
/// a database record with the data
|
||||
pub async fn create_test(&self, test: Json<PostTest<'_>>) -> Result<(), sqlx::Error> {
|
||||
// Test to see whether the secret is correct
|
||||
let user = sqlx::query!("
|
||||
let user = sqlx::query!(
|
||||
"
|
||||
Select secret
|
||||
From Users
|
||||
Where user_id=?",
|
||||
Where user_id=?",
|
||||
test.user_id
|
||||
).fetch_one(&self.0).await?;
|
||||
)
|
||||
.fetch_one(&self.0)
|
||||
.await?;
|
||||
|
||||
if user.secret != test.secret {
|
||||
return Err(sqlx::Error::RowNotFound)
|
||||
return Err(sqlx::Error::RowNotFound);
|
||||
}
|
||||
|
||||
sqlx::query!("
|
||||
@ -86,25 +95,43 @@ 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, secret: &str) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("
|
||||
pub async fn create_user(
|
||||
&self,
|
||||
username: &str,
|
||||
password: &str,
|
||||
secret: &str,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO Users (username, password, secret)
|
||||
VALUES (?1, ?2, ?3)",
|
||||
username, password, secret
|
||||
).execute(&self.0).await?;
|
||||
VALUES (?1, ?2, ?3)",
|
||||
username,
|
||||
password,
|
||||
secret
|
||||
)
|
||||
.execute(&self.0)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// takes a username and password as inputs and returns the
|
||||
/// user_id and secret of the user if one exists
|
||||
pub async fn find_user(&self, username: &str, password: &str) -> Result<Option<(u32, String)>, sqlx::Error> {
|
||||
let user = sqlx::query!("
|
||||
pub async fn find_user(
|
||||
&self,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Result<Option<(u32, String)>, sqlx::Error> {
|
||||
let user = sqlx::query!(
|
||||
"
|
||||
SELECT user_id, secret
|
||||
FROM Users
|
||||
WHERE username=? AND password=?",
|
||||
username, password
|
||||
).fetch_one(&self.0).await?;
|
||||
username,
|
||||
password
|
||||
)
|
||||
.fetch_one(&self.0)
|
||||
.await?;
|
||||
|
||||
let user_id = user.user_id.unwrap() as u32;
|
||||
let secret = user.secret.clone();
|
||||
@ -114,51 +141,111 @@ impl Database {
|
||||
|
||||
/// returns all the tests that a given user_id has
|
||||
/// completed from the database
|
||||
pub async fn get_user_tests(&self, user_id: u32, secret: &str) -> Result<Vec<Test>, sqlx::Error> {
|
||||
let tests = sqlx::query!("
|
||||
pub async fn get_user_tests(
|
||||
&self,
|
||||
user_id: u32,
|
||||
secret: &str,
|
||||
) -> Result<Vec<Test>, sqlx::Error> {
|
||||
let tests = sqlx::query!(
|
||||
"
|
||||
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=? AND users.secret=?",
|
||||
user_id, secret
|
||||
).fetch_all(&self.0).await?;
|
||||
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(),
|
||||
test_length: test.test_length.unwrap() as u32,
|
||||
test_time: test.test_time.unwrap() as u32,
|
||||
test_seed: test.test_seed.unwrap(),
|
||||
quote_id: test.quote_id.unwrap() as i32,
|
||||
wpm: test.wpm.unwrap() as u8,
|
||||
accuracy: test.accuracy.unwrap() as u8
|
||||
let user_tests = tests
|
||||
.iter()
|
||||
.map(|test| Test {
|
||||
test_type: test.test_type.clone(),
|
||||
test_length: test.test_length.unwrap() as u32,
|
||||
test_time: test.test_time.unwrap() as u32,
|
||||
test_seed: test.test_seed.unwrap(),
|
||||
quote_id: test.quote_id.unwrap() as i32,
|
||||
wpm: test.wpm.unwrap() as u8,
|
||||
accuracy: test.accuracy.unwrap() as u8,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(user_tests)
|
||||
}
|
||||
|
||||
/// returns a vector of leaderboard tests, where each one is the fastest words
|
||||
/// per minute that a given user has achieved
|
||||
pub async fn get_leaderboard(&self, _user_id: u32) -> Result<Vec<LeaderBoardTest>, sqlx::Error> {
|
||||
pub async fn get_leaderboard(
|
||||
&self,
|
||||
_user_id: u32,
|
||||
) -> Result<Vec<LeaderBoardTest>, sqlx::Error> {
|
||||
let tests = sqlx::query!(
|
||||
"SELECT users.username, tests.wpm
|
||||
FROM tests
|
||||
INNER JOIN users ON users.user_id = tests.user_id
|
||||
GROUP BY users.username
|
||||
ORDER BY tests.wpm DESC",
|
||||
).fetch_all(&self.0).await?;
|
||||
)
|
||||
.fetch_all(&self.0)
|
||||
.await?;
|
||||
|
||||
let leaderboard_tests = tests.iter()
|
||||
.map(|test| LeaderBoardTest {
|
||||
username: test.username.clone(),
|
||||
wpm: test.wpm.unwrap() as u8
|
||||
let leaderboard_tests = tests
|
||||
.iter()
|
||||
.map(|test| LeaderBoardTest {
|
||||
username: test.username.clone(),
|
||||
wpm: test.wpm.unwrap() as u8,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(leaderboard_tests)
|
||||
}
|
||||
|
||||
/// Authenticates a user based on their user ID and secret.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `user_id` - The ID of the user to authenticate.
|
||||
/// * `secret` - The secret associated with the user.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a `Result` indicating whether the authentication was successful or not.
|
||||
/// - `Ok(true)` if authentication is successful.
|
||||
/// - `Ok(false)` if authentication fails.
|
||||
/// - `Err` if there's an error accessing the database.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use crate::sql::Database;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let db = Database::new().await.expect("Failed to create database connection");
|
||||
///
|
||||
/// // Authenticate user with user ID 123 and secret "example_secret"
|
||||
/// let authenticated = db.authenticate_user(123, "example_secret").await;
|
||||
/// assert_eq!(authenticated, Ok(true));
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn authenticate_user(&self, user_id: u32, secret: &str) -> Result<bool, sqlx::Error> {
|
||||
// Test to see whether the secret is correct
|
||||
let user = sqlx::query!(
|
||||
"
|
||||
Select secret
|
||||
From Users
|
||||
Where user_id=?",
|
||||
user_id
|
||||
)
|
||||
.fetch_one(&self.0)
|
||||
.await?;
|
||||
|
||||
// Compare the fetched secret with the provided one
|
||||
Ok(user.secret == secret)
|
||||
}
|
||||
}
|
||||
|
||||
/// struct representing data that needs to be sent
|
||||
@ -182,4 +269,4 @@ pub struct Test {
|
||||
pub struct LeaderBoardTest {
|
||||
username: String,
|
||||
wpm: u8,
|
||||
}
|
||||
}
|
@ -1,16 +1,10 @@
|
||||
use crate::typing::sql::Database;
|
||||
use crate::api::sql::Database;
|
||||
use rand::{rngs::ThreadRng, Rng};
|
||||
use rocket::{
|
||||
serde::{ Deserialize, json::Json },
|
||||
State
|
||||
};
|
||||
use rand::{
|
||||
Rng,
|
||||
rngs::ThreadRng
|
||||
};
|
||||
use std::{
|
||||
fs,
|
||||
vec,
|
||||
serde::{json::Json, Deserialize},
|
||||
State,
|
||||
};
|
||||
use std::{fs, vec};
|
||||
|
||||
/// the datascructure that the webserver will recieve
|
||||
/// when a post is made to the http://url/api/post_test route
|
||||
@ -25,7 +19,7 @@ pub struct PostTest<'r> {
|
||||
pub wpm: u8,
|
||||
pub accuracy: u8,
|
||||
pub user_id: u32,
|
||||
pub secret: &'r str
|
||||
pub secret: &'r str,
|
||||
}
|
||||
|
||||
/// Api Route that accepts test data and posts it to the database
|
||||
@ -34,8 +28,12 @@ pub struct PostTest<'r> {
|
||||
pub async fn create_test(test: Json<PostTest<'_>>, database: &State<Database>) {
|
||||
let user_id = test.user_id;
|
||||
match database.create_test(test).await {
|
||||
Err(why) => { println!("A database error occured creating a test, {why}"); }
|
||||
Ok(()) => { println!("Successfully created test for {user_id}"); }
|
||||
Err(why) => {
|
||||
println!("A database error occured creating a test, {why}");
|
||||
}
|
||||
Ok(()) => {
|
||||
println!("Successfully created test for {user_id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +41,7 @@ pub async fn create_test(test: Json<PostTest<'_>>, database: &State<Database>) {
|
||||
/// Accessible from http://url/api/get_test
|
||||
#[get("/new_test")]
|
||||
pub fn new_test() -> Json<Vec<String>> {
|
||||
let mut word_vec: Vec<&str> = vec![];
|
||||
let mut word_vec: Vec<&str> = vec![];
|
||||
let words: String = fs::read_to_string("wordlist.txt").unwrap();
|
||||
for word in words.split('\n') {
|
||||
word_vec.push(word);
|
||||
@ -58,4 +56,4 @@ pub fn new_test() -> Json<Vec<String>> {
|
||||
}
|
||||
|
||||
Json(return_list.clone())
|
||||
}
|
||||
}
|
148
src/api/user.rs
Normal file
148
src/api/user.rs
Normal file
@ -0,0 +1,148 @@
|
||||
use rocket::{
|
||||
http::Status,
|
||||
serde::{json::Json, Deserialize, Serialize},
|
||||
State,
|
||||
};
|
||||
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
|
||||
use crate::api::sql::{Database, Test};
|
||||
|
||||
/// Struct representing the user
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct User<'r> {
|
||||
username: &'r str,
|
||||
password: &'r str,
|
||||
}
|
||||
|
||||
/// Route takes data about the user as a struct
|
||||
/// and then creates the user in the database
|
||||
/// Acessible from http://url/api/create_user
|
||||
#[post("/create_user", data = "<user>")]
|
||||
pub async fn sign_up(user: Json<User<'_>>, database: &State<Database>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves tests associated with a specific user from the database and returns them as a JSON array.
|
||||
///
|
||||
/// # Endpoint
|
||||
///
|
||||
/// GET /api/get_tests/<user_id>/<secret>
|
||||
///
|
||||
/// # Path Parameters
|
||||
///
|
||||
/// - `user_id`: User ID of the user whose tests need to be retrieved.
|
||||
/// - `secret`: Secret key for authentication.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a JSON array containing the user's tests if the user is authenticated and the tests are found.
|
||||
///
|
||||
/// If the user authentication fails, returns a `401 Unauthorized` status.
|
||||
///
|
||||
/// If the tests are not found or any database-related error occurs, returns a `404 Not Found` status.
|
||||
///
|
||||
/// # Example Request
|
||||
///
|
||||
/// ```bash
|
||||
/// curl -X GET "https://example.com/api/get_tests/123/your_secret_key_here"
|
||||
/// ```
|
||||
///
|
||||
/// # Example Response
|
||||
///
|
||||
/// ```json
|
||||
/// [
|
||||
/// {
|
||||
/// "test_type": "typing",
|
||||
/// "test_length": 100,
|
||||
/// "test_time": 300,
|
||||
/// "test_seed": 987654321,
|
||||
/// "quote_id": 123,
|
||||
/// "wpm": 65,
|
||||
/// "accuracy": 98
|
||||
/// },
|
||||
/// {
|
||||
/// "test_type": "multiple_choice",
|
||||
/// "test_length": 50,
|
||||
/// "test_time": 150,
|
||||
/// "test_seed": 123456789,
|
||||
/// "quote_id": null,
|
||||
/// "wpm": null,
|
||||
/// "accuracy": 85
|
||||
/// }
|
||||
/// ]
|
||||
/// ```
|
||||
|
||||
#[get("/get_tests/<user_id>/<secret>")]
|
||||
pub async fn get_tests(user_id: u32, secret: &str, database: &State<Database>) -> Result<Json<Vec<Test>>, Status> {
|
||||
match database.authenticate_user(user_id, &secret).await {
|
||||
Err(_) => return Err(Status::InternalServerError),
|
||||
Ok(authenticated) => {
|
||||
if !authenticated {
|
||||
return Err(Status::Unauthorized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match database.get_user_tests(user_id, &secret).await {
|
||||
Err(why) => {
|
||||
println!("A database error occured during getting_tests, {why}");
|
||||
Err(Status::NotFound)
|
||||
}
|
||||
Ok(tests) => {
|
||||
println!("Succesfully Found Tests for User {user_id}");
|
||||
Ok(Json(tests))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// takes the users login information and returns the users user id
|
||||
/// 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>,
|
||||
) -> Json<Option<LoginResponse>> {
|
||||
match database
|
||||
.find_user(username, &sha256::digest(password))
|
||||
.await
|
||||
{
|
||||
Err(why) => {
|
||||
println!("A database error occured during login for {username}, {why}");
|
||||
Json(None)
|
||||
}
|
||||
Ok(user) => match user {
|
||||
None => Json(None),
|
||||
Some(user) => Json(Some(LoginResponse {
|
||||
user_id: user.0,
|
||||
secret: user.1,
|
||||
})),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct LoginResponse {
|
||||
user_id: u32,
|
||||
secret: String,
|
||||
}
|
1
src/catchers/mod.rs
Normal file
1
src/catchers/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod not_found;
|
16
src/catchers/not_found.rs
Normal file
16
src/catchers/not_found.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use rocket::{response::content::RawHtml, Request};
|
||||
|
||||
#[catch(404)]
|
||||
pub fn api_not_found(req: &Request) -> String {
|
||||
format!("Sorry, '{}' couldn't be found.", req.uri())
|
||||
}
|
||||
|
||||
#[catch(404)]
|
||||
pub fn frontend_not_found<'a>(_req: &Request) -> RawHtml<&'a str> {
|
||||
RawHtml(include_str!("../../public/Error/not_found.html"))
|
||||
}
|
||||
|
||||
#[catch(404)]
|
||||
pub fn documentation_not_found<'a>(_req: &Request) -> RawHtml<&'a str> {
|
||||
RawHtml(include_str!("../../documentation/not_found.html"))
|
||||
}
|
23
src/main.rs
23
src/main.rs
@ -40,12 +40,17 @@ use rocket::{
|
||||
Build, Rocket,
|
||||
};
|
||||
|
||||
mod typing;
|
||||
mod api;
|
||||
mod catchers;
|
||||
|
||||
use crate::typing::leaderboard::leaderboard;
|
||||
use crate::typing::sql::Database;
|
||||
use crate::typing::test::{create_test, new_test};
|
||||
use crate::typing::user::{get_tests, login, sign_up};
|
||||
use crate::api::leaderboard::leaderboard;
|
||||
use crate::api::sql::Database;
|
||||
use crate::api::test::{create_test, new_test};
|
||||
use crate::api::user::{get_tests, login, sign_up};
|
||||
|
||||
use catchers::not_found::api_not_found;
|
||||
use catchers::not_found::frontend_not_found;
|
||||
use catchers::not_found::documentation_not_found;
|
||||
|
||||
// Imports for sql, see sql.rs for more information
|
||||
|
||||
@ -66,6 +71,8 @@ async fn rocket() -> Rocket<Build> {
|
||||
.mount("/test", routes![test])
|
||||
// hosts the api routes necessary for the website
|
||||
// to interact with the database
|
||||
.mount("/api/documentation", FileServer::from(relative!("documentation")))
|
||||
.register("/api/documentation", catchers![documentation_not_found])
|
||||
.mount(
|
||||
"/api",
|
||||
routes![
|
||||
@ -77,7 +84,11 @@ async fn rocket() -> Rocket<Build> {
|
||||
new_test,
|
||||
],
|
||||
)
|
||||
.register("/api", catchers![api_not_found])
|
||||
|
||||
|
||||
// hosts the fileserver
|
||||
.mount("/typing", FileServer::from(relative!("websites/Typing")))
|
||||
.mount("/typing", FileServer::from(relative!("public")))
|
||||
.register("/typing", catchers![frontend_not_found])
|
||||
.manage(Database::new().await.unwrap())
|
||||
}
|
||||
|
@ -1,76 +0,0 @@
|
||||
use rocket::{
|
||||
serde::{Deserialize, json::Json, Serialize},
|
||||
State
|
||||
};
|
||||
|
||||
use rand::{ distributions::Alphanumeric, Rng };
|
||||
|
||||
use crate::typing::sql::{ Database, Test };
|
||||
|
||||
/// Struct representing the user
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct User<'r> {
|
||||
username: &'r str,
|
||||
password: &'r str
|
||||
}
|
||||
|
||||
/// Route takes data about the user as a struct
|
||||
/// and then creates the user in the database
|
||||
/// Acessible from http://url/api/create_user
|
||||
#[post("/create_user", data = "<user>")]
|
||||
pub async fn sign_up(user: Json<User<'_>>, database: &State<Database>) {
|
||||
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); }
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the users tests from the database and returns it as a
|
||||
/// json array.
|
||||
/// Accessible from http://url/api/get_user_tests
|
||||
#[get("/get_tests/<user_id>/<secret>")]
|
||||
pub async fn get_tests(user_id: u32, secret: String, database: &State<Database>) -> Option<Json<Vec<Test>>> {
|
||||
match database.get_user_tests(user_id, &secret).await {
|
||||
Err(why) => {
|
||||
println!("A database error occured during getting_tests, {why}");
|
||||
None
|
||||
}
|
||||
Ok(tests) => {
|
||||
println!("Succesfully Found Tests for User {user_id}");
|
||||
Some(Json(tests))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// takes the users login information and returns the users user id
|
||||
/// 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>) -> Json<Option<LoginResponse>> {
|
||||
match database.find_user(username, &sha256::digest(password)).await {
|
||||
Err(why) => {
|
||||
println!("A database error occured during login for {username}, {why}");
|
||||
Json(None)
|
||||
}
|
||||
Ok(user) => {
|
||||
match user {
|
||||
None => Json(None),
|
||||
Some(user) => { Json(Some(LoginResponse { user_id: user.0, secret: user.1 })) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct LoginResponse {
|
||||
user_id: u32,
|
||||
secret: String,
|
||||
}
|
Loading…
Reference in New Issue
Block a user