Compare commits

...

31 Commits

Author SHA1 Message Date
3fa2c3c530 Update Rust crate clap to 4.5.40
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-06-10 00:24:08 +00:00
3b87d66a36 Update Rust crate clap to 4.5.39
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-05-28 00:09:50 +00:00
8dca766f8e Update Rust crate futures-util to 0.3.31
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-05-19 00:08:43 +00:00
7b74250c86 Update Rust crate actix-web to 4.11.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-05-13 00:18:49 +00:00
2037576627 Update Rust crate clap to 4.5.38
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-05-12 00:18:03 +00:00
46a40ef66a Update Rust crate clap to 4.5.37
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-19 00:29:07 +00:00
b6dd41c4cc Update Rust crate clap to 4.5.36
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-12 00:28:39 +00:00
48f9a79b05 Update Rust crate env_logger to 0.11.8
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-03 00:36:28 +00:00
c25396d62a Update Rust crate clap to 4.5.35
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-02 00:36:49 +00:00
431816900c Update Rust crate tar to 0.4.44
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-31 20:52:17 +00:00
b07d8ae520 Update Rust crate log to 0.4.27
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-31 00:36:23 +00:00
4b7db1f0ab Update Rust crate bytes to 1.10.1
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-30 02:22:17 +00:00
c8a97be01b Update Rust crate actix-web to 4.10.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-29 22:58:20 +00:00
c46cb5ee0e Update Rust crate actix-files to 0.6.6
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-29 18:15:35 +00:00
7fd6c4085b Update renovate.json
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-29 17:32:51 +00:00
3de1d25038 Update renovate.json
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-29 13:01:28 +00:00
7bc2768c25 Cargo clippy
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-28 17:05:36 +01:00
6172b36b26 Enable CI
Some checks failed
continuous-integration/drone/push Build is failing
2025-03-28 16:59:22 +01:00
8d0dabfe0e Merge pull request 'Update Rust crate clap to 3.1.18' (#5) from renovate/clap-3.x into master
Reviewed-on: #5
2022-06-13 07:25:04 +00:00
d123d9d09d Update Rust crate clap to 3.1.18 2022-05-12 00:16:39 +00:00
ef5c64805a Merge pull request 'Update Rust crate clap to 3.1.12' (#4) from renovate/clap-3.x into master
Reviewed-on: #4
2022-05-05 16:21:30 +00:00
e0dff00925 Update Rust crate clap to 3.1.12 2022-04-23 00:16:47 +00:00
350d8a83b8 Merge pull request 'Update Rust crate clap to 3.1.8' (#3) from renovate/clap-3.x into master
Reviewed-on: #3
2022-04-09 12:30:55 +02:00
df9c16a0d0 Merge pull request 'Update Rust crate futures-util to 0.3.21' (#2) from renovate/futures-util-0.x into master
Reviewed-on: #2
2022-04-09 12:30:07 +02:00
dfed5daebc Update Rust crate futures-util to 0.3.21 2022-04-04 00:14:52 +00:00
5f3bb5d72e Update Rust crate clap to 3.1.8 2022-04-04 00:14:48 +00:00
8574a328c9 cargo clippy & cargo fmt 2022-04-03 14:31:57 +02:00
d2daeb5879 Like in GitLab Pages, can bypass html extensions 2022-04-03 14:30:44 +02:00
8fd0981e78 Remove useless unsafe code 2022-04-03 14:08:18 +02:00
673001ed36 Merge pull request 'Configure Renovate' (#1) from renovate/configure into master
Reviewed-on: #1
2022-03-30 08:23:33 +02:00
8542588ded Add renovate.json 2022-03-30 00:13:35 +00:00
5 changed files with 1204 additions and 564 deletions

15
.drone.yml Normal file
View File

@ -0,0 +1,15 @@
---
kind: pipeline
type: docker
name: default
steps:
- name: cargo_check
image: rust
commands:
- rustup component add clippy
- cargo clippy -- -D warnings
- cargo test

1606
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,18 @@
[package]
name = "pages_server"
version = "0.1.0"
edition = "2021"
edition = "2024"
license = "GPL-3.0-only"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "3.1.6", features = ["derive", "env"] }
actix-web = "4"
actix-files = "0.6"
actix-multipart = "0.4"
env_logger = "0.9.0"
log = "0.4"
bytes = "1.1.0"
futures-util = { version = "0.3.7", default-features = false, features = ["std"] }
tar = "0.4.38"
clap = { version = "4.5.40", features = ["derive", "env"] }
actix-web = "4.11.0"
actix-files = "0.6.6"
actix-multipart = "0.7.2"
env_logger = "0.11.8"
log = "0.4.27"
bytes = "1.10.1"
futures-util = { version = "0.3.31", default-features = false, features = ["std"] }
tar = "0.4.44"

3
renovate.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": ["local>renovate/presets"]
}

View File

@ -3,10 +3,10 @@ use std::path::{Path, PathBuf};
use actix_files::{Files, NamedFile};
use actix_multipart::Multipart;
use actix_web::{App, Error, HttpRequest, HttpResponse, HttpServer, web};
use actix_web::dev::{fn_service, ServiceRequest, ServiceResponse};
use actix_web::dev::{ServiceRequest, ServiceResponse, fn_service};
use actix_web::error::{ErrorBadRequest, ErrorInternalServerError, ErrorUnauthorized};
use actix_web::middleware::Logger;
use actix_web::{App, Error, HttpRequest, HttpResponse, HttpServer, web};
use bytes::BufMut;
use clap::Parser;
use futures_util::TryStreamExt;
@ -42,6 +42,10 @@ struct Args {
/// Optional proxy IP
#[clap(short, long, env)]
proxy_ip: Option<String>,
/// Specify whether HTML extensions can be bypassed
#[clap(short, long, env)]
can_bypass_html_ext: bool,
}
impl Args {
@ -52,13 +56,11 @@ impl Args {
pub fn default_handler_path(&self) -> Option<PathBuf> {
match self.not_found_file.is_empty() {
true => None,
false => Some(self.storage_path().join(&self.not_found_file))
false => Some(self.storage_path().join(&self.not_found_file)),
}
}
}
static mut ARGS: Option<Args> = None;
struct NewFile {
path: PathBuf,
bytes: Vec<u8>,
@ -77,7 +79,6 @@ pub fn match_ip(pattern: &str, ip: &str) -> bool {
false
}
/// Get the remote IP address
fn get_remote_ip(req: &HttpRequest, args: &Args) -> String {
let mut ip = req.peer_addr().unwrap().ip().to_string();
@ -89,7 +90,6 @@ fn get_remote_ip(req: &HttpRequest, args: &Args) -> String {
let header: Vec<String> = header
.to_str()
.unwrap()
.to_string()
.split(',')
.map(|f| f.to_string())
.collect();
@ -104,14 +104,19 @@ fn get_remote_ip(req: &HttpRequest, args: &Args) -> String {
ip
}
/// Replace all the files of the website
async fn replace_files(args: web::Data<Args>, req: HttpRequest, mut payload: Multipart) -> Result<HttpResponse, Error> {
async fn replace_files(
args: web::Data<Args>,
req: HttpRequest,
mut payload: Multipart,
) -> Result<HttpResponse, Error> {
// Validate remote IP
let remote_ip = get_remote_ip(&req, &args);
if !match_ip(&args.allowed_ips_for_update, &remote_ip) {
log::warn!("Block unauthorized attempt to perform site update from {}", remote_ip);
log::warn!(
"Block unauthorized attempt to perform site update from {}",
remote_ip
);
return Err(ErrorUnauthorized("You are not allowed to perform updates!"));
}
@ -120,8 +125,9 @@ async fn replace_files(args: web::Data<Args>, req: HttpRequest, mut payload: Mul
None => {
return Err(ErrorUnauthorized("Token required!"));
}
Some(t) => t.to_str()
.map_err(|_| ErrorInternalServerError("Failed to parse token!"))?
Some(t) => t
.to_str()
.map_err(|_| ErrorInternalServerError("Failed to parse token!"))?,
};
if !token.eq(&args.update_token) || args.update_token.is_empty() {
@ -131,8 +137,9 @@ async fn replace_files(args: web::Data<Args>, req: HttpRequest, mut payload: Mul
// Get base folder to keep from tar-file
let base_uri = match req.headers().get("BaseURI") {
None => "/",
Some(t) => t.to_str()
.map_err(|_| ErrorInternalServerError("Failed to parse base URI to keep!"))?
Some(t) => t
.to_str()
.map_err(|_| ErrorInternalServerError("Failed to parse base URI to keep!"))?,
};
let mut new_files = Vec::new();
@ -147,8 +154,10 @@ async fn replace_files(args: web::Data<Args>, req: HttpRequest, mut payload: Mul
}
let mut archive = tar::Archive::new(b.as_ref());
for entry in archive.entries()
.map_err(|_| ErrorInternalServerError("Failed to parse TAR archive!"))? {
for entry in archive
.entries()
.map_err(|_| ErrorInternalServerError("Failed to parse TAR archive!"))?
{
let mut file = entry?;
let inner_path = file.header().path()?;
let inner_path_str = inner_path.to_string_lossy();
@ -207,6 +216,42 @@ async fn replace_files(args: web::Data<Args>, req: HttpRequest, mut payload: Mul
Ok(HttpResponse::Ok().into())
}
async fn default_files_handler(req: ServiceRequest) -> Result<ServiceResponse, Error> {
let (req, _) = req.into_parts();
let args: &web::Data<Args> = req.app_data().unwrap();
// Search for alternate paths
if args.can_bypass_html_ext
&& !req.path().ends_with(".html")
&& !req.path().ends_with('/')
&& !req.path().is_empty()
{
let alt_file = args
.storage_path()
.join(format!("{}.html", &req.path()[1..]));
if alt_file.exists() {
let file = NamedFile::open_async(alt_file).await?;
let res = file.into_response(&req);
return Ok(ServiceResponse::new(req, res));
}
}
// Default handler
if let Some(h) = args.default_handler_path() {
let file = NamedFile::open_async(h).await?;
let res = file.into_response(&req);
Ok(ServiceResponse::new(req, res))
}
// Dummy response
else {
Ok(ServiceResponse::new(
req,
HttpResponse::NotFound().body("404 Not found"),
))
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let args: Args = Args::parse();
@ -219,41 +264,34 @@ async fn main() -> std::io::Result<()> {
panic!("Specified files path does not exists!");
}
unsafe { ARGS = Some(args.clone()); }
HttpServer::new(move || {
App::new()
// Update service
.service(web::resource("/_mgmt/replace_files")
.route(web::post().to(replace_files)))
.service(web::resource("/_mgmt/replace_files").route(web::post().to(replace_files)))
// Serve a tree of static files at the web root and specify the index file.
// Note that the root path should always be defined as the last item. The paths are
// resolved in the order they are defined. If this would be placed before the `/images`
// path then the service for the static images would never be reached.
.service(Files::new("/", &args.files_path)
.index_file(&args.index_file)
.default_handler(fn_service(|req: ServiceRequest| async {
let (req, _) = req.into_parts();
if let Some(h) = unsafe { ARGS.as_ref().unwrap() }.default_handler_path().as_ref() {
let file = NamedFile::open_async(h).await?;
let res = file.into_response(&req);
Ok(ServiceResponse::new(req, res))
} else {
Ok(ServiceResponse::new(req, HttpResponse::NotFound()
.body("404 Not found")))
}
}))
.service(
Files::new("/", &args.files_path)
.index_file(&args.index_file)
.default_handler(fn_service(default_files_handler)),
)
// Enable the logger.
.wrap(Logger::default())
.app_data(web::Data::new(args.clone()))
})
.bind(listen_address)?
.run()
.await
.bind(listen_address)?
.run()
.await
}
#[cfg(test)]
mod test {
use crate::Args;
#[test]
fn verify_cli() {
use clap::CommandFactory;
Args::command().debug_assert()
}
}