2023-11-27 19:38:32 +00:00
|
|
|
use headless_chrome::Browser;
|
2023-11-28 17:17:11 +00:00
|
|
|
use headless_chrome::types::PrintToPdfOptions;
|
2023-12-07 21:02:36 +00:00
|
|
|
use nanobyte_opentelemetry::rocket::{TracingSpan, OtelReqwestClient};
|
2023-12-03 20:43:12 +00:00
|
|
|
use nanobyte_tera::l18n::LanguageDescription;
|
|
|
|
use ovlach_data::cv::data::CV;
|
2023-12-01 23:52:53 +00:00
|
|
|
use rocket::fs::NamedFile;
|
|
|
|
use ::rocket::{State, http::Status};
|
|
|
|
use ::rocket::get;
|
2023-11-29 13:04:36 +00:00
|
|
|
use tempfile::NamedTempFile;
|
|
|
|
use tera::Context;
|
2023-12-03 23:01:37 +00:00
|
|
|
use tracing::{info_span, error, debug, Instrument};
|
2023-12-01 23:52:53 +00:00
|
|
|
use crate::{chromium::rocket::BrowserHolder, tools::{tera::NanoTera, pdf::PdfStream, rocket::RequestLanguage}, services::cv::fetch_cv_data_from_backend, CVBackendConfig};
|
2023-11-27 21:56:10 +00:00
|
|
|
|
2023-11-29 13:04:36 +00:00
|
|
|
// TODO: request-id
|
|
|
|
fn generate_pdf(browser: Browser, file: &NamedTempFile) -> Vec<u8> {
|
2023-11-27 19:38:32 +00:00
|
|
|
let tab = browser.new_tab().unwrap();
|
2023-11-29 13:04:36 +00:00
|
|
|
let path = format!("file://{}", file.path().to_str().unwrap());
|
2023-11-28 16:09:50 +00:00
|
|
|
info_span!("open_pdf").in_scope(|| {
|
2023-11-29 13:04:36 +00:00
|
|
|
debug!("Render pdf from {}", &path);
|
|
|
|
tab.navigate_to(&path).unwrap().wait_until_navigated().unwrap();
|
2023-11-28 16:09:50 +00:00
|
|
|
});
|
|
|
|
|
2023-11-27 19:38:32 +00:00
|
|
|
let options = PrintToPdfOptions{
|
|
|
|
margin_bottom: Some(0.0),
|
|
|
|
margin_left: Some(0.0),
|
|
|
|
margin_right: Some(0.0),
|
|
|
|
margin_top: Some(0.0),
|
|
|
|
print_background: Some(true),
|
2023-12-03 23:01:37 +00:00
|
|
|
paper_width: Some(8.3),
|
|
|
|
paper_height: Some(11.7),
|
|
|
|
landscape: Some(false),
|
2023-11-27 19:38:32 +00:00
|
|
|
..PrintToPdfOptions::default()
|
|
|
|
};
|
|
|
|
|
2023-12-03 20:43:12 +00:00
|
|
|
//thread::sleep(Duration::from_secs(10));
|
2023-11-27 19:38:32 +00:00
|
|
|
|
2023-12-07 21:02:36 +00:00
|
|
|
info_span!("print_pdf").in_scope(|| {
|
2023-11-28 16:09:50 +00:00
|
|
|
tab.print_to_pdf(Some(options)).unwrap()
|
2023-12-07 21:02:36 +00:00
|
|
|
})
|
2023-11-29 13:04:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tracing::instrument]
|
2023-12-01 23:52:53 +00:00
|
|
|
fn render_template(template_name: &str, file: &NamedTempFile, tera: NanoTera, cv: CV, language: String) {
|
2023-11-29 13:04:36 +00:00
|
|
|
// TODO: handle errors
|
2023-12-01 23:52:53 +00:00
|
|
|
let mut tera_context = Context::new();
|
|
|
|
tera_context.insert("cv", &cv);
|
2023-12-03 20:43:12 +00:00
|
|
|
tera_context.insert("lang", &LanguageDescription::new(&language, "ovlach_pdf"));
|
|
|
|
|
2023-12-01 23:52:53 +00:00
|
|
|
match tera.0.render_to("two_column.html.tera", &tera_context, file) {
|
|
|
|
Ok(_) => {
|
|
|
|
debug!("Rendered template to {}", file.path().to_str().unwrap());
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
error!("Error rendering template {}: {}", &template_name, e);
|
|
|
|
}
|
|
|
|
}
|
2023-11-27 19:38:32 +00:00
|
|
|
}
|
|
|
|
|
2023-12-01 23:52:53 +00:00
|
|
|
#[get("/cv/<username>/<language>/output.pdf")]
|
|
|
|
pub async fn render_pdf_cv(username: &str, tera: NanoTera, tracing: TracingSpan, request_client: OtelReqwestClient,
|
2023-12-07 21:02:36 +00:00
|
|
|
cv_config: &State<CVBackendConfig>, language: RequestLanguage, browser: BrowserHolder) -> Result<PdfStream, Status> {
|
2023-12-03 23:01:37 +00:00
|
|
|
async move {
|
2023-12-07 21:02:36 +00:00
|
|
|
match fetch_cv_data_from_backend(&cv_config.cv_backend_path, &username.to_string(), &request_client.0).await {
|
2023-12-03 23:01:37 +00:00
|
|
|
Ok(cv_data) => {
|
|
|
|
let file = tempfile::Builder::new().suffix(".html").tempfile().unwrap();
|
|
|
|
render_template("two_column", &file, tera, cv_data, language.language);
|
|
|
|
let span = info_span!("render_pdf", username = username);
|
|
|
|
let pdf = span.in_scope(||{
|
|
|
|
generate_pdf(browser.browser, &file)
|
|
|
|
});
|
|
|
|
Ok(PdfStream::new(pdf))
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
error!("Error fetching cv data: {:?}", e);
|
|
|
|
Err(Status::InternalServerError)
|
|
|
|
}
|
2023-12-01 23:52:53 +00:00
|
|
|
}
|
2023-12-03 23:01:37 +00:00
|
|
|
}.instrument(tracing.0).await
|
2023-11-29 13:04:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Route only for debuging
|
2023-12-01 23:52:53 +00:00
|
|
|
#[get("/cv/<username>/<language>/output.html")]
|
|
|
|
pub async fn render_html_cv(username: &str, tera: NanoTera, tracing: TracingSpan, request_client: OtelReqwestClient,
|
2023-12-07 21:02:36 +00:00
|
|
|
cv_config: &State<CVBackendConfig>, language: RequestLanguage) -> Result<NamedFile, Status> {
|
2023-12-03 23:01:37 +00:00
|
|
|
async move {
|
2023-12-07 21:02:36 +00:00
|
|
|
match fetch_cv_data_from_backend(&cv_config.cv_backend_path, &username.to_string(), &request_client.0).await {
|
2023-12-03 23:01:37 +00:00
|
|
|
Ok(cv_data) => {
|
|
|
|
let file = tempfile::Builder::new().suffix(".html").tempfile().unwrap();
|
|
|
|
render_template("two_column", &file, tera, cv_data, language.language);
|
|
|
|
Ok(NamedFile::open(file.path()).await.unwrap())
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
error!("Error fetching cv data: {:?}", e);
|
|
|
|
Err(Status::InternalServerError)
|
|
|
|
}
|
2023-12-01 23:52:53 +00:00
|
|
|
}
|
2023-12-03 23:01:37 +00:00
|
|
|
}.instrument(tracing.0).await
|
2023-12-03 20:43:12 +00:00
|
|
|
|
|
|
|
}
|