feat: add robots.txt support
Diff
rgit.conf.example | 2 ++-
src/configuration/mod.rs | 8 +++++++-
src/main.rs | 58 +++++++++++++++++++++++++++++++++++++++----------
statics/robots.txt | 2 ++-
4 files changed, 59 insertions(+), 11 deletions(-)
@@ -34,5 +34,7 @@ max_commit_message_length = 80
max_repo_desc_length = 80
# Use custom css on all pages
css = "/custom.scss"
# Set a custom file to serve at `/robots.txt`
robots_txt = "/robots.txt"
# Change the header style
header = "Image"
\ No newline at end of file
@@ -98,6 +98,13 @@ pub struct AppConfig {
pub css: Option<String>,
pub robots_txt: Option<String>,
@@ -133,6 +140,7 @@ impl ::std::default::Default for AppConfig {
max_commit_message_length: 0,
max_repo_desc_length: 0,
css: None,
robots_txt: None,
header: Some(HeaderStyle::default()),
}
}
@@ -62,6 +62,8 @@ static GLOBAL_CSS_HASH: Lazy<Box<str>> = Lazy::new(|| build_asset_hash(GLOBAL_CS
static HIGHLIGHT_CSS_HASH: OnceCell<Box<str>> = OnceCell::new();
static DARK_HIGHLIGHT_CSS_HASH: OnceCell<Box<str>> = OnceCell::new();
static GLOBAL_ROBOTS_TXT: &[u8] = include_bytes!("../statics/robots.txt");
#[derive(Parser, Debug)]
#[clap(author, version, about)]
pub struct Args {
@@ -201,28 +203,30 @@ async fn main() -> Result<(), anyhow::Error> {
}
};
let static_file = |file: String| {
let static_file = |file: Option<String>, fallback_data: Option<&'static [u8]>| {
move || async move {
let mime_type = mime_guess::from_path(&file)
let mime_type = mime_guess::from_path(&file.clone().unwrap_or_default())
.first()
.map_or("text/plain".to_string(), |mime| {
mime.essence_str().to_string()
});
let content = std::fs::read(&PathBuf::from(&file)).unwrap_or_else(|_| {
if file.is_empty() {
warn!("Requested filename is blank.");
}
error!("Unable to load file: {}", file);
Vec::new()
});
let mut content = Vec::new();
match file.clone() {
Some(path) => content = read_file_contents(path.clone(), fallback_data).await,
None => (),
};
let mut resp = Response::new(Body::from(content));
resp.headers_mut().insert(
http::header::CONTENT_TYPE,
HeaderValue::from_str(&mime_type).unwrap_or_else(|_| {
warn!("Invalid MIME type: {} for file: {}", mime_type, file);
warn!(
"Invalid MIME type: {} for file: {}",
mime_type,
file.unwrap_or_default()
);
HeaderValue::from_static("text/plain")
}),
);
@@ -257,7 +261,14 @@ async fn main() -> Result<(), anyhow::Error> {
.route("/about", get(methods::about::handle))
.route(
&format!("/{}", get_logo_filename(config.logo.clone())),
get(static_file(config.logo.clone())),
get(static_file(Some(config.logo.clone()), None)),
)
.route(
"/robots.txt",
get(static_file(
Some(config.robots_txt.clone().unwrap_or_default()),
Some(GLOBAL_ROBOTS_TXT),
)),
)
.fallback(methods::repo::service)
.layer(TimeoutLayer::new(args.request_timeout.into()))
@@ -434,3 +445,28 @@ pub fn get_logo_filename(filename: String) -> String {
.to_string();
format!("logo.{}", extension)
}
async fn read_file_contents(file: String, fallback_data: Option<&'static [u8]>) -> Vec<u8> {
let content = tokio::fs::read(&PathBuf::from(&file))
.await
.unwrap_or_else(|_| {
let data = match (file.is_empty(), fallback_data) {
(true, Some(fallback)) => {
fallback.into()
}
(true, None) => {
warn!("Fallback data missing.");
Vec::new()
}
(false, _) => {
file.into()
}
};
data
});
content
}
@@ -0,0 +1,2 @@
User-agent: *
Allow: /