1mod auth;
41mod handlers;
42mod http;
43mod storage;
44
45pub use auth::{AuthRejection, BearerClaims};
46pub use http::{
47 content_range_none_value, content_range_value, etag_matches_if_none_match, if_range_matches,
48 parse_range_header, ParsedRange, RangeParseError,
49};
50pub use storage::{FsStorage, FsSink};
51
52use std::sync::Arc;
53
54use axum::Router;
55use libfw_core::auth::{AuthError, TokenVerifier, Validator};
56use libfw_core::compress::CompressionFormat;
57use libfw_core::storage::StorageBackend;
58use libfw_core::DEFAULT_MAX_UPLOAD_SIZE;
59pub use libfw_core::{HEADER_COMPRESS, HEADER_FILE_META, HEADER_OFFSET};
60
61pub struct ServerState {
63 pub storage: Arc<dyn StorageBackend>,
65 pub verifier: Arc<dyn TokenVerifier>,
67 pub validator: Arc<dyn Validator>,
69 pub compression: CompressionFormat,
71 pub max_upload_size: u64,
73}
74
75impl ServerState {
76 pub fn builder() -> ServerStateBuilder {
78 ServerStateBuilder::default()
79 }
80
81 pub fn authorize(
83 &self,
84 claims: &libfw_core::claims::TokenClaims,
85 path: &str,
86 action: libfw_core::auth::Action,
87 ) -> Result<(), AuthError> {
88 self.validator.validate(claims, path, action)
89 }
90}
91
92pub struct ServerStateBuilder {
94 storage: Option<Arc<dyn StorageBackend>>,
95 verifier: Option<Arc<dyn TokenVerifier>>,
96 validator: Option<Arc<dyn Validator>>,
97 compression: CompressionFormat,
98 max_upload_size: u64,
99}
100
101impl Default for ServerStateBuilder {
102 fn default() -> Self {
103 ServerStateBuilder {
104 storage: None,
105 verifier: None,
106 validator: None,
107 compression: CompressionFormat::Zrip,
108 max_upload_size: DEFAULT_MAX_UPLOAD_SIZE,
109 }
110 }
111}
112
113impl ServerStateBuilder {
114 pub fn storage(mut self, storage: impl StorageBackend) -> Self {
116 self.storage = Some(Arc::new(storage));
117 self
118 }
119
120 pub fn verifier(mut self, verifier: impl TokenVerifier) -> Self {
122 self.verifier = Some(Arc::new(verifier));
123 self
124 }
125
126 pub fn validator(mut self, validator: impl Validator) -> Self {
128 self.validator = Some(Arc::new(validator));
129 self
130 }
131
132 pub fn compression(mut self, format: CompressionFormat) -> Self {
134 self.compression = format;
135 self
136 }
137
138 pub fn max_upload_size(mut self, size: u64) -> Self {
140 self.max_upload_size = size;
141 self
142 }
143
144 pub fn build(self) -> ServerState {
146 ServerState {
147 storage: self.storage.expect("storage is required"),
148 verifier: self.verifier.expect("verifier is required"),
149 validator: self.validator.expect("validator is required"),
150 compression: self.compression,
151 max_upload_size: self.max_upload_size,
152 }
153 }
154}
155
156pub fn router(state: Arc<ServerState>) -> Router {
165 use axum::routing::{get, post};
166
167 Router::new()
168 .route("/file/{*path}", get(handlers::download).head(handlers::head_file))
169 .route("/file/{*path}", post(handlers::upload))
170 .route("/dir/{*path}", get(handlers::list_dir))
171 .with_state(state)
172}
173
174pub fn validate_rel_path(path: &str) -> Result<String, &'static str> {
178 if path.contains('\0') {
179 return Err("path contains NUL byte");
180 }
181 if path.starts_with('/') {
182 return Err("path must be relative");
183 }
184 let mut out = String::with_capacity(path.len());
185 for segment in path.split('/') {
186 match segment {
187 "" | "." => {}
188 ".." => return Err("path escapes the mount root"),
189 seg => {
190 if !out.is_empty() {
191 out.push('/');
192 }
193 out.push_str(seg);
194 }
195 }
196 }
197 Ok(out)
198}