Skip to content

Commit 3208d25

Browse files
committed
fix file loading from database
1 parent 3c3cff4 commit 3208d25

File tree

5 files changed

+41
-30
lines changed

5 files changed

+41
-30
lines changed

src/file_cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ impl<T: AsyncFromStrWithState> FileCache<T> {
105105
.file_system
106106
.read_file(app_state, path)
107107
.await
108-
.with_context(|| format!("Reading {path:?} to load it in cache"));
108+
.with_context(|| format!("Couldn't load {path:?} into cache"));
109109
let parsed = match file_contents {
110110
Ok(contents) => Ok(T::from_str_with_state(app_state, &contents).await?),
111111
Err(e) => Err(e),

src/filesystem.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ use sqlx::any::{AnyKind, AnyStatement, AnyTypeInfo};
66
use sqlx::postgres::types::PgTimeTz;
77
use sqlx::{Postgres, Statement, Type};
88
use std::io::ErrorKind;
9-
use std::path::{Path, PathBuf};
9+
use std::path::{Component, Path, PathBuf};
1010

1111
pub(crate) struct FileSystem {
1212
local_root: PathBuf,
1313
db_fs_queries: Option<DbFsQueries>,
1414
}
1515

1616
impl FileSystem {
17-
pub async fn init(db: &Database) -> Self {
17+
pub async fn init(local_root: &Path, db: &Database) -> Self {
1818
Self {
19-
local_root: PathBuf::new(),
19+
local_root: PathBuf::from(local_root),
2020
db_fs_queries: match DbFsQueries::init(db).await {
2121
Ok(q) => Some(q),
2222
Err(e) => {
@@ -37,7 +37,8 @@ impl FileSystem {
3737
path: &Path,
3838
since: DateTime<Utc>,
3939
) -> anyhow::Result<bool> {
40-
let local_result = file_modified_since_local(&self.local_root.join(path), since).await;
40+
let local_path = self.safe_local_path(path)?;
41+
let local_result = file_modified_since_local(&local_path, since).await;
4142
match (local_result, &self.db_fs_queries) {
4243
(Ok(modified), _) => Ok(modified),
4344
(Err(e), Some(db_fs)) if e.kind() == ErrorKind::NotFound => {
@@ -53,7 +54,8 @@ impl FileSystem {
5354
}
5455

5556
pub async fn read_file(&self, app_state: &AppState, path: &Path) -> anyhow::Result<String> {
56-
let local_result = tokio::fs::read_to_string(&self.local_root.join(path)).await;
57+
let local_path = self.safe_local_path(path)?;
58+
let local_result = tokio::fs::read_to_string(&local_path).await;
5759
match (local_result, &self.db_fs_queries) {
5860
(Ok(f), _) => Ok(f),
5961
(Err(e), Some(db_fs)) if e.kind() == ErrorKind::NotFound => {
@@ -63,6 +65,14 @@ impl FileSystem {
6365
(Err(e), _) => Err(e).with_context(|| format!("Unable to read local file {path:?}")),
6466
}
6567
}
68+
69+
fn safe_local_path(&self, path: &Path) -> anyhow::Result<PathBuf> {
70+
anyhow::ensure!(
71+
path.components().all(|c| matches!(c, Component::Normal(_))),
72+
"Unsupported path: {path:?}"
73+
);
74+
Ok(self.local_root.join(path))
75+
}
6676
}
6777

6878
async fn file_modified_since_local(path: &Path, since: DateTime<Utc>) -> tokio::io::Result<bool> {

src/main.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use std::net::{SocketAddr, ToSocketAddrs};
1717
use std::path::PathBuf;
1818
use templates::AllTemplates;
1919

20-
const WEB_ROOT: &str = ".";
2120
const CONFIG_DIR: &str = "sqlpage";
2221
const TEMPLATES_DIR: &str = "sqlpage/templates";
2322
const MIGRATIONS_DIR: &str = "sqlpage/migrations";
@@ -39,9 +38,9 @@ impl AppState {
3938
let db = Database::init(&database_url).await?;
4039
log::info!("Connecting to database: {database_url}");
4140
let all_templates = AllTemplates::init()?;
42-
let web_root = std::fs::canonicalize(WEB_ROOT)?;
41+
let web_root = get_web_root();
4342
let sql_file_cache = FileCache::new();
44-
let file_system = FileSystem::init(&db).await;
43+
let file_system = FileSystem::init(&web_root, &db).await;
4544
Ok(AppState {
4645
db,
4746
all_templates,
@@ -52,6 +51,13 @@ impl AppState {
5251
}
5352
}
5453

54+
fn get_web_root() -> PathBuf {
55+
env::var("WEB_ROOT").map_or_else(
56+
|_| PathBuf::from(&std::path::Component::CurDir),
57+
PathBuf::from,
58+
)
59+
}
60+
5561
pub struct Config {
5662
listen_on: SocketAddr,
5763
}

src/webserver/database/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ impl Database {
218218
);
219219
let connection = AnyPool::connect_with(connect_options)
220220
.await
221-
.with_context(|| format!("Unable to open connection to {}", database_url))?;
221+
.with_context(|| format!("Unable to open connection to {database_url}"))?;
222222
Ok(Database { connection })
223223
}
224224
}

src/webserver/http.rs

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::collections::HashMap;
1919
use std::io::Write;
2020
use std::mem;
2121
use std::net::IpAddr;
22-
use std::path::{Component, Path, PathBuf};
22+
use std::path::{Component, PathBuf};
2323
use std::pin::Pin;
2424
use std::sync::Arc;
2525
use tokio::sync::mpsc;
@@ -306,22 +306,15 @@ async fn extract_request_info(req: &mut ServiceRequest) -> RequestInfo {
306306
}
307307

308308
/// Resolves the path in a query to the path to a local SQL file if there is one that matches
309-
fn path_to_sql_file(root: &Path, path: &str) -> Option<PathBuf> {
310-
let mut path_buf: PathBuf = PathBuf::from(root);
311-
path_buf.push(path.strip_prefix('/')?);
312-
let request_path = PathBuf::from(path);
313-
let extension = request_path.extension();
314-
if !matches!(extension, Some(ext) if ext.eq_ignore_ascii_case("sql")) {
315-
path_buf.push("index.sql");
316-
if !path_buf.is_file() {
317-
return None;
309+
fn path_to_sql_file(path: &str) -> Option<PathBuf> {
310+
let mut path = PathBuf::from(path.strip_prefix('/').unwrap_or(path));
311+
match path.extension() {
312+
None => {
313+
path.push("index.sql");
314+
Some(path)
318315
}
319-
}
320-
let final_path = path_buf.canonicalize().ok()?;
321-
if final_path.starts_with(root) {
322-
Some(final_path)
323-
} else {
324-
None
316+
Some(ext) if ext == "sql" => Some(path),
317+
Some(_other) => None,
325318
}
326319
}
327320

@@ -334,7 +327,11 @@ async fn process_sql_request(
334327
.sql_file_cache
335328
.get(app_state, &sql_path)
336329
.await
337-
.map_err(ErrorInternalServerError)?;
330+
.map_err(|e| {
331+
ErrorInternalServerError(format!(
332+
"An error occurred while trying to handle your request: {e:#}"
333+
))
334+
})?;
338335
let response = render_sql(&mut req, sql_file).await?;
339336
Ok(req.into_response(response))
340337
}
@@ -365,8 +362,7 @@ pub fn create_app(
365362
App::new()
366363
.route("sqlpage.js", web::get().to(handle_static_js))
367364
.wrap_fn(|req, srv| {
368-
let app_state: web::Data<AppState> = web::Data::clone(req.app_data().expect("app_state"));
369-
let sql_file_path = path_to_sql_file(&app_state.web_root, req.path());
365+
let sql_file_path = path_to_sql_file(req.path());
370366
let sql_file = if let Some(sql_path) = sql_file_path {
371367
Ok((req, sql_path))
372368
} else {
@@ -381,7 +377,6 @@ pub fn create_app(
381377
})
382378
.default_service(
383379
actix_files::Files::new("/", &app_state.web_root)
384-
.index_file("index.sql")
385380
.path_filter(|path, _|
386381
!matches!(path.components().next(), Some(Component::Normal(x)) if x == CONFIG_DIR))
387382
.show_files_listing()

0 commit comments

Comments
 (0)