118 lines
4.5 KiB
Rust
118 lines
4.5 KiB
Rust
//! Host-owned HTML and JavaScript. Public review data enters HTML only as escaped text.
|
|
use super::{Api, Error, Result, request_view};
|
|
use bytes::Bytes;
|
|
use http::Response;
|
|
|
|
fn escape(text: &str) -> String {
|
|
text.replace('&', "&")
|
|
.replace('<', "<")
|
|
.replace('>', ">")
|
|
.replace('"', """)
|
|
.replace('\'', "'")
|
|
}
|
|
pub(super) fn get(ctx: &Api, path: &str) -> Result<Option<Response<Bytes>>> {
|
|
let parts: Vec<_> = path.trim_matches('/').split('/').collect();
|
|
let (mime, text) = match parts.as_slice() {
|
|
["app.js"] => (
|
|
"text/javascript; charset=utf-8",
|
|
include_str!("app.js").to_string(),
|
|
),
|
|
["register"] => (
|
|
"text/html; charset=utf-8",
|
|
include_str!("register.html").to_string(),
|
|
),
|
|
["approve", id] => {
|
|
let request = ctx
|
|
.cred
|
|
.request_list()?
|
|
.into_iter()
|
|
.find(|request| request.id == *id)
|
|
.ok_or(Error(404, "request_missing"))?;
|
|
let items = request
|
|
.payload
|
|
.get("items")
|
|
.and_then(serde_json::Value::as_array)
|
|
.cloned()
|
|
.unwrap_or_default();
|
|
let grant = request
|
|
.payload
|
|
.get("grant_create")
|
|
.unwrap_or(&request.payload);
|
|
let grant_request = request.payload.get("grant_create").is_some() || !items.is_empty();
|
|
let duration = grant
|
|
.get("duration_secs")
|
|
.and_then(serde_json::Value::as_u64)
|
|
.or_else(|| {
|
|
grant
|
|
.get("exp")
|
|
.and_then(serde_json::Value::as_i64)
|
|
.map(|expiry| {
|
|
expiry.saturating_sub(crate::cred::store::now()).max(1) as u64
|
|
})
|
|
})
|
|
.unwrap_or(3600);
|
|
let uses = grant
|
|
.get("max_uses")
|
|
.and_then(serde_json::Value::as_u64)
|
|
.map(|uses| uses.to_string())
|
|
.unwrap_or_default();
|
|
let mut review = request_view(request)?;
|
|
if !items.is_empty() {
|
|
review["items"] = super::value(
|
|
ctx.cred
|
|
.item_list()?
|
|
.into_iter()
|
|
.filter(|item| items.iter().any(|id| id.as_str() == Some(&item.id)))
|
|
.collect::<Vec<_>>(),
|
|
)?;
|
|
}
|
|
let text = include_str!("approve.html")
|
|
.replace(
|
|
"{{grant_request}}",
|
|
if grant_request { "true" } else { "false" },
|
|
)
|
|
.replace(
|
|
"{{limits_hidden}}",
|
|
if grant_request { "" } else { "hidden disabled" },
|
|
)
|
|
.replace("{{duration}}", &duration.to_string())
|
|
.replace("{{uses}}", &uses)
|
|
.replace("{{request_id}}", &escape(id))
|
|
.replace(
|
|
"{{summary}}",
|
|
&escape(review["summary"].as_str().expect("summary string")),
|
|
)
|
|
.replace(
|
|
"{{details}}",
|
|
&escape(
|
|
&serde_json::to_string_pretty(&review)
|
|
.map_err(|_| Error(500, "serialization"))?,
|
|
),
|
|
);
|
|
("text/html; charset=utf-8", text)
|
|
}
|
|
["entry", item, field] => {
|
|
let snapshot = ctx.cred.item_get(item)?;
|
|
let selected = super::auth::field(ctx, item, field)?;
|
|
let text = include_str!("entry.html")
|
|
.replace("{{item_id}}", &escape(item))
|
|
.replace("{{field_id}}", &escape(field))
|
|
.replace("{{item_name}}", &escape(&snapshot.name))
|
|
.replace("{{field_name}}", &escape(&selected.name))
|
|
.replace(
|
|
"{{public_key}}",
|
|
&serde_json::to_string(&ctx.entry.public_key())
|
|
.map_err(|_| Error(500, "serialization"))?,
|
|
);
|
|
("text/html; charset=utf-8", text)
|
|
}
|
|
_ => return Ok(None),
|
|
};
|
|
Ok(Some(
|
|
Response::builder()
|
|
.header("content-type", mime)
|
|
.body(Bytes::from(text))
|
|
.expect("static headers"),
|
|
))
|
|
}
|