mirror of
https://github.com/YGGverse/nexy.git
synced 2026-03-31 17:25:27 +00:00
reorganize template processor, implement new server options, optimize component init arguments
This commit is contained in:
parent
60f702b3d1
commit
b93f389304
9 changed files with 126 additions and 61 deletions
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "nexy"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
|
|
|
|||
22
README.md
22
README.md
|
|
@ -36,12 +36,28 @@ nexy -p /path/to/public_dir
|
|||
|
||||
[default: ei]
|
||||
|
||||
-t, --template <TEMPLATE>
|
||||
Absolute path to the template files directory
|
||||
|
||||
-p, --public <PUBLIC>
|
||||
Absolute path to the public files directory
|
||||
|
||||
--template-access-denied <TEMPLATE_ACCESS_DENIED>
|
||||
Absolute path to the `Access denied` template file
|
||||
|
||||
--template-internal-server-error <TEMPLATE_INTERNAL_SERVER_ERROR>
|
||||
Absolute path to the `Internal server error` template file
|
||||
|
||||
--template-not-found <TEMPLATE_NOT_FOUND>
|
||||
Absolute path to the `Not found` template file
|
||||
|
||||
--template-welcome <TEMPLATE_WELCOME>
|
||||
Absolute path to the `Welcome` template file. Unlike `template_index`, this applies only to the `public` location
|
||||
|
||||
**Patterns** * `{list}` - entries list for the `public` directory
|
||||
|
||||
--template-index <TEMPLATE_INDEX>
|
||||
Absolute path to the `Index` template file for each directory
|
||||
|
||||
**Patterns** * `{list}` - entries list for the current directory
|
||||
|
||||
-r, --read-chunk <READ_CHUNK>
|
||||
Optimize memory usage on reading large files or stream
|
||||
|
||||
|
|
|
|||
|
|
@ -23,14 +23,37 @@ pub struct Config {
|
|||
#[arg(short, long, default_value_t = String::from("ei"))]
|
||||
pub debug: String,
|
||||
|
||||
/// Absolute path to the template files directory
|
||||
#[arg(short, long)]
|
||||
pub template: Option<String>,
|
||||
|
||||
/// Absolute path to the public files directory
|
||||
#[arg(short, long)]
|
||||
pub public: String,
|
||||
|
||||
/// Absolute path to the `Access denied` template file
|
||||
#[arg(long)]
|
||||
pub template_access_denied: Option<String>,
|
||||
|
||||
/// Absolute path to the `Internal server error` template file
|
||||
#[arg(long)]
|
||||
pub template_internal_server_error: Option<String>,
|
||||
|
||||
/// Absolute path to the `Not found` template file
|
||||
#[arg(long)]
|
||||
pub template_not_found: Option<String>,
|
||||
|
||||
/// Absolute path to the `Welcome` template file.
|
||||
/// Unlike `template_index`, this applies only to the `public` location
|
||||
///
|
||||
/// **Patterns**
|
||||
/// * `{list}` - entries list for the `public` directory
|
||||
#[arg(long)]
|
||||
pub template_welcome: Option<String>,
|
||||
|
||||
/// Absolute path to the `Index` template file for each directory
|
||||
///
|
||||
/// **Patterns**
|
||||
/// * `{list}` - entries list for the current directory
|
||||
#[arg(long)]
|
||||
pub template_index: Option<String>,
|
||||
|
||||
/// Optimize memory usage on reading large files or stream
|
||||
#[arg(short, long, default_value_t = 1024)]
|
||||
pub read_chunk: usize,
|
||||
|
|
|
|||
|
|
@ -7,5 +7,7 @@ pub enum Response<'a> {
|
|||
/// Includes reference to the original request
|
||||
NotFound(&'a str),
|
||||
/// Includes bytes array
|
||||
Success(&'a [u8]),
|
||||
File(&'a [u8]),
|
||||
/// Includes bytes array + public root directory status
|
||||
Directory(String, bool),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,24 +59,31 @@ impl Connection {
|
|||
|
||||
fn response(&mut self, response: Response) {
|
||||
let bytes = match response {
|
||||
Response::Success(b) => b,
|
||||
Response::File(b) => b,
|
||||
Response::Directory(b, is_root) => {
|
||||
&if is_root {
|
||||
self.session.template.welcome(Some(&b))
|
||||
} else {
|
||||
self.session.template.index(Some(&b))
|
||||
}
|
||||
}
|
||||
Response::InternalServerError(e) => {
|
||||
self.session.debug.error(&e);
|
||||
self.session.template.internal_server_error.as_bytes()
|
||||
self.session.template.internal_server_error()
|
||||
}
|
||||
Response::AccessDenied(q) => {
|
||||
self.session.debug.error(&format!(
|
||||
"[{}] < [{}] access to `{q}` denied.",
|
||||
self.address.server, self.address.client
|
||||
));
|
||||
self.session.template.access_denied.as_bytes()
|
||||
self.session.template.access_denied()
|
||||
}
|
||||
Response::NotFound(q) => {
|
||||
self.session.debug.error(&format!(
|
||||
"[{}] < [{}] requested resource `{q}` not found.",
|
||||
self.address.server, self.address.client
|
||||
));
|
||||
self.session.template.not_found.as_bytes()
|
||||
self.session.template.not_found()
|
||||
}
|
||||
};
|
||||
match self.stream.write_all(bytes) {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ pub struct Session {
|
|||
impl Session {
|
||||
pub fn init(config: &crate::config::Config) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
debug: Debug::init(&config.debug)?,
|
||||
storage: Storage::init(&config.public, config.read_chunk)?,
|
||||
template: Template::init(&config.template)?,
|
||||
debug: Debug::init(config)?,
|
||||
storage: Storage::init(config)?,
|
||||
template: Template::init(config)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ use level::Level;
|
|||
pub struct Debug(Vec<Level>);
|
||||
|
||||
impl Debug {
|
||||
pub fn init(levels: &str) -> anyhow::Result<Self> {
|
||||
let mut l = Vec::with_capacity(levels.len());
|
||||
for s in levels.to_lowercase().chars() {
|
||||
pub fn init(config: &crate::config::Config) -> anyhow::Result<Self> {
|
||||
let mut l = Vec::with_capacity(config.debug.len());
|
||||
for s in config.debug.to_lowercase().chars() {
|
||||
l.push(Level::parse(s)?);
|
||||
}
|
||||
Ok(Self(l))
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ pub struct Storage {
|
|||
}
|
||||
|
||||
impl Storage {
|
||||
pub fn init(path: &str, read_chunk: usize) -> Result<Self> {
|
||||
let public_dir = PathBuf::from_str(path)?.canonicalize()?;
|
||||
pub fn init(config: &crate::config::Config) -> Result<Self> {
|
||||
let public_dir = PathBuf::from_str(&config.public)?.canonicalize()?;
|
||||
let t = fs::metadata(&public_dir)?;
|
||||
if !t.is_dir() {
|
||||
bail!("Storage destination is not directory!");
|
||||
|
|
@ -19,7 +19,7 @@ impl Storage {
|
|||
}
|
||||
Ok(Self {
|
||||
public_dir,
|
||||
read_chunk,
|
||||
read_chunk: config.read_chunk,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ impl Storage {
|
|||
match fs::metadata(&p) {
|
||||
Ok(t) => match (t.is_dir(), t.is_file()) {
|
||||
(true, _) => callback(match self.list(&p) {
|
||||
Ok(ref l) => Response::Success(l.as_bytes()),
|
||||
Ok(l) => Response::Directory(l, p == self.public_dir),
|
||||
Err(e) => Response::InternalServerError(e.to_string()),
|
||||
}),
|
||||
(_, true) => match fs::File::open(p) {
|
||||
|
|
@ -49,7 +49,7 @@ impl Storage {
|
|||
let mut b = vec![0; self.read_chunk];
|
||||
match f.read(&mut b) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => callback(Response::Success(&b[..n])),
|
||||
Ok(n) => callback(Response::File(&b[..n])),
|
||||
Err(e) => {
|
||||
return callback(Response::InternalServerError(format!(
|
||||
"failed to read response chunk for `{query}`: `{e}`"
|
||||
|
|
|
|||
|
|
@ -1,46 +1,63 @@
|
|||
use anyhow::{Result, bail};
|
||||
|
||||
pub struct Template {
|
||||
pub access_denied: String,
|
||||
pub internal_server_error: String,
|
||||
pub not_found: String,
|
||||
access_denied: Vec<u8>,
|
||||
index: Vec<u8>,
|
||||
internal_server_error: Vec<u8>,
|
||||
not_found: Vec<u8>,
|
||||
welcome: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Template {
|
||||
pub fn init(directory: &Option<String>) -> Result<Self> {
|
||||
use std::{fs::read_to_string, path::PathBuf};
|
||||
|
||||
const ACCESS_DENIED: (&str, &str) = ("access_denied.txt", "Access denied");
|
||||
const INTERNAL_SERVER_ERROR: (&str, &str) =
|
||||
("internal_server_error.txt", "Internal server error");
|
||||
const NOT_FOUND: (&str, &str) = ("not_found.txt", "Not found");
|
||||
|
||||
fn path(directory: &str, file: &str) -> Result<PathBuf> {
|
||||
let mut p = PathBuf::from(directory).canonicalize()?;
|
||||
p.push(file);
|
||||
if !p.exists() {
|
||||
bail!("Template path `{}` does not exist", p.to_string_lossy())
|
||||
}
|
||||
if !p.is_file() {
|
||||
bail!("Template path `{}` is not file", p.to_string_lossy())
|
||||
}
|
||||
if p.is_symlink() {
|
||||
bail!("Symlinks yet not supported!");
|
||||
}
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
Ok(match directory {
|
||||
Some(d) => Self {
|
||||
access_denied: read_to_string(path(d, ACCESS_DENIED.0)?)?,
|
||||
internal_server_error: read_to_string(path(d, INTERNAL_SERVER_ERROR.0)?)?,
|
||||
not_found: read_to_string(path(d, NOT_FOUND.0)?)?,
|
||||
pub fn init(config: &crate::config::Config) -> anyhow::Result<Self> {
|
||||
use std::fs::read;
|
||||
Ok(Self {
|
||||
access_denied: match config.template_access_denied {
|
||||
Some(ref p) => read(p)?,
|
||||
None => "Access denied".into(),
|
||||
},
|
||||
None => Self {
|
||||
access_denied: ACCESS_DENIED.1.to_string(),
|
||||
internal_server_error: INTERNAL_SERVER_ERROR.1.to_string(),
|
||||
not_found: NOT_FOUND.1.to_string(),
|
||||
index: match config.template_access_denied {
|
||||
Some(ref p) => read(p)?,
|
||||
None => "{list}".into(),
|
||||
},
|
||||
internal_server_error: match config.template_access_denied {
|
||||
Some(ref p) => read(p)?,
|
||||
None => "Internal server error".into(),
|
||||
},
|
||||
not_found: match config.template_access_denied {
|
||||
Some(ref p) => read(p)?,
|
||||
None => "Not found".into(),
|
||||
},
|
||||
welcome: match config.template_access_denied {
|
||||
Some(ref p) => read(p)?,
|
||||
None => "Welcome to Nexy!\n{list}".into(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn access_denied(&self) -> &[u8] {
|
||||
&self.access_denied
|
||||
}
|
||||
|
||||
pub fn index(&self, list: Option<&str>) -> Vec<u8> {
|
||||
let l = list.unwrap_or_default();
|
||||
match std::str::from_utf8(&self.index) {
|
||||
Ok(s) => s.replace("{list}", l).into(),
|
||||
Err(_) => l.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn internal_server_error(&self) -> &[u8] {
|
||||
&self.internal_server_error
|
||||
}
|
||||
|
||||
pub fn not_found(&self) -> &[u8] {
|
||||
&self.not_found
|
||||
}
|
||||
|
||||
pub fn welcome(&self, list: Option<&str>) -> Vec<u8> {
|
||||
let l = list.unwrap_or_default();
|
||||
match std::str::from_utf8(&self.welcome) {
|
||||
Ok(s) => s.replace("{list}", l).into(),
|
||||
Err(_) => l.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue