Compare commits
3
Commits
b0cb29d911
...
7806fba9e6
No files matched your search
+3
-3
@@ -7,8 +7,8 @@ publish = ["gitea"]
|
||||
[dependencies]
|
||||
actix-cors = "0.7"
|
||||
actix-web = "4"
|
||||
chrono = "0.4.41"
|
||||
fancy-regex = "0.14.0"
|
||||
icalendar = "0.16.13"
|
||||
chrono = "0.4"
|
||||
fancy-regex = "0.14"
|
||||
icalendar = "0.16"
|
||||
reqwest = {version = "0.12", features = ["blocking"]}
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
@@ -0,0 +1,37 @@
|
||||
// type calparseerrtype = <Calendar as std::str::FromStr>::Err; // String
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
#[derive(Debug)]
|
||||
pub enum asspullError {
|
||||
calparse(String), // type calparseerrtype
|
||||
regexnew(fancy_regex::Error),
|
||||
}
|
||||
|
||||
// impl From<calparseerrtype> for asspullError {
|
||||
// fn from(value: calparseerrtype) -> Self {
|
||||
// asspullError::calparse(value)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl From<String> for asspullError {
|
||||
fn from(value: String) -> Self {
|
||||
asspullError::calparse(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<fancy_regex::Error> for asspullError {
|
||||
fn from(value: fancy_regex::Error) -> Self {
|
||||
asspullError::regexnew(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for asspullError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
asspullError::calparse(i) => std::fmt::Display::fmt(i, f),
|
||||
asspullError::regexnew(i) => std::fmt::Display::fmt(i, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for asspullError {}
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use tokio::time::Instant;
|
||||
|
||||
pub struct LdaTimer<'s>{timer: Instant, message: &'s str}
|
||||
|
||||
+120
-62
@@ -1,12 +1,18 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
error::Error as _,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use actix_cors::Cors;
|
||||
use actix_web::{App, HttpRequest, HttpResponse, HttpServer, Responder, get, http::header, web};
|
||||
use actix_web::{
|
||||
App, HttpRequest, HttpResponse, HttpServer, Responder, get, http::header, post, web,
|
||||
};
|
||||
use chrono::{NaiveDate, TimeDelta};
|
||||
use fancy_regex::Regex;
|
||||
use icalendar::{Calendar, CalendarComponent, Component};
|
||||
use reqwest::Client;
|
||||
mod funny_timer;
|
||||
|
||||
// use regex::Regex;
|
||||
|
||||
const ICS_URL: &str = "https://calendar.google.com/calendar/ical/i7p4ol56gi8sqe0dq7uc3m0o4c%40group.calendar.google.com/public/basic.ics";
|
||||
@@ -17,81 +23,107 @@ async fn filtered_ics(
|
||||
now: web::Data<NaiveDate>,
|
||||
query: web::Query<HashMap<String, String>>,
|
||||
) -> impl Responder {
|
||||
let cumah = funny_timer::LdaTimer::new("total time idfk");
|
||||
println!(
|
||||
"got hit from {}, path is obviously {}",
|
||||
req.peer_addr()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or("deadass dont know".into()),
|
||||
.unwrap_or_else(|| "deadass dont know".into()),
|
||||
req.path()
|
||||
);
|
||||
let now: NaiveDate = **now;
|
||||
let client = Client::new();
|
||||
let ics_url = query.get("url").map_or(ICS_URL, String::as_str);
|
||||
|
||||
let timer = tokio::time::Instant::now();
|
||||
let now = **now;
|
||||
let client = Client::new();
|
||||
let ics_url = query.get("url").map_or(ICS_URL, |d| {
|
||||
println!("ooh! url query from {d} idk if its good");
|
||||
d.as_str()
|
||||
});
|
||||
|
||||
// Fetch the .ics content
|
||||
let response = match client.get(ics_url).send().await {
|
||||
Ok(resp) => resp,
|
||||
Err(e) => return HttpResponse::BadGateway().body(format!("Failed to fetch ICS file: {e}")),
|
||||
Err(e) => {
|
||||
eprintln!("rq failed: {e}");
|
||||
if e.is_timeout() {
|
||||
return HttpResponse::GatewayTimeout()
|
||||
.body("Request timed out when fetching ICS file.");
|
||||
} else if e.is_connect() {
|
||||
return HttpResponse::BadGateway()
|
||||
.body("Failed to connect to the ICS server (maybe blocked or DNS issue).");
|
||||
} else if e.is_status() {
|
||||
return HttpResponse::BadGateway().body(format!(
|
||||
"ICS server returned error status: {}",
|
||||
e.status().unwrap() // is_status checked
|
||||
));
|
||||
} else if e.is_request() {
|
||||
return HttpResponse::BadRequest().body("Malformed request to ICS server.");
|
||||
} else {
|
||||
// Print error chain for debugging
|
||||
let mut source = e.source();
|
||||
while let Some(s) = source {
|
||||
eprintln!("caused by: {s}");
|
||||
source = s.source();
|
||||
}
|
||||
return HttpResponse::BadGateway().body(format!("Failed to fetch ICS file: {e}"));
|
||||
}
|
||||
}
|
||||
};
|
||||
println!("got a response in {:?}", timer.elapsed());
|
||||
cumah.ping("function time after response");
|
||||
|
||||
let body = match response.text().await {
|
||||
Ok(text) => text,
|
||||
Err(e) => {
|
||||
return HttpResponse::InternalServerError()
|
||||
.body(format!("Failed to read response body: {e}"));
|
||||
eprintln!("full body read failed: {e}");
|
||||
return HttpResponse::InternalServerError().body(format!(
|
||||
"Failed to read response body (connection dropped or invalid encoding?): {e}"
|
||||
));
|
||||
}
|
||||
};
|
||||
println!("with reading file: {:?}", timer.elapsed());
|
||||
cumah.ping("function time after reading");
|
||||
|
||||
let timer = tokio::time::Instant::now();
|
||||
// Parse the .ics content
|
||||
let calendar = match body.parse::<Calendar>() {
|
||||
Ok(cal) => cal,
|
||||
Err(e) => {
|
||||
return HttpResponse::InternalServerError()
|
||||
.body(format!("Failed to parse ICS file: {e}"));
|
||||
// Compile regex
|
||||
let regex_rules = match query.get("regex") {
|
||||
Some(rule) => match Regex::new(rule) {
|
||||
Ok(reg_mc) => Some(reg_mc),
|
||||
Err(e) => {
|
||||
eprintln!("request regex is ass: {e}");
|
||||
return HttpResponse::BadRequest().body(format!("Invalid regex rule: {e}"));
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
if let Some(real) = ®ex_rules {
|
||||
println!("there is a regex rule and it is {}", real.as_str())
|
||||
};
|
||||
// Do the CPU-heavy stuff on another thread
|
||||
let block_task = tokio::task::spawn_blocking(move || -> Result<String, String> {
|
||||
let calendar = body.parse::<Calendar>()?;
|
||||
|
||||
let start_date = now - TimeDelta::days(365);
|
||||
let end_date = now + TimeDelta::days(365);
|
||||
|
||||
let filtered_events =
|
||||
filter_events_by_date_and_regex(calendar, regex_rules, start_date, end_date);
|
||||
|
||||
let filtered_calendar = Calendar {
|
||||
components: filtered_events,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(filtered_calendar.to_string())
|
||||
});
|
||||
|
||||
// Await the result of the blocking task
|
||||
match block_task.await {
|
||||
Ok(Ok(ics_output)) => HttpResponse::Ok()
|
||||
.content_type("text/calendar")
|
||||
.body(ics_output),
|
||||
Ok(Err(e)) => {
|
||||
eprintln!("parse err {e}");
|
||||
HttpResponse::InternalServerError().body(format!("Failed to process ICS file: {e}"))
|
||||
}
|
||||
};
|
||||
println!("parsing took {:?}", timer.elapsed());
|
||||
cumah.ping("function time after parsing");
|
||||
|
||||
let start_date = now - TimeDelta::days(365);
|
||||
let end_date = now + TimeDelta::days(365);
|
||||
|
||||
let regex_rules = query
|
||||
.get("regex")
|
||||
.and_then(|r| Regex::new(r).inspect_err(|e| eprintln!("{e}")).ok()); // silently discard a faulty regex
|
||||
// or
|
||||
// let include = query.get("").map(|c|c.split(",")); // ?
|
||||
// let exclude = query.get("").map(|c|c.split(",")); // ?
|
||||
// Filter events by summary and date range
|
||||
let timer = tokio::time::Instant::now();
|
||||
let filtered_events =
|
||||
filter_events_by_date_and_regex(calendar, regex_rules, start_date, end_date);
|
||||
println!("filtering took {:?}", timer.elapsed());
|
||||
cumah.ping("function time after filtering");
|
||||
|
||||
// Create a new calendar with filtered events
|
||||
let filtered_calendar = Calendar {
|
||||
components: filtered_events,
|
||||
..Default::default()
|
||||
};
|
||||
cumah.ping("hi");
|
||||
|
||||
// Serialize the filtered calendar
|
||||
let timer = tokio::time::Instant::now();
|
||||
let ics_output = filtered_calendar.to_string();
|
||||
println!("string building took {:?}", timer.elapsed());
|
||||
cumah.ping("function time after building the string");
|
||||
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/calendar")
|
||||
.body(ics_output)
|
||||
Err(join_err) => {
|
||||
eprintln!("insane cooked thread err {join_err}");
|
||||
HttpResponse::InternalServerError().body(format!("Blocking thread failed: {join_err}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_events_by_date_and_regex(
|
||||
@@ -138,7 +170,10 @@ fn filter_events_by_date_and_regex(
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(|| {
|
||||
let shutdown_handle = Arc::new(Mutex::new(None));
|
||||
let shutdown_handle_clone = shutdown_handle.clone();
|
||||
|
||||
let server = HttpServer::new(move || {
|
||||
let cors = Cors::default()
|
||||
.allow_any_origin()
|
||||
.allowed_methods(vec!["GET"])
|
||||
@@ -147,9 +182,32 @@ async fn main() -> std::io::Result<()> {
|
||||
App::new()
|
||||
.wrap(cors)
|
||||
.app_data(web::Data::new(chrono::Local::now().date_naive())) // how to freeze date
|
||||
.app_data(web::Data::new(shutdown_handle_clone.clone()))
|
||||
.service(filtered_ics)
|
||||
.service(die)
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
.run();
|
||||
|
||||
*shutdown_handle.lock().unwrap() = Some(server.handle());
|
||||
server.await
|
||||
}
|
||||
|
||||
mod asspull;
|
||||
mod funny_timer;
|
||||
|
||||
use actix_web::dev::ServerHandle;
|
||||
|
||||
#[post("/die")]
|
||||
async fn die(shutdown: web::Data<Arc<Mutex<Option<ServerHandle>>>>) -> impl Responder {
|
||||
// Trigger shutdown in a new task so we can respond first
|
||||
if let Some(handle) = shutdown.lock().unwrap().take() {
|
||||
actix_web::rt::spawn(async move {
|
||||
handle.stop(true).await;
|
||||
});
|
||||
println!("dieing now.");
|
||||
HttpResponse::Ok().body("Server is shutting down gracefully.")
|
||||
} else {
|
||||
HttpResponse::ServiceUnavailable().body("Shutdown already in progress or not available.")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user