#![feature(proc_macro_hygiene, decl_macro)]
#![allow(clippy::implicit_hasher, clippy::let_unit_value)]
#[macro_use]
extern crate rocket;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::thread;
use rocket::request::Form;
use rocket::response::status::{Accepted, NotFound};
use rocket::response::NamedFile;
use rocket::Data;
use rocket_contrib::json::Json;
use rocket_contrib::templates::Template;
use cortex::backend::Backend;
use cortex::frontend::cached::cache_worker;
use cortex::frontend::concerns::{
serve_entry, serve_entry_preview, serve_report, serve_rerun, UNKNOWN,
};
use cortex::frontend::cors::CORS;
use cortex::frontend::helpers::*;
use cortex::frontend::params::{ReportParams, RerunRequestParams, TemplateContext};
use cortex::models::{Corpus, HistoricalRun, RunMetadata, RunMetadataStack, Service};
use cortex::sysinfo;
#[get("/")]
fn root() -> Template {
let mut context = TemplateContext::default();
let mut global = HashMap::new();
global.insert(
"title".to_string(),
"Overview of available Corpora".to_string(),
);
global.insert(
"description".to_string(),
"An analysis framework for corpora of TeX/LaTeX documents - overview page".to_string(),
);
let backend = Backend::default();
let corpora = backend
.corpora()
.iter()
.map(Corpus::to_hash)
.collect::<Vec<_>>();
context.global = global;
context.corpora = Some(corpora);
decorate_uri_encodings(&mut context);
Template::render("overview", context)
}
#[get("/admin")]
fn admin() -> Template {
let mut global = HashMap::new();
global.insert("title".to_string(), "Admin Interface".to_string());
global.insert(
"description".to_string(),
"An analysis framework for corpora of TeX/LaTeX documents - admin interface.".to_string(),
);
match sysinfo::report(&mut global) {
Ok(_) => {},
Err(e) => println!("Sys report failed: {:?}", e),
};
let context = TemplateContext {
global,
..TemplateContext::default()
};
Template::render("admin", context)
}
#[get("/workers/<service_name>")]
fn worker_report(service_name: String) -> Result<Template, NotFound<String>> {
let backend = Backend::default();
let service_name = uri_unescape(Some(&service_name)).unwrap_or_else(|| UNKNOWN.to_string());
if let Ok(service) = Service::find_by_name(&service_name, &backend.connection) {
let mut global = HashMap::new();
global.insert(
"title".to_string(),
format!("Worker report for service {} ", &service_name),
);
global.insert(
"description".to_string(),
format!(
"Worker report for service {} as registered by the CorTeX dispatcher",
&service_name
),
);
global.insert("service_name".to_string(), service_name.to_string());
global.insert(
"service_description".to_string(),
service.description.clone(),
);
let mut context = TemplateContext {
global,
..TemplateContext::default()
};
let workers = service
.select_workers(&backend.connection)
.unwrap()
.into_iter()
.map(Into::into)
.collect();
context.workers = Some(workers);
Ok(Template::render("workers", context))
} else {
Err(NotFound(String::from("no such service")))
}
}
#[get("/corpus/<corpus_name>")]
fn corpus(corpus_name: String) -> Result<Template, NotFound<String>> {
let backend = Backend::default();
let corpus_name = uri_unescape(Some(&corpus_name)).unwrap_or_else(|| UNKNOWN.to_string());
let corpus_result = Corpus::find_by_name(&corpus_name, &backend.connection);
if let Ok(corpus) = corpus_result {
let mut global = HashMap::new();
global.insert(
"title".to_string(),
"Registered services for ".to_string() + &corpus_name,
);
global.insert(
"description".to_string(),
"An analysis framework for corpora of TeX/LaTeX documents - registered services for "
.to_string()
+ &corpus_name,
);
global.insert("corpus_name".to_string(), corpus_name);
global.insert("corpus_description".to_string(), corpus.description.clone());
let mut context = TemplateContext {
global,
..TemplateContext::default()
};
let services_result = corpus.select_services(&backend.connection);
if let Ok(backend_services) = services_result {
let services = backend_services
.iter()
.map(Service::to_hash)
.collect::<Vec<_>>();
let mut service_reports = Vec::new();
for service in services {
service_reports.push(service);
}
context.services = Some(service_reports);
}
decorate_uri_encodings(&mut context);
return Ok(Template::render("services", context));
}
Err(NotFound(format!(
"Corpus {} is not registered",
&corpus_name
)))
}
#[get("/corpus/<corpus_name>/<service_name>")]
fn top_service_report(
corpus_name: String,
service_name: String,
) -> Result<Template, NotFound<String>>
{
serve_report(corpus_name, service_name, None, None, None, None)
}
#[get("/corpus/<corpus_name>/<service_name>/<severity>")]
fn severity_service_report(
corpus_name: String,
service_name: String,
severity: String,
) -> Result<Template, NotFound<String>>
{
serve_report(corpus_name, service_name, Some(severity), None, None, None)
}
#[get("/corpus/<corpus_name>/<service_name>/<severity>?<params..>")]
fn severity_service_report_all(
corpus_name: String,
service_name: String,
severity: String,
params: Option<Form<ReportParams>>,
) -> Result<Template, NotFound<String>>
{
serve_report(
corpus_name,
service_name,
Some(severity),
None,
None,
params,
)
}
#[get("/corpus/<corpus_name>/<service_name>/<severity>/<category>")]
fn category_service_report(
corpus_name: String,
service_name: String,
severity: String,
category: String,
) -> Result<Template, NotFound<String>>
{
serve_report(
corpus_name,
service_name,
Some(severity),
Some(category),
None,
None,
)
}
#[get("/corpus/<corpus_name>/<service_name>/<severity>/<category>?<params..>")]
fn category_service_report_all(
corpus_name: String,
service_name: String,
severity: String,
category: String,
params: Option<Form<ReportParams>>,
) -> Result<Template, NotFound<String>>
{
serve_report(
corpus_name,
service_name,
Some(severity),
Some(category),
None,
params,
)
}
#[get("/corpus/<corpus_name>/<service_name>/<severity>/<category>/<what>")]
fn what_service_report(
corpus_name: String,
service_name: String,
severity: String,
category: String,
what: String,
) -> Result<Template, NotFound<String>>
{
serve_report(
corpus_name,
service_name,
Some(severity),
Some(category),
Some(what),
None,
)
}
#[get("/corpus/<corpus_name>/<service_name>/<severity>/<category>/<what>?<params..>")]
fn what_service_report_all(
corpus_name: String,
service_name: String,
severity: String,
category: String,
what: String,
params: Option<Form<ReportParams>>,
) -> Result<Template, NotFound<String>>
{
serve_report(
corpus_name,
service_name,
Some(severity),
Some(category),
Some(what),
params,
)
}
#[get("/history/<corpus_name>/<service_name>")]
fn historical_runs(
corpus_name: String,
service_name: String,
) -> Result<Template, NotFound<String>>
{
let mut context = TemplateContext::default();
let mut global = HashMap::new();
let backend = Backend::default();
let corpus_name = corpus_name.to_lowercase();
if let Ok(corpus) = Corpus::find_by_name(&corpus_name, &backend.connection) {
if let Ok(service) = Service::find_by_name(&service_name, &backend.connection) {
if let Ok(runs) = HistoricalRun::find_by(&corpus, &service, &backend.connection) {
let runs_meta = runs
.into_iter()
.map(Into::into)
.collect::<Vec<RunMetadata>>();
let runs_meta_stack: Vec<RunMetadataStack> = RunMetadataStack::transform(&runs_meta);
context.history_serialized = Some(serde_json::to_string(&runs_meta_stack).unwrap());
global.insert(
"history_length".to_string(),
runs_meta
.iter()
.filter(|run| !run.end_time.is_empty())
.count()
.to_string(),
);
context.history = Some(runs_meta);
}
}
}
global.insert(
"description".to_string(),
format!(
"Historical runs of service {} over corpus {}",
service_name, corpus_name
),
);
global.insert("service_name".to_string(), service_name);
global.insert("corpus_name".to_string(), corpus_name);
context.global = global;
decorate_uri_encodings(&mut context);
Ok(Template::render("history", context))
}
#[get("/preview/<corpus_name>/<service_name>/<entry_name>")]
fn preview_entry(
corpus_name: String,
service_name: String,
entry_name: String,
) -> Result<Template, NotFound<String>>
{
serve_entry_preview(corpus_name, service_name, entry_name)
}
#[post("/entry/<service_name>/<entry_id>", data = "<data>")]
fn entry_fetch(
service_name: String,
entry_id: usize,
data: Data,
) -> Result<NamedFile, NotFound<String>>
{
serve_entry(service_name, entry_id, data)
}
#[get("/expire_captcha")]
fn expire_captcha() -> Result<Template, NotFound<String>> {
let mut context = TemplateContext::default();
let mut global = HashMap::new();
global.insert(
"description".to_string(),
"Expire captcha cache for CorTeX.".to_string(),
);
context.global = global;
Ok(Template::render("expire_captcha", context))
}
#[post(
"/rerun/<corpus_name>/<service_name>",
format = "application/json",
data = "<rr>"
)]
fn rerun_corpus(
corpus_name: String,
service_name: String,
rr: Json<RerunRequestParams>,
) -> Result<Accepted<String>, NotFound<String>>
{
let corpus_name = corpus_name.to_lowercase();
serve_rerun(corpus_name, service_name, None, None, None, rr)
}
#[post(
"/rerun/<corpus_name>/<service_name>/<severity>",
format = "application/json",
data = "<rr>"
)]
fn rerun_severity(
corpus_name: String,
service_name: String,
severity: String,
rr: Json<RerunRequestParams>,
) -> Result<Accepted<String>, NotFound<String>>
{
serve_rerun(corpus_name, service_name, Some(severity), None, None, rr)
}
#[post(
"/rerun/<corpus_name>/<service_name>/<severity>/<category>",
format = "application/json",
data = "<rr>"
)]
fn rerun_category(
corpus_name: String,
service_name: String,
severity: String,
category: String,
rr: Json<RerunRequestParams>,
) -> Result<Accepted<String>, NotFound<String>>
{
serve_rerun(
corpus_name,
service_name,
Some(severity),
Some(category),
None,
rr,
)
}
#[post(
"/rerun/<corpus_name>/<service_name>/<severity>/<category>/<what>",
format = "application/json",
data = "<rr>"
)]
fn rerun_what(
corpus_name: String,
service_name: String,
severity: String,
category: String,
what: String,
rr: Json<RerunRequestParams>,
) -> Result<Accepted<String>, NotFound<String>>
{
serve_rerun(
corpus_name,
service_name,
Some(severity),
Some(category),
Some(what),
rr,
)
}
#[get("/favicon.ico")]
fn favicon() -> Result<NamedFile, NotFound<String>> {
let path = Path::new("public/").join("favicon.ico");
NamedFile::open(&path).map_err(|_| NotFound(format!("Bad path: {:?}", path)))
}
#[get("/robots.txt")]
fn robots() -> Result<NamedFile, NotFound<String>> {
let path = Path::new("public/").join("robots.txt");
NamedFile::open(&path).map_err(|_| NotFound(format!("Bad path: {:?}", path)))
}
#[get("/public/<file..>")]
fn files(file: PathBuf) -> Result<NamedFile, NotFound<String>> {
let path = Path::new("public/").join(file);
NamedFile::open(&path).map_err(|_| NotFound(format!("Bad path: {:?}", path)))
}
fn rocket() -> rocket::Rocket {
rocket::ignite()
.mount(
"/",
routes![
root,
admin,
corpus,
favicon,
robots,
files,
worker_report,
top_service_report,
severity_service_report,
category_service_report,
what_service_report,
severity_service_report_all,
category_service_report_all,
what_service_report_all,
preview_entry,
entry_fetch,
rerun_corpus,
rerun_severity,
rerun_category,
rerun_what,
expire_captcha,
historical_runs
],
)
.attach(Template::fairing())
.attach(CORS())
}
fn main() {
let _ = thread::spawn(move || {
cache_worker();
});
rocket().launch();
}