Merge pull request 'Changed to separate page structure & api updates' (#1) from feature/html-navbar into master
Reviewed-on: #1
@ -41,7 +41,7 @@ This project is a web server built using the Rocket framework in Rust. It provid
|
||||
## Dependencies
|
||||
- **Rocket**: Web framework for Rust.
|
||||
- **Serde**: Serialization and deserialization library for Rust.
|
||||
- **Rusqlite**: SQLite database driver for Rust.
|
||||
- **SQLX**: SQLite database driver for Rust.
|
||||
- **Rand**: Random number generation library for Rust.
|
||||
|
||||
## Contributors
|
||||
|
@ -1,113 +0,0 @@
|
||||
<html>
|
||||
<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>
|
||||
<p>Post Test Data This API endpoint allows you to post test data, recording the results of a test taken by a user.</p>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Endpoint</h1><a id="user-content-endpoint" class="anchor" aria-label="Permalink: Endpoint" href="#endpoint"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<pre><code>POST /api/post_test
|
||||
</code></pre>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Request Parameters</h1><a id="user-content-request-parameters" class="anchor" aria-label="Permalink: Request Parameters" href="#request-parameters"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th align="center">Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>testType</td>
|
||||
<td align="center">String</td>
|
||||
<td>Type of the test (e.g., "typing", "multiple choice")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>testLength</td>
|
||||
<td align="center">Integer</td>
|
||||
<td>Length of the test in number of items or questions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>testTime</td>
|
||||
<td align="center">Integer</td>
|
||||
<td>Duration of the test in seconds</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>testSeed</td>
|
||||
<td align="center">String</td>
|
||||
<td>Seed for generating randomized test content</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>quoteId</td>
|
||||
<td align="center">String</td>
|
||||
<td>Identifier for a specific quote, if applicable</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>wpm</td>
|
||||
<td align="center">Integer</td>
|
||||
<td>Words per minute (typing speed)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>accuracy</td>
|
||||
<td align="center">Integer</td>
|
||||
<td>Accuracy of responses (e.g., percentage)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>userId</td>
|
||||
<td align="center">String</td>
|
||||
<td>Identifier of the user taking the test</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>secret</td>
|
||||
<td align="center">String</td>
|
||||
<td>Secret key for authentication and authorization</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Example Request</h1><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X POST <span class="pl-s"><span class="pl-pds">"</span>https://example.com/api/post_test<span class="pl-pds">"</span></span> \
|
||||
-H <span class="pl-s"><span class="pl-pds">"</span>Content-Type: application/json<span class="pl-pds">"</span></span> <span class="pl-cce">\ </span>
|
||||
-d <span class="pl-s"><span class="pl-pds">'</span>{ </span>
|
||||
<span class="pl-s"> "testType": "typing", </span>
|
||||
<span class="pl-s"> "testLength": 100, </span>
|
||||
<span class="pl-s"> "testTime": 600, </span>
|
||||
<span class="pl-s"> "testSeed": "random_seed_123", </span>
|
||||
<span class="pl-s"> "quoteId": "quote_456", </span>
|
||||
<span class="pl-s"> "wpm": 65.5, </span>
|
||||
<span class="pl-s"> "accuracy": 98.2, </span>
|
||||
<span class="pl-s"> "userId": "user_789", </span>
|
||||
<span class="pl-s"> "secret": "your_secret_key_here" </span>
|
||||
<span class="pl-s"> }<span class="pl-pds">'</span></span></pre></div>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Example Response</h1><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Upon successful submission, you will receive a JSON response with the following structure:</p>
|
||||
<div class="highlight highlight-source-json"><pre>{
|
||||
<span class="pl-ent">"status"</span>: <span class="pl-s"><span class="pl-pds">"</span>success<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"message"</span>: <span class="pl-s"><span class="pl-pds">"</span>Test results successfully recorded<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"testId"</span>: <span class="pl-s"><span class="pl-pds">"</span>test_123456789<span class="pl-pds">"</span></span>
|
||||
}</pre></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>status</code>: Indicates the status of the request (either "success" or "error").</li>
|
||||
<li>
|
||||
<code>message</code>: Describes the outcome of the request.</li>
|
||||
<li>
|
||||
<code>testId</code>: Unique identifier assigned to the recorded test data.</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
@ -1,54 +0,0 @@
|
||||
Post Test Data This API endpoint allows you to post test data, recording the results of a test taken by a user.
|
||||
|
||||
# Endpoint
|
||||
|
||||
```
|
||||
POST /api/post_test
|
||||
```
|
||||
|
||||
# Request Parameters
|
||||
| Parameter | Type | Description |
|
||||
| ---------- | :-----: | ---------------------------------------------------- |
|
||||
| testType | String | Type of the test (e.g., "typing", "multiple choice") |
|
||||
| testLength | Integer | Length of the test in number of items or questions |
|
||||
| testTime | Integer | Duration of the test in seconds |
|
||||
| testSeed | String | Seed for generating randomized test content |
|
||||
| quoteId | String | Identifier for a specific quote, if applicable |
|
||||
| wpm | Integer | Words per minute (typing speed) |
|
||||
| accuracy | Integer | Accuracy of responses (e.g., percentage) |
|
||||
| userId | String | Identifier of the user taking the test |
|
||||
| secret | String | Secret key for authentication and authorization |
|
||||
|
||||
# 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"
|
||||
}'
|
||||
```
|
||||
|
||||
# Example Response
|
||||
|
||||
Upon successful submission, you will receive a JSON response with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Test results successfully recorded",
|
||||
"testId": "test_123456789"
|
||||
}
|
||||
```
|
||||
|
||||
- `status`: Indicates the status of the request (either "success" or "error").
|
||||
- `message`: Describes the outcome of the request.
|
||||
- `testId`: Unique identifier assigned to the recorded test data.
|
@ -1,36 +0,0 @@
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Create User</h1><a id="user-content-create-user" class="anchor" aria-label="Permalink: Create User" href="#create-user"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Creates a new user in the database.
|
||||
Endpoint</p>
|
||||
<div class="highlight highlight-source-js"><pre><span class="pl-c1">POST</span> <span class="pl-c1">/</span><span class="pl-s1">api</span><span class="pl-c1">/</span><span class="pl-s1">create_user</span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Request Body</h2><a id="user-content-request-body" class="anchor" aria-label="Permalink: Request Body" href="#request-body"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>{
|
||||
<span class="pl-ent">"username"</span>: <span class="pl-s"><span class="pl-pds">"</span>example_user<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"password"</span>: <span class="pl-s"><span class="pl-pds">"</span>example_password<span class="pl-pds">"</span></span>
|
||||
<br>}</pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X POST <span class="pl-s"><span class="pl-pds">"</span>https://example.com/api/create_user<span class="pl-pds">"</span></span> \
|
||||
-H <span class="pl-s"><span class="pl-pds">"</span>Content-Type: application/json<span class="pl-pds">"</span></span> \
|
||||
-d <span class="pl-s"><span class="pl-pds">'</span>{"username": "example_user", "password": "example_password"}<span class="pl-pds">'</span></span></pre></div>
|
||||
</body>
|
||||
</html>
|
@ -4,12 +4,27 @@
|
||||
|
||||
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.
|
||||
|
||||
## Request Parameters
|
||||
|
||||
- `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.
|
||||
|
||||
## Responses
|
||||
|
||||
- `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.
|
||||
|
||||
|
||||
## Example Request
|
||||
|
||||
```bash
|
||||
curl -X GET "https://url/api/leaderboard"
|
||||
```
|
||||
|
||||
## Example Response
|
||||
|
||||
```json
|
||||
@ -31,10 +46,3 @@ Returns the highest test data from each user as a JSON array. The data includes
|
||||
]
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
- `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.
|
21
documentation/get_ping.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Ping API 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"
|
||||
```
|
39
documentation/get_test.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Test API Endpoint
|
||||
|
||||
## GET `/api/test`
|
||||
|
||||
Returns 100 random words to be used to give a typing test. Retrieved from `wordlist.txt`
|
||||
|
||||
## Responses
|
||||
|
||||
- `200 OK`: Successfully retrieves the 100 words.
|
||||
- `404 Not Found`: Indicates that the wordlist was not found.
|
||||
|
||||
## Example Request
|
||||
|
||||
```bash
|
||||
curl -X GET "https://url/api/test"
|
||||
```
|
||||
|
||||
## Example Response
|
||||
|
||||
```json
|
||||
[
|
||||
"separate",
|
||||
"stand",
|
||||
"think",
|
||||
"island",
|
||||
"have",
|
||||
"air",
|
||||
"heard",
|
||||
"notice",
|
||||
"yellow",
|
||||
"smell",
|
||||
"heart",
|
||||
"island",
|
||||
"chief",
|
||||
"view",
|
||||
"top",
|
||||
...
|
||||
]
|
||||
```
|
31
documentation/get_user_login.md
Normal file
@ -0,0 +1,31 @@
|
||||
# User Login Endpoint
|
||||
|
||||
## 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.
|
||||
|
||||
## Request Parameters
|
||||
|
||||
- `username`: The username of the user.
|
||||
- `password`: The password of the user.
|
||||
|
||||
## Responses
|
||||
|
||||
- `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.
|
||||
|
||||
## Example Request
|
||||
|
||||
```bash
|
||||
curl -X GET "https://url/api/user/login/example_user/example_password"
|
||||
```
|
||||
|
||||
## Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"user_id": "1234567890",
|
||||
"secret": "abcdefghijklmnopqrstuvwxyz"
|
||||
}
|
||||
```
|
30
documentation/get_user_test.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Get User Test
|
||||
|
||||
## GET `/api/user/test/<user_id>/<secret>`
|
||||
|
||||
Retrieves tests associated with a specific user from the database.
|
||||
|
||||
## Parameters
|
||||
|
||||
- `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://url/api/user/test/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
|
||||
}
|
||||
```
|
@ -1,61 +0,0 @@
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Get User Tests</h1><a id="user-content-get-user-tests" class="anchor" aria-label="Permalink: Get User Tests" href="#get-user-tests"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Retrieves tests associated with a specific user from the database.</p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Endpoint</h2><a id="user-content-endpoint" class="anchor" aria-label="Permalink: Endpoint" href="#endpoint"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-js"><pre><span class="pl-c1">GET</span> <span class="pl-c1">/</span><span class="pl-s1">api</span><span class="pl-c1">/</span><span class="pl-s1">get_tests</span><span class="pl-c1">/</span><span class="pl-c1"><</span><span class="pl-ent">user_id</span><span class="pl-c1">></span>/<span class="pl-c1"><</span><span class="pl-ent">secret</span><span class="pl-c1">></span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Parameters</h2><a id="user-content-parameters" class="anchor" aria-label="Permalink: Parameters" href="#parameters"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>user_id</td>
|
||||
<td>integer</td>
|
||||
<td>User ID of the user whose tests need to be retrieved</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>secret</td>
|
||||
<td>String</td>
|
||||
<td>Secret key for authentication</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X GET <span class="pl-s"><span class="pl-pds">"</span>https://example.com/api/get_tests/123/your_secret_key_here<span class="pl-pds">"</span></span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Response</h2><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>{
|
||||
<span class="pl-ent">"test_type"</span>: <span class="pl-s"><span class="pl-pds">"</span>words<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"test_length"</span>: <span class="pl-c1">100</span>,
|
||||
<span class="pl-ent">"test_time"</span>: <span class="pl-c1">300</span>,
|
||||
<span class="pl-ent">"test_seed"</span>: <span class="pl-c1">987654321</span>,
|
||||
<span class="pl-ent">"quote_id"</span>: <span class="pl-c1">123</span>,
|
||||
<span class="pl-ent">"wpm"</span>: <span class="pl-c1">65</span>,
|
||||
<span class="pl-ent">"accuracy"</span>: <span class="pl-c1">98</span>
|
||||
<br>}</pre></div>
|
||||
</body>
|
||||
</html>
|
@ -1,36 +1,42 @@
|
||||
# Get User Tests
|
||||
|
||||
## GET `/api/user/tests/<user_id>/<secret>`
|
||||
|
||||
Retrieves tests associated with a specific user from the database.
|
||||
|
||||
## Endpoint
|
||||
## Request Parameters
|
||||
|
||||
```js
|
||||
GET /api/get_tests/<user_id>/<secret>
|
||||
```
|
||||
|
||||
## 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,
|
||||
"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
|
||||
}
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
70
documentation/html/get_leaderboard.html
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Leaderboard API Endpoint</h1><a id="user-content-leaderboard-api-endpoint" class="anchor" aria-label="Permalink: Leaderboard API Endpoint" href="#leaderboard-api-endpoint"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">GET <code>/api/leaderboard</code>
|
||||
</h2><a id="user-content-get-apileaderboard" class="anchor" aria-label="Permalink: GET /api/leaderboard" href="#get-apileaderboard"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>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.</p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Request Parameters</h2><a id="user-content-request-parameters" class="anchor" aria-label="Permalink: Request Parameters" href="#request-parameters"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>username</code>: The name of the user.</li>
|
||||
<li>
|
||||
<code>wpm</code>: Words per minute, indicating the typing speed.</li>
|
||||
<li>
|
||||
<code>accuracy</code>: The accuracy of the user's typing, in percentage.</li>
|
||||
<li>
|
||||
<code>test_time</code>: The total time taken to complete the test, in seconds.</li>
|
||||
<li>
|
||||
<code>test_length</code>: The length of the test, typically measured in number of words.</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Responses</h2><a id="user-content-responses" class="anchor" aria-label="Permalink: Responses" href="#responses"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>200 OK</code>: Successfully retrieves the leaderboard data.</li>
|
||||
<li>
|
||||
<code>404 Not Found</code>: Indicates that the leaderboard was not found.</li>
|
||||
<li>
|
||||
<code>500 Internal Server Error</code>: Indicates an issue with accessing the database.</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X GET <span class="pl-s"><span class="pl-pds">"</span>https://url/api/leaderboard<span class="pl-pds">"</span></span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Response</h2><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>[
|
||||
{
|
||||
<span class="pl-ent">"username"</span>: <span class="pl-s"><span class="pl-pds">"</span>user1<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"wpm"</span>: <span class="pl-c1">75</span>,
|
||||
<span class="pl-ent">"accuracy"</span>: <span class="pl-c1">97</span>,
|
||||
<span class="pl-ent">"test_time"</span>: <span class="pl-c1">120</span>,
|
||||
<span class="pl-ent">"test_length"</span>: <span class="pl-c1">250</span>
|
||||
},
|
||||
{
|
||||
<span class="pl-ent">"username"</span>: <span class="pl-s"><span class="pl-pds">"</span>user2<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"wpm"</span>: <span class="pl-c1">73</span>,
|
||||
<span class="pl-ent">"accuracy"</span>: <span class="pl-c1">95</span>,
|
||||
<span class="pl-ent">"test_time"</span>: <span class="pl-c1">115</span>,
|
||||
<span class="pl-ent">"test_length"</span>: <span class="pl-c1">240</span>
|
||||
}
|
||||
]</pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
39
documentation/html/get_ping.html
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Ping API Endpoint</h1><a id="user-content-ping-api-endpoint" class="anchor" aria-label="Permalink: Ping API Endpoint" href="#ping-api-endpoint"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">GET <code>/api/ping</code>
|
||||
</h2><a id="user-content-get-apiping" class="anchor" aria-label="Permalink: GET /api/ping" href="#get-apiping"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Shows whether the api is up and running. Accessible from <a href="http://url/api/ping" rel="nofollow">http://url/api/ping</a></p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Responses</h2><a id="user-content-responses" class="anchor" aria-label="Permalink: Responses" href="#responses"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>200 OK</code>: The api is up and running</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X GET <span class="pl-s"><span class="pl-pds">"</span>https://url/api/ping</span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Response</h2><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<pre><code>"Hello World! I'm A rocket Webserver"
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
||||
|
57
documentation/html/get_test.html
Normal file
@ -0,0 +1,57 @@
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Test API Endpoint</h1><a id="user-content-test-api-endpoint" class="anchor" aria-label="Permalink: Test API Endpoint" href="#test-api-endpoint"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">GET <code>/api/test</code>
|
||||
</h2><a id="user-content-get-apitest" class="anchor" aria-label="Permalink: GET /api/test" href="#get-apitest"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Returns 100 random words to be used to give a typing test. Retrieved from <code>wordlist.txt</code></p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Responses</h2><a id="user-content-responses" class="anchor" aria-label="Permalink: Responses" href="#responses"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>200 OK</code>: Successfully retrieves the 100 words.</li>
|
||||
<li>
|
||||
<code>404 Not Found</code>: Indicates that the wordlist was not found.</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X GET <span class="pl-s"><span class="pl-pds">"</span>https://url/api/test<span class="pl-pds">"</span></span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Response</h2><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>[
|
||||
<span class="pl-s"><span class="pl-pds">"</span>separate<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>stand<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>think<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>island<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>have<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>air<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>heard<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>notice<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>yellow<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>smell<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>heart<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>island<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>chief<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>view<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-s"><span class="pl-pds">"</span>top<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ii">...</span>
|
||||
]</pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
52
documentation/html/get_user_login.html
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">User Login Endpoint</h1><a id="user-content-user-login-endpoint" class="anchor" aria-label="Permalink: User Login Endpoint" href="#user-login-endpoint"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">GET <code>/api/user/login/<username>/<password></code>
|
||||
</h2><a id="user-content-get-apiuserloginusernamepassword" class="anchor" aria-label="Permalink: GET /api/user/login/<username>/<password>" href="#get-apiuserloginusernamepassword"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Takes the user's login information and returns the user's user ID, which can be used to identify their tests, etc. Accessible from <a href="http://url/api/login" rel="nofollow">http://url/api/login</a>.</p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Request Parameters</h2><a id="user-content-request-parameters" class="anchor" aria-label="Permalink: Request Parameters" href="#request-parameters"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>username</code>: The username of the user.</li>
|
||||
<li>
|
||||
<code>password</code>: The password of the user.</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Responses</h2><a id="user-content-responses" class="anchor" aria-label="Permalink: Responses" href="#responses"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>200 OK</code>: Successfully retrieves the user ID.</li>
|
||||
<li>
|
||||
<code>401 Unauthorized</code>: Indicates that the login credentials are invalid.</li>
|
||||
<li>
|
||||
<code>500 Internal Server Error</code>: Indicates an issue with accessing the database.</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X GET <span class="pl-s"><span class="pl-pds">"</span>https://url/api/user/login/example_user/example_password<span class="pl-pds">"</span></span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Response</h2><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>{
|
||||
<span class="pl-ent">"user_id"</span>: <span class="pl-s"><span class="pl-pds">"</span>1234567890<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"secret"</span>: <span class="pl-s"><span class="pl-pds">"</span>abcdefghijklmnopqrstuvwxyz<span class="pl-pds">"</span></span>
|
||||
}</pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
47
documentation/html/get_user_test.html
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Get User Test</h1><a id="user-content-get-user-test" class="anchor" aria-label="Permalink: Get User Test" href="#get-user-test"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element"><code>GET /api/user/test/<user_id>/<secret></code></h2><a id="user-content-get-apiusertestuser_idsecret" class="anchor" aria-label="Permalink: GET /api/user/test/<user_id>/<secret>" href="#get-apiusertestuser_idsecret"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Retrieves tests associated with a specific user from the database.</p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Parameters</h2><a id="user-content-parameters" class="anchor" aria-label="Permalink: Parameters" href="#parameters"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>user_id</code> - User ID of the user whose tests need to be retrieved</li>
|
||||
<li>
|
||||
<code>secret</code> - Secret key for authentication</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X GET <span class="pl-s"><span class="pl-pds">"</span>https://url/api/user/test/123/your_secret_key_here<span class="pl-pds">"</span></span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Response</h2><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>{
|
||||
<span class="pl-ent">"test_type"</span>: <span class="pl-s"><span class="pl-pds">"</span>words<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"test_length"</span>: <span class="pl-c1">100</span>,
|
||||
<span class="pl-ent">"test_time"</span>: <span class="pl-c1">300</span>,
|
||||
<span class="pl-ent">"test_seed"</span>: <span class="pl-c1">987654321</span>,
|
||||
<span class="pl-ent">"quote_id"</span>: <span class="pl-c1">123</span>,
|
||||
<span class="pl-ent">"wpm"</span>: <span class="pl-c1">65</span>,
|
||||
<span class="pl-ent">"accuracy"</span>: <span class="pl-c1">98</span>
|
||||
}</pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
60
documentation/html/get_user_tests.html
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Get User Tests</h1><a id="user-content-get-user-tests" class="anchor" aria-label="Permalink: Get User Tests" href="#get-user-tests"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">GET <code>/api/user/tests/<user_id>/<secret></code>
|
||||
</h2><a id="user-content-get-apiusertestsuser_idsecret" class="anchor" aria-label="Permalink: GET /api/user/tests/<user_id>/<secret>" href="#get-apiusertestsuser_idsecret"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Retrieves tests associated with a specific user from the database.</p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Request Parameters</h2><a id="user-content-request-parameters" class="anchor" aria-label="Permalink: Request Parameters" href="#request-parameters"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>user_id</code> - User ID of the user whose tests need to be retrieved</li>
|
||||
<li>
|
||||
<code>secret</code> - Secret key for authentication</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X GET <span class="pl-s"><span class="pl-pds">"</span>https://url/api/user/tests/123/your_secret_key_here<span class="pl-pds">"</span></span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Response</h2><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>[
|
||||
{
|
||||
<span class="pl-ent">"test_type"</span>: <span class="pl-s"><span class="pl-pds">"</span>words<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"test_length"</span>: <span class="pl-c1">300</span>,
|
||||
<span class="pl-ent">"test_time"</span>: <span class="pl-c1">60</span>,
|
||||
<span class="pl-ent">"test_seed"</span>: <span class="pl-c1">0</span>,
|
||||
<span class="pl-ent">"quote_id"</span>: <span class="pl-c1">0</span>,
|
||||
<span class="pl-ent">"wpm"</span>: <span class="pl-c1">60</span>,
|
||||
<span class="pl-ent">"accuracy"</span>: <span class="pl-c1">100</span>
|
||||
},
|
||||
{
|
||||
<span class="pl-ent">"test_type"</span>: <span class="pl-s"><span class="pl-pds">"</span>words<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"test_length"</span>: <span class="pl-c1">47</span>,
|
||||
<span class="pl-ent">"test_time"</span>: <span class="pl-c1">15</span>,
|
||||
<span class="pl-ent">"test_seed"</span>: <span class="pl-c1">0</span>,
|
||||
<span class="pl-ent">"quote_id"</span>: <span class="pl-c1">0</span>,
|
||||
<span class="pl-ent">"wpm"</span>: <span class="pl-c1">37</span>,
|
||||
<span class="pl-ent">"accuracy"</span>: <span class="pl-c1">98</span>
|
||||
},
|
||||
<span class="pl-ii">...</span>
|
||||
]</pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
39
documentation/html/index.html
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Routes</h1><a id="user-content-routes" class="anchor" aria-label="Permalink: Routes" href="#routes"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element"><code>/api</code></h2><a id="user-content-api" class="anchor" aria-label="Permalink: /api" href="#api"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h3 class="heading-element"><code>/user</code></h3><a id="user-content-user" class="anchor" aria-label="Permalink: /user" href="#user"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li><a href="./post_user_create.html">POST <code>/create</code></a></li>
|
||||
<li><a href="./get_user_login.html">GET <code>/login</code></a></li>
|
||||
<li><a href="./get_user_tests.html">GET <code>/tests</code></a></li>
|
||||
<li><a href="./post_user_test.html">POST <code>/test</code></a></li>
|
||||
<li><a href="./get_user_test.html">GET <code>/test</code></a></li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h3 class="heading-element"><a href="./get_ping.html">GET <code>/ping</code></a></h3><a id="user-content-get-ping" class="anchor" aria-label="Permalink: GET /ping" href="#get-ping"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h3 class="heading-element"><a href="./get_leaderboard.html">GET <code>/leaderboard</code></a></h3><a id="user-content-get-leaderboard" class="anchor" aria-label="Permalink: GET /leaderboard" href="#get-leaderboard"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h3 class="heading-element"><a href="./get_test.html">GET <code>/test</code></a></h3><a id="user-content-get-test" class="anchor" aria-label="Permalink: GET /test" href="#get-test"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h3 class="heading-element"><code>/Documentation</code></h3><a id="user-content-documentation" class="anchor" aria-label="Permalink: /Documentation" href="#documentation"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
</body>
|
||||
</html>
|
||||
|
38
documentation/html/post_user_create.html
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Create User API Endpoint</h1><a id="user-content-create-user-api-endpoint" class="anchor" aria-label="Permalink: Create User API Endpoint" href="#create-user-api-endpoint"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">POST <code>/api/user/create</code>
|
||||
</h2><a id="user-content-post-apiusercreate" class="anchor" aria-label="Permalink: POST /api/user/create" href="#post-apiusercreate"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Creates a new user in the database.</p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Request Parameters</h2><a id="user-content-request-parameters" class="anchor" aria-label="Permalink: Request Parameters" href="#request-parameters"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>{
|
||||
<span class="pl-ent">"username"</span>: <span class="pl-s"><span class="pl-pds">"</span>example_user<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"password"</span>: <span class="pl-s"><span class="pl-pds">"</span>example_password<span class="pl-pds">"</span></span>
|
||||
}</pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X POST <span class="pl-s"><span class="pl-pds">"</span>https://url/api/user/create<span class="pl-pds">"</span></span> \
|
||||
-H <span class="pl-s"><span class="pl-pds">"</span>Content-Type: application/json<span class="pl-pds">"</span></span> \
|
||||
-d <span class="pl-s"><span class="pl-pds">'</span>{"username": "example_user", "password": "example_password"}<span class="pl-pds">'</span></span></pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
73
documentation/html/post_user_test.html
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Create Test API Endpoint</h1><a id="user-content-create-test-api-endpoint" class="anchor" aria-label="Permalink: Create Test API Endpoint" href="#create-test-api-endpoint"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">POST <code>/api/post_test</code>
|
||||
</h2><a id="user-content-post-apipost_test" class="anchor" aria-label="Permalink: POST /api/post_test" href="#post-apipost_test"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Post Test Data This API endpoint allows you to post test data, recording the results of a test taken by a user.</p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Request Parameters</h2><a id="user-content-request-parameters" class="anchor" aria-label="Permalink: Request Parameters" href="#request-parameters"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>testType</code> - Type of the test ("words", "time", "quote", etc)</li>
|
||||
<li>
|
||||
<code>testLength</code> - Length of the test in number of items or</li>
|
||||
<li>
|
||||
<code>testTime</code> - Duration of the test in seconds</li>
|
||||
<li>
|
||||
<code>testSeed</code> - Seed for generating randomized test content</li>
|
||||
<li>
|
||||
<code>quoteId</code> - Identifier for a specific quote, if applicable</li>
|
||||
<li>
|
||||
<code>wpm</code> - Words per minute (typing speed)</li>
|
||||
<li>
|
||||
<code>accuracy</code> - Accuracy of responses (e.g., percentage)</li>
|
||||
<li>
|
||||
<code>userId</code> - Identifier of the user taking the test</li>
|
||||
<li>
|
||||
<code>secret</code> - Secret key for authentication and authorization</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Responses</h2><a id="user-content-responses" class="anchor" aria-label="Permalink: Responses" href="#responses"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>200 OK</code> - The test has been added to the database sucessfully</li>
|
||||
<li>
|
||||
<code>401 UNAUTHORIZED</code> - The user has not been authenticated correctly</li>
|
||||
<li>
|
||||
<code>500 INTERNAL SERVER ERROR</code> - There has been a database error when attempting to</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X POST <span class="pl-s"><span class="pl-pds">"</span>https://example.com/api/post_test<span class="pl-pds">"</span></span> \
|
||||
-H <span class="pl-s"><span class="pl-pds">"</span>Content-Type: application/json<span class="pl-pds">"</span></span> <span class="pl-cce">\ </span>
|
||||
-d <span class="pl-s"><span class="pl-pds">'</span>{ </span>
|
||||
<span class="pl-s"> "testType": "typing", </span>
|
||||
<span class="pl-s"> "testLength": 100, </span>
|
||||
<span class="pl-s"> "testTime": 600, </span>
|
||||
<span class="pl-s"> "testSeed": "random_seed_123", </span>
|
||||
<span class="pl-s"> "quoteId": "quote_456", </span>
|
||||
<span class="pl-s"> "wpm": 65.5, </span>
|
||||
<span class="pl-s"> "accuracy": 98.2, </span>
|
||||
<span class="pl-s"> "userId": "user_789", </span>
|
||||
<span class="pl-s"> "secret": "your_secret_key_here" </span>
|
||||
<span class="pl-s"> }<span class="pl-pds">'</span></span></pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,32 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Links</h1><a id="user-content-links" class="anchor" aria-label="Permalink: Links" href="#links"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li><a href="./create_user.html">POST <code>/api/Create_User</code></a></li>
|
||||
<li><a href="./create_test.html">POST <code>/api/Post Test</code></a></li>
|
||||
<li><a href="./get_user_tests.html">GET <code>/api/Get_User_Tests</code></a></li>
|
||||
<li><a href="./leaderboard.html">GET <code>/api/Leaderboard</code></a></li>
|
||||
<li><a href="./login.html">GET <code>/api/Login</code></a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
@ -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`
|
@ -1,67 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Leaderboard API Endpoint</h1><a id="user-content-leaderboard-api-endpoint" class="anchor" aria-label="Permalink: Leaderboard API Endpoint" href="#leaderboard-api-endpoint"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">GET <code>/api/leaderboard</code>
|
||||
</h2><a id="user-content-get-apileaderboard" class="anchor" aria-label="Permalink: GET /api/leaderboard" href="#get-apileaderboard"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>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.</p>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Responses</h2><a id="user-content-responses" class="anchor" aria-label="Permalink: Responses" href="#responses"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>200 OK</code>: Successfully retrieves the leaderboard data.</li>
|
||||
<li>
|
||||
<code>404 Not Found</code>: Indicates that the leaderboard was not found.</li>
|
||||
<li>
|
||||
<code>500 Internal Server Error</code>: Indicates an issue with accessing the database.</li>
|
||||
</ul>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Response</h2><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>[
|
||||
{
|
||||
<span class="pl-ent">"username"</span>: <span class="pl-s"><span class="pl-pds">"</span>user1<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"wpm"</span>: <span class="pl-c1">75</span>,
|
||||
<span class="pl-ent">"accuracy"</span>: <span class="pl-c1">97</span>,
|
||||
<span class="pl-ent">"test_time"</span>: <span class="pl-c1">120</span>,
|
||||
<span class="pl-ent">"test_length"</span>: <span class="pl-c1">250</span>
|
||||
},
|
||||
{
|
||||
<span class="pl-ent">"username"</span>: <span class="pl-s"><span class="pl-pds">"</span>user2<span class="pl-pds">"</span></span>,
|
||||
<span class="pl-ent">"wpm"</span>: <span class="pl-c1">73</span>,
|
||||
<span class="pl-ent">"accuracy"</span>: <span class="pl-c1">95</span>,
|
||||
<span class="pl-ent">"test_time"</span>: <span class="pl-c1">115</span>,
|
||||
<span class="pl-ent">"test_length"</span>: <span class="pl-c1">240</span>
|
||||
}
|
||||
]</pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Fields</h2><a id="user-content-fields" class="anchor" aria-label="Permalink: Fields" href="#fields"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<ul>
|
||||
<li>
|
||||
<code>username</code>: The name of the user.</li>
|
||||
<li>
|
||||
<code>wpm</code>: Words per minute, indicating the typing speed.</li>
|
||||
<li>
|
||||
<code>accuracy</code>: The accuracy of the user's typing, in percentage.</li>
|
||||
<li>
|
||||
<code>test_time</code>: The total time taken to complete the test, in seconds.</li>
|
||||
<li>
|
||||
<code>test_length</code>: The length of the test, typically measured in number of words.</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
@ -1,56 +0,0 @@
|
||||
<html>
|
||||
<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>
|
||||
<div class="markdown-heading"><h1 class="heading-element">Login</h1><a id="user-content-login" class="anchor" aria-label="Permalink: Login" href="#login"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<p>Authenticates a user and returns their user ID along with a secret key.
|
||||
Endpoint</p>
|
||||
<div class="highlight highlight-source-js"><pre><span class="pl-c1">GET</span> <span class="pl-c1">/</span><span class="pl-s1">api</span><span class="pl-c1">/</span><span class="pl-s1">login</span><span class="pl-c1">/</span><span class="pl-c1"><</span><span class="pl-ent">username</span><span class="pl-c1">></span>/<span class="pl-c1"><</span><span class="pl-ent">password</span><span class="pl-c1">></span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Parameters</h2><a id="user-content-parameters" class="anchor" aria-label="Permalink: Parameters" href="#parameters"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>username</td>
|
||||
<td>String</td>
|
||||
<td>Username of the user</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>password</td>
|
||||
<td>String</td>
|
||||
<td>Password of the user</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Request</h2><a id="user-content-example-request" class="anchor" aria-label="Permalink: Example Request" href="#example-request"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-shell"><pre>curl -X GET <span class="pl-s"><span class="pl-pds">"</span>https://example.com/api/login/example_user/example_password<span class="pl-pds">"</span></span></pre></div>
|
||||
<div class="markdown-heading"><h2 class="heading-element">Example Response</h2><a id="user-content-example-response" class="anchor" aria-label="Permalink: Example Response" href="#example-response"><span aria-hidden="true" class="octicon octicon-link"></span></a></div>
|
||||
<div class="highlight highlight-source-json"><pre>{
|
||||
<span class="pl-ent">"user_id"</span>: <span class="pl-c1">123</span>,
|
||||
<span class="pl-ent">"secret"</span>: <span class="pl-s"><span class="pl-pds">"</span>random_secret_key<span class="pl-pds">"</span></span>
|
||||
}</pre></div>
|
||||
</body>
|
||||
</html>
|
@ -1,31 +0,0 @@
|
||||
# Login
|
||||
|
||||
Authenticates a user and returns their user ID along with a secret key.
|
||||
Endpoint
|
||||
|
||||
|
||||
```js
|
||||
GET /api/login/<username>/<password>
|
||||
```
|
||||
|
||||
## 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"
|
||||
}
|
||||
```
|
@ -1,18 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Read markdown content from file
|
||||
markdown_content=$(cat $1)
|
||||
# HTML structure
|
||||
html_start='
|
||||
<html>
|
||||
<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;
|
||||
}
|
||||
|
||||
# Convert markdown to HTML
|
||||
html_content=$(gh api \
|
||||
@media (max-width: 767px) {
|
||||
.markdown-body {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
'
|
||||
|
||||
html_end='
|
||||
</body>
|
||||
</html>
|
||||
'
|
||||
|
||||
# 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 file
|
||||
echo "$html_content" > $2
|
||||
# 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 $2"
|
||||
echo "Conversion completed. HTML content written to $html_file"
|
||||
done
|
@ -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"}'
|
||||
```
|
42
documentation/post_user_test.md
Normal file
@ -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"
|
||||
}'
|
||||
```
|
||||
|
@ -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(((test.length - inaccurateLetters) / test.length) * 100);
|
||||
const accuracy = Math.round((correctLetters / test.length) * 100);
|
||||
const wpm = Math.round((correctLetters / 5) * (60 / testTime));
|
||||
|
||||
// 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));
|
||||
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}
|
||||
];
|
||||
|
||||
// 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
|
||||
|
||||
if ( typeof testType !== "string" ) {
|
||||
console.error(`testType is value ${typeof testType}\nshould be a string`);
|
||||
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;
|
||||
}
|
||||
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
|
||||
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
|
||||
};
|
||||
|
||||
if (testType !== "words") {
|
||||
// currently words is the only acceptable type but
|
||||
// this will change in later iterations
|
||||
try {
|
||||
const response = await fetch(`${this.url}/user/test`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
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`)
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
|
||||
// there will be other tests here in later iterations but for now these tests should suffice
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
this.postTest(testType, testLength, testTime, testSeed, quoteId, wpm, accuracy, userId);
|
||||
/**
|
||||
* 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<Object>} 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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();
|
||||
}
|
65
public/pages/account/index.html
Normal file
@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>User Tests</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav id="navbar">
|
||||
<a href="/typing/pages/account/index.html">Account</a>
|
||||
<a href="/typing/pages/test/index.html">Test</a>
|
||||
<a href="/typing/pages/leaderboard/index.html">Leaderboard</a>
|
||||
<a href="/typing/pages/test-settings/index.html">Test Settings</a>
|
||||
</nav>
|
||||
|
||||
<h1>User Tests</h1>
|
||||
<table id="userTestsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Test Type</th>
|
||||
<th>Test Length</th>
|
||||
<th>Test Time</th>
|
||||
<th>WPM</th>
|
||||
<th>Accuracy</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="userTestsBody">
|
||||
<!-- Table body will be filled by JavaScript -->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
async function fetchUserTests() {
|
||||
const userId = localStorage.getItem("userId"); // Replace with the actual user ID
|
||||
const secret = localStorage.getItem("secret"); // Replace with the actual secret key
|
||||
try {
|
||||
const response = await fetch(`/api/user/tests/${userId}/${secret}`);
|
||||
const tests = await response.json();
|
||||
populateUserTests(tests);
|
||||
} catch (error) {
|
||||
console.error('Error fetching user tests:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function populateUserTests(tests) {
|
||||
const tableBody = document.getElementById('userTestsBody');
|
||||
tableBody.innerHTML = ''; // Clear existing rows
|
||||
tests.forEach(test => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${test.test_type}</td>
|
||||
<td>${test.test_length}</td>
|
||||
<td>${test.test_time}</td>
|
||||
<td>${test.wpm ? test.wpm : 'N/A'}</td>
|
||||
<td>${test.accuracy ? test.accuracy + '%' : 'N/A'}</td>
|
||||
`;
|
||||
tableBody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
fetchUserTests(); // Fetch and populate user tests when the page loads
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
22
public/pages/account/script.js
Normal file
@ -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);
|
82
public/pages/account/styles.css
Normal file
@ -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 */
|
||||
}
|
26
public/pages/end/index.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>User Dashboard</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav id="navbar">
|
||||
<a href="/typing/pages/account/index.html">Account</a>
|
||||
<a href="/typing/pages/test/index.html">Test</a>
|
||||
<a href="/typing/pages/leaderboard/index.html">Leaderboard</a>
|
||||
<a href="/typing/pages/test-settings/index.html">Test Settings</a>
|
||||
</nav>
|
||||
<h1>User Dashboard</h1>
|
||||
<div id="username">
|
||||
<!-- Username will be displayed here -->
|
||||
</div>
|
||||
<h2>Most Recent Test Result</h2>
|
||||
<div id="testResults">
|
||||
<!-- Test results will be displayed here -->
|
||||
</div>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
69
public/pages/end/script.js
Normal file
@ -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 = '<p>No test data available.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct HTML for the table
|
||||
const html = `
|
||||
<table>
|
||||
<tr>
|
||||
<th>Test Type</th>
|
||||
<th>Test Length</th>
|
||||
<th>Test Time</th>
|
||||
<th>Test Seed</th>
|
||||
<th>Quote ID</th>
|
||||
<th>Words Per Minute (WPM)</th>
|
||||
<th>Accuracy</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${testData.test_type}</td>
|
||||
<td>${testData.test_length}</td>
|
||||
<td>${testData.test_time}</td>
|
||||
<td>${testData.test_seed}</td>
|
||||
<td>${testData.quote_id}</td>
|
||||
<td>${testData.wpm}</td>
|
||||
<td>${testData.accuracy}</td>
|
||||
</tr>
|
||||
</table>
|
||||
`;
|
||||
|
||||
// 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 = '<p>No username available.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the usernameDiv with the username
|
||||
usernameDiv.innerHTML = `<p><strong>Welcome, ${username}!</strong></p>`;
|
||||
}
|
||||
|
||||
// Call the fetchUsername function when the page loads
|
||||
window.onload = fetchUsername;
|
85
public/pages/end/styles.css
Normal file
@ -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 */
|
||||
}
|
35
public/pages/leaderboard/index.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Leaderboard</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav id="navbar">
|
||||
<a href="/typing/pages/account/index.html">Account</a>
|
||||
<a href="/typing/pages/test/index.html">Test</a>
|
||||
<a href="/typing/pages/leaderboard/index.html">Leaderboard</a>
|
||||
<a href="/typing/pages/test-settings/index.html">Test Settings</a>
|
||||
</nav>
|
||||
<H1>Leaderboard</H1>
|
||||
<table id="leaderboardTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>WPM</th>
|
||||
<th>Accuracy (%)</th>
|
||||
<th>Test Time (s)</th>
|
||||
<th>Test Length (characters)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Rows will be filled by JavaScript -->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
82
public/pages/leaderboard/styles.css
Normal file
@ -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 */
|
||||
}
|
@ -7,7 +7,13 @@
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<H1>Leaderboard</H1>
|
||||
<nav id="navbar">
|
||||
<a href="/typing/pages/account/index.html">Account</a>
|
||||
<a href="/typing/pages/test/index.html">Test</a>
|
||||
<a href="/typing/pages/leaderboard/index.html">Leaderboard</a>
|
||||
<a href="/typing/pages/test-settings/index.html">Test Settings</a>
|
||||
</nav>
|
||||
<H1>Test Settings</H1>
|
||||
<button class="refresh-button" onclick="fetchLeaderboard()">⟳</button>
|
||||
<a href="../index.html"><button>Go Back</button></a>
|
||||
<table id="leaderboardTable">
|
22
public/pages/test-settings/script.js
Normal file
@ -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);
|
@ -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 */
|
||||
}
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 968 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 732 B After Width: | Height: | Size: 732 B |
Before Width: | Height: | Size: 752 B After Width: | Height: | Size: 752 B |
@ -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() {
|
@ -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
|
74
public/pages/test/index.css
Executable file
@ -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;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>TypeFast</title>
|
||||
<title>Typing - Test</title>
|
||||
|
||||
<!-- CSS Files -->
|
||||
<link rel="stylesheet" href="index.css">
|
||||
@ -15,6 +15,13 @@
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="./assets/favicon/favicon-16x16.png">
|
||||
<link rel="manifest" href="./assets/favicon/site.webmanifest">
|
||||
|
||||
<nav id="navbar">
|
||||
<a href="/typing/pages/account/index.html">Account</a>
|
||||
<a href="/typing/pages/test/index.html">Test</a>
|
||||
<a href="/typing/pages/leaderboard/index.html">Leaderboard</a>
|
||||
<a href="/typing/pages/test-settings/index.html">Test Settings</a>
|
||||
</nav>
|
||||
|
||||
<!-- Main Script Files -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.5.0/lib/p5.js"></script>
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/p5@1.5.0/lib/p5.min.js"></script> -->
|
||||
@ -31,19 +38,11 @@
|
||||
|
||||
<!-- Screen Files-->
|
||||
<script src="./screens/screenmanager.js" defer="true"></script>
|
||||
<script src="./screens/startscreen.js" defer="true"></script>
|
||||
<script src="./screens/testscreen.js" defer="true"></script>
|
||||
<script src="./screens/endscreen.js" defer="true"></script>
|
||||
<script src="./screens/accountScreen.js" defer="true"></script>
|
||||
<script src="./screens/signUpScreen.js" defer="true"></script>
|
||||
<script src="./screens/loginscreen.js" defer="true"></script>
|
||||
<script src="./screens/profilescreen.js" defer="true"></script>
|
||||
<script src="./screens/leaderboardscreen.js" defer="true"></script>
|
||||
<script src="./screens/settingsScreen.js" defer="true"></script>
|
||||
|
||||
<!-- API Script Files -->
|
||||
<script src="./api/api.js" defer="true"></script>
|
||||
<script src="./api/user.js" defer="true"></script>
|
||||
<script src="/typing/api/api.js" defer="true"></script>
|
||||
<script src="/typing/api/user.js" defer="true"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>Please Enable Javascript</noscript>
|
72
public/pages/test/index.js
Normal file
@ -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();
|
||||
}
|
@ -31,11 +31,17 @@ 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();
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -2,3 +2,4 @@ pub mod leaderboard;
|
||||
pub mod sql;
|
||||
pub mod test;
|
||||
pub mod user;
|
||||
pub mod ping;
|
17
src/api/ping.rs
Normal file
@ -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"
|
||||
}
|
@ -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<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
|
||||
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<Test, sqlx::Error> {
|
||||
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<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, tests.accuracy, tests.test_time, tests.test_length
|
||||
FROM tests
|
||||
|
@ -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 = "<test>")]
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an array of words as Json
|
||||
/// Accessible from http://url/api/get_test
|
||||
#[get("/new_test")]
|
||||
pub fn new_test() -> Json<Vec<String>> {
|
||||
#[get("/test")]
|
||||
pub fn generate_test() -> Result<Json<Vec<String>>, 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<String> = 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))
|
||||
}
|
||||
|
148
src/api/user.rs
@ -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 = "<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,
|
||||
}
|
60
src/api/user/create.rs
Normal file
@ -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 = "<user>")]
|
||||
pub async fn create(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);
|
||||
}
|
||||
}
|
||||
}
|
69
src/api/user/login.rs
Normal file
@ -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/<username>/<password>`
|
||||
///
|
||||
/// # 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/<username>/<password>")]
|
||||
pub async fn login(username: &str, password: &str, database: &State<Database>) -> Result<Json<LoginResponse>, 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,
|
||||
}
|
4
src/api/user/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod login;
|
||||
pub mod tests;
|
||||
pub mod test;
|
||||
pub mod create;
|
146
src/api/user/test.rs
Normal file
@ -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 = "<test>")]
|
||||
pub async fn post_test(test: Json<PostTest<'_>>, database: &State<Database>) -> 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/<user_id>/<secret>`
|
||||
///
|
||||
/// # 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/<user_id>/<secret>")]
|
||||
pub async fn get_latest_test(user_id: u32, secret: &str, database: &State<Database>) -> Result<Json<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_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))
|
||||
}
|
||||
}
|
||||
}
|
74
src/api/user/tests.rs
Normal file
@ -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/<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://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/<user_id>/<secret>")]
|
||||
pub async fn 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}", tests.len());
|
||||
Ok(Json(tests))
|
||||
}
|
||||
}
|
||||
}
|
51
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<Build> {
|
||||
|
||||
// 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<Build> {
|
||||
.mount("/leaderboard", routes![leaderboard_redirect])
|
||||
|
||||
// testing only, should return "Hello world"
|
||||
.mount("/test", routes![test])
|
||||
|
||||
}
|
||||
|