mirror of
https://github.com/YGGverse/nexy.git
synced 2026-03-31 09:15:28 +00:00
implement connections count with template macro, add --show-hidden option, fix configuration variable names
This commit is contained in:
parent
8cdd7ea8ae
commit
c484a59579
8 changed files with 119 additions and 19 deletions
|
|
@ -51,6 +51,11 @@ nexy -p /path/to/public_dir
|
|||
-p, --public <PUBLIC>
|
||||
Absolute path to the public files directory
|
||||
|
||||
--show-hidden
|
||||
Show hidden entries (in the directory listing)
|
||||
|
||||
* Important: this option does not prevent access to hidden files!
|
||||
|
||||
--template-access-denied <TEMPLATE_ACCESS_DENIED>
|
||||
Absolute path to the `Access denied` template file
|
||||
|
||||
|
|
@ -71,7 +76,7 @@ nexy -p /path/to/public_dir
|
|||
|
||||
* this template file expects pattern and cannot be in binary format
|
||||
|
||||
**Patterns** * `{list}` - entries list for the `public` directory
|
||||
**Patterns** * `{list}` - entries list for the `public` directory * `{hosts}` - unique visitors count * `{hits}` - requests count
|
||||
|
||||
--template-index <TEMPLATE_INDEX>
|
||||
Absolute path to the `Index` template file for each directory
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ pub struct Config {
|
|||
#[arg(short, long)]
|
||||
pub public: String,
|
||||
|
||||
/// Show hidden entries (in the directory listing)
|
||||
///
|
||||
/// * Important: this option does not prevent access to hidden files!
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub show_hidden: bool,
|
||||
|
||||
/// Absolute path to the `Access denied` template file
|
||||
///
|
||||
/// * this template file can be in binary format (e.g. image)
|
||||
|
|
@ -56,6 +62,8 @@ pub struct Config {
|
|||
///
|
||||
/// **Patterns**
|
||||
/// * `{list}` - entries list for the `public` directory
|
||||
/// * `{hosts}` - unique visitors count
|
||||
/// * `{hits}` - requests count
|
||||
#[arg(long)]
|
||||
pub template_welcome: Option<String>,
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ impl Connection {
|
|||
}
|
||||
|
||||
pub fn handle(mut self) {
|
||||
self.session.event.connection.update(&self.address.client);
|
||||
let mut t = 0; // total bytes
|
||||
match self.request() {
|
||||
Ok(q) => {
|
||||
|
|
@ -71,7 +72,11 @@ impl Connection {
|
|||
Response::File(b) => b,
|
||||
Response::Directory(ref s, is_root) => {
|
||||
&if is_root {
|
||||
self.session.template.welcome(Some(s))
|
||||
self.session.template.welcome(
|
||||
Some(s),
|
||||
Some(self.session.event.connection.hosts()),
|
||||
Some(self.session.event.connection.hits()),
|
||||
)
|
||||
} else {
|
||||
self.session.template.index(Some(s))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,32 @@
|
|||
mod access_log;
|
||||
mod debug;
|
||||
mod event;
|
||||
mod storage;
|
||||
mod template;
|
||||
|
||||
use {access_log::AccessLog, debug::Debug, storage::Storage, template::Template};
|
||||
use {access_log::AccessLog, debug::Debug, event::Event, storage::Storage, template::Template};
|
||||
|
||||
/// Shared, multi-thread features for the current server session
|
||||
pub struct Session {
|
||||
pub debug: Debug,
|
||||
pub access_log: AccessLog,
|
||||
pub debug: Debug,
|
||||
pub event: Event,
|
||||
pub storage: Storage,
|
||||
pub template: Template,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn init(config: &crate::config::Config) -> anyhow::Result<Self> {
|
||||
let template = Template::init(config)?;
|
||||
Ok(Self {
|
||||
debug: Debug::init(config)?,
|
||||
access_log: AccessLog::init(config)?,
|
||||
debug: Debug::init(config)?,
|
||||
event: Event::init(
|
||||
// do not init `Connection` event if its features not in use
|
||||
template.welcome.contains("{hosts}") || template.welcome.contains("{hits}"),
|
||||
)?,
|
||||
storage: Storage::init(config)?,
|
||||
template: Template::init(config)?,
|
||||
template,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
src/session/event.rs
Normal file
15
src/session/event.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
mod connection;
|
||||
use connection::Connection;
|
||||
|
||||
pub struct Event {
|
||||
pub connection: Connection,
|
||||
// another features...
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn init(is_connection_enabled: bool) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
connection: Connection::init(is_connection_enabled),
|
||||
})
|
||||
}
|
||||
}
|
||||
44
src/session/event/connection.rs
Normal file
44
src/session/event/connection.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
net::{IpAddr, SocketAddr},
|
||||
sync::RwLock,
|
||||
};
|
||||
|
||||
/// Count peer connections (for the current server session)
|
||||
pub struct Connection(Option<RwLock<HashMap<IpAddr, usize>>>);
|
||||
|
||||
impl Connection {
|
||||
pub fn init(is_enabled: bool) -> Self {
|
||||
if is_enabled {
|
||||
Self(Some(RwLock::new(HashMap::with_capacity(100))))
|
||||
} else {
|
||||
Self(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&self, peer: &SocketAddr) {
|
||||
if let Some(ref this) = self.0 {
|
||||
this.write()
|
||||
.unwrap()
|
||||
.entry(peer.ip())
|
||||
.and_modify(|c| *c += 1)
|
||||
.or_insert(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hosts(&self) -> usize {
|
||||
if let Some(ref this) = self.0 {
|
||||
this.read().unwrap().len()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hits(&self) -> usize {
|
||||
if let Some(ref this) = self.0 {
|
||||
this.read().unwrap().values().sum()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,10 @@ pub struct Storage {
|
|||
public_dir: PathBuf,
|
||||
/// Streaming buffer options
|
||||
read_chunk: usize,
|
||||
/// Show hidden entries (in the directory listing)
|
||||
///
|
||||
/// * important: this option does not prevent access to hidden files!
|
||||
show_hidden: bool,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
|
|
@ -34,6 +38,7 @@ impl Storage {
|
|||
list_config: ListConfig::init(config)?,
|
||||
public_dir,
|
||||
read_chunk: config.read_chunk,
|
||||
show_hidden: config.show_hidden,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -124,18 +129,19 @@ impl Storage {
|
|||
let mut files = Vec::with_capacity(C);
|
||||
for entry in fs::read_dir(path)? {
|
||||
let e = entry?;
|
||||
let name = e.file_name().to_string_lossy().to_string();
|
||||
if !self.show_hidden && name.starts_with('.') {
|
||||
continue;
|
||||
}
|
||||
let meta = fs::metadata(e.path())?;
|
||||
match (meta.is_dir(), meta.is_file()) {
|
||||
(true, _) => dirs.push(Dir {
|
||||
meta,
|
||||
name: e.file_name().to_string_lossy().to_string(),
|
||||
name,
|
||||
count: fs::read_dir(e.path()).map_or(0, |i| i.count()),
|
||||
}),
|
||||
(_, true) => files.push(File {
|
||||
meta,
|
||||
name: e.file_name().to_string_lossy().to_string(),
|
||||
}),
|
||||
_ => {} // @TODO symlinks support?
|
||||
(_, true) => files.push(File { meta, name }),
|
||||
_ => continue, // @TODO symlinks support?
|
||||
}
|
||||
}
|
||||
// build resulting list
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pub struct Template {
|
|||
index: String,
|
||||
internal_server_error: Vec<u8>,
|
||||
not_found: Vec<u8>,
|
||||
welcome: String,
|
||||
pub welcome: String,
|
||||
}
|
||||
|
||||
impl Template {
|
||||
|
|
@ -14,21 +14,21 @@ impl Template {
|
|||
Some(ref p) => read(p)?,
|
||||
None => "Access denied".into(),
|
||||
},
|
||||
index: match config.template_access_denied {
|
||||
index: match config.template_index {
|
||||
Some(ref p) => read_to_string(p)?,
|
||||
None => "{list}".into(),
|
||||
},
|
||||
internal_server_error: match config.template_access_denied {
|
||||
internal_server_error: match config.template_internal_server_error {
|
||||
Some(ref p) => read(p)?,
|
||||
None => "Internal server error".into(),
|
||||
},
|
||||
not_found: match config.template_access_denied {
|
||||
not_found: match config.template_not_found {
|
||||
Some(ref p) => read(p)?,
|
||||
None => "Not found".into(),
|
||||
},
|
||||
welcome: match config.template_access_denied {
|
||||
welcome: match config.template_welcome {
|
||||
Some(ref p) => read_to_string(p)?,
|
||||
None => "Welcome to Nexy!\n\n{list}".into(),
|
||||
None => "Welcome to Nexy!\n\n{list}\n\n👁 {hosts} / {hits}".into(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
@ -51,9 +51,19 @@ impl Template {
|
|||
&self.not_found
|
||||
}
|
||||
|
||||
pub fn welcome(&self, list: Option<&str>) -> Vec<u8> {
|
||||
pub fn welcome(
|
||||
&self,
|
||||
list: Option<&str>,
|
||||
hosts: Option<usize>,
|
||||
hits: Option<usize>,
|
||||
) -> Vec<u8> {
|
||||
fn format_count(v: Option<usize>) -> String {
|
||||
v.map_or(String::new(), |c| c.to_string())
|
||||
}
|
||||
self.welcome
|
||||
.replace("{list}", list.unwrap_or_default())
|
||||
.replace("{hosts}", &format_count(hosts))
|
||||
.replace("{hits}", &format_count(hits))
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue