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]
|
[package]
|
||||||
name = "nexy"
|
name = "nexy"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
||||||
22
README.md
22
README.md
|
|
@ -36,12 +36,28 @@ nexy -p /path/to/public_dir
|
||||||
|
|
||||||
[default: ei]
|
[default: ei]
|
||||||
|
|
||||||
-t, --template <TEMPLATE>
|
|
||||||
Absolute path to the template files directory
|
|
||||||
|
|
||||||
-p, --public <PUBLIC>
|
-p, --public <PUBLIC>
|
||||||
Absolute path to the public files directory
|
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>
|
-r, --read-chunk <READ_CHUNK>
|
||||||
Optimize memory usage on reading large files or stream
|
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"))]
|
#[arg(short, long, default_value_t = String::from("ei"))]
|
||||||
pub debug: String,
|
pub debug: String,
|
||||||
|
|
||||||
/// Absolute path to the template files directory
|
|
||||||
#[arg(short, long)]
|
|
||||||
pub template: Option<String>,
|
|
||||||
|
|
||||||
/// Absolute path to the public files directory
|
/// Absolute path to the public files directory
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub public: String,
|
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
|
/// Optimize memory usage on reading large files or stream
|
||||||
#[arg(short, long, default_value_t = 1024)]
|
#[arg(short, long, default_value_t = 1024)]
|
||||||
pub read_chunk: usize,
|
pub read_chunk: usize,
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,7 @@ pub enum Response<'a> {
|
||||||
/// Includes reference to the original request
|
/// Includes reference to the original request
|
||||||
NotFound(&'a str),
|
NotFound(&'a str),
|
||||||
/// Includes bytes array
|
/// 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) {
|
fn response(&mut self, response: Response) {
|
||||||
let bytes = match 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) => {
|
Response::InternalServerError(e) => {
|
||||||
self.session.debug.error(&e);
|
self.session.debug.error(&e);
|
||||||
self.session.template.internal_server_error.as_bytes()
|
self.session.template.internal_server_error()
|
||||||
}
|
}
|
||||||
Response::AccessDenied(q) => {
|
Response::AccessDenied(q) => {
|
||||||
self.session.debug.error(&format!(
|
self.session.debug.error(&format!(
|
||||||
"[{}] < [{}] access to `{q}` denied.",
|
"[{}] < [{}] access to `{q}` denied.",
|
||||||
self.address.server, self.address.client
|
self.address.server, self.address.client
|
||||||
));
|
));
|
||||||
self.session.template.access_denied.as_bytes()
|
self.session.template.access_denied()
|
||||||
}
|
}
|
||||||
Response::NotFound(q) => {
|
Response::NotFound(q) => {
|
||||||
self.session.debug.error(&format!(
|
self.session.debug.error(&format!(
|
||||||
"[{}] < [{}] requested resource `{q}` not found.",
|
"[{}] < [{}] requested resource `{q}` not found.",
|
||||||
self.address.server, self.address.client
|
self.address.server, self.address.client
|
||||||
));
|
));
|
||||||
self.session.template.not_found.as_bytes()
|
self.session.template.not_found()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match self.stream.write_all(bytes) {
|
match self.stream.write_all(bytes) {
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ pub struct Session {
|
||||||
impl Session {
|
impl Session {
|
||||||
pub fn init(config: &crate::config::Config) -> anyhow::Result<Self> {
|
pub fn init(config: &crate::config::Config) -> anyhow::Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
debug: Debug::init(&config.debug)?,
|
debug: Debug::init(config)?,
|
||||||
storage: Storage::init(&config.public, config.read_chunk)?,
|
storage: Storage::init(config)?,
|
||||||
template: Template::init(&config.template)?,
|
template: Template::init(config)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ use level::Level;
|
||||||
pub struct Debug(Vec<Level>);
|
pub struct Debug(Vec<Level>);
|
||||||
|
|
||||||
impl Debug {
|
impl Debug {
|
||||||
pub fn init(levels: &str) -> anyhow::Result<Self> {
|
pub fn init(config: &crate::config::Config) -> anyhow::Result<Self> {
|
||||||
let mut l = Vec::with_capacity(levels.len());
|
let mut l = Vec::with_capacity(config.debug.len());
|
||||||
for s in levels.to_lowercase().chars() {
|
for s in config.debug.to_lowercase().chars() {
|
||||||
l.push(Level::parse(s)?);
|
l.push(Level::parse(s)?);
|
||||||
}
|
}
|
||||||
Ok(Self(l))
|
Ok(Self(l))
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ pub struct Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Storage {
|
impl Storage {
|
||||||
pub fn init(path: &str, read_chunk: usize) -> Result<Self> {
|
pub fn init(config: &crate::config::Config) -> Result<Self> {
|
||||||
let public_dir = PathBuf::from_str(path)?.canonicalize()?;
|
let public_dir = PathBuf::from_str(&config.public)?.canonicalize()?;
|
||||||
let t = fs::metadata(&public_dir)?;
|
let t = fs::metadata(&public_dir)?;
|
||||||
if !t.is_dir() {
|
if !t.is_dir() {
|
||||||
bail!("Storage destination is not directory!");
|
bail!("Storage destination is not directory!");
|
||||||
|
|
@ -19,7 +19,7 @@ impl Storage {
|
||||||
}
|
}
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
public_dir,
|
public_dir,
|
||||||
read_chunk,
|
read_chunk: config.read_chunk,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ impl Storage {
|
||||||
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(ref l) => Response::Success(l.as_bytes()),
|
Ok(l) => Response::Directory(l, 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) {
|
||||||
|
|
@ -49,7 +49,7 @@ impl Storage {
|
||||||
let mut b = vec![0; self.read_chunk];
|
let mut b = vec![0; self.read_chunk];
|
||||||
match f.read(&mut b) {
|
match f.read(&mut b) {
|
||||||
Ok(0) => break,
|
Ok(0) => break,
|
||||||
Ok(n) => callback(Response::Success(&b[..n])),
|
Ok(n) => callback(Response::File(&b[..n])),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return callback(Response::InternalServerError(format!(
|
return callback(Response::InternalServerError(format!(
|
||||||
"failed to read response chunk for `{query}`: `{e}`"
|
"failed to read response chunk for `{query}`: `{e}`"
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,63 @@
|
||||||
use anyhow::{Result, bail};
|
|
||||||
|
|
||||||
pub struct Template {
|
pub struct Template {
|
||||||
pub access_denied: String,
|
access_denied: Vec<u8>,
|
||||||
pub internal_server_error: String,
|
index: Vec<u8>,
|
||||||
pub not_found: String,
|
internal_server_error: Vec<u8>,
|
||||||
|
not_found: Vec<u8>,
|
||||||
|
welcome: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Template {
|
impl Template {
|
||||||
pub fn init(directory: &Option<String>) -> Result<Self> {
|
pub fn init(config: &crate::config::Config) -> anyhow::Result<Self> {
|
||||||
use std::{fs::read_to_string, path::PathBuf};
|
use std::fs::read;
|
||||||
|
Ok(Self {
|
||||||
const ACCESS_DENIED: (&str, &str) = ("access_denied.txt", "Access denied");
|
access_denied: match config.template_access_denied {
|
||||||
const INTERNAL_SERVER_ERROR: (&str, &str) =
|
Some(ref p) => read(p)?,
|
||||||
("internal_server_error.txt", "Internal server error");
|
None => "Access denied".into(),
|
||||||
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)?)?,
|
|
||||||
},
|
},
|
||||||
None => Self {
|
index: match config.template_access_denied {
|
||||||
access_denied: ACCESS_DENIED.1.to_string(),
|
Some(ref p) => read(p)?,
|
||||||
internal_server_error: INTERNAL_SERVER_ERROR.1.to_string(),
|
None => "{list}".into(),
|
||||||
not_found: NOT_FOUND.1.to_string(),
|
},
|
||||||
|
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