1use rocket::{serde::json::Json, tokio::fs::remove_file, State};
2use serde::{Deserialize, Serialize};
3use surrealdb::{engine::remote::ws::Client, Surreal};
4
5use crate::{
6 models::{
7 account::Account,
8 error::Error,
9 problem::{CreateProblem, Problem, ProblemVisibility, UserProblem},
10 response::{Empty, Response},
11 Credentials, OwnedCredentials, OwnedId,
12 },
13 utils::{account, problem, session},
14 Result,
15};
16
17#[derive(Serialize, Deserialize, Debug)]
18#[serde(crate = "rocket::serde")]
19pub struct ProblemResponse {
20 pub id: String,
21}
22
23#[post("/create", data = "<problem>")]
24pub async fn create(
25 db: &State<Surreal<Client>>,
26 problem: Json<CreateProblem<'_>>,
27) -> Result<OwnedId> {
28 if !session::verify(db, problem.id, problem.token).await {
29 return Err(Error::Unauthorized(Json("Invalid token".into())));
30 }
31
32 let problem = problem::create(db, problem.into_inner())
33 .await?
34 .ok_or(Error::ServerError(Json(
35 "Failed to create problem, please try again later.".into(),
36 )))?;
37
38 Ok(Json(Response {
39 success: true,
40 message: "Problem created successfully".to_string(),
41 data: Some(OwnedId {
42 id: problem.id.unwrap().id.to_string(),
43 }),
44 }))
45}
46
47#[post("/get/<id>", data = "<auth>")]
48pub async fn get(
49 db: &State<Surreal<Client>>,
50 id: &str,
51 auth: Json<Option<Credentials<'_>>>,
52) -> Result<UserProblem> {
53 let problem = problem::get::<Problem>(db, id)
54 .await?
55 .ok_or(Error::NotFound(Json(
56 "Problem with specified id not found".into(),
57 )))?;
58
59 let authed_id = if let Some(auth) = auth.into_inner() {
60 if !session::verify(db, auth.id, auth.token).await {
61 return Err(Error::Unauthorized(Json("Invalid credentials".into())));
62 } else {
63 Some(auth.id)
64 }
65 } else {
66 None
67 };
68
69 let has_permission = if authed_id.is_none() && problem.visibility != ProblemVisibility::Public {
70 false
71 } else {
72 match problem.visibility {
73 ProblemVisibility::ContestOnly => {
74 todo!()
76 }
77 ProblemVisibility::Public => true,
78 ProblemVisibility::Private => problem.owner.id.to_string() == authed_id.unwrap(),
79 ProblemVisibility::Internal => {
80 todo!()
82 }
83 }
84 };
85
86 if !has_permission {
87 return Err(Error::Unauthorized(Json(
88 "You have no permission to access this problem".into(),
89 )));
90 }
91
92 Ok(Json(Response {
93 success: true,
94 message: "Problem found".to_string(),
95 data: Some(problem.into()),
96 }))
97}
98
99#[derive(Serialize, Deserialize)]
100#[serde(crate = "rocket::serde")]
101pub struct ListProblem {
102 pub identity: Option<String>,
103 pub auth: Option<OwnedCredentials>,
104 pub limit: Option<u32>,
105}
106
107#[post("/list", data = "<data>")]
108pub async fn list(
109 db: &State<Surreal<Client>>,
110 data: Json<ListProblem>,
111) -> Result<Vec<UserProblem>> {
112 let authed_id = if let Some(auth) = &data.auth {
113 if !session::verify(db, &auth.id, &auth.token).await {
114 return Err(Error::Unauthorized(Json("Invalid credentials".into())));
115 };
116 Some(auth.id.clone())
117 } else {
118 None
119 };
120
121 let data = data.into_inner();
122
123 let account_id = if let Some(identity) = data.identity.clone() {
124 Some(
125 account::get_by_identity::<Account>(db, &identity)
126 .await?
127 .ok_or(Error::Unauthorized(Json("Invalid identity".into())))?
128 .id
129 .unwrap()
130 .id
131 .to_string(),
132 )
133 } else {
134 None
135 };
136
137 let problems =
138 problem::list_for_account::<Problem>(db, account_id, authed_id, data.limit).await?;
139
140 Ok(Json(Response {
141 success: true,
142 message: "Problems found".to_string(),
143 data: Some(problems.into_iter().map(|p| p.into()).collect()),
144 }))
145}
146
147#[post("/update/<id>", data = "<problem>")]
148pub async fn update(
149 db: &State<Surreal<Client>>,
150 id: &str,
151 problem: Json<CreateProblem<'_>>,
152) -> Result<Empty> {
153 if !session::verify(db, problem.id, problem.token).await {
154 return Err(Error::Unauthorized(Json("Invalid credentials".into())));
155 }
156
157 problem::update(db, id, problem.into_inner())
158 .await?
159 .ok_or(Error::ServerError(Json(
160 "Failed to update problem, please try again later.".into(),
161 )))?;
162
163 Ok(Json(Response {
164 success: true,
165 message: "Problem updated successfully".to_string(),
166 data: None,
167 }))
168}
169
170#[delete("/delete/<id>", data = "<auth>")]
171pub async fn delete(
172 db: &State<Surreal<Client>>,
173 id: &str,
174 auth: Json<Credentials<'_>>,
175) -> Result<Empty> {
176 if !session::verify(db, auth.id, auth.token).await {
177 return Err(Error::Unauthorized(Json("Invalid credentials".into())));
178 }
179
180 for test_case in problem::get_test_cases_by_id(db, id).await? {
181 remove_file(test_case.input).await?;
182 remove_file(test_case.output).await?;
183 }
184 println!("Down");
185
186 problem::delete(db, id).await?.ok_or(Error::NotFound(Json(
187 "Problem with specified id not found".into(),
188 )))?;
189
190 Ok(Json(Response {
191 success: true,
192 message: "Problem deleted successfully".to_string(),
193 data: None,
194 }))
195}
196
197pub fn routes() -> Vec<rocket::Route> {
198 use rocket::routes;
199 routes![create, get, update, list, delete]
200}