aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA Farzat <a@farzat.xyz>2026-02-11 11:13:36 +0300
committerA Farzat <a@farzat.xyz>2026-02-11 11:13:36 +0300
commit2d9314aa3145ec7948341f38164e13c2a2d945ad (patch)
tree29f69a0b89983fbeb2f0227698aab9dd884d9a54
parent3321918c009e9d7a7a3c3c2a1f490bb91fefb2bc (diff)
downloadsafaribooks-rs-2d9314aa3145ec7948341f38164e13c2a2d945ad.tar.gz
safaribooks-rs-2d9314aa3145ec7948341f38164e13c2a2d945ad.zip
Add a login check
Try to access the profile page to see if the cookies work or not.
-rw-r--r--Cargo.lock13
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs18
-rw-r--r--src/orly.rs22
4 files changed, 52 insertions, 2 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6d02809..ef277f6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1006,6 +1006,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
+ "tokio",
"tracing",
"tracing-subscriber",
]
@@ -1265,10 +1266,22 @@ dependencies = [
"mio",
"pin-project-lite",
"socket2",
+ "tokio-macros",
"windows-sys 0.61.2",
]
[[package]]
+name = "tokio-macros"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "tokio-rustls"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index dc2f515..42e52da 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,5 +10,6 @@ colored = "3.1"
reqwest = { version = "0.13", default-features = false, features = ["rustls"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
+tokio = { version = "1.49", features = ["rt-multi-thread", "macros"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
diff --git a/src/main.rs b/src/main.rs
index 6981b8e..f79f191 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,14 +3,18 @@ mod config;
mod cookies;
mod display;
mod http_client;
+mod orly;
use clap::Parser;
use cli::Args;
use cookies::CookieStore;
use display::Display;
use http_client::HttpClient;
+use orly::check_login;
+use reqwest::Client;
-fn main() {
+#[tokio::main]
+async fn main() {
let args = Args::parse();
let mut ui = Display::new(&args.bookid);
@@ -40,12 +44,22 @@ fn main() {
));
// Build the HTTP client with our cookies (no network calls yet).
- let _client = match HttpClient::from_store(&store) {
+ let client = match HttpClient::from_store(&store) {
Ok(c) => c,
Err(e) => ui.error_and_exit(&format!("Failed to build HTTP client: {e}")),
};
ui.info("HTTP client initialized with cookies (no requests performed).");
+ // Check whether the cookies work (are we logged in?).
+ match check_login(&client).await {
+ Ok(true) => ui.info("Login confirmed..."),
+ Ok(false) => ui.error_and_exit(
+ "Logged out. Cookies could be stale or invalid.\n\
+ Try refreshing your cookies.json and trying again.",
+ ),
+ Err(e) => ui.error_and_exit(&format!("Login check failed: {e}")),
+ };
+
let output_dir = config::books_root().join(format!("(pending) ({})", args.bookid));
ui.set_output_dir(output_dir);
diff --git a/src/orly.rs b/src/orly.rs
new file mode 100644
index 0000000..cd8b645
--- /dev/null
+++ b/src/orly.rs
@@ -0,0 +1,22 @@
+use crate::http_client::HttpClient;
+use anyhow::{bail, Result};
+
+pub const PROFILE_URL: &str = "https://learning.oreilly.com/profile/";
+
+/// Check whether cookies keep us logged in by fetching the profile page.
+/// Returns:
+/// - Ok(true) => HTTP 200 (assume logged in)
+/// - Ok(false) => Redirect or 401/403 (assume not logged in)
+/// - Err(..) => Network/other error
+pub async fn check_login(client: &HttpClient) -> Result<bool> {
+ let res = client.client().get(PROFILE_URL).send().await?;
+ let status = res.status();
+
+ if status.is_redirection() {
+ Ok(false)
+ } else if status == 200 {
+ Ok(true)
+ } else {
+ bail!("Profile request returned unexpected status {}", status)
+ }
+}