mirror of
https://github.com/Tim-Paik/srv.git
synced 2024-10-13 00:29:43 +00:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
d4714cd8f5
|
|||
91f9993315
|
|||
fc809f9bc9
|
|||
479f6006a6
|
|||
c15c09f07c
|
670
Cargo.lock
generated
670
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
@ -3,21 +3,22 @@ authors = ["Tim_Paik <timpaikc@outlook.com>"]
|
|||||||
description = "simple http server written in rust"
|
description = "simple http server written in rust"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "srv"
|
name = "srv"
|
||||||
version = "1.0.4"
|
version = "1.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-files = "0.6"
|
actix-files = "0.6"
|
||||||
actix-web = {version = "4.1", features = ["rustls"]}
|
actix-web = { version = "4.1", features = ["rustls"] }
|
||||||
actix-web-httpauth = "0.7"
|
actix-web-httpauth = "0.8"
|
||||||
askama = "0.11"
|
askama = "0.11"
|
||||||
askama_actix = "0.13"
|
askama_actix = "0.13"
|
||||||
clap = {version = "3.2", features = ["wrap_help", "color", "cargo"]}
|
clap = { version = "4.0", features = ["derive", "wrap_help", "color", "cargo"] }
|
||||||
|
comrak = { version = "0.14.0", default-features = false }
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime_guess = "2.0"
|
mime_guess = "2.0"
|
||||||
rustls = "0.20"
|
rustls = "0.20"
|
||||||
rustls-pemfile = "1.0"
|
rustls-pemfile = "1.0"
|
||||||
serde = {version = "1.0", features = ["derive"]}
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
time = { version = "0.3", features = ["formatting", "parsing"] }
|
time = { version = "0.3", features = ["formatting", "parsing"] }
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
|
210
src/main.rs
210
src/main.rs
@ -14,7 +14,7 @@ use actix_web_httpauth::{
|
|||||||
middleware::HttpAuthentication,
|
middleware::HttpAuthentication,
|
||||||
};
|
};
|
||||||
use askama_actix::TemplateToResponse;
|
use askama_actix::TemplateToResponse;
|
||||||
use clap::Arg;
|
use clap::{arg, command, ArgAction};
|
||||||
use env_logger::fmt::Color;
|
use env_logger::fmt::Color;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -22,11 +22,11 @@ use sha2::Digest;
|
|||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
env::{set_var, var},
|
env::{set_var, var},
|
||||||
fs::{self, metadata, read_dir},
|
fs::{self, metadata, read_dir, read_to_string},
|
||||||
io::{self, BufReader, Read, Write},
|
io::{self, BufReader, Read, Write},
|
||||||
net::IpAddr,
|
net::IpAddr,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::Command,
|
process::{Command, Stdio},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@ -60,6 +60,7 @@ struct File {
|
|||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct IndexContext {
|
struct IndexContext {
|
||||||
title: String,
|
title: String,
|
||||||
|
readme: String,
|
||||||
paths: Vec<String>,
|
paths: Vec<String>,
|
||||||
dirs: Vec<Dir>,
|
dirs: Vec<Dir>,
|
||||||
files: Vec<File>,
|
files: Vec<File>,
|
||||||
@ -86,6 +87,7 @@ fn render_index(
|
|||||||
let show_dot_files = var("DOTFILES").unwrap_or_else(|_| "false".to_string()) == "true";
|
let show_dot_files = var("DOTFILES").unwrap_or_else(|_| "false".to_string()) == "true";
|
||||||
let mut context = IndexContext {
|
let mut context = IndexContext {
|
||||||
title: "".to_string(),
|
title: "".to_string(),
|
||||||
|
readme: "".to_string(),
|
||||||
paths: vec![],
|
paths: vec![],
|
||||||
dirs: vec![],
|
dirs: vec![],
|
||||||
files: vec![],
|
files: vec![],
|
||||||
@ -98,6 +100,7 @@ fn render_index(
|
|||||||
let path = path.into_owned();
|
let path = path.into_owned();
|
||||||
context.paths.push(path);
|
context.paths.push(path);
|
||||||
}
|
}
|
||||||
|
let mut readme_str = "".to_string();
|
||||||
match read_dir(&dir.path) {
|
match read_dir(&dir.path) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(target: "read_dir", "[ERROR] Read dir error: {}", e.to_string());
|
error!(target: "read_dir", "[ERROR] Read dir error: {}", e.to_string());
|
||||||
@ -150,10 +153,44 @@ fn render_index(
|
|||||||
filetype,
|
filetype,
|
||||||
modified,
|
modified,
|
||||||
});
|
});
|
||||||
|
if path.file_name().to_ascii_lowercase() == "readme.md" {
|
||||||
|
readme_str = read_to_string(path.path()).unwrap_or_else(|_| "".to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if var("NOREADME").unwrap_or_else(|_| "false".to_string()) != "true" {
|
||||||
|
context.readme = comrak::markdown_to_html(
|
||||||
|
&readme_str,
|
||||||
|
&comrak::ComrakOptions {
|
||||||
|
extension: comrak::ComrakExtensionOptions {
|
||||||
|
strikethrough: true,
|
||||||
|
tagfilter: true,
|
||||||
|
table: true,
|
||||||
|
autolink: true,
|
||||||
|
tasklist: true,
|
||||||
|
superscript: true,
|
||||||
|
header_ids: None,
|
||||||
|
footnotes: true,
|
||||||
|
description_lists: true,
|
||||||
|
front_matter_delimiter: None,
|
||||||
|
},
|
||||||
|
parse: comrak::ComrakParseOptions {
|
||||||
|
smart: false,
|
||||||
|
default_info_string: None,
|
||||||
|
},
|
||||||
|
render: comrak::ComrakRenderOptions {
|
||||||
|
hardbreaks: false,
|
||||||
|
github_pre_lang: false,
|
||||||
|
width: 1000,
|
||||||
|
unsafe_: true,
|
||||||
|
escape: false,
|
||||||
|
list_style: comrak::ListStyleType::default(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
context.title = context.paths.last().unwrap_or(&"/".to_string()).to_string();
|
context.title = context.paths.last().unwrap_or(&"/".to_string()).to_string();
|
||||||
context.dirs.sort();
|
context.dirs.sort();
|
||||||
context.files.sort();
|
context.files.sort();
|
||||||
@ -202,7 +239,7 @@ async fn main() -> io::Result<()> {
|
|||||||
let check_does_dir_exits = |path: &str| match metadata(path) {
|
let check_does_dir_exits = |path: &str| match metadata(path) {
|
||||||
Ok(meta) => {
|
Ok(meta) => {
|
||||||
if meta.is_dir() {
|
if meta.is_dir() {
|
||||||
Ok(())
|
Ok(path.to_string())
|
||||||
} else {
|
} else {
|
||||||
Err("Parameter is not a directory".to_owned())
|
Err("Parameter is not a directory".to_owned())
|
||||||
}
|
}
|
||||||
@ -212,7 +249,7 @@ async fn main() -> io::Result<()> {
|
|||||||
let check_does_file_exits = |path: &str| match metadata(path) {
|
let check_does_file_exits = |path: &str| match metadata(path) {
|
||||||
Ok(metadata) => {
|
Ok(metadata) => {
|
||||||
if metadata.is_file() {
|
if metadata.is_file() {
|
||||||
Ok(())
|
Ok(path.to_string())
|
||||||
} else {
|
} else {
|
||||||
Err("Parameter is not a file".to_owned())
|
Err("Parameter is not a file".to_owned())
|
||||||
}
|
}
|
||||||
@ -220,11 +257,11 @@ async fn main() -> io::Result<()> {
|
|||||||
Err(e) => Err(e.to_string()),
|
Err(e) => Err(e.to_string()),
|
||||||
};
|
};
|
||||||
let check_is_ip_addr = |s: &str| match IpAddr::from_str(s) {
|
let check_is_ip_addr = |s: &str| match IpAddr::from_str(s) {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(s.to_string()),
|
||||||
Err(e) => Err(e.to_string()),
|
Err(e) => Err(e.to_string()),
|
||||||
};
|
};
|
||||||
let check_is_port_num = |s: &str| match s.parse::<u16>() {
|
let check_is_port_num = |s: &str| match s.parse::<u16>() {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(s.to_string()),
|
||||||
Err(e) => Err(e.to_string()),
|
Err(e) => Err(e.to_string()),
|
||||||
};
|
};
|
||||||
let check_is_auth = |s: &str| {
|
let check_is_auth = |s: &str| {
|
||||||
@ -234,81 +271,90 @@ async fn main() -> io::Result<()> {
|
|||||||
} else if parts[0].is_empty() {
|
} else if parts[0].is_empty() {
|
||||||
Err("Username not found".to_owned())
|
Err("Username not found".to_owned())
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(s.to_string())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let matches = clap::command!()
|
let matches = command!()
|
||||||
.arg(Arg::new("noindex").long("noindex").help("Disable automatic index page generation"))
|
.arg(arg!(--noindex "Disable automatic index page generation").required(false))
|
||||||
.arg(Arg::new("nocache").long("nocache").help("Disable HTTP cache"))
|
.arg(arg!(--noreadme "Disable automatic readme rendering").required(false))
|
||||||
.arg(Arg::new("nocolor").long("nocolor").help("Disable cli colors"))
|
.arg(arg!(--nocache "Disable HTTP cache").required(false))
|
||||||
.arg(Arg::new("cors").long("cors").takes_value(true).min_values(0).max_values(1).help("Enable CORS [with custom value]"))
|
.arg(arg!(--nocolor "Disable cli colors").required(false))
|
||||||
.arg(Arg::new("spa").long("spa").help("Enable Single-Page Application mode (always serve /index.html when the file is not found)"))
|
.arg(arg!(--cors [hostname] "Enable CORS [with custom value]").required(false).action(ArgAction::Append))
|
||||||
.arg(Arg::new("dotfiles").short('d').long("dotfiles").help("Show dotfiles"))
|
.arg(arg!(--spa "Enable Single-Page Application mode (always serve /index.html when the file is not found)").required(false))
|
||||||
.arg(Arg::new("open").short('o').long("open").help("Open the page in the default browser"))
|
.arg(arg!(-d --dotfiles "Show dotfiles").required(false))
|
||||||
.arg(Arg::new("quiet").short('q').long("quiet").help("Disable access log output"))
|
.arg(arg!(-o --open "Open the page in the default browser").required(false))
|
||||||
.arg(Arg::new("quietall").long("quietall").help("Disable all output"))
|
.arg(arg!(-q --quiet "Disable access log output").required(false))
|
||||||
.arg(Arg::new("ROOT").default_value(".").validator(check_does_dir_exits).help("Root directory"))
|
.arg(arg!(--quietall "Disable all output").required(false))
|
||||||
.arg(Arg::new("address").short('a').long("address").default_value("0.0.0.0").takes_value(true).validator(check_is_ip_addr).help("IP address to serve on"))
|
.arg(arg!([root] "Root directory").default_value(".").value_parser(check_does_dir_exits))
|
||||||
.arg(Arg::new("port").short('p').long("port").default_value("8000").takes_value(true).validator(check_is_port_num).help("Port to serve on"))
|
.arg(arg!(-a --address <ipaddr> "IP address to serve on").default_value("0.0.0.0").value_parser(check_is_ip_addr))
|
||||||
.arg(Arg::new("auth").long("auth").takes_value(true).validator(check_is_auth).help("HTTP Auth (username:password)"))
|
.arg(arg!(-p --port <port> "Port to serve on").default_value("8000").value_parser(check_is_port_num))
|
||||||
.arg(Arg::new("cert").long("cert").takes_value(true).validator(check_does_file_exits).help("Path of TLS/SSL public key (certificate)"))
|
.arg(arg!(--auth <pattern> "HTTP Auth (username:password)").required(false).value_parser(check_is_auth))
|
||||||
.arg(Arg::new("key").long("key").takes_value(true).validator(check_does_file_exits).help("Path of TLS/SSL private key"))
|
.arg(arg!(--cert <path> "Path of TLS/SSL public key (certificate)").required(false).value_parser(check_does_file_exits))
|
||||||
|
.arg(arg!(--key <path> "Path of TLS/SSL private key").required(false).value_parser(check_does_file_exits))
|
||||||
.subcommand(clap::Command::new("doc")
|
.subcommand(clap::Command::new("doc")
|
||||||
.about("Open cargo doc via local server (Need cargo installation)")
|
.about("Open cargo doc via local server (Need cargo installation)")
|
||||||
.arg(Arg::new("nocolor").long("nocolor").help("Disable cli colors"))
|
.arg(arg!(--nocolor "Disable cli colors"))
|
||||||
.arg(Arg::new("noopen").long("noopen").help("Do not open the page in the default browser"))
|
.arg(arg!(--noopen "Do not open the page in the default browser"))
|
||||||
.arg(Arg::new("log").long("log").help("Enable access log output [default: disabled]"))
|
.arg(arg!(--log "Enable access log output [default: disabled]"))
|
||||||
.arg(Arg::new("quietall").long("quietall").help("Disable all output"))
|
.arg(arg!(--quietall "Disable all output"))
|
||||||
.arg(Arg::new("address").short('a').long("address").default_value("0.0.0.0").takes_value(true).validator(check_is_ip_addr).help("IP address to serve on"))
|
.arg(arg!(-a --address <ipaddr> "IP address to serve on").required(false).default_value("0.0.0.0").value_parser(check_is_ip_addr))
|
||||||
.arg(Arg::new("port").short('p').long("port").default_value("8000").takes_value(true).validator(check_is_port_num).help("Port to serve on"))
|
.arg(arg!(-p --port <port> "Port to serve on").required(false).default_value("8000").value_parser(check_is_port_num))
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
set_var(
|
set_var(
|
||||||
"ROOT",
|
"ROOT",
|
||||||
display_path(Path::new(matches.value_of("ROOT").unwrap_or("."))),
|
display_path(Path::new(
|
||||||
|
matches
|
||||||
|
.get_one::<String>("root")
|
||||||
|
.unwrap_or(&".".to_string()),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
set_var("NOINDEX", matches.is_present("noindex").to_string());
|
set_var("NOINDEX", matches.get_flag("noindex").to_string());
|
||||||
set_var("SPA", matches.is_present("spa").to_string());
|
set_var("NOREADME", matches.get_flag("noreadme").to_string());
|
||||||
set_var("DOTFILES", matches.is_present("dotfiles").to_string());
|
set_var("SPA", matches.get_flag("spa").to_string());
|
||||||
set_var("NOCACHE", matches.is_present("nocache").to_string());
|
set_var("DOTFILES", matches.get_flag("dotfiles").to_string());
|
||||||
|
set_var("NOCACHE", matches.get_flag("nocache").to_string());
|
||||||
|
|
||||||
if matches.is_present("quiet") {
|
if matches.get_flag("quiet") {
|
||||||
set_var("RUST_LOG", "info,actix_web::middleware::logger=off");
|
set_var("RUST_LOG", "info,actix_web::middleware::logger=off");
|
||||||
}
|
}
|
||||||
if matches.is_present("quietall") {
|
if matches.get_flag("quietall") {
|
||||||
set_var("RUST_LOG", "off");
|
set_var("RUST_LOG", "off");
|
||||||
}
|
}
|
||||||
if matches.is_present("nocolor") {
|
if matches.get_flag("nocolor") {
|
||||||
set_var("RUST_LOG_STYLE", "never");
|
set_var("RUST_LOG_STYLE", "never");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = matches.value_of("auth") {
|
if let Some(s) = matches.get_one::<String>("auth") {
|
||||||
set_var("ENABLE_AUTH", matches.is_present("auth").to_string());
|
set_var("ENABLE_AUTH", matches.get_flag("auth").to_string());
|
||||||
let parts = s.splitn(2, ':').collect::<Vec<&str>>();
|
let parts = s.splitn(2, ':').collect::<Vec<&str>>();
|
||||||
set_var("AUTH_USERNAME", parts[0]);
|
set_var("AUTH_USERNAME", parts[0]);
|
||||||
set_var("AUTH_PASSWORD", hash(parts[1]));
|
set_var("AUTH_PASSWORD", hash(parts[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches.is_present("cors") {
|
if let Some(mut cors) = matches.get_many::<String>("cors") {
|
||||||
set_var("ENABLE_CORS", matches.is_present("cors").to_string());
|
set_var("ENABLE_CORS", "true");
|
||||||
match matches.value_of("cors") {
|
match cors.next() {
|
||||||
Some(str) => {
|
Some(value) => set_var("CORS", value),
|
||||||
set_var("CORS", str);
|
None => set_var("CORS", "*"),
|
||||||
}
|
|
||||||
None => {
|
|
||||||
set_var("CORS", "*");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let enable_tls = matches.is_present("cert") && matches.is_present("key");
|
let enable_tls =
|
||||||
|
matches.get_one::<String>("cert").is_some() && matches.get_one::<String>("key").is_some();
|
||||||
let ip = matches
|
let ip = matches
|
||||||
.value_of("address")
|
.get_one::<String>("address")
|
||||||
.unwrap_or("127.0.0.1")
|
.unwrap_or(&"127.0.0.1".to_string())
|
||||||
.to_string();
|
.to_string();
|
||||||
let addr = format!("{}:{}", ip, matches.value_of("port").unwrap_or("8000"));
|
let addr = format!(
|
||||||
|
"{}:{}",
|
||||||
|
ip,
|
||||||
|
matches
|
||||||
|
.get_one::<String>("port")
|
||||||
|
.unwrap_or(&"8000".to_string())
|
||||||
|
);
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}{}:{}",
|
"{}{}:{}",
|
||||||
if enable_tls {
|
if enable_tls {
|
||||||
@ -317,7 +363,9 @@ async fn main() -> io::Result<()> {
|
|||||||
"http://".to_string()
|
"http://".to_string()
|
||||||
},
|
},
|
||||||
if ip == "0.0.0.0" { "127.0.0.1" } else { &ip },
|
if ip == "0.0.0.0" { "127.0.0.1" } else { &ip },
|
||||||
matches.value_of("port").unwrap_or("8000")
|
matches
|
||||||
|
.get_one::<String>("port")
|
||||||
|
.unwrap_or(&"8000".to_string())
|
||||||
);
|
);
|
||||||
|
|
||||||
let open_in_browser = |url: &str| {
|
let open_in_browser = |url: &str| {
|
||||||
@ -336,18 +384,18 @@ async fn main() -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches.is_present("open") {
|
if matches.get_flag("open") {
|
||||||
open_in_browser(&url);
|
open_in_browser(&url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("doc") {
|
if let Some(matches) = matches.subcommand_matches("doc") {
|
||||||
if !matches.is_present("log") {
|
if !matches.get_flag("log") {
|
||||||
set_var("RUST_LOG", "info,actix_web::middleware::logger=off");
|
set_var("RUST_LOG", "info,actix_web::middleware::logger=off");
|
||||||
}
|
}
|
||||||
if matches.is_present("quietall") {
|
if matches.get_flag("quietall") {
|
||||||
set_var("RUST_LOG", "off");
|
set_var("RUST_LOG", "off");
|
||||||
}
|
}
|
||||||
if matches.is_present("nocolor") {
|
if matches.get_flag("nocolor") {
|
||||||
set_var("RUST_LOG_STYLE", "never");
|
set_var("RUST_LOG_STYLE", "never");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -460,17 +508,19 @@ async fn main() -> io::Result<()> {
|
|||||||
};
|
};
|
||||||
let crate_name = contents.package.name;
|
let crate_name = contents.package.name;
|
||||||
info!("[INFO] Generating document (may take a while)");
|
info!("[INFO] Generating document (may take a while)");
|
||||||
match Command::new("cargo").arg("doc").output() {
|
match Command::new("cargo")
|
||||||
Ok(output) => {
|
.arg("doc")
|
||||||
let output = std::str::from_utf8(&output.stderr).unwrap_or("");
|
.stdin(Stdio::inherit())
|
||||||
if output.starts_with("error: could not find `Cargo.toml` in") {
|
.stdout(Stdio::inherit())
|
||||||
error!("[ERROR] Cargo.toml Not Found");
|
.stderr(Stdio::inherit())
|
||||||
return Ok(());
|
.status()
|
||||||
} else if output.starts_with("error: ") {
|
{
|
||||||
error!(
|
Ok(status) => {
|
||||||
"[ERROR] {}",
|
if !status.success() {
|
||||||
output.strip_prefix("error: ").unwrap_or(output)
|
match status.code() {
|
||||||
);
|
Some(code) => error!("[ERROR] Cargo exited with status code: {code}"),
|
||||||
|
None => error!("[ERROR] Cargo terminated by signal"),
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -488,17 +538,25 @@ async fn main() -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
set_var("ROOT", display_path(path));
|
set_var("ROOT", display_path(path));
|
||||||
let ip = matches
|
let ip = matches
|
||||||
.value_of("address")
|
.get_one::<String>("address")
|
||||||
.unwrap_or("127.0.0.1")
|
.unwrap_or(&"127.0.0.1".to_string())
|
||||||
.to_string();
|
.to_string();
|
||||||
let addr = format!("{}:{}", ip, matches.value_of("port").unwrap_or("8000"));
|
let addr = format!(
|
||||||
|
"{}:{}",
|
||||||
|
ip,
|
||||||
|
matches
|
||||||
|
.get_one::<String>("port")
|
||||||
|
.unwrap_or(&"8000".to_string())
|
||||||
|
);
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"http://{}:{}/{}/index.html",
|
"http://{}:{}/{}/index.html",
|
||||||
if ip == "0.0.0.0" { "127.0.0.1" } else { &ip },
|
if ip == "0.0.0.0" { "127.0.0.1" } else { &ip },
|
||||||
matches.value_of("port").unwrap_or("8000"),
|
matches
|
||||||
|
.get_one::<String>("port")
|
||||||
|
.unwrap_or(&"8000".to_string()),
|
||||||
crate_name,
|
crate_name,
|
||||||
);
|
);
|
||||||
if !matches.is_present("noopen") {
|
if !matches.get_flag("noopen") {
|
||||||
open_in_browser(&url);
|
open_in_browser(&url);
|
||||||
}
|
}
|
||||||
addr
|
addr
|
||||||
@ -576,10 +634,10 @@ async fn main() -> io::Result<()> {
|
|||||||
});
|
});
|
||||||
let server = if enable_tls {
|
let server = if enable_tls {
|
||||||
let cert = &mut BufReader::new(
|
let cert = &mut BufReader::new(
|
||||||
fs::File::open(Path::new(matches.value_of("cert").unwrap())).unwrap(),
|
fs::File::open(Path::new(matches.get_one::<String>("cert").unwrap())).unwrap(),
|
||||||
);
|
);
|
||||||
let key = &mut BufReader::new(
|
let key = &mut BufReader::new(
|
||||||
fs::File::open(Path::new(matches.value_of("key").unwrap())).unwrap(),
|
fs::File::open(Path::new(matches.get_one::<String>("key").unwrap())).unwrap(),
|
||||||
);
|
);
|
||||||
let cert = rustls_pemfile::certs(cert)
|
let cert = rustls_pemfile::certs(cert)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
1
templates/github-markdown.css.html
Normal file
1
templates/github-markdown.css.html
Normal file
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
{# This Source Code Form is subject to the terms of the Mozilla Public
|
{# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/. #}
|
# file, You can obtain one at https://mozilla.org/MPL/2.0/. -#}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
@ -145,7 +145,7 @@
|
|||||||
#meta,
|
#meta,
|
||||||
#listing {
|
#listing {
|
||||||
color: #cacaca;
|
color: #cacaca;
|
||||||
background: #000000;
|
background: #0d1117;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header nav span a {
|
#header nav span a {
|
||||||
@ -154,7 +154,7 @@
|
|||||||
|
|
||||||
#header {
|
#header {
|
||||||
padding: 1.5rem 5% 1rem;
|
padding: 1.5rem 5% 1rem;
|
||||||
background-color: #0e0e0e;
|
background-color: #161b22;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing table {
|
#listing table {
|
||||||
@ -313,6 +313,26 @@
|
|||||||
<div style="text-align: center; margin: 1rem; color: #cccccc;">Nothing here</div>
|
<div style="text-align: center; margin: 1rem; color: #cccccc;">Nothing here</div>
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
</div>
|
</div>
|
||||||
|
{% if readme != "".to_string() -%}
|
||||||
|
<div id="readme">
|
||||||
|
{{ readme|safe }}
|
||||||
|
</div>
|
||||||
|
{% include "github-markdown.css.html" %}
|
||||||
|
<style>
|
||||||
|
#readme {
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 980px;
|
||||||
|
margin: 10px auto;
|
||||||
|
padding: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#readme {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endif -%}
|
||||||
</main>
|
</main>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
|
Reference in New Issue
Block a user