implement visitors count macro for each directory index

This commit is contained in:
yggverse 2025-06-28 18:13:17 +03:00
parent 450bbe60f3
commit 56830be4a9
7 changed files with 35 additions and 19 deletions

View file

@ -83,7 +83,7 @@ nexy -p /path/to/public_dir
* this template file expects pattern and cannot be in binary format * this template file expects pattern and cannot be in binary format
**Patterns** * `{list}` - entries list for the current directory **Patterns** * `{list}` - entries list for the current directory * `{hosts}` - unique visitors count * `{hits}` - requests count
--list-dir-show-count --list-dir-show-count
Show files count in dir (as the alternative text for navigation links) Show files count in dir (as the alternative text for navigation links)

View file

@ -73,6 +73,8 @@ pub struct Config {
/// ///
/// **Patterns** /// **Patterns**
/// * `{list}` - entries list for the current directory /// * `{list}` - entries list for the current directory
/// * `{hosts}` - unique visitors count
/// * `{hits}` - requests count
#[arg(long)] #[arg(long)]
pub template_index: Option<String>, pub template_index: Option<String>,

View file

@ -8,6 +8,6 @@ pub enum Response<'a> {
NotFound(&'a str), NotFound(&'a str),
/// Includes bytes array /// Includes bytes array
File(&'a [u8]), File(&'a [u8]),
/// Includes bytes array + public root directory status /// Includes query, list + is public root directory status
Directory(String, bool), Directory(&'a str, String, bool),
} }

View file

@ -70,15 +70,19 @@ impl Connection {
fn response(&mut self, response: Response) -> usize { fn response(&mut self, response: Response) -> usize {
let bytes = match response { let bytes = match response {
Response::File(b) => b, Response::File(b) => b,
Response::Directory(ref s, is_root) => { Response::Directory(q, ref s, is_root) => {
&if is_root { &if is_root {
self.session.template.welcome( self.session.template.welcome(
Some(s), Some(s),
Some(self.session.request.count()), Some(self.session.request.count()),
Some(self.session.request.total()), Some(self.session.request.total(None)),
) )
} else { } else {
self.session.template.index(Some(s)) self.session.template.index(
Some(s),
Some(self.session.request.count()),
Some(self.session.request.total(Some(q))),
)
} }
} }
Response::InternalServerError(e) => { Response::InternalServerError(e) => {

View file

@ -60,7 +60,7 @@ impl Public {
match fs::metadata(&p) { match fs::metadata(&p) {
Ok(t) => match (t.is_dir(), t.is_file()) { Ok(t) => match (t.is_dir(), t.is_file()) {
(true, _) => callback(match self.list(&p) { (true, _) => callback(match self.list(&p) {
Ok(l) => Response::Directory(l, p == self.public_dir), Ok(list) => Response::Directory(query, list, p == self.public_dir),
Err(e) => Response::InternalServerError(e.to_string()), Err(e) => Response::InternalServerError(e.to_string()),
}), }),
(_, true) => match fs::File::open(p) { (_, true) => match fs::File::open(p) {

View file

@ -37,15 +37,22 @@ impl Request {
} }
} }
pub fn total(&self) -> usize { pub fn total(&self, query_prefix: Option<&str>) -> usize {
let mut t = 0;
if let Some(ref this) = self.0 { if let Some(ref this) = self.0 {
let mut t = 0; for queries in this.read().unwrap().values() {
for c in this.read().unwrap().values() { match query_prefix {
t += c.len() Some(p) => {
for q in queries {
if q.value.starts_with(p) {
t += 1
}
}
}
None => t += queries.len(),
}
} }
t
} else {
0
} }
t
} }
} }

View file

@ -16,7 +16,7 @@ impl Template {
}, },
index: match config.template_index { index: match config.template_index {
Some(ref p) => read_to_string(p)?, Some(ref p) => read_to_string(p)?,
None => "{list}".into(), None => "{list}\n\n👁 {hosts} / {hits}".into(),
}, },
internal_server_error: match config.template_internal_server_error { internal_server_error: match config.template_internal_server_error {
Some(ref p) => read(p)?, Some(ref p) => read(p)?,
@ -37,9 +37,11 @@ impl Template {
&self.access_denied &self.access_denied
} }
pub fn index(&self, list: Option<&str>) -> Vec<u8> { pub fn index(&self, list: Option<&str>, hosts: Option<usize>, hits: Option<usize>) -> Vec<u8> {
self.index self.index
.replace("{list}", list.unwrap_or_default()) .replace("{list}", list.unwrap_or_default())
.replace("{hosts}", &format_count(hosts))
.replace("{hits}", &format_count(hits))
.into() .into()
} }
@ -57,9 +59,6 @@ impl Template {
hosts: Option<usize>, hosts: Option<usize>,
hits: Option<usize>, hits: Option<usize>,
) -> Vec<u8> { ) -> Vec<u8> {
fn format_count(v: Option<usize>) -> String {
v.map_or(String::new(), |c| c.to_string())
}
self.welcome self.welcome
.replace("{list}", list.unwrap_or_default()) .replace("{list}", list.unwrap_or_default())
.replace("{hosts}", &format_count(hosts)) .replace("{hosts}", &format_count(hosts))
@ -67,3 +66,7 @@ impl Template {
.into() .into()
} }
} }
fn format_count(v: Option<usize>) -> String {
v.map_or(String::new(), |c| c.to_string())
}