Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send fastn.app mountpoints to .wasm modules via x-fastn-app-mounts header #2059

Merged
merged 4 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion fastn-core/src/commands/serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,9 +587,20 @@ async fn handle_endpoints(
tracing::info!("url: {}", url);

if url.starts_with("wasm+proxy://") {
let app_mounts = match config.app_mounts() {
Err(e) => return Some(Err(e)),
Ok(v) => v,
};

return match config
.ds
.handle_wasm(url, req, endpoint.mountpoint.to_string(), session_id)
.handle_wasm(
url,
req,
endpoint.mountpoint.to_string(),
app_mounts,
session_id,
)
.await
{
Ok(r) => Some(Ok(to_response(r))),
Expand Down
54 changes: 52 additions & 2 deletions fastn-core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,9 +1080,19 @@ impl Config {
// fastn installed Apps
config.package.apps = {
let apps_temp: Vec<fastn_core::package::app::AppTemp> = fastn_doc.get("fastn#app")?;
let mut apps = vec![];
let mut apps: Vec<fastn_core::package::app::App> = vec![];

for app in apps_temp.into_iter() {
apps.push(app.into_app(&config, session_id).await?);
let new_app_package = app.package.clone();
let new_app = app.into_app(&config, session_id).await?;

if let Some(found_app) = apps.iter().find(|a| a.package.name.eq(&new_app_package)) {
return Err(fastn_core::Error::PackageError {
message: format!("Mounting the same package twice is not yet allowed. Tried mounting `{}` which is aready mounted at `{}`", new_app_package, found_app.mount_point),
});
}

apps.push(new_app);
}
apps
};
Expand Down Expand Up @@ -1214,6 +1224,46 @@ impl Config {
.unwrap_or_else(|_| "sqlite:///fastn.sqlite".to_string()),
}
}

/// Get mounted apps (package's system name, mount point)
///
/// ```ftd
/// ;; FASTN.ftd
/// -- fastn.app: Auth App
/// package: lets-auth.fifthtry.site
/// mount-point: /-/auth/
///
/// -- fastn.app: Let's Talk App
/// package: lets-talk.fifthtry.site
/// mount-point: /talk/
/// ```
///
/// Then the value will be a json string:
///
/// ```json
/// { "lets-auth": "/-/auth/", "lets-talk": "/talk/" }
/// ```
///
/// Keys `lets-auth` and `lets-talk` are `system` names of the associated packages.
pub fn app_mounts(&self) -> fastn_core::Result<std::collections::HashMap<String, String>> {
let mut mounts = std::collections::HashMap::new();

for a in &self.package.apps {
if a.package.system.is_none() {
return fastn_core::usage_error(format!(
"Package {} used for app {} is not a system package",
a.package.name, a.name
));
}

mounts.insert(
a.package.system.clone().expect("already checked for None"),
a.mount_point.clone(),
);
}

Ok(mounts)
}
}

#[cfg(feature = "use-config-json")]
Expand Down
6 changes: 6 additions & 0 deletions fastn-core/src/library2022/processor/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,19 @@ pub async fn process(
let mountpoint = mountpoint.ok_or(ftd::interpreter::Error::OtherError(
"Mountpoint not found!".to_string(),
))?;
let app_mounts = req_config
.config
.app_mounts()
.map_err(|e| ftd::interpreter::Error::OtherError(e.to_string()))?;

match req_config
.config
.ds
.handle_wasm(
url.to_string(),
&req_config.request,
mountpoint,
app_mounts,
// FIXME: we don't know how to handle unsaved wasm files. Maybe there is no way
// that an unsaved .wasm file can exist and this is fine.
&None,
Expand Down
9 changes: 9 additions & 0 deletions fastn-ds/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ impl DocumentStore {
wasm_url: String,
req: &T,
mountpoint: String,
app_mounts: std::collections::HashMap<String, String>,
session_id: &Option<String>,
) -> Result<ft_sys_shared::Request, HttpError>
where
Expand All @@ -486,10 +487,18 @@ impl DocumentStore {
.iter()
.map(|(k, v)| (k.as_str().to_string(), v.as_bytes().to_vec()))
.collect();

headers.push((
fastn_utils::FASTN_MOUNTPOINT.to_string(),
mountpoint.into_bytes(),
));

let app_mounts = serde_json::to_string(&app_mounts).unwrap();
headers.push((
fastn_utils::FASTN_APP_MOUNTS.to_string(),
app_mounts.into_bytes(),
));

headers
};

Expand Down
39 changes: 39 additions & 0 deletions fastn-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,43 @@ pub fn row_to_json(r: &rusqlite::Row, count: usize) -> Result<Vec<serde_json::Va
Ok(row)
}

/// Mountpoint is the path on which the wasm file is mounted.
///
/// If in FASTN.ftd, we have:
///
/// ```ftd
/// -- import: fastn
/// -- fastn.package: hello-world
/// -- fastn.url-mappings:
/// /foo/* -> wasm+proxy://hello-world.wasm/*
/// ```
///
/// Then the `mountpoint` is `/foo/`.
pub const FASTN_MOUNTPOINT: &str = "x-fastn-mountpoint";

/// A json object that contains the mountpoints of `fastn.app`s
///
/// If in FASTN.ftd, we have:
///
/// ```ftd
/// -- import: fastn
/// -- fastn.package: hello-world
///
/// -- fastn.app: Auth App
/// package: lets-auth.fifthtry.site
/// mount-point: /-/auth/
///
/// -- fastn.app: Let's Talk App
/// package: lets-talk.fifthtry.site
/// mount-point: /talk/
/// ```
///
/// Then the value will be a json string:
///
/// ```json
/// { "lets-auth": "/-/auth/", "lets-talk": "/talk/" }
/// ```
///
/// NOTE: `lets-auth.fifthtry.site` and `lets-talk.fifthtry.site` are required to be a system
/// package. The names `lets-auth` and `lets-talk` are taken from their `system` field
pub const FASTN_APP_MOUNTS: &str = "x-fastn-app-mounts";