summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/database/database.rs53
-rw-r--r--src/database/sql/fetch_problem.sql1
-rw-r--r--src/database/sql/initialize.sql5
-rw-r--r--src/database/sql/insert_submission.sql1
-rw-r--r--src/database/submission.rs15
-rw-r--r--src/main.rs12
-rw-r--r--src/routes.rs5
-rw-r--r--src/routes/auth.rs6
-rw-r--r--src/routes/errors.rs4
-rw-r--r--src/routes/problem.rs13
-rw-r--r--src/routes/submission.rs31
-rw-r--r--static/submit.html7
-rw-r--r--static/submit.js16
13 files changed, 143 insertions, 26 deletions
diff --git a/src/database/database.rs b/src/database/database.rs
index 7444779..6ebbe1c 100644
--- a/src/database/database.rs
+++ b/src/database/database.rs
@@ -4,6 +4,8 @@ use r2d2::Pool;
use r2d2_sqlite::SqliteConnectionManager;
use r2d2_sqlite::rusqlite::OptionalExtension;
+use crate::database::submission::Submission;
+
use super::problem::Problem;
use super::user::User;
@@ -120,6 +122,27 @@ impl Database {
.map_err(|e| DatabaseError::Query(e.to_string()))?
)
}
+
+ pub fn fetch_problem(&self, problem_id: i64) -> Result<Option<Problem>, DatabaseError> {
+ static QUERY: &str = include_str!("sql/fetch_problem.sql");
+ let conn = self.pool
+ .get()
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+ let mut statement = conn.prepare(QUERY)
+ .map_err(|e| DatabaseError::Query(e.to_string()))?;
+
+ Ok(statement
+ .query_one([problem_id], |row| {
+ Ok(Problem::new(
+ row.get("id")?,
+ row.get("title")?,
+ row.get("description")?,
+ ))
+ })
+ .optional()
+ .map_err(|e| DatabaseError::Query(e.to_string()))?
+ )
+ }
pub fn fetch_problems(&self) -> Result<Vec<Problem>, DatabaseError> {
static QUERY: &str = include_str!("sql/fetch_problems.sql");
@@ -143,6 +166,36 @@ impl Database {
)
}
+ pub fn insert_submission(
+ &self,
+ user_id: i64,
+ problem_id: i64,
+ language: &str,
+ details: &str,
+ code: &str,
+ ) -> Result<Submission, DatabaseError> {
+ static QUERY: &str = include_str!("sql/insert_submission.sql");
+ let conn = self.pool
+ .get()
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+ let mut statement = conn.prepare(QUERY)
+ .map_err(|e| DatabaseError::Query(e.to_string()))?;
+
+ Ok(statement
+ .query_one((user_id, problem_id, language, details, code), |row| {
+ Ok(Submission::new(
+ row.get("id")?,
+ user_id,
+ problem_id,
+ language.to_owned(),
+ details.to_owned(),
+ code.to_owned()
+ ))
+ })
+ .map_err(|e| DatabaseError::Query(e.to_string()))?
+ )
+ }
+
pub fn initialize(&self) -> Result<(), DatabaseError> {
static QUERY: &str = include_str!("sql/initialize.sql");
let conn = self.pool
diff --git a/src/database/sql/fetch_problem.sql b/src/database/sql/fetch_problem.sql
new file mode 100644
index 0000000..f319c2f
--- /dev/null
+++ b/src/database/sql/fetch_problem.sql
@@ -0,0 +1 @@
+SELECT * FROM problem WHERE problem.id == ?1;
diff --git a/src/database/sql/initialize.sql b/src/database/sql/initialize.sql
index 72b7468..8c0f2ef 100644
--- a/src/database/sql/initialize.sql
+++ b/src/database/sql/initialize.sql
@@ -16,9 +16,10 @@ CREATE TABLE IF NOT EXISTS submission (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
problem_id INTEGER NOT NULL,
- code TEXT NOT NULL,
language TEXT NOT NULL,
- validated INTEGER NOT NULL
+ details TEXT NOT NULL,
+ code TEXT NOT NULL
+
);
INSERT INTO problem (title, description) VALUES (
diff --git a/src/database/sql/insert_submission.sql b/src/database/sql/insert_submission.sql
new file mode 100644
index 0000000..96df322
--- /dev/null
+++ b/src/database/sql/insert_submission.sql
@@ -0,0 +1 @@
+INSERT INTO submission (user_id, problem_id, language, details, code) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING id;
diff --git a/src/database/submission.rs b/src/database/submission.rs
index 2ec1287..4a83edd 100644
--- a/src/database/submission.rs
+++ b/src/database/submission.rs
@@ -5,9 +5,9 @@ pub struct Submission {
id: i64,
user_id: i64,
problem_id: i64,
- code: String,
language: String,
- validated: bool,
+ details: String,
+ code: String,
}
impl Submission {
@@ -15,17 +15,18 @@ impl Submission {
id: i64,
user_id: i64,
problem_id: i64,
- code: String,
language: String,
- validated: bool,
+ details: String,
+ code: String,
) -> Self {
- Self { id, user_id, problem_id, code, language, validated }
+ Self { id, user_id, problem_id, language, details, code }
}
pub fn id(&self) -> i64 { self.id }
pub fn user_id(&self) -> i64 { self.user_id }
pub fn problem_id(&self) -> i64 { self.problem_id }
- pub fn code(&self) -> &str { &self.code }
pub fn language(&self) -> &str { &self.language }
- pub fn validated(&self) -> bool { self.validated }
+ pub fn details(&self) -> &str { &self.details }
+ pub fn code(&self) -> &str { &self.code }
+
}
diff --git a/src/main.rs b/src/main.rs
index b1979b1..73dccef 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,12 +9,12 @@ use axum::{
Router,
};
-use routes::problem::{get_problems, create_problem};
+use routes::problem::{get_problem, get_problems, create_problem};
use routes::user::{create_user, me};
use routes::auth::{login, logout};
use tower_http::services::ServeDir;
-use crate::{database::Database, utils::register_admin};
+use crate::{database::Database, routes::submission::create_submission, utils::register_admin};
#[derive(Clone)]
struct AppState {
@@ -61,8 +61,10 @@ async fn main() {
let app = Router::new()
.route("/login", post(login))
.route("/logout", post(logout))
+ .route("/problem/{problem_id}", get(get_problem))
.route("/problem", get(get_problems))
.route("/problem", post(create_problem))
+ .route("/submission", post(create_submission))
.route("/user", post(create_user))
.route("/me", get(me))
.nest_service("/static", static_files)
@@ -72,9 +74,3 @@ async fn main() {
axum::serve(listener, app).await.unwrap();
}
-#[cfg(test)]
-mod tests {
- fn test_app() -> String {
- "hello".into()
- }
-}
diff --git a/src/routes.rs b/src/routes.rs
index e0adba6..b008273 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -1,4 +1,5 @@
-pub mod problem;
-pub mod user;
pub mod auth;
+pub mod user;
+pub mod problem;
+pub mod submission;
mod errors;
diff --git a/src/routes/auth.rs b/src/routes/auth.rs
index ab7a393..8e8d99b 100644
--- a/src/routes/auth.rs
+++ b/src/routes/auth.rs
@@ -18,9 +18,10 @@ use crate::routes::errors::RouteError;
#[derive(Serialize, Deserialize)]
pub struct Claims {
- pub sub: String,
+ pub sub: i64,
pub exp: usize,
pub iat: usize,
+ pub email: String,
pub username: String,
pub is_admin: bool,
}
@@ -90,9 +91,10 @@ pub async fn login(
.as_secs() as usize;
let claims = Claims {
- sub: user.email().to_string(),
+ sub: user.id(),
iat: now,
exp: now + 60 * 60 * 24,
+ email: user.email().to_string(),
username: user.username().to_string(),
is_admin: false
};
diff --git a/src/routes/errors.rs b/src/routes/errors.rs
index 78b4e3a..f6a0901 100644
--- a/src/routes/errors.rs
+++ b/src/routes/errors.rs
@@ -7,6 +7,7 @@ pub enum RouteError {
UserCreateUsernameExists(String),
UnregisteredEmail(String),
AuthorizationFailure(),
+ NotFound(String),
}
impl IntoResponse for RouteError {
@@ -26,6 +27,9 @@ impl IntoResponse for RouteError {
},
RouteError::AuthorizationFailure() => {
(StatusCode::UNAUTHORIZED, format!("failed to authorize"))
+ },
+ RouteError::NotFound(resource) => {
+ (StatusCode::NOT_FOUND, format!("{resource} not found"))
}
};
diff --git a/src/routes/problem.rs b/src/routes/problem.rs
index 9b2eba3..69d1b66 100644
--- a/src/routes/problem.rs
+++ b/src/routes/problem.rs
@@ -1,4 +1,4 @@
-use axum::{extract::State, http::StatusCode, response::{IntoResponse, Json}};
+use axum::{extract::{Path, State}, http::StatusCode, response::{IntoResponse, Json}};
use serde::Deserialize;
use crate::{AppState, routes::errors::RouteError};
@@ -31,3 +31,14 @@ pub async fn get_problems(
Ok((StatusCode::CREATED, Json(problems)))
}
+
+pub async fn get_problem(
+ State(state): State<AppState>,
+ Path(problem_id): Path<i64>,
+) -> Result<impl IntoResponse, RouteError> {
+ match state.database.fetch_problem(problem_id) {
+ Err(_) => Err(RouteError::Internal("database action failed".into())),
+ Ok(None) => Err(RouteError::NotFound("problem".into())),
+ Ok(Some(problem)) => Ok(Json(problem))
+ }
+}
diff --git a/src/routes/submission.rs b/src/routes/submission.rs
new file mode 100644
index 0000000..50bb53d
--- /dev/null
+++ b/src/routes/submission.rs
@@ -0,0 +1,31 @@
+use axum::{Json, extract::State, http::StatusCode, response::IntoResponse};
+use serde::Deserialize;
+
+use crate::{AppState, routes::{auth::AuthUser, errors::RouteError}};
+
+#[derive(Deserialize)]
+pub(crate) struct CreateSubmissionRequest {
+ problem_id: i64,
+ language: String,
+ details: String,
+ code: String,
+}
+
+pub async fn create_submission(
+ State(state): State<AppState>,
+ AuthUser(claims): AuthUser,
+ Json(request): Json<CreateSubmissionRequest>
+) -> Result<impl IntoResponse, RouteError> {
+ let user_id = claims.sub;
+
+ match state.database.insert_submission(
+ user_id,
+ request.problem_id,
+ &request.language,
+ &request.details,
+ &request.code
+ ) {
+ Ok(submission) => Ok((StatusCode::CREATED, Json(submission))),
+ Err(_) => Err(RouteError::Internal(format!("unable to insert submission")))
+ }
+}
diff --git a/static/submit.html b/static/submit.html
index 393b2e2..9cab87b 100644
--- a/static/submit.html
+++ b/static/submit.html
@@ -37,12 +37,13 @@
<option value="other">Other (specify below)</option>
</select>
<br><br>
- <p>If your code requires any special instructions or additional details, provide them below. Languages not listed above can be specified here too. If you would like a language added to the list above, reach out to Daniel.</p><br>
- <label for="submission-details">Additional Details</label><br>
- <textarea id="submission-details" rows="4" cols="87"></textarea><br><br>
<label for="submission-code">Code</label><br>
<textarea id="submission-code" rows="16" cols="87"></textarea>
<br><br>
+ <p>If your code requires any special instructions or additional details, provide them below. Languages not listed above can be specified here too. If you would like a language added to the list above, reach out to Daniel.</p><br>
+ <label for="submission-details">Additional Details</label><br>
+ <textarea id="submission-details" rows="4" cols="87"></textarea>
+ <br><br>
<button id="submission-button" onclick="submit()">Submit</button>
<br><br>
</div>
diff --git a/static/submit.js b/static/submit.js
index 03dfd69..9708e0c 100644
--- a/static/submit.js
+++ b/static/submit.js
@@ -25,6 +25,20 @@ async function submit() {
console.log(result);
}
-function init() {
+async function init() {
+ const url_params = new URLSearchParams(window.location.search);
+ const problem_id = url_params.get("problem_id");
+ const response = await fetch(`/problem/${problem_id}`);
+ const problem = await response.json();
+
+ const problem_div = document.getElementById("submission-problem");
+
+ const title = document.createElement("h2");
+ title.innerHTML = `Problem: ${problem.title}`;
+ problem_div.appendChild(title);
+
+ const description = document.createElement("p");
+ description.innerHTML = problem.description;
+ problem_div.appendChild(description);
}