-```
-
-## Parameters
-
-| Parameter | Type | Description |
-| - | - | - |
-| user_id | integer | User ID of the user whose tests need to be retrieved |
-| secret | String | Secret key for authentication |
+- `user_id` - User ID of the user whose tests need to be retrieved
+- `secret` - Secret key for authentication
## Example Request
```bash
-curl -X GET "https://example.com/api/get_tests/123/your_secret_key_here"
+curl -X GET "https://url/api/user/tests/123/your_secret_key_here"
```
## Example Response
```json
-{
- "test_type": "words",
- "test_length": 100,
- "test_time": 300,
- "test_seed": 987654321,
- "quote_id": 123,
- "wpm": 65,
- "accuracy": 98
-}
+[
+ {
+ "test_type": "words",
+ "test_length": 300,
+ "test_time": 60,
+ "test_seed": 0,
+ "quote_id": 0,
+ "wpm": 60,
+ "accuracy": 100
+ },
+ {
+ "test_type": "words",
+ "test_length": 47,
+ "test_time": 15,
+ "test_seed": 0,
+ "quote_id": 0,
+ "wpm": 37,
+ "accuracy": 98
+ },
+ ...
+]
```
\ No newline at end of file
diff --git a/documentation/html/get_leaderboard.html b/documentation/html/get_leaderboard.html
new file mode 100644
index 0000000..bc50b73
--- /dev/null
+++ b/documentation/html/get_leaderboard.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+Returns the highest test data from each user as a JSON array. The data includes metrics such as username, words per minute (WPM), accuracy percentage, the time taken for the test, and the length of the test for a comprehensive overview of user performance.
+
+
+
+username
: The name of the user.
+
+wpm
: Words per minute, indicating the typing speed.
+
+accuracy
: The accuracy of the user's typing, in percentage.
+
+test_time
: The total time taken to complete the test, in seconds.
+
+test_length
: The length of the test, typically measured in number of words.
+
+
+
+
+200 OK
: Successfully retrieves the leaderboard data.
+
+404 Not Found
: Indicates that the leaderboard was not found.
+
+500 Internal Server Error
: Indicates an issue with accessing the database.
+
+
+curl -X GET " https://url/api/leaderboard"
+
+[
+ {
+ "username" : " user1" ,
+ "wpm" : 75 ,
+ "accuracy" : 97 ,
+ "test_time" : 120 ,
+ "test_length" : 250
+ },
+ {
+ "username" : " user2" ,
+ "wpm" : 73 ,
+ "accuracy" : 95 ,
+ "test_time" : 115 ,
+ "test_length" : 240
+ }
+]
+
+
+
diff --git a/documentation/html/get_ping.html b/documentation/html/get_ping.html
new file mode 100644
index 0000000..ee1f0a4
--- /dev/null
+++ b/documentation/html/get_ping.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+Shows whether the api is up and running. Accessible from http://url/api/ping
+
+
+
+200 OK
: The api is up and running
+
+
+curl -X GET " https://url/api/ping
+
+"Hello World! I'm A rocket Webserver"
+
+
+
+
diff --git a/documentation/html/get_test.html b/documentation/html/get_test.html
new file mode 100644
index 0000000..a072a0b
--- /dev/null
+++ b/documentation/html/get_test.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+Returns 100 random words to be used to give a typing test. Retrieved from wordlist.txt
+
+
+
+200 OK
: Successfully retrieves the 100 words.
+
+404 Not Found
: Indicates that the wordlist was not found.
+
+
+curl -X GET " https://url/api/test"
+
+[
+ " separate" ,
+ " stand" ,
+ " think" ,
+ " island" ,
+ " have" ,
+ " air" ,
+ " heard" ,
+ " notice" ,
+ " yellow" ,
+ " smell" ,
+ " heart" ,
+ " island" ,
+ " chief" ,
+ " view" ,
+ " top" ,
+ ...
+]
+
+
+
diff --git a/documentation/html/get_user_login.html b/documentation/html/get_user_login.html
new file mode 100644
index 0000000..cc0bf98
--- /dev/null
+++ b/documentation/html/get_user_login.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+GET /api/user/login/<username>/<password>
+
+Takes the user's login information and returns the user's user ID, which can be used to identify their tests, etc. Accessible from http://url/api/login .
+
+
+
+username
: The username of the user.
+
+password
: The password of the user.
+
+
+
+
+200 OK
: Successfully retrieves the user ID.
+
+401 Unauthorized
: Indicates that the login credentials are invalid.
+
+500 Internal Server Error
: Indicates an issue with accessing the database.
+
+
+curl -X GET " https://url/api/user/login/example_user/example_password"
+
+{
+ "user_id" : " 1234567890" ,
+ "secret" : " abcdefghijklmnopqrstuvwxyz"
+}
+
+
+
diff --git a/documentation/html/get_user_test.html b/documentation/html/get_user_test.html
new file mode 100644
index 0000000..c67b85e
--- /dev/null
+++ b/documentation/html/get_user_test.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+GET /api/user/test/<user_id>/<secret>
+Retrieves tests associated with a specific user from the database.
+
+
+
+user_id
- User ID of the user whose tests need to be retrieved
+
+secret
- Secret key for authentication
+
+
+curl -X GET " https://url/api/user/test/123/your_secret_key_here"
+
+{
+ "test_type" : " words" ,
+ "test_length" : 100 ,
+ "test_time" : 300 ,
+ "test_seed" : 987654321 ,
+ "quote_id" : 123 ,
+ "wpm" : 65 ,
+ "accuracy" : 98
+}
+
+
+
diff --git a/documentation/html/get_user_tests.html b/documentation/html/get_user_tests.html
new file mode 100644
index 0000000..022bc28
--- /dev/null
+++ b/documentation/html/get_user_tests.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+GET /api/user/tests/<user_id>/<secret>
+
+Retrieves tests associated with a specific user from the database.
+
+
+
+user_id
- User ID of the user whose tests need to be retrieved
+
+secret
- Secret key for authentication
+
+
+curl -X GET " https://url/api/user/tests/123/your_secret_key_here"
+
+[
+ {
+ "test_type" : " words" ,
+ "test_length" : 300 ,
+ "test_time" : 60 ,
+ "test_seed" : 0 ,
+ "quote_id" : 0 ,
+ "wpm" : 60 ,
+ "accuracy" : 100
+ },
+ {
+ "test_type" : " words" ,
+ "test_length" : 47 ,
+ "test_time" : 15 ,
+ "test_seed" : 0 ,
+ "quote_id" : 0 ,
+ "wpm" : 37 ,
+ "accuracy" : 98
+ },
+ ...
+]
+
+
+
diff --git a/documentation/github-markdown.css b/documentation/html/github-markdown.css
similarity index 100%
rename from documentation/github-markdown.css
rename to documentation/html/github-markdown.css
diff --git a/documentation/html/index.html b/documentation/html/index.html
new file mode 100644
index 0000000..7b5f522
--- /dev/null
+++ b/documentation/html/index.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/documentation/html/post_user_create.html b/documentation/html/post_user_create.html
new file mode 100644
index 0000000..75695bd
--- /dev/null
+++ b/documentation/html/post_user_create.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+Creates a new user in the database.
+
+{
+ "username" : " example_user" ,
+ "password" : " example_password"
+}
+
+curl -X POST " https://url/api/user/create" \
+-H " Content-Type: application/json" \
+-d ' {"username": "example_user", "password": "example_password"}'
+
+
+
diff --git a/documentation/html/post_user_test.html b/documentation/html/post_user_test.html
new file mode 100644
index 0000000..d580b5f
--- /dev/null
+++ b/documentation/html/post_user_test.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+Post Test Data This API endpoint allows you to post test data, recording the results of a test taken by a user.
+
+
+
+testType
- Type of the test ("words", "time", "quote", etc)
+
+testLength
- Length of the test in number of items or
+
+testTime
- Duration of the test in seconds
+
+testSeed
- Seed for generating randomized test content
+
+quoteId
- Identifier for a specific quote, if applicable
+
+wpm
- Words per minute (typing speed)
+
+accuracy
- Accuracy of responses (e.g., percentage)
+
+userId
- Identifier of the user taking the test
+
+secret
- Secret key for authentication and authorization
+
+
+
+
+200 OK
- The test has been added to the database sucessfully
+
+401 UNAUTHORIZED
- The user has not been authenticated correctly
+
+500 INTERNAL SERVER ERROR
- There has been a database error when attempting to
+
+
+curl -X POST " https://example.com/api/post_test" \
+ -H " Content-Type: application/json" \
+ -d ' {
+ "testType": "typing",
+ "testLength": 100,
+ "testTime": 600,
+ "testSeed": "random_seed_123",
+ "quoteId": "quote_456",
+ "wpm": 65.5,
+ "accuracy": 98.2,
+ "userId": "user_789",
+ "secret": "your_secret_key_here"
+ }'
+
+
+
diff --git a/documentation/index.html b/documentation/index.html
deleted file mode 100644
index aa49121..0000000
--- a/documentation/index.html
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/documentation/index.md b/documentation/index.md
index 4e5348d..a734605 100644
--- a/documentation/index.md
+++ b/documentation/index.md
@@ -1,7 +1,13 @@
-# Links
+# Routes
-- [POST `/api/Create_User`](./create_user.md)
-- [POST `/api/Post_Test`](./create_test.md)
-- [GET `/api/Get_User_Tests`](./get_user_tests.md)
-- [GET `/api/Leaderboard`](./leaderboard.md)
-- [GET `/api/Login`](./login.md)
+## `/api`
+### `/user`
+- [POST `/create`](./post_user_create.md)
+- [GET `/login`](./get_user_login.md)
+- [GET `/tests`](./get_user_tests.md)
+- [POST `/test`](./post_user_test.md)
+- [GET `/test`](./get_user_test.md)
+### [GET `/ping`](./get_ping.md)
+### [GET `/leaderboard`](./get_leaderboard.md)
+### [GET `/test`](./get_test.md)
+### `/Documentation`
\ No newline at end of file
diff --git a/documentation/leaderboard.html b/documentation/leaderboard.html
deleted file mode 100644
index e2c4045..0000000
--- a/documentation/leaderboard.html
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- Returns the highest test data from each user as a JSON array. The data includes metrics such as username, words per minute (WPM), accuracy percentage, the time taken for the test, and the length of the test for a comprehensive overview of user performance.
-
-
-
- 200 OK
: Successfully retrieves the leaderboard data.
-
- 404 Not Found
: Indicates that the leaderboard was not found.
-
- 500 Internal Server Error
: Indicates an issue with accessing the database.
-
-
- [
- {
- "username" : " user1" ,
- "wpm" : 75 ,
- "accuracy" : 97 ,
- "test_time" : 120 ,
- "test_length" : 250
- },
- {
- "username" : " user2" ,
- "wpm" : 73 ,
- "accuracy" : 95 ,
- "test_time" : 115 ,
- "test_length" : 240
- }
-]
-
-
-
- username
: The name of the user.
-
- wpm
: Words per minute, indicating the typing speed.
-
- accuracy
: The accuracy of the user's typing, in percentage.
-
- test_time
: The total time taken to complete the test, in seconds.
-
- test_length
: The length of the test, typically measured in number of words.
-
-
-
\ No newline at end of file
diff --git a/documentation/login.html b/documentation/login.html
deleted file mode 100644
index dd8cfe5..0000000
--- a/documentation/login.html
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
- Authenticates a user and returns their user ID along with a secret key.
- Endpoint
- GET / api / login / < username > /< password >
-
-
-
-
- Parameter
- Type
- Description
-
-
-
-
- username
- String
- Username of the user
-
-
- password
- String
- Password of the user
-
-
-
-
- curl -X GET " https://example.com/api/login/example_user/example_password"
-
- {
- "user_id" : 123 ,
- "secret" : " random_secret_key"
- }
-
-
\ No newline at end of file
diff --git a/documentation/login.md b/documentation/login.md
deleted file mode 100644
index 17e66d7..0000000
--- a/documentation/login.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Login
-
-Authenticates a user and returns their user ID along with a secret key.
-Endpoint
-
-
-```js
-GET /api/login//
-```
-
-## Parameters
-
-| Parameter | Type | Description |
-| - | - | - |
-| username | String |Username of the user |
-| password | String |Password of the user |
-
-## Example Request
-
-```bash
-curl -X GET "https://example.com/api/login/example_user/example_password"
-```
-
-## Example Response
-
-```json
-{
- "user_id": 123,
- "secret": "random_secret_key"
-}
-```
\ No newline at end of file
diff --git a/documentation/markdown-to-html.sh b/documentation/markdown-to-html.sh
index 12c318a..1e8ef68 100755
--- a/documentation/markdown-to-html.sh
+++ b/documentation/markdown-to-html.sh
@@ -1,18 +1,52 @@
#!/bin/bash
-# Read markdown content from file
-markdown_content=$(cat $1)
+# HTML structure
+html_start='
+
+
+
+
+
+
+
+'
-# Write HTML content to file
-echo "$html_content" > $2
+html_end='
+
+
+'
-echo "Conversion completed. HTML content written to $2"
+# Iterate over each markdown file in the current directory
+for file in *.md; do
+ # Read markdown content from file
+ markdown_content=$(cat "$file")
+
+ # Convert markdown to HTML
+ html_content=$(gh api \
+ --method POST \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ /markdown \
+ -f text="$markdown_content"
+ )
+
+ # Write HTML content to a new file
+ html_file="./html/${file%.md}.html"
+ echo "$html_start$html_content$html_end" > "$html_file"
+
+ echo "Conversion completed. HTML content written to $html_file"
+done
\ No newline at end of file
diff --git a/documentation/create_user.md b/documentation/post_user_create.md
similarity index 66%
rename from documentation/create_user.md
rename to documentation/post_user_create.md
index 9ac469b..5125bdd 100644
--- a/documentation/create_user.md
+++ b/documentation/post_user_create.md
@@ -1,13 +1,10 @@
-# Create User
+# Create User API Endpoint
+
+## POST `/api/user/create`
Creates a new user in the database.
-Endpoint
-```js
-POST /api/create_user
-```
-
-## Request Body
+## Request Parameters
```json
{
@@ -19,7 +16,7 @@ POST /api/create_user
## Example Request
```bash
-curl -X POST "https://example.com/api/create_user" \
+curl -X POST "https://url/api/user/create" \
-H "Content-Type: application/json" \
-d '{"username": "example_user", "password": "example_password"}'
```
\ No newline at end of file
diff --git a/documentation/post_user_test.md b/documentation/post_user_test.md
new file mode 100644
index 0000000..16586c5
--- /dev/null
+++ b/documentation/post_user_test.md
@@ -0,0 +1,42 @@
+# Create Test API Endpoint
+
+## POST `/api/post_test`
+
+Post Test Data This API endpoint allows you to post test data, recording the results of a test taken by a user.
+
+## Request Parameters
+
+- `testType` - Type of the test ("words", "time", "quote", etc)
+- `testLength` - Length of the test in number of items or
+- `testTime` - Duration of the test in seconds
+- `testSeed` - Seed for generating randomized test content
+- `quoteId` - Identifier for a specific quote, if applicable
+- `wpm` - Words per minute (typing speed)
+- `accuracy` - Accuracy of responses (e.g., percentage)
+- `userId` - Identifier of the user taking the test
+- `secret` - Secret key for authentication and authorization
+
+## Responses
+
+- `200 OK` - The test has been added to the database sucessfully
+- `401 UNAUTHORIZED` - The user has not been authenticated correctly
+- `500 INTERNAL SERVER ERROR` - There has been a database error when attempting to
+
+## Example Request
+
+```bash
+curl -X POST "https://example.com/api/post_test" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "testType": "typing",
+ "testLength": 100,
+ "testTime": 600,
+ "testSeed": "random_seed_123",
+ "quoteId": "quote_456",
+ "wpm": 65.5,
+ "accuracy": 98.2,
+ "userId": "user_789",
+ "secret": "your_secret_key_here"
+ }'
+```
+
diff --git a/public/api/api.js b/public/api/api.js
index 3368602..d449c96 100644
--- a/public/api/api.js
+++ b/public/api/api.js
@@ -8,155 +8,102 @@
* This class provides all the useful methods to interact with the api.
*/
class API {
-
constructor() { this.url = "/api"; }
- /**
- * This takes the validated data and makes a post
- * request to the rocket server
- * @param {String} testType
- * @param {int} testLength
- * @param {int} testTime
- * @param {int} testSeed
- * @param {int} quoteId
- * @param {int} wpm
- * @param {int} accuracy
- * @param {int} userId
- */
- postTest(pTestType, pTestLength, pTestTime, pTestSeed, pQuoteId, pWpm, pAccuracy, pUserId) {
- const data = {
- 'test_type': pTestType,
- 'test_length': pTestLength,
- 'test_time': pTestTime,
- 'test_seed': pTestSeed,
- 'quote_id': pQuoteId,
- 'wpm': pWpm,
- 'accuracy': pAccuracy,
- 'user_id': pUserId,
- 'secret': user.secret
- }
-
- const xhr = new XMLHttpRequest();
- xhr.open(
- "POST",
- `${this.url}/post_test/`
- );
-
- xhr.send(
- JSON.stringify(data)
- );
-
- user.lastTest = data;
- }
-
- /**
- * Validates all the parameters used for the postTest function which it then calls
- */
- validateTest() {
- const test = screenManager.screen.textbox.getWords();
+ async createTest() {
+ const test = screenManager.screen.textbox.getLetters();
const testType = "words";
- let testLength = test.length;
- let testTime = screenManager.screen.timer.getTime();
+ const testLength = test.length;
+ const testTime = screenManager.screen.timer.getTime();
const testSeed = 0;
const quoteId = 0;
- let wpm;
const userId = Number(user.userId);
- let test_content = screenManager.screen.textbox.getTestContent();
+ const testContent = screenManager.screen.textbox.getTestContent();
- let string = "";
- let inaccurateLetters = 0;
- for (let letter = 0; letter < test.length; letter++) {
- if (test[letter] === test_content[letter]) {
- string += test[letter];
- } else {
- inaccurateLetters += 1;
+ let correctLetters = 0;
+ test.forEach((letter, index) => {
+ if (letter === testContent[index]) correctLetters++;
+ });
+
+ const accuracy = Math.round((correctLetters / test.length) * 100);
+ const wpm = Math.round((correctLetters / 5) * (60 / testTime));
+
+ const validations = [
+ {value: testType, type: "string"},
+ {value: testLength, type: "number", min: 0},
+ {value: testTime, type: "number", min: 1},
+ {value: testSeed, type: "number", min: 0},
+ {value: quoteId, type: "number", min: 0},
+ {value: wpm, type: "number", min: 0},
+ {value: accuracy, type: "number", min: 0, max: 100},
+ {value: userId, type: "number", min: 0}
+ ];
+
+ for (let {value, type, min, max} of validations) {
+ if (typeof value !== type || value < min || (max !== undefined && value > max)) {
+ console.error(`Validation failed for value: ${value}, Type: ${type}, Min: ${min}, Max: ${max}`);
+ return;
}
}
- const accuracy = Math.round(((test.length - inaccurateLetters) / test.length) * 100);
+ const data = {
+ test_type: testType,
+ test_length: testLength,
+ test_time: testTime,
+ test_seed: testSeed,
+ quote_id: quoteId,
+ wpm: wpm,
+ accuracy: accuracy,
+ user_id: userId,
+ secret: user.secret
+ };
- // this is the wpm calculation factoring in the time of test
- // it assumes that all words are 5 characters long because on average
- // they are
- wpm = Math.round((string.length / 5) * (60 / testTime));
+ try {
+ const response = await fetch(`${this.url}/user/test`, {
+ method: "POST",
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(data)
+ });
+
+ if (!response.ok) {
+ throw new Error('Network response was not ok');
+ }
- // the following code is a series of if statements that checks the
- // types of the variables is correct if not it errors it and returns
- // out of the function
+ // const responseData = await response.json();
+ // console.log(responseData);
+ user.lastTest = data; // Consider updating user.lastTest only on successful response
+ window.location.href = "/typing/pages/end/index.html"
+ } catch (error) {
+ console.error('There has been a problem with your fetch operation:', error);
+ }
+ }
- if ( typeof testType !== "string" ) {
- console.error(`testType is value ${typeof testType}\nshould be a string`);
- return;
+ /**
+ * Fetches the latest test for a given user.
+ * @param {number} userId The user's ID.
+ * @param {string} secret The secret key for authentication.
+ * @returns {Promise} The latest test object or an error message.
+ */
+ async getLatestUserTest() {
+ let userId = Number(user.userId);
+ let secret = user.secret;
+ const url = `${this.url}/user/test/${userId}/${secret}`;
+
+ try {
+ const response = await fetch(url);
+ if (!response.ok) {
+ // Handle HTTP errors
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ const data = await response.json();
+ console.log("Successfully fetched latest test:", data);
+ return data;
+ } catch (error) {
+ console.error("Error fetching the latest user test:", error);
+ return null; // or handle as needed
}
- if ( typeof testLength !== "number") {
- console.error(`testLength is value ${typeof testLength}\n should be a number`);
- return;
- }
- if ( typeof testTime !== "number") {
- console.error(`testTime is value ${typeof testTime}\n should be a number`);
- return;
- }
- if ( typeof testSeed !== "number") {
- console.error(`testSeed is value ${typeof testSeed}\n should be a number`);
- return;
- }
- if ( typeof quoteId !== "number") {
- console.error(`quoteId is value ${typeof quoteId}\n should be a number`);
- return;
- }
- if ( typeof wpm !== "number") {
- console.error(`wpm is value ${typeof wpm}\n should be a number`);
- return;
- }
- if ( typeof accuracy !== "number") {
- console.error(`accuracy is value ${typeof accuracy}\n should be a number`);
- return;
- }
- if ( typeof userId !== "number") {
- console.error(`userId is value ${typeof userId}\n should be a number`);
- return;
- }
-
- // after checking that all variables are of the correct type these if statements check
- // that they are acceptable values or are in acceptable bounds depending on variable types
-
- if (testType !== "words") {
- // currently words is the only acceptable type but
- // this will change in later iterations
-
- console.error(`testType is invalid\nacceptable options ['words']`);
- }
- // upper bounds for these numbers are less of a concern because the server will automatically
- // return an error if values are over the limit
- if (testLength < 0) {
- console.error(`testLength is too small, min value 0`)
- }
- if (testTime < 1) {
- console.error(`testTime is too small, min value 1`)
- }
- if (testSeed < 0) {
- console.error(`testSeed is too small, min value 0`)
- }
- if (quoteId < 0) {
- console.error(`quoteId is too small, min value 0`)
- }
- if (wpm < 0) {
- console.error(`wpm is too small, min value 0`)
- }
- // accuracy needs an upper bound check because users can't have more than 100%
- // accuracy when completing their tests
- if (accuracy < 0) {
- console.error(`accuracy is too small, min value 0`)
- } else if (accuracy > 100) {
- console.error(`accuracy is too big, max value 100`)
- }
- if (userId < 0) {
- console.error(`userId is too small, min value 0`)
- }
-
- // 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);
}
/**
@@ -175,7 +122,7 @@ class API {
};
const xhr = new XMLHttpRequest();
- xhr.open( "POST", `${this.url}/create_user/` );
+ xhr.open( "POST", `${this.url}/user/create/` );
xhr.send( JSON.stringify(user) );
@@ -215,7 +162,7 @@ class API {
}
let xhr = new XMLHttpRequest();
- xhr.open('GET', `${this.url}/login/${pUsername}/${pPassword}`);
+ xhr.open('GET', `${this.url}/user/login/${pUsername}/${pPassword}`);
xhr.send();
xhr.onload = () => {
let response = JSON.parse(xhr.response);
@@ -253,29 +200,35 @@ class API {
}
let xhr = new XMLHttpRequest();
- xhr.open('GET', `${this.url}/get_tests/${user.userId}/${user.secret}`);
+ xhr.open('GET', `${this.url}/user/tests/${user.userId}/${user.secret}`);
xhr.send();
xhr.onload = () => {
user.tests = JSON.parse(xhr.response);
};
}
- getLeaderBoard() {
- let xhr = new XMLHttpRequest();
- xhr.open('GET', `${this.url}/leaderboard/`);
- xhr.send();
- xhr.onload = () => {
- user.leaderboard = JSON.parse(xhr.response);
- };
+ async getLeaderBoard() {
+ try {
+ const response = await fetch(`${this.url}/leaderboard/`);
+ if (!response.ok) {
+ throw new Error('Network response was not ok.');
+ }
+ user.leaderboard = await response.json();
+ } catch (error) {
+ console.error('Failed to fetch leaderboard:', error);
+ }
}
+
+
+ async getTest() {
+ try {
+ const response = await fetch(`${this.url}/test/`);
+ if (!response.ok) {
+ throw new Error('Network response was not ok.');
+ }
- getTest() {
- let xhr = new XMLHttpRequest();
- xhr.open('GET', `${this.url}/new_test/`);
- xhr.send();
- xhr.onload = () =>{
const effectiveWidth = (windowWidth - 200) / 13;
- let textArr = JSON.parse(xhr.response);
+ let textArr = await response.json();
let finalText = [];
let text = "";
for (let i = 0; i < textArr.length; i++) {
@@ -287,6 +240,8 @@ class API {
}
}
user.nextTest = finalText;
- };
+ } catch (error) {
+ console.error('Failed to fetch a test:', error);
+ }
}
}
\ No newline at end of file
diff --git a/public/index.css b/public/index.css
deleted file mode 100755
index b7ac9cf..0000000
--- a/public/index.css
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-Index.css
-Description: This file is the stylesheet for the html that the
- user will see if they do not have javascript enabled.
-Author: Arlo Filley
-*/
-
-:root {
- --background-color: #000000;
- --text-color: yellow;
- --font: Verdana, Geneva, Tahoma, sans-serif;
-}
-
-body {
- background-color: var(--background-color);
- position: absolute;
- margin: 0;
-}
-
-noscript {
- display: block;
- text-align: center;
-
- font-family: var(--font);
- color: var(--text-color);
- font-size: large;
-}
\ No newline at end of file
diff --git a/public/index.js b/public/index.js
deleted file mode 100644
index af54c6b..0000000
--- a/public/index.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * @file This files is the root of the website.
- * @author Arlo Filley
- */
-
-// these are all of the globally accessible variables that are
-// needed for the site to run correctly
-let canvas, api, screenManager, user;
-
-/**
- * loads the any assets before the setup function
- * this allows p5.js to acess these assets including: sprites,
- * fonts, etc
-*/
-function preload() {
- roboto = loadFont('./assets/fonts/RobotoMono-Medium.ttf');
- accountIconBlack = loadImage('./assets/icons/account_circle_black.svg');
- accountIconWhite = loadImage('./assets/icons/account_circle_white.svg');
-}
-
-/**
- * defines variables and sets up the p5.js canvas
- * ready to be drawn with using the draw() function
-*/
-function setup() {
- canvas = new Canvas();
- canvas.resize();
- canvas.center();
-
- frameRate(60);
-
- api = new API();
- screenManager = new ScreenManager();
- user = new User();
- screenManager.setScreen(new StartScreen());
-
- api.login(null, null, true);
- api.getTest();
- textFont(roboto);
-}
-
-
-/**
- * called once per frame. draws all other elements onto the canvas
- * mostly will just call the screenManager.draw() method to make
- * sure that the correct screen is being drawn
-*/
-function draw() {
- background(user.colorScheme.background);
- screenManager.draw();
-}
-
-/**
- * called whenever a key is pressed, the variable key contains the
- * key that the user last pressed
-*/
-function keyPressed() {
- screenManager.letterTyped(key);
-}
-
-
-/**
- * called whenever the user resizes the window. Uses methods from the canvas wrapper class
- * to resize and center the canvas such that it displays correctly
-*/
-function windowResized() {
- canvas.resize();
- canvas.center();
-}
\ No newline at end of file
diff --git a/public/Admin/Delete User/index.html b/public/pages/Admin/Delete User/index.html
similarity index 100%
rename from public/Admin/Delete User/index.html
rename to public/pages/Admin/Delete User/index.html
diff --git a/public/Admin/Delete User/index.js b/public/pages/Admin/Delete User/index.js
similarity index 100%
rename from public/Admin/Delete User/index.js
rename to public/pages/Admin/Delete User/index.js
diff --git a/public/Admin/index.css b/public/pages/Admin/index.css
similarity index 100%
rename from public/Admin/index.css
rename to public/pages/Admin/index.css
diff --git a/public/Admin/index.html b/public/pages/Admin/index.html
similarity index 100%
rename from public/Admin/index.html
rename to public/pages/Admin/index.html
diff --git a/public/pages/account/index.html b/public/pages/account/index.html
new file mode 100644
index 0000000..5b746c4
--- /dev/null
+++ b/public/pages/account/index.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+ User Tests
+
+
+
+
+ Account
+ Test
+ Leaderboard
+ Test Settings
+
+
+ User Tests
+
+
+
+ Test Type
+ Test Length
+ Test Time
+ WPM
+ Accuracy
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/pages/account/script.js b/public/pages/account/script.js
new file mode 100644
index 0000000..7d3f28e
--- /dev/null
+++ b/public/pages/account/script.js
@@ -0,0 +1,22 @@
+async function fetchLeaderboardData() {
+ const response = await fetch('/api/user/tests/');
+ if (!response.ok) {
+ console.error('Failed to fetch leaderboard data');
+ return;
+ }
+ const data = await response.json();
+ const tableBody = document.getElementById('leaderboardTable').getElementsByTagName('tbody')[0];
+ tableBody.innerHTML = ''; // Clear existing rows
+ data.forEach(item => {
+ const row = tableBody.insertRow();
+ row.insertCell(0).innerText = item.username;
+ row.insertCell(1).innerText = item.wpm;
+ row.insertCell(2).innerText = item.accuracy;
+ row.insertCell(3).innerText = item.test_time;
+ row.insertCell(4).innerText = item.test_length;
+ });
+}
+
+// Ensure this function is called on page load and when the refresh button is clicked.
+document.addEventListener('DOMContentLoaded', fetchLeaderboardData);
+document.getElementById('refreshButton').addEventListener('click', fetchLeaderboardData);
diff --git a/public/pages/account/styles.css b/public/pages/account/styles.css
new file mode 100644
index 0000000..b3c9fbb
--- /dev/null
+++ b/public/pages/account/styles.css
@@ -0,0 +1,82 @@
+body {
+ font-family: Arial, sans-serif;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+ margin-top: 5%;
+}
+table {
+ width: 60%;
+ border-collapse: collapse;
+ margin-top: 20px;
+}
+th, td {
+ border: 1px solid #ddd;
+ text-align: left;
+ padding: 8px;
+}
+th {
+ background-color: #f2f2f2;
+}
+tr:nth-child(even) {
+ background-color: #f9f9f9;
+}
+.refresh-button {
+ cursor: pointer;
+ border: none;
+ background-color: transparent;
+ display: flex;
+ align-items: center;
+ color: #4CAF50;
+}
+
+.refresh-button:hover {
+ transform: rotate(90deg);
+}
+
+/* Dark Mode styles */
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #121212;
+ color: #e0e0e0;
+ }
+ table {
+ border-color: #424242;
+ }
+ th {
+ background-color: #333;
+ }
+ tr:nth-child(even) {
+ background-color: #2a2a2a;
+ }
+ .refresh-icon {
+ fill: #90caf9;
+ }
+}
+
+#navbar {
+ background-color: #333; /* Navbar background color */
+ position: fixed;
+ top: 0;
+ width: 100%;
+ height: 5%; /* Covering the top 20% of the screen */
+ z-index: 1000; /* Ensures the navbar is on top of other content */
+ display: flex;
+ justify-content: center; /* Center horizontally */
+ align-items: center; /* Center vertically */
+}
+
+#navbar a {
+ text-decoration: none;
+ color: #fff; /* Text color */
+ padding: 0.75% 20px; /* Padding for each link */
+}
+
+#navbar a:hover {
+ background-color: #555; /* Background color on hover */
+}
+
+#navbar a.active {
+ background-color: #555; /* Background color for active link */
+}
\ No newline at end of file
diff --git a/public/pages/end/index.html b/public/pages/end/index.html
new file mode 100644
index 0000000..47dcccf
--- /dev/null
+++ b/public/pages/end/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+ User Dashboard
+
+
+
+
+ Account
+ Test
+ Leaderboard
+ Test Settings
+
+ User Dashboard
+
+
+
+ Most Recent Test Result
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/pages/end/script.js b/public/pages/end/script.js
new file mode 100644
index 0000000..c4020e7
--- /dev/null
+++ b/public/pages/end/script.js
@@ -0,0 +1,69 @@
+// Function to fetch the username
+async function fetchUsername() {
+ const userId = localStorage.getItem("userId"); // Replace with the actual user ID
+ const secret = localStorage.getItem("secret")
+
+ try {
+ const response = await fetch(`/api/user/test/${userId}/${secret}`);
+ const userData = await response.json();
+
+ // Call function to display username
+ displayUsername(localStorage.getItem("username"));
+ displayTestResults(userData);
+ } catch (error) {
+ console.error('Error fetching username:', error);
+ }
+}
+
+// Function to display test results in a table
+function displayTestResults(testData) {
+ const testResultsDiv = document.getElementById('testResults');
+
+ if (!testData) {
+ testResultsDiv.innerHTML = 'No test data available.
';
+ return;
+ }
+
+ // Construct HTML for the table
+ const html = `
+
+
+ Test Type
+ Test Length
+ Test Time
+ Test Seed
+ Quote ID
+ Words Per Minute (WPM)
+ Accuracy
+
+
+ ${testData.test_type}
+ ${testData.test_length}
+ ${testData.test_time}
+ ${testData.test_seed}
+ ${testData.quote_id}
+ ${testData.wpm}
+ ${testData.accuracy}
+
+
+ `;
+
+ // Update the testResultsDiv with the HTML
+ testResultsDiv.innerHTML = html;
+}
+
+// Function to display username
+function displayUsername(username) {
+ const usernameDiv = document.getElementById('username');
+
+ if (!username) {
+ usernameDiv.innerHTML = 'No username available.
';
+ return;
+ }
+
+ // Update the usernameDiv with the username
+ usernameDiv.innerHTML = `Welcome, ${username}!
`;
+}
+
+// Call the fetchUsername function when the page loads
+window.onload = fetchUsername;
\ No newline at end of file
diff --git a/public/pages/end/styles.css b/public/pages/end/styles.css
new file mode 100644
index 0000000..ea41cb2
--- /dev/null
+++ b/public/pages/end/styles.css
@@ -0,0 +1,85 @@
+body {
+ font-family: Arial, sans-serif;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+ margin-top: 5%;
+
+}
+table {
+ width: 100%;
+ height: 100%;
+ font-size: larger;
+ border-collapse: collapse;
+ margin-top: 20px;
+}
+th, td {
+ border: 1px solid #ddd;
+ text-align: center;
+ padding: 8px;
+}
+th {
+ background-color: #f2f2f2;
+}
+tr:nth-child(even) {
+ background-color: #f9f9f9;
+}
+.refresh-button {
+ cursor: pointer;
+ border: none;
+ background-color: transparent;
+ display: flex;
+ align-items: center;
+ color: #4CAF50;
+}
+
+.refresh-button:hover {
+ transform: rotate(90deg);
+}
+
+/* Dark Mode styles */
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #121212;
+ color: #e0e0e0;
+ }
+ table {
+ border-color: #424242;
+ }
+ th {
+ background-color: #333;
+ }
+ tr:nth-child(even) {
+ background-color: #2a2a2a;
+ }
+ .refresh-icon {
+ fill: #90caf9;
+ }
+}
+
+#navbar {
+ background-color: #333; /* Navbar background color */
+ position: fixed;
+ top: 0;
+ width: 100%;
+ height: 5%; /* Covering the top 20% of the screen */
+ z-index: 1000; /* Ensures the navbar is on top of other content */
+ display: flex;
+ justify-content: center; /* Center horizontally */
+ align-items: center; /* Center vertically */
+}
+
+#navbar a {
+ text-decoration: none;
+ color: #fff; /* Text color */
+ padding: 0.75% 20px; /* Padding for each link */
+}
+
+#navbar a:hover {
+ background-color: #555; /* Background color on hover */
+}
+
+#navbar a.active {
+ background-color: #555; /* Background color for active link */
+}
\ No newline at end of file
diff --git a/public/pages/leaderboard/index.html b/public/pages/leaderboard/index.html
new file mode 100644
index 0000000..e707802
--- /dev/null
+++ b/public/pages/leaderboard/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Leaderboard
+
+
+
+
+ Account
+ Test
+ Leaderboard
+ Test Settings
+
+ Leaderboard
+
+
+
+ Username
+ WPM
+ Accuracy (%)
+ Test Time (s)
+ Test Length (characters)
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/leaderboard/script.js b/public/pages/leaderboard/script.js
similarity index 100%
rename from public/leaderboard/script.js
rename to public/pages/leaderboard/script.js
diff --git a/public/pages/leaderboard/styles.css b/public/pages/leaderboard/styles.css
new file mode 100644
index 0000000..b3c9fbb
--- /dev/null
+++ b/public/pages/leaderboard/styles.css
@@ -0,0 +1,82 @@
+body {
+ font-family: Arial, sans-serif;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+ margin-top: 5%;
+}
+table {
+ width: 60%;
+ border-collapse: collapse;
+ margin-top: 20px;
+}
+th, td {
+ border: 1px solid #ddd;
+ text-align: left;
+ padding: 8px;
+}
+th {
+ background-color: #f2f2f2;
+}
+tr:nth-child(even) {
+ background-color: #f9f9f9;
+}
+.refresh-button {
+ cursor: pointer;
+ border: none;
+ background-color: transparent;
+ display: flex;
+ align-items: center;
+ color: #4CAF50;
+}
+
+.refresh-button:hover {
+ transform: rotate(90deg);
+}
+
+/* Dark Mode styles */
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #121212;
+ color: #e0e0e0;
+ }
+ table {
+ border-color: #424242;
+ }
+ th {
+ background-color: #333;
+ }
+ tr:nth-child(even) {
+ background-color: #2a2a2a;
+ }
+ .refresh-icon {
+ fill: #90caf9;
+ }
+}
+
+#navbar {
+ background-color: #333; /* Navbar background color */
+ position: fixed;
+ top: 0;
+ width: 100%;
+ height: 5%; /* Covering the top 20% of the screen */
+ z-index: 1000; /* Ensures the navbar is on top of other content */
+ display: flex;
+ justify-content: center; /* Center horizontally */
+ align-items: center; /* Center vertically */
+}
+
+#navbar a {
+ text-decoration: none;
+ color: #fff; /* Text color */
+ padding: 0.75% 20px; /* Padding for each link */
+}
+
+#navbar a:hover {
+ background-color: #555; /* Background color on hover */
+}
+
+#navbar a.active {
+ background-color: #555; /* Background color for active link */
+}
\ No newline at end of file
diff --git a/public/leaderboard/index.html b/public/pages/test-settings/index.html
similarity index 71%
rename from public/leaderboard/index.html
rename to public/pages/test-settings/index.html
index 78ff213..7f44ded 100644
--- a/public/leaderboard/index.html
+++ b/public/pages/test-settings/index.html
@@ -7,7 +7,13 @@
- Leaderboard
+
+ Account
+ Test
+ Leaderboard
+ Test Settings
+
+ Test Settings
⟳
Go Back
diff --git a/public/pages/test-settings/script.js b/public/pages/test-settings/script.js
new file mode 100644
index 0000000..0b78b2c
--- /dev/null
+++ b/public/pages/test-settings/script.js
@@ -0,0 +1,22 @@
+async function fetchLeaderboardData() {
+ const response = await fetch('/api/leaderboard');
+ if (!response.ok) {
+ console.error('Failed to fetch leaderboard data');
+ return;
+ }
+ const data = await response.json();
+ const tableBody = document.getElementById('leaderboardTable').getElementsByTagName('tbody')[0];
+ tableBody.innerHTML = ''; // Clear existing rows
+ data.forEach(item => {
+ const row = tableBody.insertRow();
+ row.insertCell(0).innerText = item.username;
+ row.insertCell(1).innerText = item.wpm;
+ row.insertCell(2).innerText = item.accuracy;
+ row.insertCell(3).innerText = item.test_time;
+ row.insertCell(4).innerText = item.test_length;
+ });
+}
+
+// Ensure this function is called on page load and when the refresh button is clicked.
+document.addEventListener('DOMContentLoaded', fetchLeaderboardData);
+document.getElementById('refreshButton').addEventListener('click', fetchLeaderboardData);
diff --git a/public/leaderboard/styles.css b/public/pages/test-settings/styles.css
similarity index 59%
rename from public/leaderboard/styles.css
rename to public/pages/test-settings/styles.css
index 361e575..78918f3 100644
--- a/public/leaderboard/styles.css
+++ b/public/pages/test-settings/styles.css
@@ -54,3 +54,29 @@ tr:nth-child(even) {
fill: #90caf9;
}
}
+
+#navbar {
+ background-color: #333; /* Navbar background color */
+ position: fixed;
+ top: 0;
+ width: 100%;
+ height: 5%; /* Covering the top 20% of the screen */
+ z-index: 1000; /* Ensures the navbar is on top of other content */
+ display: flex;
+ justify-content: center; /* Center horizontally */
+ align-items: center; /* Center vertically */
+}
+
+#navbar a {
+ text-decoration: none;
+ color: #fff; /* Text color */
+ padding: 0.75% 20px; /* Padding for each link */
+}
+
+#navbar a:hover {
+ background-color: #555; /* Background color on hover */
+}
+
+#navbar a.active {
+ background-color: #555; /* Background color for active link */
+}
\ No newline at end of file
diff --git a/public/assets/favicon/android-chrome-192x192.png b/public/pages/test/assets/favicon/android-chrome-192x192.png
similarity index 100%
rename from public/assets/favicon/android-chrome-192x192.png
rename to public/pages/test/assets/favicon/android-chrome-192x192.png
diff --git a/public/assets/favicon/android-chrome-512x512.png b/public/pages/test/assets/favicon/android-chrome-512x512.png
similarity index 100%
rename from public/assets/favicon/android-chrome-512x512.png
rename to public/pages/test/assets/favicon/android-chrome-512x512.png
diff --git a/public/assets/favicon/apple-touch-icon.png b/public/pages/test/assets/favicon/apple-touch-icon.png
similarity index 100%
rename from public/assets/favicon/apple-touch-icon.png
rename to public/pages/test/assets/favicon/apple-touch-icon.png
diff --git a/public/assets/favicon/favicon-16x16.png b/public/pages/test/assets/favicon/favicon-16x16.png
similarity index 100%
rename from public/assets/favicon/favicon-16x16.png
rename to public/pages/test/assets/favicon/favicon-16x16.png
diff --git a/public/assets/favicon/favicon-32x32.png b/public/pages/test/assets/favicon/favicon-32x32.png
similarity index 100%
rename from public/assets/favicon/favicon-32x32.png
rename to public/pages/test/assets/favicon/favicon-32x32.png
diff --git a/public/assets/favicon/favicon.ico b/public/pages/test/assets/favicon/favicon.ico
similarity index 100%
rename from public/assets/favicon/favicon.ico
rename to public/pages/test/assets/favicon/favicon.ico
diff --git a/public/assets/favicon/site.webmanifest b/public/pages/test/assets/favicon/site.webmanifest
similarity index 100%
rename from public/assets/favicon/site.webmanifest
rename to public/pages/test/assets/favicon/site.webmanifest
diff --git a/public/assets/fonts/RobotoMono-Medium.ttf b/public/pages/test/assets/fonts/RobotoMono-Medium.ttf
similarity index 100%
rename from public/assets/fonts/RobotoMono-Medium.ttf
rename to public/pages/test/assets/fonts/RobotoMono-Medium.ttf
diff --git a/public/assets/icons/account_circle_black.svg b/public/pages/test/assets/icons/account_circle_black.svg
similarity index 100%
rename from public/assets/icons/account_circle_black.svg
rename to public/pages/test/assets/icons/account_circle_black.svg
diff --git a/public/assets/icons/account_circle_white.svg b/public/pages/test/assets/icons/account_circle_white.svg
similarity index 100%
rename from public/assets/icons/account_circle_white.svg
rename to public/pages/test/assets/icons/account_circle_white.svg
diff --git a/public/components/button.js b/public/pages/test/components/button.js
similarity index 100%
rename from public/components/button.js
rename to public/pages/test/components/button.js
diff --git a/public/components/canvas.js b/public/pages/test/components/canvas.js
similarity index 74%
rename from public/components/canvas.js
rename to public/pages/test/components/canvas.js
index 7aa8eac..722eda6 100755
--- a/public/components/canvas.js
+++ b/public/pages/test/components/canvas.js
@@ -17,12 +17,12 @@ class Canvas {
center() {
- this.canvas.position(this.x, this.y);
+ this.canvas.position(this.x, this.y + windowHeight / 100 * 5);
}
resize() {
- this.canvas.resize(windowWidth, windowHeight);
+ this.canvas.resize(windowWidth, windowHeight - windowHeight / 100 * 5);
}
disable() {
diff --git a/public/components/menu.js b/public/pages/test/components/menu.js
similarity index 100%
rename from public/components/menu.js
rename to public/pages/test/components/menu.js
diff --git a/public/components/textbox.js b/public/pages/test/components/textbox.js
similarity index 100%
rename from public/components/textbox.js
rename to public/pages/test/components/textbox.js
diff --git a/public/components/timemenu.js b/public/pages/test/components/timemenu.js
similarity index 100%
rename from public/components/timemenu.js
rename to public/pages/test/components/timemenu.js
diff --git a/public/components/timer.js b/public/pages/test/components/timer.js
similarity index 96%
rename from public/components/timer.js
rename to public/pages/test/components/timer.js
index 950be0a..f99a567 100755
--- a/public/components/timer.js
+++ b/public/pages/test/components/timer.js
@@ -48,6 +48,7 @@ class Timer {
this.timeElapsed = 0;
this.ended;
this.hasStarted = false;
+ this.hasEnded = false;
}
getX() {
@@ -163,7 +164,7 @@ class Timer {
*/
tick() {
this.timeElapsed = (millis() - this.startTime) / 1000;
- if (this.timeElapsed >= this.time) {
+ if (this.timeElapsed >= this.time && !this.testEnded) {
this.end();
};
}
@@ -172,14 +173,13 @@ class Timer {
* this function is called at the end of the timer
*/
end() {
+ api.createTest()
this.visible = false;
- api.validateTest();
this.timeElapsed = 0;
this.time = 0;
- api.getTest();
// Then this function will call all other functions necessary to complete the test
// this will likely including changing the screen and interacting with the api
- screenManager.setScreen(new EndScreen());
+ this.hasEnded = true
}
/**
@@ -188,6 +188,7 @@ class Timer {
draw() {
// if the time shouldn't be rendered it quickly exits out of this method
if (!this.visible) return;
+ if (this.hasEnded) return;
textAlign(LEFT);
// adds a border for the bar if one is needed
diff --git a/public/components/user.js b/public/pages/test/components/user.js
similarity index 100%
rename from public/components/user.js
rename to public/pages/test/components/user.js
diff --git a/public/pages/test/index.css b/public/pages/test/index.css
new file mode 100755
index 0000000..9a48dc5
--- /dev/null
+++ b/public/pages/test/index.css
@@ -0,0 +1,74 @@
+/*
+Index.css
+Description: This file is the stylesheet for the html that the
+ user will see if they do not have javascript enabled.
+Author: Arlo Filley
+*/
+
+:root {
+ --background-color: #000000;
+ --text-color: yellow;
+ --font: Verdana, Geneva, Tahoma, sans-serif;
+}
+
+body {
+ background-color: var(--background-color);
+ position: absolute;
+ margin: 0;
+ font-family: Arial, sans-serif;
+}
+
+noscript {
+ display: block;
+ text-align: center;
+
+ font-family: var(--font);
+ color: var(--text-color);
+ font-size: large;
+}
+
+#navbar {
+ background-color: #333; /* Navbar background color */
+ position: fixed;
+ top: 0;
+ width: 100%;
+ height: 5%; /* Covering the top 20% of the screen */
+ z-index: 1000; /* Ensures the navbar is on top of other content */
+ display: flex;
+ justify-content: center; /* Center horizontally */
+ align-items: center; /* Center vertically */
+}
+
+#navbar a {
+ text-decoration: none;
+ color: #fff; /* Text color */
+ padding: 0.7% 20px; /* Padding for each link */
+}
+
+#navbar a:hover {
+ background-color: #555; /* Background color on hover */
+}
+
+#navbar a.active {
+ background-color: #555; /* Background color for active link */
+}
+
+/* Dark Mode styles */
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #121212;
+ color: #e0e0e0;
+ }
+ table {
+ border-color: #424242;
+ }
+ th {
+ background-color: #333;
+ }
+ tr:nth-child(even) {
+ background-color: #2a2a2a;
+ }
+ .refresh-icon {
+ fill: #90caf9;
+ }
+}
\ No newline at end of file
diff --git a/public/index.html b/public/pages/test/index.html
similarity index 70%
rename from public/index.html
rename to public/pages/test/index.html
index 5933f71..547d035 100755
--- a/public/index.html
+++ b/public/pages/test/index.html
@@ -4,7 +4,7 @@
- TypeFast
+ Typing - Test
@@ -15,6 +15,13 @@
+
+ Account
+ Test
+ Leaderboard
+ Test Settings
+
+
@@ -31,19 +38,11 @@
-
-
-
-
-
-
-
-
-
-
+
+
Please Enable Javascript
diff --git a/public/pages/test/index.js b/public/pages/test/index.js
new file mode 100644
index 0000000..19384fb
--- /dev/null
+++ b/public/pages/test/index.js
@@ -0,0 +1,72 @@
+/**
+ * @file This file is the root of the website.
+ * @author Arlo Filley
+ */
+
+/**
+ * Loads any assets before the setup function.
+ * This allows p5.js to access these assets including: sprites,
+ * fonts, etc.
+ * @returns {void}
+ */
+function preload() {
+ roboto = loadFont('./assets/fonts/RobotoMono-Medium.ttf');
+}
+
+/**
+ * Defines variables and sets up the p5.js canvas
+ * ready to be drawn with using the draw() function.
+ * @returns {void}
+ */
+async function setup() {
+ canvas = new Canvas();
+ canvas.resize();
+ canvas.center();
+
+ frameRate(60);
+
+ api = new API();
+ screenManager = new ScreenManager();
+ /**
+ * @type {User}
+ */
+ user = new User();
+ await api.getTest();
+ console.log(user.nextTest);
+ screenManager.setScreen(new TestScreen());
+
+ api.login(null, null, true);
+ textFont(roboto);
+}
+
+
+/**
+ * Called once per frame. Draws all other elements onto the canvas.
+ * Mostly will just call the screenManager.draw() method to make
+ * sure that the correct screen is being drawn.
+ * @returns {void}
+ */
+function draw() {
+ background(user.colorScheme.background);
+ screenManager.draw();
+}
+
+/**
+ * Called whenever a key is pressed. The variable key contains the
+ * key that the user last pressed.
+ * @returns {void}
+ */
+function keyPressed() {
+ screenManager.letterTyped(key);
+}
+
+
+/**
+ * Called whenever the user resizes the window. Uses methods from the canvas wrapper class
+ * to resize and center the canvas such that it displays correctly.
+ * @returns {void}
+ */
+function windowResized() {
+ canvas.resize();
+ canvas.center();
+}
\ No newline at end of file
diff --git a/public/screens/screenmanager.js b/public/pages/test/screens/screenmanager.js
similarity index 100%
rename from public/screens/screenmanager.js
rename to public/pages/test/screens/screenmanager.js
diff --git a/public/screens/testscreen.js b/public/pages/test/screens/testscreen.js
similarity index 84%
rename from public/screens/testscreen.js
rename to public/pages/test/screens/testscreen.js
index a96e7e7..a3a0c1e 100755
--- a/public/screens/testscreen.js
+++ b/public/pages/test/screens/testscreen.js
@@ -31,15 +31,21 @@ class TestScreen {
}
this.stopButton.draw();
if (this.stopButton.isPressed()) {
- screenManager.setScreen(new StartScreen())
+ window.location.href = "/typing/pages/leaderboard/index.html"
+ }
+
+ if (this.timer.hasEnded) {
+ this.stopButton.y = 0
+ text("Test Has Ended", 100, 100);
}
}
letterTyped(key) {
+ if (this.timer.hasEnded) return;
this.textbox.letterTyped(key);
if (!this.timerStarted) {
this.timer.start();
this.timerStarted = true;
- }
+ }
}
}
\ No newline at end of file
diff --git a/public/screens/accountScreen.js b/public/screens/accountScreen.js
deleted file mode 100755
index bf793dc..0000000
--- a/public/screens/accountScreen.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * @file This file provides a screen for the user to login through
- * @author Arlo Filley
- *
- * TODO:
- * - move into an seperated account page with signup and logout
- * - make passwords not display plain text
- */
-
-/**
- * this class displays a number of textboxes that allows the user to input a
- * username and password. Then find out the user_id of that account through the
- * necessary api routes.
- */
-class AccountScreen {
- constructor() {
- this.textboxes = [
- new Textbox(
- 120, 350, 500, 100, 0, true, "#000", false,
- "#000", "#000", true
- ),
-
- new Textbox(
- 120, 500, 500, 100, 0, true, "#000", false,
- "#000", "#000", false, false, true
- )
- ]
-
- this.buttons = [
- new Button(windowWidth / 2 - 400, 300, 500, 100, "", false, true, "#000", "#000", "#fff", "#000", "#000", "#fff"),
- new Button(windowWidth / 2 - 400, 450, 500, 100, "", false, true, "#000", "#000", "#fff", "#000", "#000", "#fff"),
- new Button(windowWidth / 2 + 200, 300, 100, 50, "Login"),
- new Button(windowWidth / 2 + 200, 400, 100, 50, "Sign up"),
- new Button(windowWidth / 2 + 200, 500, 100, 50, "Logout"),
- ]
-
- this.menu = new Menu();
-
- // keeps track of which textbox the user last clicked on
- this.activeTextBox = 0
- }
-
- /**
- * Draws the SignUpScreen class with all
- * appropriate elements
- */
- draw() {
- textSize(100);
- fill(user.colorScheme.text);
- text("Account", 0, 100, windowWidth, 110);
- for (let i = 0; i < this.buttons.length; i++) {
- this.buttons[i].draw();
- }
-
- for (let i = 0; i < this.textboxes.length; i++) {
- this.textboxes[i].draw();
- }
-
- textSize(30);
- text("Username", windowWidth / 2 - 400, 275);
- text("Password", windowWidth / 2 - 400, 425);
-
- if (this.buttons[0].isPressed()) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=0;
- this.textboxes[this.activeTextBox].line = true;
- } else if (this.buttons[1].isPressed()) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=1;
- this.textboxes[this.activeTextBox].line = true;
- } else if (this.buttons[2].isPressed()) {
- api.login(
- this.textboxes[0].getWords(),
- this.textboxes[1].getWords()
- )
- screenManager.setScreen(new StartScreen());
- } else if (this.buttons[3].isPressed()) {
- api.createUser(
- this.textboxes[0].getWords(),
- this.textboxes[1].getWords()
- )
- screenManager.setScreen(new StartScreen());
- } else if (this.buttons[4].isPressed()) {
- api.logout();
- screenManager.setScreen(new StartScreen());
- }
- this.menu.draw();
-
- fill(user.colorScheme.text);
- }
-
- /**
- *
- * @param {key} key
- */
- letterTyped(key) {
- if (key === "Tab" && this.activeTextBox === 0) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=1;
- this.textboxes[this.activeTextBox].line = true;
- } else if (key === "Tab" && this.activeTextBox === 1) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=0;
- this.textboxes[this.activeTextBox].line = true;
- } else if (key === "Enter") {
- api.login(
- this.textboxes[0].getWords(),
- this.textboxes[1].getWords()
- )
- screenManager.setScreen(new StartScreen());
- } else {
- this.textboxes[this.activeTextBox].letterTyped(key);
- }
- }
-}
\ No newline at end of file
diff --git a/public/screens/endscreen.js b/public/screens/endscreen.js
deleted file mode 100755
index d99b244..0000000
--- a/public/screens/endscreen.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * @file This file provides a screen class that can be displayed at the end of a test
- * @author Arlo Filley
- *
- * TODO:
- * - provide the user with the data of the test that they have just
- * completed, such as their wpm, accuracy, etc.
- */
-
-/**
- * This class is for a screen that is displayed at the end of a test,
- * currently it just tells the user to press start to enter another test
- */
-class EndScreen {
- constructor() {
- this.menu = new Menu();
- }
-
- draw() {
- textSize(100);
- textAlign(CENTER, CENTER);
- fill(user.colorScheme.text);
- text("Test Complete", 0, 0, windowWidth - 100, windowHeight / 6);
-
- textSize(30);
- text(`${user.lastTest.wpm} words per minute`, windowWidth / 2, 200);
- text(`${user.lastTest.accuracy}% accuracy`, windowWidth / 2, 240);
- text(`${user.lastTest.test_length} characters typed`, windowWidth / 2, 280);
- text(`${user.lastTest.test_time}s`, windowWidth / 2, 320);
- this.menu.draw();
- }
-
- letterTyped(key) {
- if (key === "Enter") screenManager.setScreen(new TestScreen());
- }
-}
\ No newline at end of file
diff --git a/public/screens/leaderboardscreen.js b/public/screens/leaderboardscreen.js
deleted file mode 100755
index ef8b57a..0000000
--- a/public/screens/leaderboardscreen.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * @file This file provides a leaderboard for the user to compare times.
- * @author Arlo Filley
- *
- * TODO:
- * - implement a way for the user to scroll down the tests.
- * - display more tests on the screen at once, up to 15
- * - store the leaderboard in localstorage as a cache for the most recent results
- */
-
-/**
- * this class is a screen which shows the current leaderboard from the
- * results gotten via the api.
- */
-class LeaderboardScreen {
- constructor() {
- this.menu = new Menu();
- api.getLeaderBoard();
- this.testButtons;
- // this.buttons = [
- // new Button(1150, 270, 240, 120, "up"),
- // new Button(1150, 390, 240, 120, "down"),
- // ]
- this.offset = 0;
- this.scroll_bar_button = new Button(1200, 200, 20, 20, "")
- }
-
- draw() {
- textSize(100);
- textAlign(CENTER, CENTER);
- fill(user.colorScheme.text);
- text("Leaderboard", 0, 100, windowWidth, 120);
- this.menu.draw();
-
- textSize(20);
- fill(user.colorScheme.text);
- if (user.leaderboard != undefined) {
- if (this.testButtons === undefined) {
- this.createTestButtons();
- }
- }
-
- fill(user.colorScheme.testBad);
- rect(1200, 270, 20, 420);
-
- fill(user.colorScheme.testGood);
- rect(1200, 270, 20, 420 / user.leaderboard.length * (this.offset + 1));
- this.scroll_bar_button.height = (user.leaderboard.length)
-
- if (this.scroll_bar_button.isPressed()) {
- this.scroll_bar_button.y = mouseY - this.scroll_bar_button.height / 2;
- }
-
- // the furthest up the scrollbar can go is the top
- if (this.scroll_bar_button.y < 270) {
- this.scroll_bar_button.y = 270;
- }
-
- if (this.scroll_bar_button.y > 690 - this.scroll_bar_button.height) {
- this.scroll_bar_button.y = 690 - this.scroll_bar_button.height;
- }
- this.scroll_bar_button.draw();
-
- if (this.testButtons !== undefined && this.testButtons.length > 1) {
- for (let i = 0; i < this.testButtons.length; i++) {
- this.testButtons[i][0].draw()
- this.testButtons[i][1].draw()
- this.testButtons[i][2].draw()
- }
-
- this.offset = Number((
- // number of pixels from top of screen / total range of options, put to a whole integer to produce the correct offset
- (this.scroll_bar_button.y - 270) / ((420 - this.scroll_bar_button.height) / (user.leaderboard.length - 13))
- ).toFixed(0));
-
- this.createTestButtons(this.offset)
- } else {
- fill(user.colorScheme.text);
- text("Looks Like There Isn't A Leaderboard", windowWidth / 2, 300);
- }
-
- fill(user.colorScheme.text);
- }
-
- createTestButtons(offset = 0) {
- this.testButtons = [[
- new Button(400, 270, 100, 30, "ranking"), // test # button
- new Button(500, 270, 400, 30, "username"), // wpm button
- new Button(900, 270, 240, 30, "words per minute"), // accuracy button
- ]];
- let j = 300;
- for (let i = 0 + offset; i < user.leaderboard.length && i <= 12+offset; i++) {
- this.testButtons.push([
- new Button(400, j, 100, 30, `${i+1}`, true, true, "#000", "#000", "#fff"), // test # button
- new Button(500, j, 400, 30, `${user.leaderboard[i].username}`, true, true, "#000", "#000", "#fff"), // accuracy button
- new Button(900, j, 240, 30, `${user.leaderboard[i].wpm}`, true, true, "#000", "#000", "#fff"), // wpm button
- ])
- j+=30;
- }
- }
-}
\ No newline at end of file
diff --git a/public/screens/loginscreen.js b/public/screens/loginscreen.js
deleted file mode 100755
index 20802f0..0000000
--- a/public/screens/loginscreen.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
- * @file This file provides a screen for the user to login through
- * @author Arlo Filley
- *
- * TODO:
- * - move into an seperated account page with signup and logout
- * - make passwords not display plain text
- */
-
-/**
- * this class displays a number of textboxes that allows the user to input a
- * username and password. Then find out the user_id of that account through the
- * necessary api routes.
- */
-class LoginScreen {
- constructor() {
- this.textboxes = [
- new Textbox(
- 120, 250, 500, 100, 0, true, user.colorScheme.text, false,
- "#000", "#000", true, false
- ),
-
- new Textbox(
- 120, 400, 500, 100, 0, true, user.colorScheme.text, false,
- "000", "#000", false, false
- )
- ]
-
- this.buttons = [
- new Button(
- 100, 200, 500, 100, 0, true, user.colorScheme.buttonBG, false,
- "#000", "#fff", ""
- ),
-
- new Button(
- 100, 350, 500, 100, 0, true, "#000", false,
- "#000", "#fff", ""
- ),
-
- new Button(
- 700, 300, 100, 50, 0, true, "#000", false,
- "#000", "#00ff00", "Login"
- ),
- ]
-
- this.menu = new Menu();
-
- // keeps track of which textbox the user last clicked on
- this.activeTextBox = 0
- }
-
- /**
- * Draws the SignUpScreen class with all
- * appropriate elements
- */
- draw() {
- fill(user.colorScheme.text);
- text("Username", 110, 175);
- text("Password", 110, 325);
-
- for (let i = 0; i < this.buttons.length; i++) {
- this.buttons[i].draw();
- }
-
- for (let i = 0; i < this.textboxes.length; i++) {
- this.textboxes[i].draw();
- }
-
- if (this.buttons[0].isPressed()) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=0;
- this.textboxes[this.activeTextBox].line = true;
- } else if (this.buttons[1].isPressed()) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=1;
- this.textboxes[this.activeTextBox].line = true;
- } else if (this.buttons[2].isPressed()) {
- api.login(this.textboxes[0].getWords(), this.textboxes[1].getWords())
- screenManager.setScreen(new StartScreen());
- }
-
- this.menu.draw();
- }
-
- /**
- *
- * @param {key} key
- */
- letterTyped(key) {
- if (key === "Tab" && this.activeTextBox === 0) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox = 1;
- this.textboxes[this.activeTextBox].line = true;
- } else if (key === "Tab" && this.activeTextBox === 1) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox = 0;
- this.textboxes[this.activeTextBox].line = true;
- } else if (key === "Enter") {
- api.login(
- this.textboxes[0].getWords(),
- this.textboxes[1].getWords()
- )
- screenManager.setScreen(new StartScreen());
- } else {
- this.textboxes[this.activeTextBox].letterTyped(key);
- }
- }
-}
\ No newline at end of file
diff --git a/public/screens/profilescreen.js b/public/screens/profilescreen.js
deleted file mode 100755
index 6837d02..0000000
--- a/public/screens/profilescreen.js
+++ /dev/null
@@ -1,111 +0,0 @@
-/**
- * @file This file provides the user with their profilescreen, where they can see their own tests
- * @author Arlo Filley
- *
- * TODO:
- * - change button name
- * - provide filters for tests
- * - implement a way to scroll through tests
- * - create a way to have personal bests and track them
- * - store tests in localstorage.
- * - show user tests even if they are not logged in
- */
-
-/**
- * This class displays all of the test data for a given user
- */
-class ProfileScreen {
- constructor() {
- this.menu = new Menu();
- api.getUserTests();
- this.testButtons;
- // this.buttons = [
- // new Button(950, 270, 240, 120, "up"),
- // new Button(950, 390, 240, 120, "down"),
- // ]
- this.offset = 0;
- this.scroll_bar_button = new Button(1200, 200, 20, 20, "")
- }
-
- draw() {
- textSize(100);
- textAlign(CENTER, CENTER);
- fill(user.colorScheme.text);
- text("Profile", 0, 100, windowWidth, 120);
-
- this.menu.draw();
-
- textSize(20);
- fill(user.colorScheme.text);
- if (user.tests != undefined) {
- if (this.testButtons === undefined) {
- this.createTestButtons();
- }
- }
-
- if (this.testButtons !== undefined && this.testButtons.length > 1) {
- for (let i = 0; i < this.testButtons.length; i++) {
- this.testButtons[i][0].draw()
- this.testButtons[i][1].draw()
- this.testButtons[i][2].draw()
- this.testButtons[i][3].draw()
- }
- } else {
- fill(user.colorScheme.text);
- text("Looks Like You Don't have any tests :(", windowWidth / 2, 300);
- }
-
- fill(user.colorScheme.text);
-
- if (user.tests === undefined) {
- return;
- }
-
- fill(user.colorScheme.testBad);
- rect(1200, 270, 20, 420);
-
- fill(user.colorScheme.testGood);
- rect(1200, 270, 20, 420 / user.tests.length * (this.offset + 1));
- this.scroll_bar_button.height = (user.tests.length)
-
- if (this.scroll_bar_button.isPressed()) {
- this.scroll_bar_button.y = mouseY - this.scroll_bar_button.height / 2;
- }
-
-
- // the furthest up the scrollbar can go is the top
- if (this.scroll_bar_button.y < 270) {
- this.scroll_bar_button.y = 270;
- }
-
- if (this.scroll_bar_button.y > 690 - this.scroll_bar_button.height) {
- this.scroll_bar_button.y = 690 - this.scroll_bar_button.height;
- }
- this.scroll_bar_button.draw();
-
- this.offset = Number((
- // number of pixels from top of screen / total range of options, put to a whole integer to produce the correct offset
- (this.scroll_bar_button.y - 270) / ((420 - this.scroll_bar_button.height) / (user.tests.length - 13))
- ).toFixed(0));
- this.createTestButtons(this.offset);
- }
-
- createTestButtons(offset = 0) {
- this.testButtons = [[
- new Button(600, 270, 100, 30, "test #"), // test # button
- new Button(700, 270, 100, 30, "wpm"), // wpm button
- new Button(800, 270, 100, 30, "accuracy"), // accuracy button
- new Button(900, 270, 240, 30, "characters typed")
- ]];
- let j = 300;
- for (let i = user.tests.length-1-offset; i >= user.tests.length-13-offset && i >= 0; i--) {
- this.testButtons.push([
- new Button(600, j, 100, 30, `${i+1}`, true, true , "#000", "#000", "#fff"), // test # button
- new Button(700, j, 100, 30, `${user.tests[i].wpm}`, true, true , "#000", "#000", "#fff"), // accuracy button
- new Button(800, j, 100, 30, `${user.tests[i].accuracy}`, true, true , "#000", "#000", "#fff"), // wpm button
- new Button(900, j, 240, 30, `${user.tests[i].test_length}`, true, true , "#000", "#000", "#fff")
- ])
- j+=30;
- }
- }
-}
\ No newline at end of file
diff --git a/public/screens/settingsScreen.js b/public/screens/settingsScreen.js
deleted file mode 100755
index 1c44857..0000000
--- a/public/screens/settingsScreen.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * @file This file provides a screen where the user can edit the settings of their tests
- * @author Arlo Filley
- */
-
-/**
- * This class provides all of the necessary settings for the user to be able to edit test settings
- */
-class SettingsScreen {
- constructor() {
- this.menu = new Menu();
- this.timeMenu = new TimeMenu();
- }
-
- draw() {
- textAlign(CENTER, CENTER);
-
- textSize(100);
- fill(user.colorScheme.text);
- text("Test Settings", 0, 100, windowWidth, 110);
-
- this.menu.draw();
-
- fill(user.colorScheme.text);
- text("Test Duration", windowWidth / 2 - 250, 265)
- this.timeMenu.draw();
- fill("#000");
- }
-}
\ No newline at end of file
diff --git a/public/screens/signUpScreen.js b/public/screens/signUpScreen.js
deleted file mode 100755
index 95f2691..0000000
--- a/public/screens/signUpScreen.js
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * @file This file provides a way for the user to sign up for an account
- * @author Arlo Filley
- *
- * TODO:
- * - move into an seperated account page with signup and logout
- * - make passwords not display plain text
- */
-
-/**
- * This class provides the textboxes and methods necessary for a user
- * to sign up for a new account, which it should then log them into
- */
-class SignUpScreen {
- constructor() {
- this.textboxes = [
- new Textbox(
- 120, 250, 500, 100,0, true, "#000", false,
- "#000", "#000", true
- ),
-
- new Textbox(
- 120, 400, 500, 100, 0, true, "#000", false,
- "000", "#000", false
- )
- ]
-
- this.buttons = [
- new Button(
- 100, 200, 500, 100, 0, true, "#000", false,
- "#000", "#fff", ""
- ),
-
- new Button(
- 100, 350, 500, 100, 0, true, "#000", false,
- "#000", "#fff", ""
- ),
-
- new Button(
- 700, 300, 100, 50, 0, true, "#000", false,
- "#000", "#00ff00", "Sign Up"
- ),
- ]
-
- this.menu = new Menu();
-
- this.activeTextBox = 0
- // keeps track of which textbox the user last clicked on
- }
-
- /**
- * Draws the SignUpScreen class with all
- * appropriate elements
- */
- draw() {
- for (let i = 0; i < this.buttons.length; i++) {
- this.buttons[i].draw();
- }
-
- for (let i = 0; i < this.textboxes.length; i++) {
- this.textboxes[i].draw();
- }
-
- fill(user.colorScheme.text);
- text("Username", 110, 175);
- text("Password", 110, 325);
-
- if (this.buttons[0].isPressed()) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=0;
- this.textboxes[this.activeTextBox].line = true;
- } else if (this.buttons[1].isPressed()) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=1;
- this.textboxes[this.activeTextBox].line = true;
- } else if (this.buttons[2].isPressed()) {
- api.createUser(
- this.textboxes[0].getWords(),
- this.textboxes[1].getWords()
- )
- screenManager.setScreen(new StartScreen());
- }
- this.menu.draw();
- }
-
- /**
- *
- * @param {key} key
- */
- letterTyped(key) {
- if (key === "Tab" && this.activeTextBox === 0) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=1;
- this.textboxes[this.activeTextBox].line = true;
- } else if (key === "Tab" && this.activeTextBox === 1) {
- this.textboxes[this.activeTextBox].line = false;
- this.activeTextBox=0;
- this.textboxes[this.activeTextBox].line = true;
- } else if (key === "Enter") {
- api.createUser(
- this.textboxes[0].getWords(),
- this.textboxes[1].getWords()
- )
- screenManager.setScreen(new StartScreen());
- } else {
- this.textboxes[this.activeTextBox].letterTyped(key);
- }
- }
-}
\ No newline at end of file
diff --git a/public/screens/startscreen.js b/public/screens/startscreen.js
deleted file mode 100755
index 6d5aa41..0000000
--- a/public/screens/startscreen.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * @file This file is the base screen when the user visits the site
- * @author Arlo Filley
- */
-
-/**
- * This screen class is the base screen. It provides the user with basic instructions
- * and a set of menus to navigate the site
- */
-class StartScreen {
- constructor() {
- this.menu = new Menu();
- }
-
- draw() {
- textSize(100);
- textAlign(CENTER, CENTER);
- fill(user.colorScheme.text);
- text("Press enter to start test", 0, 0, windowWidth, windowHeight);
-
- this.menu.draw();
-
- fill(user.colorScheme.text);
- }
-
- letterTyped(key) {
- if (key === "Enter") {
- screenManager.setScreen(new TestScreen());
- }
- }
-}
\ No newline at end of file
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 0bda601..ba7d887 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -2,3 +2,4 @@ pub mod leaderboard;
pub mod sql;
pub mod test;
pub mod user;
+pub mod ping;
\ No newline at end of file
diff --git a/src/api/ping.rs b/src/api/ping.rs
new file mode 100644
index 0000000..db8fcb8
--- /dev/null
+++ b/src/api/ping.rs
@@ -0,0 +1,17 @@
+/// # Ping Endpoint
+/// ## GET `/api/ping`
+/// Shows whether the api is up and running. Accessible from http://url/api/ping
+/// ## Responses
+/// - `200 OK`: The api is up and running
+/// ## Example Request
+/// ```bash
+/// curl -X GET "https://url/api/ping"
+/// ```
+/// ## Example Response
+/// ```
+/// "Hello World! I'm A rocket Webserver"
+/// ```
+#[get("/ping")]
+pub fn ping() -> &'static str {
+ "Hello World! I'm A rocket Webserver"
+}
\ No newline at end of file
diff --git a/src/api/sql.rs b/src/api/sql.rs
index e1edf3a..4c076ef 100644
--- a/src/api/sql.rs
+++ b/src/api/sql.rs
@@ -13,7 +13,7 @@
use rocket::serde::{json::Json, Serialize};
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
-use crate::api::test::PostTest;
+use crate::api::user::test::PostTest;
/// Contains the database connection pool
pub struct Database(SqlitePool);
@@ -141,25 +141,17 @@ 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, sqlx::Error> {
- let tests = sqlx::query!(
- "
+ pub async fn get_user_tests(&self, user_id: u32, secret: &str) -> Result, 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
+ user_id, secret
)
.fetch_all(&self.0)
.await?;
- println!("{}", tests.len());
-
let user_tests = tests
.iter()
.map(|test| Test {
@@ -176,12 +168,35 @@ impl Database {
Ok(user_tests)
}
+ pub async fn get_user_test(&self, user_id: u32) -> Result {
+ let test = 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=?
+ ORDER BY test_id DESC
+ LIMIT 1;",
+ user_id
+ )
+ .fetch_one(&self.0)
+ .await?;
+
+ let user_tests = 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,
+ };
+
+ 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, sqlx::Error> {
+ pub async fn get_leaderboard(&self, _user_id: u32) -> Result, sqlx::Error> {
let tests = sqlx::query!(
"SELECT users.username, tests.wpm, tests.accuracy, tests.test_time, tests.test_length
FROM tests
diff --git a/src/api/test.rs b/src/api/test.rs
index 108c281..e379a22 100644
--- a/src/api/test.rs
+++ b/src/api/test.rs
@@ -1,59 +1,40 @@
-use crate::api::sql::Database;
use rand::{rngs::ThreadRng, Rng};
-use rocket::{
- serde::{json::Json, Deserialize},
- State,
-};
+use rocket::serde::json::Json;
+use rocket::http::Status;
use std::{fs, vec};
-/// the datascructure that the webserver will recieve
-/// when a post is made to the http://url/api/post_test route
-#[derive(Deserialize)]
-#[serde(crate = "rocket::serde")]
-pub struct PostTest<'r> {
- pub test_type: &'r str,
- pub test_length: u32,
- pub test_time: u32,
- pub test_seed: i64,
- pub quote_id: i32,
- pub wpm: u8,
- pub accuracy: u8,
- pub user_id: u32,
- pub secret: &'r str,
-}
-
-/// Api Route that accepts test data and posts it to the database
-/// Acessible from http://url/api/post_test
-#[post("/post_test", data = "")]
-pub async fn create_test(test: Json>, database: &State) {
- 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}");
- }
- }
-}
-
/// Returns an array of words as Json
/// Accessible from http://url/api/get_test
-#[get("/new_test")]
-pub fn new_test() -> Json> {
+#[get("/test")]
+pub fn generate_test() -> Result>, Status> {
let mut word_vec: Vec<&str> = vec![];
- let words: String = fs::read_to_string("wordlist.txt").unwrap();
- for word in words.split('\n') {
+
+ // Read file
+ let words = match fs::read_to_string("wordlist.txt") {
+ Err(_) => {
+ println!(" >> wordlist.txt could not be found");
+ return Err(Status::NotFound)
+ },
+ Ok(words) => words,
+ };
+
+ // Populate word_vec
+ for word in words.lines() {
word_vec.push(word);
}
+ // Check if word_vec is empty
+ if word_vec.is_empty() {
+ return Err(Status::InternalServerError);
+ }
+
let mut return_list: Vec = vec![];
let mut rng: ThreadRng = rand::thread_rng();
for _ in 0..100 {
- let word = rng.gen_range(0..999);
- return_list.push(word_vec[word].to_string())
+ let word_index = rng.gen_range(0..word_vec.len());
+ return_list.push(word_vec[word_index].to_string());
}
- Json(return_list.clone())
+ Ok(Json(return_list))
}
diff --git a/src/api/user.rs b/src/api/user.rs
deleted file mode 100644
index 637d4e8..0000000
--- a/src/api/user.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-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 = "")]
-pub async fn sign_up(user: Json>, database: &State) {
- 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//
-///
-/// # 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//")]
-pub async fn get_tests(user_id: u32, secret: &str, database: &State) -> Result>, 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//")]
-pub async fn login(
- username: &str,
- password: &str,
- database: &State,
-) -> Json> {
- 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,
-}
diff --git a/src/api/user/create.rs b/src/api/user/create.rs
new file mode 100644
index 0000000..ac8d762
--- /dev/null
+++ b/src/api/user/create.rs
@@ -0,0 +1,60 @@
+use rocket::{
+ // http::Status,
+ serde::{json::Json, Deserialize},
+ State,
+};
+use crate::api::sql::Database;
+
+use rand::{distributions::Alphanumeric, Rng};
+
+/// Struct representing the user.
+#[derive(Deserialize)]
+#[serde(crate = "rocket::serde")]
+pub struct User<'r> {
+ /// The username of the user.
+ username: &'r str,
+ /// The password of the user.
+ password: &'r str,
+}
+
+/// Creates a new user in the database.
+///
+/// # Endpoint
+///
+/// POST `/api/user/create`
+///
+/// # Parameters
+///
+/// - `user`: Data about the user to be created.
+/// - `database`: Instance of the database state.
+///
+/// # Returns
+///
+/// Upon successful creation, returns a status indicating success (`200 OK`).
+/// If a database error occurs, returns a `500 Internal Server Error`.
+///
+/// # Example Request
+///
+/// ```bash
+/// curl -X POST -H "Content-Type: application/json" -d '{"username":"example_user","password":"example_password"}' https://url/api/user/create
+/// ```
+#[post("/create", data = "")]
+pub async fn create(user: Json>, database: &State) {
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/api/user/login.rs b/src/api/user/login.rs
new file mode 100644
index 0000000..7e9c729
--- /dev/null
+++ b/src/api/user/login.rs
@@ -0,0 +1,69 @@
+use rocket::{
+ http::Status,
+ serde::{json::Json, Serialize},
+ State,
+};
+use crate::api::sql::Database;
+
+/// Logs in the user and returns the user ID and secret.
+///
+/// # Endpoint
+///
+/// GET `/api/user/login//`
+///
+/// # Path Parameters
+///
+/// - `username`: The username of the user.
+/// - `password`: The password of the user.
+///
+/// # Returns
+///
+/// Returns the user ID and secret upon successful login.
+///
+/// If the login credentials are invalid, returns a `401 Unauthorized` status.
+///
+/// If any database-related error occurs, returns a `500 Internal Server Error`.
+///
+/// # Example Request
+///
+/// ```bash
+/// curl -X GET "https://url/api/login/example_user/example_password"
+/// ```
+///
+/// # Example Response
+///
+/// ```json
+/// {
+/// "user_id": "1234567890",
+/// "secret": "abcdefghijklmnopqrstuvwxyz"
+/// }
+/// ```
+#[get("/login//")]
+pub async fn login(username: &str, password: &str, database: &State) -> Result, Status> {
+ match database
+ .find_user(username, &sha256::digest(password))
+ .await
+ {
+ Err(why) => {
+ println!("A database error occured during login for {username}, {why}");
+ return Err(Status::InternalServerError);
+ }
+ Ok(user) => match user {
+ None => return Err(Status::Unauthorized),
+ Some(user) => Ok(Json(LoginResponse {
+ user_id: user.0,
+ secret: user.1,
+ })),
+ },
+ }
+}
+
+/// Struct representing the response of login API.
+#[derive(Serialize)]
+#[serde(crate = "rocket::serde")]
+pub struct LoginResponse {
+ /// The user ID of the logged-in user.
+ user_id: u32,
+ /// The secret key associated with the user.
+ secret: String,
+}
\ No newline at end of file
diff --git a/src/api/user/mod.rs b/src/api/user/mod.rs
new file mode 100644
index 0000000..2b6d1aa
--- /dev/null
+++ b/src/api/user/mod.rs
@@ -0,0 +1,4 @@
+pub mod login;
+pub mod tests;
+pub mod test;
+pub mod create;
\ No newline at end of file
diff --git a/src/api/user/test.rs b/src/api/user/test.rs
new file mode 100644
index 0000000..ac76a9f
--- /dev/null
+++ b/src/api/user/test.rs
@@ -0,0 +1,146 @@
+use rocket::serde::{json::Json, Deserialize};
+use rocket::State;
+use rocket::http::Status;
+use crate::api::sql::Database;
+use crate::api::sql::Test;
+
+/// the datascructure that the webserver will recieve
+/// when a post is made to the http://url/api/post_test route
+#[derive(Deserialize)]
+#[serde(crate = "rocket::serde")]
+pub struct PostTest<'r> {
+ pub test_type: &'r str,
+ pub test_length: u32,
+ pub test_time: u32,
+ pub test_seed: i64,
+ pub quote_id: i32,
+ pub wpm: u8,
+ pub accuracy: u8,
+ pub user_id: u32,
+ pub secret: &'r str,
+}
+
+/// Takes a test a user has completed and adds it tho the database
+///
+/// # Endpoint
+///
+/// GET `/api/user/test`
+///
+/// # Path Parameters
+///
+/// `test_type`: Type of typing test the user has completed (CURRENTLY ONLY "WORDS")
+/// `test_length`: Number of characters the user typed
+/// `test_time`: Time in seconds that the test took to complet
+/// `test_seed`: Seed used to generate the test (NOT CURRENTLY IN USE)
+/// `quote_id`: Quote ID that the user typed (NOT CURRENTLY IN USE)
+/// `wpm`: Number of words per minute the user typed (A WORD IS COUNTED AS 5 CHARACTERS)
+/// `accuracy`: Percentage accuracy that the user achieved in the test
+/// `user_id`: User ID of the user whose completed the test
+/// `secret`: Secret key for authentication.
+///
+/// # Returns
+///
+/// Returns the latest test of the user if the user is authenticated and the test is found.
+///
+/// `401 UNAUTHORIZED` - The user has failed to be authenticated
+/// `500 INTERNAL SERVER ERROR` - The test could not be added to the database
+///
+/// # Example Request
+///
+/// ```bash
+/// curl -X GET "https://url/api/user/latest_user_test/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
+/// }
+/// ```
+#[post("/test", data = "")]
+pub async fn post_test(test: Json>, database: &State) -> Status {
+ match database.authenticate_user(test.user_id, test.secret).await {
+ Err(_) => return Status::InternalServerError,
+ Ok(authenticated) => {
+ if !authenticated { return Status::Unauthorized }
+ }
+ }
+
+ 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}");
+ }
+ }
+
+ Status::Accepted
+}
+
+/// Retrieves the latest test associated with a specific user from the database and returns it as JSON.
+///
+/// # Endpoint
+///
+/// GET `/api/user/latest_test//`
+///
+/// # Path Parameters
+///
+/// - `user_id`: User ID of the user whose latest test needs to be retrieved.
+/// - `secret`: Secret key for authentication.
+///
+/// # Returns
+///
+/// Returns the latest test of the user if the user is authenticated and the test is found.
+///
+/// If the user authentication fails, returns a `401 Unauthorized` status.
+///
+/// If the test is not found or any database-related error occurs, returns a `404 Not Found` status.
+///
+/// # Example Request
+///
+/// ```bash
+/// curl -X GET "https://url/api/user/latest_user_test/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
+/// }
+/// ```
+#[get("/test//")]
+pub async fn get_latest_test(user_id: u32, secret: &str, database: &State) -> Result, 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_test(user_id).await {
+ Err(why) => {
+ println!(" >> A database error occured during getting_tests, {why}");
+ Err(Status::NotFound)
+ }
+ Ok(test) => {
+ println!(" >> Succesfully Found latestTest for User {user_id}");
+ Ok(Json(test))
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/api/user/tests.rs b/src/api/user/tests.rs
new file mode 100644
index 0000000..ff8a65b
--- /dev/null
+++ b/src/api/user/tests.rs
@@ -0,0 +1,74 @@
+use rocket::{
+ http::Status,
+ serde::json::Json,
+ State,
+};
+use crate::api::sql::{Database, Test};
+
+/// Retrieves tests associated with a specific user from the database and returns them as a JSON array.
+///
+/// # Endpoint
+///
+/// GET /api/get_tests//
+///
+/// # 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://url/api/user/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("/tests//")]
+pub async fn tests(user_id: u32, secret: &str, database: &State) -> Result>, 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}", tests.len());
+ Ok(Json(tests))
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 6a6a89a..19606fd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,10 +22,20 @@ mod api;
mod catchers;
mod redirects;
-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 crate::api::{
+ sql::Database,
+
+ user::{
+ create::create,
+ login::login,
+ tests::tests,
+ test::get_latest_test,
+ test::post_test,
+ },
+ test::generate_test,
+ leaderboard::leaderboard,
+ ping::ping,
+};
use crate::catchers::not_found::api_not_found;
use crate::catchers::not_found::frontend_not_found;
@@ -33,16 +43,6 @@ use crate::catchers::not_found::documentation_not_found;
use crate::redirects::leaderboard_redirect;
use crate::redirects::typing_redirect;
-
-// Imports for sql, see sql.rs for more information
-
-/// Test api route that returns hello world.
-/// Acessible from http://url/test
-#[get("/")]
-fn test() -> &'static str {
- "Hello World! I'm A rocket Webserver"
-}
-
/// The main function which builds and launches the
/// webserver with all appropriate routes and fileservers
#[launch]
@@ -53,19 +53,22 @@ async fn rocket() -> Rocket {
// 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![
- sign_up,
+ .mount("/api/user", routes![
+ create,
login,
-
- new_test,
- create_test,
- get_tests,
- leaderboard
+ tests,
+ post_test,
+ get_latest_test
+ ])
+ .mount("/api", routes![
+ ping,
+ leaderboard,
+ generate_test
])
.register("/api", catchers![api_not_found])
+ .mount("/api/documentation", FileServer::from(relative!("documentation/html")))
+ .register("/api/documentation", catchers![documentation_not_found])
// hosts the fileserver
.mount("/typing", FileServer::from(relative!("public")))
@@ -78,5 +81,5 @@ async fn rocket() -> Rocket {
.mount("/leaderboard", routes![leaderboard_redirect])
// testing only, should return "Hello world"
- .mount("/test", routes![test])
+
}