[BE] Replace once_cell with RwLock. Fix classroom empty users error
This commit is contained in:
parent
525c72a9f6
commit
3d60f11f43
2
backend/Cargo.lock
generated
2
backend/Cargo.lock
generated
@ -131,7 +131,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"dotenvy",
|
||||
"isahc",
|
||||
"once_cell",
|
||||
"lazy_static",
|
||||
"reqwest",
|
||||
"rocket",
|
||||
"scraper",
|
||||
|
@ -9,12 +9,12 @@ edition = "2021"
|
||||
reqwest = { version = "0.11", features = ["json", "cookies"] }
|
||||
rocket = { version = "=0.5.0-rc.3" , features = ["json", "msgpack", "uuid"] }
|
||||
sqlx = { version = "0.7.1", features = [ "runtime-tokio", "tls-rustls", "mysql", "macros", "chrono" ] }
|
||||
once_cell = "1.18.0"
|
||||
dotenvy = "0.15.7"
|
||||
serde = "1.0.188"
|
||||
chrono = "0.4.27"
|
||||
scraper = "0.17.1"
|
||||
isahc = { version = "1.7.2", features = ["cookies"] }
|
||||
urlencoding = "2.1.3"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
|
||||
|
@ -1,12 +1,21 @@
|
||||
use lazy_static::lazy_static;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use isahc::{cookies::CookieJar, prelude::*, Request};
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::sync::RwLock;
|
||||
|
||||
struct CookieHold {
|
||||
pub jar: CookieJar,
|
||||
}
|
||||
|
||||
|
||||
lazy_static! {
|
||||
/// Stores a client with a persistent cookie store
|
||||
static ref SESSION_COOKIE: RwLock<CookieHold> = RwLock::new(CookieHold { jar: CookieJar::new()});
|
||||
/// Stores the last time a request was made, in seconds since UNIX epoch
|
||||
static ref SESSION_TIME: RwLock<u64> = RwLock::new(0);
|
||||
}
|
||||
|
||||
/// Stores a client with a persistent cookie store
|
||||
static SESSION_COOKIE: OnceCell<CookieJar> = OnceCell::new();
|
||||
/// Stores the last time a request was made, in seconds since UNIX epoch
|
||||
static SESSION_TIME: OnceCell<u64> = OnceCell::new();
|
||||
|
||||
/// Makes a request to the online classroom, and returns the html string
|
||||
pub async fn request(url: String) -> Result<String, String> {
|
||||
@ -15,9 +24,7 @@ pub async fn request(url: String) -> Result<String, String> {
|
||||
ensure_session().await?;
|
||||
|
||||
// Get the stored client
|
||||
let jar = SESSION_COOKIE
|
||||
.get()
|
||||
.expect("SESSION_COOKIE was not set, even after calling ensure_session");
|
||||
let jar = SESSION_COOKIE.read().unwrap().jar.clone();
|
||||
|
||||
let uri = format!("{}{}", classroom_url, url);
|
||||
|
||||
@ -41,10 +48,7 @@ pub async fn request(url: String) -> Result<String, String> {
|
||||
|
||||
/// Makes sure that the session cookie is set, and that it is valid
|
||||
pub async fn ensure_session() -> Result<(), String> {
|
||||
let last_usage_time = match SESSION_TIME.get() {
|
||||
Some(time) => *time,
|
||||
None => 0,
|
||||
};
|
||||
let last_usage_time = SESSION_TIME.read().unwrap().clone();
|
||||
|
||||
// Get current time in seconds
|
||||
let current_time = SystemTime::now()
|
||||
@ -57,9 +61,9 @@ pub async fn ensure_session() -> Result<(), String> {
|
||||
// Default PHP session timeout is 1440 seconds. Use a 1400 seconds timeout to be safe
|
||||
if time_passed > 1400 {
|
||||
login().await?;
|
||||
if let Err(err) = SESSION_TIME.set(current_time) {
|
||||
return Err(format!("Error setting session time: {:?}", err));
|
||||
}
|
||||
|
||||
let mut time_ptr = SESSION_TIME.write().unwrap();
|
||||
*time_ptr = current_time;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -86,10 +90,10 @@ async fn login() -> Result<(), String> {
|
||||
.send();
|
||||
|
||||
match response {
|
||||
Ok(_) => match SESSION_COOKIE.set(jar.clone()) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => Err(format!("Error saving client: {:?}", error)),
|
||||
},
|
||||
Ok(_) => {
|
||||
SESSION_COOKIE.write().unwrap().jar = jar.clone();
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => Err(format!("Error connecting to online classroom: {:?}", error)),
|
||||
}
|
||||
}
|
||||
|
@ -53,11 +53,19 @@ fn parse_users(file: &str) -> Result<Vec<ClassroomPerson>, String> {
|
||||
|
||||
let fragment = Html::parse_document(file);
|
||||
|
||||
// TODO: If no users are found, the form is not rendered, and the html is different
|
||||
// Handle this case
|
||||
let form_element = match fragment.select(&form_selector).next() {
|
||||
Some(el) => el,
|
||||
None => return Err("Error selecting form#form_users_id: not found".into()),
|
||||
// If the form is not found, it may mean that no users were found
|
||||
None => {
|
||||
let Ok(secondary_button) = Selector::parse("#img_plus_and_minus") else {
|
||||
return Err("Error parsing secondary button selector".into());
|
||||
};
|
||||
|
||||
match fragment.select(&secondary_button).next() {
|
||||
Some(_) => return Ok(Vec::new()),
|
||||
None => return Err("Error parsing html: no form or empty form found.".into()),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut result_vec = Vec::new();
|
||||
|
Loading…
Reference in New Issue
Block a user