diff --git a/Cargo.lock b/Cargo.lock index 572bc3af04..02a4720afb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1876,6 +1876,13 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foo" +version = "0.1.0" +dependencies = [ + "tokio", +] + [[package]] name = "foreign-types" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 47b87cb96b..ff2b38b589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "fastn-issues", "fastn-js", "fastn-grammar", + "foo", # "fastn-wasm", # "fastn-runtime", ] @@ -15,7 +16,6 @@ exclude = ["fastn-runtime", "fastn-wasm"] resolver = "2" [workspace.package] -version = "0.3.0" authors = [ "Amit Upadhyay ", "Arpita Jaiswal ", diff --git a/fastn-core/src/apis/cache.rs b/fastn-core/src/apis/cache.rs index d957bcbb9a..d98b0fbe14 100644 --- a/fastn-core/src/apis/cache.rs +++ b/fastn-core/src/apis/cache.rs @@ -38,7 +38,10 @@ fn query(uri: &str) -> fastn_core::Result { }) } -pub async fn clear(req: &fastn_core::http::Request) -> fastn_core::http::Response { +pub async fn clear( + config: &fastn_core::Config, + req: &fastn_core::http::Request, +) -> fastn_core::http::Response { let query = match query(req.uri()) { Ok(q) => q, Err(err) => { @@ -50,7 +53,7 @@ pub async fn clear(req: &fastn_core::http::Request) -> fastn_core::http::Respons } }; - if let Err(err) = clear_(&query, req).await { + if let Err(err) = clear_(config, &query, req).await { return fastn_core::server_error!( "fastn-Error: /-/clear-cache/, query: {:?}, error: {:?}", query, @@ -62,12 +65,10 @@ pub async fn clear(req: &fastn_core::http::Request) -> fastn_core::http::Respons } pub async fn clear_( + config: &fastn_core::Config, query: &QueryParams, - req: &fastn_core::http::Request, + _req: &fastn_core::http::Request, ) -> fastn_core::Result<()> { - let config = - fastn_core::time("Config::read()") - .it(fastn_core::Config::read(None, false, Some(req)).await?); if config.package.download_base_url.is_none() { return Err(fastn_core::Error::APIResponseError( "cannot remove anything, package does not have `download_base_url`".to_string(), @@ -112,7 +113,7 @@ pub async fn clear_( // Download FASTN.ftd again after removing all the content if !config.root.join("FASTN.ftd").exists() { - fastn_core::commands::serve::download_init_package(config.package.download_base_url) + fastn_core::commands::serve::download_init_package(&config.package.download_base_url) .await?; } diff --git a/fastn-core/src/apis/clone.rs b/fastn-core/src/apis/clone.rs index 007acfa9ac..a678f16d46 100644 --- a/fastn-core/src/apis/clone.rs +++ b/fastn-core/src/apis/clone.rs @@ -5,20 +5,17 @@ pub struct CloneResponse { pub reserved_crs: Vec, } -pub async fn clone( - req: fastn_core::http::Request, -) -> fastn_core::Result { +pub async fn clone(config: &fastn_core::Config) -> fastn_core::Result { // TODO: implement authentication - match clone_worker(req).await { + match clone_worker(config).await { Ok(data) => fastn_core::http::api_ok(data), Err(err) => fastn_core::http::api_error(err.to_string()), } } -async fn clone_worker(req: fastn_core::http::Request) -> fastn_core::Result { +async fn clone_worker(config: &fastn_core::Config) -> fastn_core::Result { use itertools::Itertools; - let config = fastn_core::Config::read(None, false, Some(&req)).await?; let all_files = config .get_all_file_path(&config.package, Default::default())? .into_iter() diff --git a/fastn-core/src/apis/cr.rs b/fastn-core/src/apis/cr.rs index b754afeaa8..1aa2644591 100644 --- a/fastn-core/src/apis/cr.rs +++ b/fastn-core/src/apis/cr.rs @@ -4,10 +4,10 @@ pub struct CreateCRRequest { } pub async fn create_cr( - req: &fastn_core::http::Request, + config: &fastn_core::Config, cr_req: CreateCRRequest, ) -> fastn_core::Result { - match create_cr_worker(req, cr_req).await { + match create_cr_worker(config, cr_req).await { Ok(cr_number) => { #[derive(serde::Serialize)] struct CreateCRResponse { @@ -21,10 +21,9 @@ pub async fn create_cr( } async fn create_cr_worker( - req: &fastn_core::http::Request, + config: &fastn_core::Config, cr_request: CreateCRRequest, ) -> fastn_core::Result { - let config = fastn_core::Config::read(None, false, Some(req)).await?; let cr_number = config.extract_cr_number().await?; let default_title = format!("CR#{cr_number}"); let cr_meta = fastn_core::cr::CRMeta { @@ -32,24 +31,25 @@ async fn create_cr_worker( cr_number: cr_number as usize, open: true, }; - fastn_core::commands::create_cr::add_cr_to_workspace(&config, &cr_meta).await?; + fastn_core::commands::create_cr::add_cr_to_workspace(config, &cr_meta).await?; Ok(cr_number as usize) } pub async fn create_cr_page( + config: &fastn_core::Config, req: fastn_core::http::Request, ) -> fastn_core::Result { - match create_cr_page_worker(req).await { + match create_cr_page_worker(config, req).await { Ok(body) => Ok(body), Err(err) => fastn_core::http::api_error(err.to_string()), } } async fn create_cr_page_worker( + config: &fastn_core::Config, req: fastn_core::http::Request, ) -> fastn_core::Result { - let mut config = fastn_core::Config::read(None, false, Some(&req)).await?; - let create_cr_ftd = fastn_core::package_info_create_cr(&config)?; + let create_cr_ftd = fastn_core::package_info_create_cr(config)?; let main_document = fastn_core::Document { id: "create-cr.ftd".to_string(), @@ -58,7 +58,10 @@ async fn create_cr_page_worker( package_name: config.package.name.clone(), }; - fastn_core::package::package_doc::read_ftd(&mut config, &main_document, "/", false, false) + let mut req_config = + fastn_core::RequestConfig::new(config, &req, main_document.id.as_str(), "/"); + + fastn_core::package::package_doc::read_ftd(&mut req_config, &main_document, "/", false, false) .await .map(Into::into) } diff --git a/fastn-core/src/apis/edit.rs b/fastn-core/src/apis/edit.rs index 53a7d1a34e..f46f3e77e4 100644 --- a/fastn-core/src/apis/edit.rs +++ b/fastn-core/src/apis/edit.rs @@ -25,16 +25,14 @@ pub struct EditResponse { } pub async fn edit( + config: &fastn_core::Config, req: &fastn_core::http::Request, req_data: EditRequest, ) -> fastn_core::Result { - let mut config = match fastn_core::Config::read(None, false, Some(req)).await { - Ok(config) => config, - Err(err) => return fastn_core::http::api_error(err.to_string()), - }; - config.current_document = Some(req_data.path.to_string()); + let mut req_config = fastn_core::RequestConfig::new(config, req, "edit.ftd", "/"); + req_config.current_document = Some(req_data.path.to_string()); - match config.can_write(req, req_data.path.as_str()).await { + match req_config.can_write(req_data.path.as_str()).await { Ok(can_write) => { if !can_write { return Ok(fastn_core::unauthorised!( @@ -59,7 +57,7 @@ pub async fn edit( } pub(crate) async fn edit_worker( - config: fastn_core::Config, + config: &fastn_core::Config, request: EditRequest, ) -> fastn_core::Result { if request.is_delete() { @@ -110,7 +108,7 @@ pub(crate) async fn edit_worker( .await { let snapshots = fastn_core::snapshot::get_latest_snapshots(&config.root).await?; - let workspaces = fastn_core::snapshot::get_workspace(&config).await?; + let workspaces = fastn_core::snapshot::get_workspace(config).await?; let file = fastn_core::get_file( config.package.name.to_string(), @@ -149,7 +147,7 @@ pub(crate) async fn edit_worker( if let Some(before_update_status) = before_update_status { let snapshots = fastn_core::snapshot::get_latest_snapshots(&config.root).await?; - let workspaces = fastn_core::snapshot::get_workspace(&config).await?; + let workspaces = fastn_core::snapshot::get_workspace(config).await?; let file = fastn_core::get_file( config.package.name.to_string(), &config.root.join(&file_name), @@ -174,14 +172,8 @@ pub(crate) async fn edit_worker( }) } -pub async fn sync( - req: fastn_core::http::Request, -) -> fastn_core::Result { - let config = match fastn_core::Config::read(None, false, Some(&req)).await { - Ok(config) => config, - Err(err) => return fastn_core::http::api_error(err.to_string()), - }; - match fastn_core::commands::sync::sync(&config, None).await { +pub async fn sync(config: &fastn_core::Config) -> fastn_core::Result { + match fastn_core::commands::sync::sync(config, None).await { Ok(_) => { #[derive(serde::Serialize)] struct SyncResponse { @@ -199,15 +191,10 @@ pub struct RevertRequest { } pub async fn revert( - req: &fastn_core::http::Request, + config: &fastn_core::Config, rev: RevertRequest, ) -> fastn_core::Result { - let config = match fastn_core::Config::read(None, false, Some(req)).await { - Ok(config) => config, - Err(err) => return fastn_core::http::api_error(err.to_string()), - }; - - match fastn_core::commands::revert::revert(&config, rev.path.as_str()).await { + match fastn_core::commands::revert::revert(config, rev.path.as_str()).await { Ok(_) => { #[derive(serde::Serialize)] struct RevertResponse { diff --git a/fastn-core/src/apis/edit_source.rs b/fastn-core/src/apis/edit_source.rs index a59f721b26..cb93e8edba 100644 --- a/fastn-core/src/apis/edit_source.rs +++ b/fastn-core/src/apis/edit_source.rs @@ -1,4 +1,7 @@ -pub(crate) async fn edit_source(req: &fastn_core::http::Request) -> fastn_core::http::Response { +pub(crate) async fn edit_source( + config: &fastn_core::Config, + req: &fastn_core::http::Request, +) -> fastn_core::http::Response { // TODO: Need to remove unwrap let path = { let mut path: camino::Utf8PathBuf = @@ -9,7 +12,7 @@ pub(crate) async fn edit_source(req: &fastn_core::http::Request) -> fastn_core:: path }; - match handle_view_source(req, path.as_str()).await { + match handle_view_source(config, req, path.as_str()).await { Ok(body) => fastn_core::http::ok(body), Err(e) => { fastn_core::server_error!("new_path: {}, Error: {:?}", path, e) @@ -18,16 +21,18 @@ pub(crate) async fn edit_source(req: &fastn_core::http::Request) -> fastn_core:: } async fn handle_view_source( + config: &fastn_core::Config, req: &fastn_core::http::Request, path: &str, ) -> fastn_core::Result> { - let mut config = fastn_core::Config::read(None, false, Some(req)).await?; + let mut req_config = fastn_core::RequestConfig::new(config, req, "editor-source.ftd", "/"); + let file_name = config.get_file_path_and_resolve(path).await?; - let file = config.get_file_and_package_by_id(path).await?; + let file = req_config.get_file_and_package_by_id(path).await?; match file { fastn_core::File::Ftd(_) | fastn_core::File::Markdown(_) | fastn_core::File::Code(_) => { - let editor_ftd = fastn_core::package_editor_source(&config, file_name.as_str())?; + let editor_ftd = fastn_core::package_editor_source(config, file_name.as_str())?; let main_document = fastn_core::Document { id: "editor-source.ftd".to_string(), content: editor_ftd, @@ -35,7 +40,7 @@ async fn handle_view_source( package_name: config.package.name.clone(), }; fastn_core::package::package_doc::read_ftd( - &mut config, + &mut req_config, &main_document, "/", false, diff --git a/fastn-core/src/apis/sync.rs b/fastn-core/src/apis/sync.rs index e7e57ba45f..617c3d2163 100644 --- a/fastn-core/src/apis/sync.rs +++ b/fastn-core/src/apis/sync.rs @@ -63,25 +63,24 @@ pub struct SyncRequest { /// If conflict occur, Then send back updated version in latest.ftd with conflicted content /// pub async fn sync( - req: &fastn_core::http::Request, + config: &fastn_core::Config, sync_req: SyncRequest, ) -> fastn_core::Result { dbg!("remote server call", &sync_req.package_name); - match sync_worker(req, sync_req).await { + match sync_worker(config, sync_req).await { Ok(data) => fastn_core::http::api_ok(data), Err(err) => fastn_core::http::api_error(err.to_string()), } } pub(crate) async fn sync_worker( - req: &fastn_core::http::Request, + config: &fastn_core::Config, request: SyncRequest, ) -> fastn_core::Result { use itertools::Itertools; // TODO: Need to call at once only - let config = fastn_core::Config::read(None, false, Some(req)).await?; let mut snapshots = fastn_core::snapshot::get_latest_snapshots(&config.root).await?; let client_snapshots = fastn_core::snapshot::resolve_snapshots(&request.latest_ftd).await?; // let latest_ftd = tokio::fs::read_to_string(config.history_dir().join(".latest.ftd")).await?; @@ -229,12 +228,12 @@ pub(crate) async fn sync_worker( } } - client_current_files(&config, &snapshots, &client_snapshots, &mut synced_files).await?; + client_current_files(config, &snapshots, &client_snapshots, &mut synced_files).await?; - let history_files = clone_history_files(&config, &snapshots, &client_snapshots).await?; + let history_files = clone_history_files(config, &snapshots, &client_snapshots).await?; fastn_core::snapshot::create_latest_snapshots( - &config, + config, &snapshots .into_iter() .map(|(filename, timestamp)| fastn_core::Snapshot { diff --git a/fastn-core/src/apis/sync2.rs b/fastn-core/src/apis/sync2.rs index debff5668b..a6d2524c05 100644 --- a/fastn-core/src/apis/sync2.rs +++ b/fastn-core/src/apis/sync2.rs @@ -110,12 +110,12 @@ pub struct File { } pub async fn sync2( - req: &fastn_core::http::Request, + config: &fastn_core::Config, sync_req: SyncRequest, ) -> fastn_core::Result { dbg!("remote server call", &sync_req.package_name); - match sync_worker(req, sync_req).await { + match sync_worker(config, sync_req).await { Ok(data) => fastn_core::http::api_ok(data), Err(err) => fastn_core::http::api_error(err.to_string()), } @@ -308,14 +308,13 @@ pub(crate) async fn do_sync( } pub(crate) async fn sync_worker( - req: &fastn_core::http::Request, + config: &fastn_core::Config, request: SyncRequest, ) -> fastn_core::Result { use itertools::Itertools; // TODO: Need to call at once only - let config = fastn_core::Config::read(None, false, Some(req)).await?; - let mut synced_files = do_sync(&config, request.files.as_slice()).await?; + let mut synced_files = do_sync(config, request.files.as_slice()).await?; let remote_history = config.get_history().await?; let remote_manifest = fastn_core::history::FileHistory::get_remote_manifest(remote_history.as_slice(), true)?; @@ -324,9 +323,9 @@ pub(crate) async fn sync_worker( let client_latest = fastn_core::history::FileHistory::get_remote_manifest(clone_history.as_slice(), true)?; - client_current_files(&config, &remote_manifest, &client_latest, &mut synced_files).await?; + client_current_files(config, &remote_manifest, &client_latest, &mut synced_files).await?; - let history_files = clone_history_files(&config, &remote_manifest, &client_latest).await?; + let history_files = clone_history_files(config, &remote_manifest, &client_latest).await?; Ok(SyncResponse { files: synced_files.into_values().collect_vec(), diff --git a/fastn-core/src/apis/view_source.rs b/fastn-core/src/apis/view_source.rs index 306616c04f..90a866982d 100644 --- a/fastn-core/src/apis/view_source.rs +++ b/fastn-core/src/apis/view_source.rs @@ -1,4 +1,7 @@ -pub(crate) async fn view_source(req: &fastn_core::http::Request) -> fastn_core::http::Response { +pub(crate) async fn view_source( + config: &fastn_core::Config, + req: &fastn_core::http::Request, +) -> fastn_core::http::Response { // TODO: Need to remove unwrap let path = { let mut path: camino::Utf8PathBuf = @@ -9,7 +12,7 @@ pub(crate) async fn view_source(req: &fastn_core::http::Request) -> fastn_core:: path }; - match handle_view_source(req, path.as_str()).await { + match handle_view_source(config, req, path.as_str()).await { Ok(body) => fastn_core::http::ok(body), Err(e) => { fastn_core::server_error!("new_path: {}, Error: {:?}", path, e) @@ -18,18 +21,19 @@ pub(crate) async fn view_source(req: &fastn_core::http::Request) -> fastn_core:: } async fn handle_view_source( + config: &fastn_core::Config, req: &fastn_core::http::Request, path: &str, ) -> fastn_core::Result> { - let mut config = fastn_core::Config::read(None, false, Some(req)).await?; + let mut req_config = fastn_core::RequestConfig::new(config, req, "editor.ftd", "/"); let file_name = config.get_file_path_and_resolve(path).await?; - let file = config.get_file_and_package_by_id(path).await?; + let file = req_config.get_file_and_package_by_id(path).await?; match file { fastn_core::File::Ftd(_) | fastn_core::File::Markdown(_) | fastn_core::File::Code(_) => { let snapshots = fastn_core::snapshot::get_latest_snapshots(&config.root).await?; let diff = get_diff(&file, &snapshots).await; - let editor_ftd = fastn_core::package_info_editor(&config, file_name.as_str(), diff)?; + let editor_ftd = fastn_core::package_info_editor(config, file_name.as_str(), diff)?; let main_document = fastn_core::Document { id: "editor.ftd".to_string(), content: editor_ftd, @@ -37,7 +41,7 @@ async fn handle_view_source( package_name: config.package.name.clone(), }; fastn_core::package::package_doc::read_ftd( - &mut config, + &mut req_config, &main_document, "/", false, diff --git a/fastn-core/src/auth/processor.rs b/fastn-core/src/auth/processor.rs index cd2dd82a9f..e392061501 100644 --- a/fastn-core/src/auth/processor.rs +++ b/fastn-core/src/auth/processor.rs @@ -3,19 +3,16 @@ pub fn user_details( section: &ftd::ftd2021::p1::Section, doc: &ftd::ftd2021::p2::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::ftd2021::p1::Result { let mut found_cookie = false; - let is_login = match &config.request { - Some(req) => { - for auth_provider in fastn_core::auth::AuthProviders::AUTH_ITER.iter() { - if req.cookie(auth_provider.as_str()).is_some() { - found_cookie = true; - } + let is_login = { + for auth_provider in fastn_core::auth::AuthProviders::AUTH_ITER.iter() { + if req_config.request.cookie(auth_provider.as_str()).is_some() { + found_cookie = true; } - found_cookie } - None => false, + found_cookie }; #[derive(Debug, serde::Serialize)] diff --git a/fastn-core/src/commands/build.rs b/fastn-core/src/commands/build.rs index 6a21114dc6..237f98a393 100644 --- a/fastn-core/src/commands/build.rs +++ b/fastn-core/src/commands/build.rs @@ -1,6 +1,6 @@ // #[tracing::instrument(skip(config))] pub async fn build( - config: &mut fastn_core::Config, + config: &fastn_core::Config, only_id: Option<&str>, base_url: &str, ignore_failed: bool, @@ -197,7 +197,7 @@ fn is_virtual_dep(path: &str) -> bool { } async fn handle_dependency_file( - config: &mut fastn_core::Config, + config: &fastn_core::Config, cache: &mut cache::Cache, documents: &std::collections::BTreeMap, base_url: &str, @@ -228,7 +228,7 @@ async fn handle_dependency_file( // removes deleted documents from cache and build folder fn remove_deleted_documents( - config: &mut fastn_core::Config, + config: &fastn_core::Config, c: &mut cache::Cache, documents: &std::collections::BTreeMap, ) -> fastn_core::Result<()> { @@ -278,7 +278,7 @@ fn remove_deleted_documents( #[tracing::instrument(skip(config, documents))] async fn incremental_build( - config: &mut fastn_core::Config, + config: &fastn_core::Config, documents: &std::collections::BTreeMap, base_url: &str, ignore_failed: bool, @@ -411,7 +411,7 @@ async fn incremental_build( #[tracing::instrument(skip(config, documents))] async fn handle_only_id( id: &str, - config: &mut fastn_core::Config, + config: &fastn_core::Config, base_url: &str, ignore_failed: bool, test: bool, @@ -432,7 +432,7 @@ async fn handle_only_id( async fn handle_file( document: &fastn_core::File, - config: &mut fastn_core::Config, + config: &fastn_core::Config, base_url: &str, ignore_failed: bool, test: bool, @@ -441,7 +441,7 @@ async fn handle_file( ) -> fastn_core::Result<()> { let start = std::time::Instant::now(); print!("Processing {} ... ", document.get_id_with_package()); - + let package_name = config.package.name.to_string(); let process_status = handle_file_( document, config, @@ -454,23 +454,13 @@ async fn handle_file( .await; if process_status.is_ok() { fastn_core::utils::print_end( - format!( - "Processed {}/{}", - config.package.name.as_str(), - document.get_id() - ) - .as_str(), + format!("Processed {}/{}", package_name.as_str(), document.get_id()).as_str(), start, ); } if process_status.is_err() { fastn_core::utils::print_error( - format!( - "Failed {}/{}", - config.package.name.as_str(), - document.get_id() - ) - .as_str(), + format!("Failed {}/{}", package_name.as_str(), document.get_id()).as_str(), start, ); return process_status; @@ -570,16 +560,13 @@ fn remove_extension(id: &str) -> String { #[tracing::instrument(skip(document, config, cache))] async fn handle_file_( document: &fastn_core::File, - config: &mut fastn_core::Config, + config: &fastn_core::Config, base_url: &str, ignore_failed: bool, test: bool, build_static_files: bool, cache: Option<&mut cache::Cache>, ) -> fastn_core::Result<()> { - config.current_document = Some(document.get_id().to_string()); - config.dependencies_during_render = vec![]; - match document { fastn_core::File::Ftd(doc) => { let file_path = if doc.id.eq("404.ftd") { @@ -606,23 +593,44 @@ async fn handle_file_( return Ok(()); } - let resp = fastn_core::package::package_doc::process_ftd( - config, - doc, - base_url, - build_static_files, - test, - file_path.as_str(), - ) - .await; + let resp = { + let req = fastn_core::http::Request::default(); + let mut req_config = + fastn_core::RequestConfig::new(config, &req, doc.id.as_str(), base_url); + req_config.current_document = Some(document.get_id().to_string()); + + let resp = fastn_core::package::package_doc::process_ftd( + &mut req_config, + doc, + base_url, + build_static_files, + test, + file_path.as_str(), + ) + .await; + + config.all_packages.borrow_mut().extend( + req_config + .config + .all_packages + .borrow() + .iter() + .map(|(k, v)| (k.clone(), v.clone())), + ); + resp + }; + match (resp, ignore_failed) { (Ok(r), _) => { + // TODO: what to do with dependencies? + // let dependencies = req_config.dependencies_during_render; + let dependencies = vec![]; if let Some(cache) = cache { cache.documents.insert( remove_extension(doc.id.as_str()), cache::Document { html_checksum: r.checksum(), - dependencies: config.dependencies_during_render.clone(), + dependencies, }, ); cache.file_checksum.insert( @@ -732,7 +740,7 @@ pub async fn default_build_files( #[tracing::instrument(skip(config))] async fn get_documents_for_current_package( - config: &mut fastn_core::Config, + config: &fastn_core::Config, ) -> fastn_core::Result> { let mut documents = std::collections::BTreeMap::from_iter( config @@ -743,13 +751,12 @@ async fn get_documents_for_current_package( ); if let Some(ref sitemap) = config.package.sitemap { - let new_config = config.clone(); let get_all_locations = sitemap.get_all_locations(); let mut files: std::collections::HashMap = Default::default(); for (doc_path, _, url) in get_all_locations { let file = { let package_name = if let Some(ref url) = url { - new_config.find_package_by_id(url).await?.1.name + config.find_package_by_id(url).await?.1.name } else { config.package.name.to_string() }; @@ -770,10 +777,6 @@ async fn get_documents_for_current_package( files.insert(file.get_id().to_string(), file); } - config - .all_packages - .borrow_mut() - .extend(new_config.all_packages.into_inner()); documents.extend(files); } diff --git a/fastn-core/src/commands/clone.rs b/fastn-core/src/commands/clone.rs index 4e9b63348b..cb7912bf33 100644 --- a/fastn-core/src/commands/clone.rs +++ b/fastn-core/src/commands/clone.rs @@ -14,7 +14,7 @@ pub async fn clone(source: &str) -> fastn_core::Result<()> { })) .await; - let config = fastn_core::Config::read(Some(root.as_str().to_string()), false, None).await?; + let config = fastn_core::Config::read(Some(root.as_str().to_string()), false).await?; config.create_clone_workspace().await?; config .write_clone_available_cr(clone_response.reserved_crs.as_slice()) diff --git a/fastn-core/src/commands/serve.rs b/fastn-core/src/commands/serve.rs index 0bbf40fc35..41a67dc94a 100644 --- a/fastn-core/src/commands/serve.rs +++ b/fastn-core/src/commands/serve.rs @@ -3,7 +3,7 @@ static LOCK: once_cell::sync::Lazy> = #[tracing::instrument(skip_all)] fn handle_redirect( - config: &mut fastn_core::Config, + config: &fastn_core::Config, path: &camino::Utf8Path, ) -> Option { config @@ -19,10 +19,10 @@ fn handle_redirect( /// #[tracing::instrument(skip_all)] async fn serve_file( - config: &mut fastn_core::Config, + config: &mut fastn_core::RequestConfig, path: &camino::Utf8Path, ) -> fastn_core::http::Response { - if let Some(r) = handle_redirect(config, path) { + if let Some(r) = handle_redirect(&config.config, path) { return r; } @@ -42,13 +42,7 @@ async fn serve_file( // Auth Stuff if !f.is_static() { - let req = if let Some(ref r) = config.request { - r - } else { - return fastn_core::server_error!("request not set"); - }; - - match config.can_read(req, path.as_str(), true).await { + match config.can_read(path.as_str(), true).await { Ok(can_read) => { if !can_read { tracing::error!( @@ -130,13 +124,13 @@ async fn serve_file( } async fn serve_cr_file( - req: &fastn_core::http::Request, - config: &mut fastn_core::Config, + req_config: &mut fastn_core::RequestConfig, path: &camino::Utf8Path, cr_number: usize, ) -> fastn_core::http::Response { let _lock = LOCK.read().await; - let f = match config + let f = match req_config + .config .get_file_and_package_by_cr_id(path.as_str(), cr_number) .await { @@ -148,7 +142,7 @@ async fn serve_cr_file( // Auth Stuff if !f.is_static() { - match config.can_read(req, path.as_str(), true).await { + match req_config.can_read(path.as_str(), true).await { Ok(can_read) => { if !can_read { return fastn_core::unauthorised!("You are unauthorized to access: {}", path); @@ -160,11 +154,11 @@ async fn serve_cr_file( } } - config.current_document = Some(f.get_id().to_string()); + req_config.current_document = Some(f.get_id().to_string()); match f { fastn_core::File::Ftd(main_document) => { match fastn_core::package::package_doc::read_ftd( - config, + req_config, &main_document, "/", false, @@ -240,32 +234,21 @@ async fn static_file(file_path: camino::Utf8PathBuf) -> fastn_core::http::Respon #[tracing::instrument(skip_all)] pub async fn serve( + config: &fastn_core::Config, req: fastn_core::http::Request, - edition: Option, - external_js: Vec, - inline_js: Vec, - external_css: Vec, - inline_css: Vec, ) -> fastn_core::Result { let _lock = LOCK.read().await; - // TODO: remove unwrap - let path: camino::Utf8PathBuf = req.path().replacen('/', "", 1).parse().unwrap(); - let mut config = fastn_core::Config::read(None, false, Some(&req)) - .await - .unwrap() - .add_edition(edition)? - .add_external_js(external_js) - .add_inline_js(inline_js) - .add_external_css(external_css) - .add_inline_css(inline_css); + let mut req_config = fastn_core::RequestConfig::new(config, &req, "", "/"); + + let path: camino::Utf8PathBuf = req.path().replacen('/', "", 1).parse()?; Ok(if path.eq(&camino::Utf8PathBuf::new().join("FASTN.ftd")) { - serve_fastn_file(&config).await + serve_fastn_file(config).await } else if path.eq(&camino::Utf8PathBuf::new().join("")) { - serve_file(&mut config, &path.join("/")).await + serve_file(&mut req_config, &path.join("/")).await } else if let Some(cr_number) = fastn_core::cr::get_cr_path_from_url(path.as_str()) { - serve_cr_file(&req, &mut config, &path, cr_number).await + serve_cr_file(&mut req_config, &path, cr_number).await } else { // url is present in config or not // If not present than proxy pass it @@ -319,7 +302,9 @@ pub async fn serve( // if request goes with mount-point /todos/api/add-todo/ // so it should say not found and pass it to proxy - let file_response = serve_file(&mut config, path.as_path()).await; + let cookies = req_config.request.cookies().clone(); + + let file_response = serve_file(&mut req_config, path.as_path()).await; // If path is not present in sitemap then pass it to proxy // TODO: Need to handle other package URL as well, and that will start from `-` // and all the static files starts with `-` @@ -337,7 +322,7 @@ pub async fn serve( // Already checked in the above method serve_file tracing::info!("executing proxy: path: {}", &path); let (package_name, url, mut conf) = - fastn_core::config::utils::get_clean_url(&config, path.as_str())?; + fastn_core::config::utils::get_clean_url(config, path.as_str())?; let package_name = package_name.unwrap_or_else(|| config.package.name.to_string()); let host = if let Some(port) = url.port() { @@ -345,12 +330,6 @@ pub async fn serve( } else { format!("{}://{}", url.scheme(), url.host_str().unwrap()) }; - let req = if let Some(r) = config.request { - r - } else { - tracing::error!(msg = "request not set"); - return Ok(fastn_core::server_error!("request not set")); - }; // TODO: read app config and send them to service as header // Adjust x-fastn header from based on the platform and the requested field @@ -360,7 +339,7 @@ pub async fn serve( if let Some(user_data) = fastn_core::auth::get_user_data_from_cookies( platform, requested_field, - req.cookies(), + &cookies, ) .await? { @@ -404,9 +383,9 @@ pub async fn serve( }) } -pub(crate) async fn download_init_package(url: Option) -> std::io::Result<()> { +pub(crate) async fn download_init_package(url: &Option) -> std::io::Result<()> { let mut package = fastn_core::Package::new("unknown-package"); - package.download_base_url = url; + package.download_base_url = url.to_owned(); package .http_download_by_id( "FASTN.ftd", @@ -421,6 +400,7 @@ pub(crate) async fn download_init_package(url: Option) -> std::io::Resul } pub async fn clear_cache( + config: &fastn_core::Config, req: fastn_core::http::Request, ) -> fastn_core::Result { fn is_login(req: &fastn_core::http::Request) -> bool { @@ -437,7 +417,7 @@ pub async fn clear_cache( let from = actix_web::web::Query::::from_query(req.query_string())?; if from.from.eq(&Some("temp-github".to_string())) { let _lock = LOCK.write().await; - return Ok(fastn_core::apis::cache::clear(&req).await); + return Ok(fastn_core::apis::cache::clear(config, &req).await); } // TODO: Remove After Demo, till here @@ -451,7 +431,7 @@ pub async fn clear_cache( } let _lock = LOCK.write().await; - fastn_core::apis::cache::clear(&req).await; + fastn_core::apis::cache::clear(config, &req).await; // TODO: Redirect to Referrer uri return Ok(actix_web::HttpResponse::Found() .append_header((actix_web::http::header::LOCATION, "/".to_string())) @@ -459,79 +439,90 @@ pub async fn clear_cache( } // TODO: Move them to routes folder -async fn sync(req: fastn_core::http::Request) -> fastn_core::Result { +async fn sync( + config: &fastn_core::Config, + req: fastn_core::http::Request, +) -> fastn_core::Result { let _lock = LOCK.write().await; - fastn_core::apis::sync(&req, req.json()?).await + fastn_core::apis::sync(config, req.json()?).await } -async fn sync2(req: fastn_core::http::Request) -> fastn_core::Result { +async fn sync2( + config: &fastn_core::Config, + req: fastn_core::http::Request, +) -> fastn_core::Result { let _lock = LOCK.write().await; - fastn_core::apis::sync2(&req, req.json()?).await + fastn_core::apis::sync2(config, req.json()?).await } -pub async fn clone( - req: fastn_core::http::Request, -) -> fastn_core::Result { +pub async fn clone(config: &fastn_core::Config) -> fastn_core::Result { let _lock = LOCK.read().await; - fastn_core::apis::clone(req).await + fastn_core::apis::clone(config).await } pub(crate) async fn view_source( + config: &fastn_core::Config, req: fastn_core::http::Request, ) -> fastn_core::Result { let _lock = LOCK.read().await; - Ok(fastn_core::apis::view_source(&req).await) + Ok(fastn_core::apis::view_source(config, &req).await) } pub(crate) async fn edit_source( + config: &fastn_core::Config, req: fastn_core::http::Request, ) -> fastn_core::Result { let _lock = LOCK.read().await; - Ok(fastn_core::apis::edit_source(&req).await) + Ok(fastn_core::apis::edit_source(config, &req).await) } pub async fn edit( + config: &fastn_core::Config, req: fastn_core::http::Request, ) -> fastn_core::Result { let _lock = LOCK.write().await; - fastn_core::apis::edit(&req, req.json()?).await + fastn_core::apis::edit(config, &req, req.json()?).await } pub async fn revert( + config: &fastn_core::Config, req: fastn_core::http::Request, ) -> fastn_core::Result { let _lock = LOCK.write().await; - fastn_core::apis::edit::revert(&req, req.json()?).await + fastn_core::apis::edit::revert(config, req.json()?).await } pub async fn editor_sync( - req: fastn_core::http::Request, + config: &fastn_core::Config, ) -> fastn_core::Result { let _lock = LOCK.write().await; - fastn_core::apis::edit::sync(req).await + fastn_core::apis::edit::sync(config).await } pub async fn create_cr( + config: &fastn_core::Config, req: fastn_core::http::Request, ) -> fastn_core::Result { let _lock = LOCK.write().await; - fastn_core::apis::cr::create_cr(&req, req.json()?).await + fastn_core::apis::cr::create_cr(config, req.json()?).await } pub async fn create_cr_page( + config: &fastn_core::Config, req: fastn_core::http::Request, ) -> fastn_core::Result { let _lock = LOCK.read().await; - fastn_core::apis::cr::create_cr_page(req).await + fastn_core::apis::cr::create_cr_page(config, req).await } -struct AppData { - edition: Option, - external_js: Vec, - inline_js: Vec, - external_css: Vec, - inline_css: Vec, - package_name: String, +#[derive(serde::Deserialize, Default, Clone)] +pub(crate) struct AppData { + pub(crate) edition: Option, + pub(crate) external_js: Vec, + pub(crate) inline_js: Vec, + pub(crate) external_css: Vec, + pub(crate) inline_css: Vec, + pub(crate) package_name: String, } fn handle_default_route( @@ -625,53 +616,54 @@ async fn test() -> fastn_core::Result { } #[tracing::instrument(skip_all)] -async fn route( +async fn actual_route( + config: &fastn_core::Config, req: actix_web::HttpRequest, body: actix_web::web::Bytes, - app_data: actix_web::web::Data, + package_name: &str, ) -> fastn_core::Result { tracing::info!(method = req.method().as_str(), uri = req.path()); tracing::info!(tutor_mode = fastn_core::tutor::is_tutor()); - let package_name = &app_data.package_name; - - if let Some(default_response) = handle_default_route(&req, package_name.as_str()) { + if let Some(default_response) = handle_default_route(&req, package_name) { return Ok(default_response); } let req = fastn_core::http::Request::from_actix(req, body); match (req.method().to_lowercase().as_str(), req.path()) { - ("post", "/-/sync/") if cfg!(feature = "remote") => sync(req).await, - ("post", "/-/sync2/") if cfg!(feature = "remote") => sync2(req).await, - ("get", "/-/clone/") if cfg!(feature = "remote") => clone(req).await, - ("get", t) if t.starts_with("/-/view-src/") => view_source(req).await, - ("get", t) if t.starts_with("/-/edit-src/") => edit_source(req).await, + ("post", "/-/sync/") if cfg!(feature = "remote") => sync(config, req).await, + ("post", "/-/sync2/") if cfg!(feature = "remote") => sync2(config, req).await, + ("get", "/-/clone/") if cfg!(feature = "remote") => clone(config).await, + ("get", t) if t.starts_with("/-/view-src/") => view_source(config, req).await, + ("get", t) if t.starts_with("/-/edit-src/") => edit_source(config, req).await, ("get", t) if t.starts_with("/-/auth/") => fastn_core::auth::routes::handle_auth(req).await, - ("post", "/-/edit/") => edit(req).await, - ("post", "/-/revert/") => revert(req).await, - ("get", "/-/editor-sync/") => editor_sync(req).await, - ("post", "/-/create-cr/") => create_cr(req).await, - ("get", "/-/create-cr-page/") => create_cr_page(req).await, - ("get", "/-/clear-cache/") => clear_cache(req).await, + ("post", "/-/edit/") => edit(config, req).await, + ("post", "/-/revert/") => revert(config, req).await, + ("get", "/-/editor-sync/") => editor_sync(config).await, + ("post", "/-/create-cr/") => create_cr(config, req).await, + ("get", "/-/create-cr-page/") => create_cr_page(config, req).await, + ("get", "/-/clear-cache/") => clear_cache(config, req).await, ("get", "/-/poll/") => fastn_core::watcher::poll().await, ("get", "/favicon.ico") => favicon().await, ("get", "/test/") => test().await, ("get", "/-/pwd/") => fastn_core::tutor::pwd().await, - ("get", "/-/shutdown/") => fastn_core::tutor::shutdown().await, - (_, _) => { - serve( - req, - app_data.edition.clone(), - app_data.external_js.clone(), - app_data.inline_js.clone(), - app_data.external_css.clone(), - app_data.inline_css.clone(), - ) - .await - } + ("get", "/-/tutor.js") => fastn_core::tutor::js().await, + ("post", "/-/tutor/start/") => fastn_core::tutor::start(req.json()?).await, + ("get", "/-/tutor/stop/") => fastn_core::tutor::stop().await, + (_, _) => serve(config, req).await, } } +#[tracing::instrument(skip_all)] +async fn route( + req: actix_web::HttpRequest, + body: actix_web::web::Bytes, + app_data: actix_web::web::Data, +) -> fastn_core::Result { + let (config, package_name) = fastn_core::tutor::config(&app_data).await?; + actual_route(&config, req, body, package_name.as_str()).await +} + //noinspection HttpUrlsUsage #[allow(clippy::too_many_arguments)] pub async fn listen( @@ -689,7 +681,7 @@ pub async fn listen( env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); if package_download_base_url.is_some() { - download_init_package(package_download_base_url).await?; + download_init_package(&package_download_base_url).await?; } if cfg!(feature = "controller") { diff --git a/fastn-core/src/commands/stop_tracking.rs b/fastn-core/src/commands/stop_tracking.rs index 64480f2230..5c06580c46 100644 --- a/fastn-core/src/commands/stop_tracking.rs +++ b/fastn-core/src/commands/stop_tracking.rs @@ -22,7 +22,7 @@ pub async fn handle_command(matches: &clap::ArgMatches) -> fastn_core::Result<() use fastn_core::utils::ValueOf; stop_tracking( - &fastn_core::Config::read(None, true, None).await?, + &fastn_core::Config::read(None, true).await?, matches.value_of_("source").unwrap(), matches.value_of_("target"), ) diff --git a/fastn-core/src/commands/sync_status.rs b/fastn-core/src/commands/sync_status.rs index d5de737a74..062f46de3e 100644 --- a/fastn-core/src/commands/sync_status.rs +++ b/fastn-core/src/commands/sync_status.rs @@ -11,7 +11,7 @@ pub async fn handle_command(matches: &clap::ArgMatches) -> fastn_core::Result<() use fastn_core::utils::ValueOf; sync_status( - &fastn_core::Config::read(None, true, None).await?, + &fastn_core::Config::read(None, true).await?, matches.value_of_("file"), // TODO: handle multiple files ) .await diff --git a/fastn-core/src/commands/update.rs b/fastn-core/src/commands/update.rs index 66093dad08..6b0363fe77 100644 --- a/fastn-core/src/commands/update.rs +++ b/fastn-core/src/commands/update.rs @@ -6,7 +6,7 @@ pub async fn update(config: &fastn_core::Config) -> fastn_core::Result<()> { } }; - let c = fastn_core::Config::read(None, false, None).await?; + let c = fastn_core::Config::read(None, false).await?; if c.package.dependencies.is_empty() { println!("No dependencies to update.") } else if c.package.dependencies.len() == 1 { diff --git a/fastn-core/src/config/mod.rs b/fastn-core/src/config/mod.rs index 6b3a309cde..d674494afe 100644 --- a/fastn-core/src/config/mod.rs +++ b/fastn-core/src/config/mod.rs @@ -34,13 +34,7 @@ pub struct Config { pub packages_root: camino::Utf8PathBuf, pub original_directory: camino::Utf8PathBuf, pub all_packages: std::cell::RefCell>, - pub downloaded_assets: std::collections::BTreeMap, pub global_ids: std::collections::HashMap, - pub named_parameters: Vec<(String, ftd::Value)>, - pub extra_data: std::collections::BTreeMap, - pub current_document: Option, - pub dependencies_during_render: Vec, - pub request: Option, // TODO: It should only contain reference pub ftd_edition: FTDEdition, pub ftd_external_js: Vec, pub ftd_inline_js: Vec, @@ -48,6 +42,249 @@ pub struct Config { pub ftd_inline_css: Vec, } +#[derive(Debug, Clone)] +pub struct RequestConfig { + pub named_parameters: Vec<(String, ftd::Value)>, + pub extra_data: std::collections::BTreeMap, + pub downloaded_assets: std::collections::BTreeMap, + pub current_document: Option, + pub dependencies_during_render: Vec, + pub request: fastn_core::http::Request, + pub config: Config, + /// If the current module being parsed is a markdown file, `.markdown` contains the name and + /// content of that file + pub markdown: Option<(String, String)>, + pub document_id: String, + pub translated_data: fastn_core::TranslationData, + pub base_url: String, + pub module_package_map: std::collections::BTreeMap, +} + +impl RequestConfig { + pub fn new( + config: &Config, + request: &fastn_core::http::Request, + document_id: &str, + base_url: &str, + ) -> Self { + RequestConfig { + named_parameters: vec![], + extra_data: Default::default(), + downloaded_assets: Default::default(), + current_document: None, + dependencies_during_render: vec![], + request: request.clone(), + config: config.clone(), + markdown: None, + document_id: document_id.to_string(), + translated_data: Default::default(), + base_url: base_url.to_string(), + module_package_map: Default::default(), + } + } + + pub fn doc_id(&self) -> Option { + self.current_document + .clone() + .map(|v| fastn_core::utils::id_to_path(v.as_str())) + .map(|v| v.trim().replace(std::path::MAIN_SEPARATOR, "/")) + } + + /// document_name_with_default("index.ftd") -> / + /// document_name_with_default("foo/index.ftd") -> /foo/ + /// document_name_with_default("foo/abc") -> /foo/abc/ + /// document_name_with_default("/foo/abc.ftd") -> /foo/abc/ + pub(crate) fn document_name_with_default(&self, document_path: &str) -> String { + let name = self + .doc_id() + .unwrap_or_else(|| document_path.to_string()) + .trim_matches('/') + .to_string(); + if name.is_empty() { + "/".to_string() + } else { + format!("/{}/", name) + } + } + + // -/kameri-app.herokuapp.com/ + // .packages/kameri-app.heroku.com/index.ftd + #[tracing::instrument(skip_all)] + pub async fn get_file_and_package_by_id( + &mut self, + path: &str, + ) -> fastn_core::Result { + tracing::info!(path = path); + // This function will return file and package by given path + // path can be mounted(mount-point) with other dependencies + // + // Sanitize the mountpoint request. + // Get the package and sanitized path + let package1; + + // TODO: The shitty code written by me ever + let (path_with_package_name, document, path_params, extra_data) = + if !fastn_core::file::is_static(path)? { + let (path_with_package_name, sanitized_package, sanitized_path) = match self + .config + .get_mountpoint_sanitized_path(&self.config.package, path) + { + Some((new_path, package, remaining_path, _)) => { + // Update the sitemap of the package, if it does not contain the sitemap information + if package.name != self.config.package.name { + package1 = self.config.update_sitemap(package).await?; + (new_path, &package1, remaining_path) + } else { + (new_path, package, remaining_path) + } + } + None => (path.to_string(), &self.config.package, path.to_string()), + }; + + // Getting `document` with dynamic parameters, if exists + // It will first resolve in sitemap + // Then it will resolve in the dynamic urls + let (document, path_params, extra_data) = + fastn_core::sitemap::resolve(sanitized_package, &sanitized_path)?; + + // document with package-name prefix + let document = document.map(|doc| { + format!( + "-/{}/{}", + sanitized_package.name.trim_matches('/'), + doc.trim_matches('/') + ) + }); + (path_with_package_name, document, path_params, extra_data) + } else { + (path.to_string(), None, vec![], Default::default()) + }; + + let path = path_with_package_name.as_str(); + + if let Some(id) = document { + let file_name = self.config.get_file_path_and_resolve(id.as_str()).await?; + let package = self.config.find_package_by_id(id.as_str()).await?.1; + let file = fastn_core::get_file( + package.name.to_string(), + &self.config.root.join(file_name), + &self.config.get_root_for_package(&package), + ) + .await?; + self.current_document = Some(path.to_string()); + self.named_parameters = path_params; + self.extra_data = extra_data; + Ok(file) + } else { + // -/fifthtry.github.io/todos/add-todo/ + // -/fifthtry.github.io/doc-site/add-todo/ + let file_name = self.config.get_file_path_and_resolve(path).await?; + // .packages/todos/add-todo.ftd + // .packages/fifthtry.github.io/doc-site/add-todo.ftd + + let package = self.config.find_package_by_id(path).await?.1; + let mut file = fastn_core::get_file( + package.name.to_string(), + &self.config.root.join(file_name.trim_start_matches('/')), + &self.config.get_root_for_package(&package), + ) + .await?; + + if path.contains("-/") { + let url = path.trim_end_matches("/index.html").trim_matches('/'); + let extension = if matches!(file, fastn_core::File::Markdown(_)) { + "/index.md".to_string() + } else if matches!(file, fastn_core::File::Ftd(_)) { + "/index.ftd".to_string() + } else { + "".to_string() + }; + file.set_id(format!("{}{}", url, extension).as_str()); + } + self.current_document = Some(file.get_id().to_string()); + Ok(file) + } + } + #[tracing::instrument(skip(self))] + pub(crate) async fn can_read( + &self, + document_path: &str, + with_confidential: bool, // can read should use confidential property or not + ) -> fastn_core::Result { + // Function Docs + // If user can read the document based on readers, user will have read access to page + // If user cannot read the document based on readers, and if confidential is false so user + // can access the page, and if confidential is true user will not be able to access the + // document + + // can_read: true, confidential: true => true (can access) + // can_read: true, confidential: false => true (can access) + // can_read: false, confidential: true => false (cannot access) + // can_read: false, confidential: false => true (can access) + + use itertools::Itertools; + let document_name = self.document_name_with_default(document_path); + if let Some(sitemap) = &self.config.package.sitemap { + // TODO: This can be buggy in case of: if groups are used directly in sitemap are foreign groups + let (document_readers, confidential) = + sitemap.readers(document_name.as_str(), &self.config.package.groups); + + // TODO: Need to check the confidential logic, if readers are not defined in the sitemap + if document_readers.is_empty() { + return Ok(true); + } + let access_identities = fastn_core::user_group::access_identities( + &self.config, + &self.request, + &document_name, + true, + ) + .await?; + + let belongs_to = fastn_core::user_group::belongs_to( + &self.config, + document_readers.as_slice(), + access_identities.iter().collect_vec().as_slice(), + )?; + + if with_confidential { + if belongs_to { + return Ok(true); + } + return Ok(!confidential); + } + return Ok(belongs_to); + } + Ok(true) + } + + #[tracing::instrument(skip(self))] + pub(crate) async fn can_write(&self, document_path: &str) -> fastn_core::Result { + use itertools::Itertools; + let document_name = self.document_name_with_default(document_path); + if let Some(sitemap) = &self.config.package.sitemap { + // TODO: This can be buggy in case of: if groups are used directly in sitemap are foreign groups + let document_writers = + sitemap.writers(document_name.as_str(), &self.config.package.groups); + let access_identities = fastn_core::user_group::access_identities( + &self.config, + &self.request, + &document_name, + false, + ) + .await?; + + return fastn_core::user_group::belongs_to( + &self.config, + document_writers.as_slice(), + access_identities.iter().collect_vec().as_slice(), + ); + } + + Ok(false) + } +} + impl Config { /// `build_dir` is where the static built files are stored. `fastn build` command creates this /// folder and stores its output here. @@ -148,23 +385,6 @@ impl Config { self.remote_history_dir().join(id_with_timestamp_extension) } - /// document_name_with_default("index.ftd") -> / - /// document_name_with_default("foo/index.ftd") -> /foo/ - /// document_name_with_default("foo/abc") -> /foo/abc/ - /// document_name_with_default("/foo/abc.ftd") -> /foo/abc/ - pub(crate) fn document_name_with_default(&self, document_path: &str) -> String { - let name = self - .doc_id() - .unwrap_or_else(|| document_path.to_string()) - .trim_matches('/') - .to_string(); - if name.is_empty() { - "/".to_string() - } else { - format!("/{}/", name) - } - } - /// history of a fastn package is stored in `.history` folder. /// /// Current design is wrong, we should move this helper to `fastn_core::Package` maybe. @@ -566,7 +786,7 @@ impl Config { } pub(crate) async fn get_file_and_package_by_cr_id( - &mut self, + &self, id: &str, cr_number: usize, ) -> fastn_core::Result { @@ -706,110 +926,6 @@ impl Config { Ok(package) } - // -/kameri-app.herokuapp.com/ - // .packages/kameri-app.heroku.com/index.ftd - #[tracing::instrument(skip_all)] - pub async fn get_file_and_package_by_id( - &mut self, - path: &str, - ) -> fastn_core::Result { - tracing::info!(path = path); - // This function will return file and package by given path - // path can be mounted(mount-point) with other dependencies - // - // Sanitize the mountpoint request. - // Get the package and sanitized path - let package1; - - // TODO: The shitty code written by me ever - let (path_with_package_name, document, path_params, extra_data) = - if !fastn_core::file::is_static(path)? { - let (path_with_package_name, sanitized_package, sanitized_path) = - match self.get_mountpoint_sanitized_path(&self.package, path) { - Some((new_path, package, remaining_path, _)) => { - // Update the sitemap of the package, if it does not contain the sitemap information - if package.name != self.package.name { - package1 = self.update_sitemap(package).await?; - (new_path, &package1, remaining_path) - } else { - (new_path, package, remaining_path) - } - } - None => (path.to_string(), &self.package, path.to_string()), - }; - - // Getting `document` with dynamic parameters, if exists - // It will first resolve in sitemap - // Then it will resolve in the dynamic urls - let (document, path_params, extra_data) = - fastn_core::sitemap::resolve(sanitized_package, &sanitized_path)?; - - // document with package-name prefix - let document = document.map(|doc| { - format!( - "-/{}/{}", - sanitized_package.name.trim_matches('/'), - doc.trim_matches('/') - ) - }); - (path_with_package_name, document, path_params, extra_data) - } else { - (path.to_string(), None, vec![], Default::default()) - }; - - let path = path_with_package_name.as_str(); - - if let Some(id) = document { - let file_name = self.get_file_path_and_resolve(id.as_str()).await?; - let package = self.find_package_by_id(id.as_str()).await?.1; - let file = fastn_core::get_file( - package.name.to_string(), - &self.root.join(file_name), - &self.get_root_for_package(&package), - ) - .await?; - self.current_document = Some(path.to_string()); - self.named_parameters = path_params; - self.extra_data = extra_data; - Ok(file) - } else { - // -/fifthtry.github.io/todos/add-todo/ - // -/fifthtry.github.io/doc-site/add-todo/ - let file_name = self.get_file_path_and_resolve(path).await?; - // .packages/todos/add-todo.ftd - // .packages/fifthtry.github.io/doc-site/add-todo.ftd - - let package = self.find_package_by_id(path).await?.1; - let mut file = fastn_core::get_file( - package.name.to_string(), - &self.root.join(file_name.trim_start_matches('/')), - &self.get_root_for_package(&package), - ) - .await?; - - if path.contains("-/") { - let url = path.trim_end_matches("/index.html").trim_matches('/'); - let extension = if matches!(file, fastn_core::File::Markdown(_)) { - "/index.md".to_string() - } else if matches!(file, fastn_core::File::Ftd(_)) { - "/index.ftd".to_string() - } else { - "".to_string() - }; - file.set_id(format!("{}{}", url, extension).as_str()); - } - self.current_document = Some(file.get_id().to_string()); - Ok(file) - } - } - - pub fn doc_id(&self) -> Option { - self.current_document - .clone() - .map(|v| fastn_core::utils::id_to_path(v.as_str())) - .map(|v| v.trim().replace(std::path::MAIN_SEPARATOR, "/")) - } - pub async fn get_file_path(&self, id: &str) -> fastn_core::Result { let (package_name, package) = self.find_package_by_id(id).await?; let mut id = id.to_string(); @@ -1251,7 +1367,6 @@ impl Config { pub async fn read( root: Option, resolve_sitemap: bool, - req: Option<&fastn_core::http::Request>, ) -> fastn_core::Result { let (root, original_directory) = match root { Some(r) => { @@ -1280,19 +1395,13 @@ impl Config { packages_root: root.clone().join(".packages"), root, original_directory, - current_document: None, all_packages: Default::default(), - downloaded_assets: Default::default(), - extra_data: Default::default(), global_ids: Default::default(), - request: req.map(ToOwned::to_owned), - named_parameters: vec![], ftd_edition: FTDEdition::default(), ftd_external_js: Default::default(), ftd_inline_js: Default::default(), ftd_external_css: Default::default(), ftd_inline_css: Default::default(), - dependencies_during_render: Default::default(), }; // Update global_ids map from the current package files @@ -1351,11 +1460,6 @@ impl Config { Ok(config) } - pub fn set_request(mut self, req: fastn_core::http::Request) -> Self { - self.request = Some(req); - self - } - pub(crate) async fn resolve_package( &self, package: &fastn_core::Package, @@ -1418,77 +1522,4 @@ impl Config { (value - (number_of_crs_to_reserve as i32))..value, )) } - - #[tracing::instrument(skip(req, self))] - pub(crate) async fn can_read( - &self, - req: &fastn_core::http::Request, - document_path: &str, - with_confidential: bool, // can read should use confidential property or not - ) -> fastn_core::Result { - // Function Docs - // If user can read the document based on readers, user will have read access to page - // If user cannot read the document based on readers, and if confidential is false so user - // can access the page, and if confidential is true user will not be able to access the - // document - - // can_read: true, confidential: true => true (can access) - // can_read: true, confidential: false => true (can access) - // can_read: false, confidential: true => false (cannot access) - // can_read: false, confidential: false => true (can access) - - use itertools::Itertools; - let document_name = self.document_name_with_default(document_path); - if let Some(sitemap) = &self.package.sitemap { - // TODO: This can be buggy in case of: if groups are used directly in sitemap are foreign groups - let (document_readers, confidential) = - sitemap.readers(document_name.as_str(), &self.package.groups); - - // TODO: Need to check the confidential logic, if readers are not defined in the sitemap - if document_readers.is_empty() { - return Ok(true); - } - let access_identities = - fastn_core::user_group::access_identities(self, req, &document_name, true).await?; - - let belongs_to = fastn_core::user_group::belongs_to( - self, - document_readers.as_slice(), - access_identities.iter().collect_vec().as_slice(), - )?; - - if with_confidential { - if belongs_to { - return Ok(true); - } - return Ok(!confidential); - } - return Ok(belongs_to); - } - Ok(true) - } - - #[tracing::instrument(skip(req, self))] - pub(crate) async fn can_write( - &self, - req: &fastn_core::http::Request, - document_path: &str, - ) -> fastn_core::Result { - use itertools::Itertools; - let document_name = self.document_name_with_default(document_path); - if let Some(sitemap) = &self.package.sitemap { - // TODO: This can be buggy in case of: if groups are used directly in sitemap are foreign groups - let document_writers = sitemap.writers(document_name.as_str(), &self.package.groups); - let access_identities = - fastn_core::user_group::access_identities(self, req, &document_name, false).await?; - - return fastn_core::user_group::belongs_to( - self, - document_writers.as_slice(), - access_identities.iter().collect_vec().as_slice(), - ); - } - - Ok(false) - } } diff --git a/fastn-core/src/controller.rs b/fastn-core/src/controller.rs index a39ed203f9..64bdedd650 100644 --- a/fastn-core/src/controller.rs +++ b/fastn-core/src/controller.rs @@ -78,7 +78,7 @@ pub async fn resolve_dependencies( ); // Resolve dependencies by reading the FASTN.ftd using config.read() // Assuming package_name and repo name are identical - fastn_core::Config::read(None, false, None).await?; + fastn_core::Config::read(None, false).await?; } else { return Err(fastn_core::Error::APIResponseError(format!( "Package {} Cloning failed: {}", diff --git a/fastn-core/src/doc.rs b/fastn-core/src/doc.rs index 2d3b988118..dd6fd4853a 100644 --- a/fastn-core/src/doc.rs +++ b/fastn-core/src/doc.rs @@ -26,10 +26,10 @@ fn cached_parse( } #[tracing::instrument(skip_all)] -pub async fn interpret_helper<'a>( +pub async fn interpret_helper( name: &str, source: &str, - lib: &'a mut fastn_core::Library2022, + lib: &mut fastn_core::Library2022, base_url: &str, download_assets: bool, line_number: usize, @@ -56,7 +56,7 @@ pub async fn interpret_helper<'a>( let (source, path, foreign_variable, foreign_function, ignore_line_numbers) = resolve_import_2022(lib, &mut st, module.as_str(), caller_module.as_str()) .await?; - lib.config.dependencies_during_render.push(path); + lib.dependencies_during_render.push(path); let doc = cached_parse(module.as_str(), source.as_str(), ignore_line_numbers)?; s = st.continue_after_import( module.as_str(), @@ -112,8 +112,8 @@ pub async fn interpret_helper<'a>( Ok(document) } -pub async fn resolve_import<'a>( - lib: &'a mut fastn_core::Library2, +pub async fn resolve_import( + lib: &mut fastn_core::Library2, state: &mut ftd::ftd2021::InterpreterState, module: &str, ) -> ftd::ftd2021::p1::Result { @@ -138,6 +138,7 @@ pub async fn resolve_import<'a>( if module.starts_with(alias) { lib.push_package_under_process(package).await?; font_ftd = lib + .config .config .all_packages .borrow() @@ -158,8 +159,8 @@ pub async fn resolve_import<'a>( } // source, foreign_variable, foreign_function -pub async fn resolve_import_2022<'a>( - lib: &'a mut fastn_core::Library2022, +pub async fn resolve_import_2022( + lib: &mut fastn_core::Library2022, _state: &mut ftd::interpreter::InterpreterState, module: &str, caller_module: &str, @@ -202,6 +203,7 @@ pub async fn resolve_import_2022<'a>( "is-reader".to_string(), "sql".to_string(), "package-query".to_string(), + "tutor".to_string(), "pg".to_string(), "package-tree".to_string(), "fetch-file".to_string(), @@ -254,6 +256,7 @@ pub async fn resolve_import_2022<'a>( "http".to_string(), "sql".to_string(), "package-query".to_string(), + "tutor".to_string(), "pg".to_string(), "toc".to_string(), "include".to_string(), @@ -350,7 +353,7 @@ pub async fn resolve_foreign_variable2022( module: &str, package: &fastn_core::Package, files: &str, - lib: &mut fastn_core::Library2022, + lib: &mut fastn_core::RequestConfig, base_url: &str, download_assets: bool, // true: in case of `fastn build` ) -> ftd::ftd2021::p1::Result { @@ -390,7 +393,6 @@ pub async fn resolve_foreign_variable2022( let light_path = format!("{}.{}", file.replace('.', "/"), ext); if download_assets && !lib - .config .downloaded_assets .contains_key(&format!("{}/{}", package.name, light_path)) { @@ -415,7 +417,7 @@ pub async fn resolve_foreign_variable2022( doc_id: lib.document_id.to_string(), line_number: 0, })?; - lib.config.downloaded_assets.insert( + lib.downloaded_assets.insert( format!("{}/{}", package.name, light_path), light_mode.to_string(), ); @@ -448,7 +450,6 @@ pub async fn resolve_foreign_variable2022( if download_assets && !file.ends_with("-dark") { let start = std::time::Instant::now(); if let Some(dark) = lib - .config .downloaded_assets .get(&format!("{}/{}", package.name, dark_path)) { @@ -478,7 +479,7 @@ pub async fn resolve_foreign_variable2022( } else { dark_mode = light_mode.clone(); } - lib.config.downloaded_assets.insert( + lib.downloaded_assets.insert( format!("{}/{}", package.name, dark_path), dark_mode.to_string(), ); @@ -543,7 +544,6 @@ async fn download( ) -> ftd::ftd2021::p1::Result<()> { if download_assets && !lib - .config .downloaded_assets .contains_key(&format!("{}/{}", package.name, path)) { @@ -568,7 +568,7 @@ async fn download( doc_id: lib.document_id.to_string(), line_number: 0, })?; - lib.config.downloaded_assets.insert( + lib.downloaded_assets.insert( format!("{}/{}", package.name, path), format!("-/{}/{}", package.name, path), ); @@ -680,7 +680,11 @@ pub async fn resolve_foreign_variable2( })?; print!("Processing {}/{} ... ", package.name.as_str(), light_path); fastn_core::utils::write( - &lib.config.build_dir().join("-").join(package.name.as_str()), + &lib.config + .config + .build_dir() + .join("-") + .join(package.name.as_str()), light_path.as_str(), light.as_slice(), ) @@ -735,7 +739,11 @@ pub async fn resolve_foreign_variable2( { print!("Processing {}/{} ... ", package.name.as_str(), dark_path); fastn_core::utils::write( - &lib.config.build_dir().join("-").join(package.name.as_str()), + &lib.config + .config + .build_dir() + .join("-") + .join(package.name.as_str()), dark_path.as_str(), dark.as_slice(), ) diff --git a/fastn-core/src/error.rs b/fastn-core/src/error.rs index 8fd75af171..ffe119e823 100644 --- a/fastn-core/src/error.rs +++ b/fastn-core/src/error.rs @@ -90,6 +90,12 @@ pub enum Error { TokioMPSCError2(#[from] tokio::sync::mpsc::error::SendError), } +impl From for Error { + fn from(_: std::convert::Infallible) -> Self { + unreachable!() + } +} + impl Error { pub fn generic + ToString>(error: T) -> Self { Self::GenericError(error.to_string()) diff --git a/fastn-core/src/http.rs b/fastn-core/src/http.rs index 6dbb9131c7..40af4beb04 100644 --- a/fastn-core/src/http.rs +++ b/fastn-core/src/http.rs @@ -73,7 +73,7 @@ pub fn ok_with_content_type( .body(data) } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct Request { method: String, uri: String, diff --git a/fastn-core/src/lib.rs b/fastn-core/src/lib.rs index 696d5f9baf..91360f3e27 100644 --- a/fastn-core/src/lib.rs +++ b/fastn-core/src/lib.rs @@ -14,7 +14,7 @@ mod file; mod font; mod history; mod package; -pub(crate) mod tutor; +pub mod tutor; pub(crate) mod watcher; #[macro_use] mod http; @@ -44,7 +44,7 @@ pub use commands::{ start_tracking::start_tracking, status::status, sync2::sync2, translation_status::translation_status, update::update, }; -pub use config::{Config, FTDEdition}; +pub use config::{Config, FTDEdition, RequestConfig}; pub use error::Error; pub use file::File; pub(crate) use file::{get_file, paths_to_files, Document, Static}; @@ -57,7 +57,7 @@ pub(crate) use package::Package; pub(crate) use snapshot::Snapshot; pub(crate) use tracker::Track; pub(crate) use translation::{TranslatedDocument, TranslationData}; -pub(crate) use utils::{copy_dir_all, time, timestamp_nanosecond}; +pub(crate) use utils::{copy_dir_all, timestamp_nanosecond}; pub(crate) use version::Version; pub use {doc::resolve_foreign_variable2, doc::resolve_import}; diff --git a/fastn-core/src/library/document.rs b/fastn-core/src/library/document.rs index 9679c9d811..a74c07dbbc 100644 --- a/fastn-core/src/library/document.rs +++ b/fastn-core/src/library/document.rs @@ -44,13 +44,13 @@ pub fn convert_to_document_id(doc_name: &str) -> String { } pub fn document_full_id( - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, doc: &ftd::ftd2021::p2::TDoc, ) -> ftd::ftd2021::p1::Result { - let full_document_id = config.doc_id().unwrap_or_else(|| { + let full_document_id = req_config.doc_id().unwrap_or_else(|| { doc.name .to_string() - .replace(config.package.name.as_str(), "") + .replace(req_config.config.package.name.as_str(), "") }); if full_document_id.trim_matches('/').is_empty() { @@ -60,93 +60,93 @@ pub fn document_full_id( Ok(format!("/{}/", full_document_id.trim_matches('/'))) } -#[allow(dead_code)] -pub mod processor { - pub fn document_id( - _section: &ftd::ftd2021::p1::Section, - doc: &ftd::ftd2021::p2::TDoc, - config: &fastn_core::Config, - ) -> ftd::ftd2021::p1::Result { - let doc_id = config.doc_id().unwrap_or_else(|| { - doc.name - .to_string() - .replace(config.package.name.as_str(), "") - }); - - let document_id = doc_id - .split_once("/-/") - .map(|x| x.0) - .unwrap_or_else(|| &doc_id) - .trim_matches('/'); - - Ok(ftd::Value::String { - text: format!("/{}/", document_id), - source: ftd::TextSource::Default, - }) - } - pub fn document_full_id( - _section: &ftd::ftd2021::p1::Section, - doc: &ftd::ftd2021::p2::TDoc, - config: &fastn_core::Config, - ) -> ftd::ftd2021::p1::Result { - Ok(ftd::Value::String { - text: super::document_full_id(config, doc)?, - source: ftd::TextSource::Default, - }) - } - - pub async fn document_name<'a>( - section: &ftd::ftd2021::p1::Section, - doc: &ftd::ftd2021::p2::TDoc<'a>, - config: &fastn_core::Config, - ) -> ftd::ftd2021::p1::Result { - let doc_id = config.doc_id().unwrap_or_else(|| { - doc.name - .to_string() - .replace(config.package.name.as_str(), "") - }); - - let file_path = config.get_file_path(&doc_id).await.map_err(|e| { - ftd::ftd2021::p1::Error::ParseError { - message: e.to_string(), - doc_id: doc.name.to_string(), - line_number: section.line_number, - } - })?; - - Ok(ftd::Value::String { - text: file_path.trim().to_string(), - source: ftd::TextSource::Default, - }) - } - - pub fn document_suffix( - _section: &ftd::ftd2021::p1::Section, - doc: &ftd::ftd2021::p2::TDoc, - config: &fastn_core::Config, - ) -> ftd::ftd2021::p1::Result { - let doc_id = config.doc_id().unwrap_or_else(|| { - doc.name - .to_string() - .replace(config.package.name.as_str(), "") - }); - - let value = doc_id - .split_once("/-/") - .map(|(_, y)| y.trim().to_string()) - .map(|suffix| ftd::Value::String { - text: suffix, - source: ftd::TextSource::Default, - }); - - Ok(ftd::Value::Optional { - data: Box::new(value), - kind: ftd::ftd2021::p2::Kind::String { - caption: false, - body: false, - default: None, - is_reference: false, - }, - }) - } -} +// #[allow(dead_code)] +// pub mod processor { +// pub fn document_id( +// _section: &ftd::ftd2021::p1::Section, +// doc: &ftd::ftd2021::p2::TDoc, +// config: &fastn_core::Config, +// ) -> ftd::ftd2021::p1::Result { +// let doc_id = config.doc_id().unwrap_or_else(|| { +// doc.name +// .to_string() +// .replace(config.package.name.as_str(), "") +// }); +// +// let document_id = doc_id +// .split_once("/-/") +// .map(|x| x.0) +// .unwrap_or_else(|| &doc_id) +// .trim_matches('/'); +// +// Ok(ftd::Value::String { +// text: format!("/{}/", document_id), +// source: ftd::TextSource::Default, +// }) +// } +// pub fn document_full_id( +// _section: &ftd::ftd2021::p1::Section, +// doc: &ftd::ftd2021::p2::TDoc, +// config: &fastn_core::Config, +// ) -> ftd::ftd2021::p1::Result { +// Ok(ftd::Value::String { +// text: super::document_full_id(config, doc)?, +// source: ftd::TextSource::Default, +// }) +// } +// +// pub async fn document_name<'a>( +// section: &ftd::ftd2021::p1::Section, +// doc: &ftd::ftd2021::p2::TDoc<'a>, +// config: &fastn_core::Config, +// ) -> ftd::ftd2021::p1::Result { +// let doc_id = config.doc_id().unwrap_or_else(|| { +// doc.name +// .to_string() +// .replace(config.package.name.as_str(), "") +// }); +// +// let file_path = config.get_file_path(&doc_id).await.map_err(|e| { +// ftd::ftd2021::p1::Error::ParseError { +// message: e.to_string(), +// doc_id: doc.name.to_string(), +// line_number: section.line_number, +// } +// })?; +// +// Ok(ftd::Value::String { +// text: file_path.trim().to_string(), +// source: ftd::TextSource::Default, +// }) +// } +// +// pub fn document_suffix( +// _section: &ftd::ftd2021::p1::Section, +// doc: &ftd::ftd2021::p2::TDoc, +// req_config: &fastn_core::RequestConfig, +// ) -> ftd::ftd2021::p1::Result { +// let doc_id = req_config.config.doc_id().unwrap_or_else(|| { +// doc.name +// .to_string() +// .replace(config.package.name.as_str(), "") +// }); +// +// let value = doc_id +// .split_once("/-/") +// .map(|(_, y)| y.trim().to_string()) +// .map(|suffix| ftd::Value::String { +// text: suffix, +// source: ftd::TextSource::Default, +// }); +// +// Ok(ftd::Value::Optional { +// data: Box::new(value), +// kind: ftd::ftd2021::p2::Kind::String { +// caption: false, +// body: false, +// default: None, +// is_reference: false, +// }, +// }) +// } +// } diff --git a/fastn-core/src/library/fastn_dot_ftd.rs b/fastn-core/src/library/fastn_dot_ftd.rs index 716674b3fa..1085ec864b 100644 --- a/fastn-core/src/library/fastn_dot_ftd.rs +++ b/fastn-core/src/library/fastn_dot_ftd.rs @@ -1,14 +1,14 @@ use crate::utils::HasElements; async fn i18n_data(lib: &fastn_core::Library) -> String { - let lang = match lib.config.package.language { + let lang = match lib.config.config.package.language { Some(ref lang) => { realm_lang::Language::from_2_letter_code(lang).unwrap_or(realm_lang::Language::English) } None => realm_lang::Language::English, }; - let primary_lang = match lib.config.package.translation_of.as_ref() { + let primary_lang = match lib.config.config.package.translation_of.as_ref() { Some(ref package) => match package.language { Some(ref lang) => realm_lang::Language::from_2_letter_code(lang) .unwrap_or(realm_lang::Language::English), @@ -19,7 +19,7 @@ async fn i18n_data(lib: &fastn_core::Library) -> String { let current_document_last_modified_on = fastn_core::utils::get_current_document_last_modified_on( - &lib.config, + &lib.config.config, lib.document_id.as_str(), ) .await; @@ -261,14 +261,14 @@ pub(crate) async fn get2022_(lib: &fastn_core::Library) -> String { capital_fastn = capital_fastn(lib), build_info = construct_fastn_cli_variables(lib), document_id = lib.document_id, - title = lib.config.package.name, - package_name = lib.config.package.name, - home_url = format!("https://{}", lib.config.package.name), + title = lib.config.config.package.name, + package_name = lib.config.config.package.name, + home_url = format!("https://{}", lib.config.config.package.name), ); - if let Ok(number_of_documents) = - futures::executor::block_on(fastn_core::utils::get_number_of_documents(&lib.config)) - { + if let Ok(number_of_documents) = futures::executor::block_on( + fastn_core::utils::get_number_of_documents(&lib.config.config), + ) { fastn_base = format!( indoc::indoc! {" {fastn_base} @@ -323,12 +323,12 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { i18n_data = i18n_data(lib).await, build_info = construct_fastn_cli_variables(lib), document_id = lib.document_id, - title = lib.config.package.name, - package_name = lib.config.package.name, - home_url = format!("https://{}", lib.config.package.name), + title = lib.config.config.package.name, + package_name = lib.config.config.package.name, + home_url = format!("https://{}", lib.config.config.package.name), ); - if lib.config.package.translation_of.is_some() { + if lib.config.config.package.translation_of.is_some() { fastn_base = format!( indoc::indoc! {" {fastn_base} @@ -339,7 +339,7 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { ); } - if lib.config.package.translations.has_elements() { + if lib.config.config.package.translations.has_elements() { fastn_base = format!( indoc::indoc! {" {fastn_base} @@ -350,7 +350,7 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { ); } - if let Some(ref zip) = lib.config.package.zip { + if let Some(ref zip) = lib.config.config.package.zip { fastn_base = format!( indoc::indoc! {" {fastn_base} @@ -390,7 +390,8 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { ); } - if lib.config.package.translation_of.is_some() || lib.config.package.translations.has_elements() + if lib.config.config.package.translation_of.is_some() + || lib.config.config.package.translations.has_elements() { fastn_base = format!( indoc::indoc! {" @@ -400,13 +401,13 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { "}, fastn_base = fastn_base, - package_name = lib.config.package.name, + package_name = lib.config.config.package.name, ); } - if let Ok(number_of_documents) = - futures::executor::block_on(fastn_core::utils::get_number_of_documents(&lib.config)) - { + if let Ok(number_of_documents) = futures::executor::block_on( + fastn_core::utils::get_number_of_documents(&lib.config.config), + ) { fastn_base = format!( indoc::indoc! {" {fastn_base} @@ -418,9 +419,9 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { ); } - if let Some(last_modified_on) = - futures::executor::block_on(fastn_core::utils::get_last_modified_on(&lib.config.root)) - { + if let Some(last_modified_on) = futures::executor::block_on( + fastn_core::utils::get_last_modified_on(&lib.config.config.root), + ) { fastn_base = format!( indoc::indoc! {" {fastn_base} @@ -434,7 +435,7 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { if let Some(last_modified_on) = futures::executor::block_on(fastn_core::utils::get_current_document_last_modified_on( - &lib.config, + &lib.config.config, lib.document_id.as_str(), )) { @@ -449,7 +450,7 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { ); } - if let Some(ref language) = lib.config.package.language { + if let Some(ref language) = lib.config.config.package.language { fastn_base = format!( indoc::indoc! {" {fastn_base} @@ -519,7 +520,7 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { ); } - if let Ok(original_path) = lib.config.original_path() { + if let Ok(original_path) = lib.config.config.original_path() { let base_url = lib .base_url .as_str() @@ -537,7 +538,7 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { if let Ok(translation_status) = fastn_core::commands::translation_status::get_translation_status( &original_snapshots, - &lib.config.root, + &lib.config.config.root, ) { let mut never_marked_files = "".to_string(); @@ -681,9 +682,9 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { } } - if lib.config.package.translations.has_elements() { + if lib.config.config.package.translations.has_elements() { let mut translation_status_list = "".to_string(); - for translation in lib.config.package.translations.iter() { + for translation in lib.config.config.package.translations.iter() { if let Some(ref status) = translation.translation_status_summary { if let Some(ref language) = translation.language { let url = format!("https://{}/-/translation-status/", translation.name); @@ -742,7 +743,7 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { } let other_language_packages = - if let Some(translation_of) = lib.config.package.translation_of.as_ref() { + if let Some(translation_of) = lib.config.config.package.translation_of.as_ref() { let mut other_language_packages = translation_of .translations .iter() @@ -751,6 +752,7 @@ pub(crate) async fn get(lib: &fastn_core::Library) -> String { other_language_packages } else { lib.config + .config .package .translations .iter() @@ -824,7 +826,7 @@ pub(crate) async fn get2(lib: &fastn_core::Library2) -> String { pub(crate) async fn get2022(lib: &fastn_core::Library2022) -> String { let lib = fastn_core::Library { - config: lib.config.clone(), + config: lib.clone(), markdown: lib.markdown.clone(), document_id: lib.document_id.clone(), translated_data: lib.translated_data.clone(), @@ -839,14 +841,14 @@ fn capital_fastn(lib: &fastn_core::Library) -> String { indoc::indoc! {" -- package-data package: {package_name} "}, - package_name = lib.config.package.name, + package_name = lib.config.config.package.name, ); - if let Some(ref zip) = lib.config.package.zip { + if let Some(ref zip) = lib.config.config.package.zip { s.push_str(format!("zip: {}", zip).as_str()); } - if let Some(ref favicon) = lib.config.package.favicon { + if let Some(ref favicon) = lib.config.config.package.favicon { s.push_str(format!("\nfavicon: {}", favicon).as_str()); } diff --git a/fastn-core/src/library/mod.rs b/fastn-core/src/library/mod.rs index 42df90e8fa..2e172386e7 100644 --- a/fastn-core/src/library/mod.rs +++ b/fastn-core/src/library/mod.rs @@ -7,7 +7,7 @@ pub use fastn_core::Library2022; #[derive(Debug)] pub struct Library { - pub config: fastn_core::Config, + pub config: fastn_core::RequestConfig, /// If the current module being parsed is a markdown file, `.markdown` contains the name and /// content of that file pub markdown: Option<(String, String)>, @@ -58,7 +58,7 @@ impl Library { for (alias, package) in package.to_owned().aliases() { if name.starts_with(alias) { - let package = lib.config.resolve_package(package).await.ok()?; + let package = lib.config.config.resolve_package(package).await.ok()?; if let Some(r) = get_data_from_package( name.replacen(alias, &package.name, 1).as_str(), &package, @@ -122,8 +122,8 @@ impl Library { package: &fastn_core::Package, lib: &Library, ) -> Option { - let path = lib.config.get_root_for_package(package); - fastn_core::Config::download_required_file(&lib.config.root, name, package) + let path = lib.config.config.get_root_for_package(package); + fastn_core::Config::download_required_file(&lib.config.config.root, name, package) .await .ok()?; // Explicit check for the current package. @@ -148,7 +148,7 @@ impl Library { #[derive(Debug)] pub struct Library2 { - pub config: fastn_core::Config, + pub config: fastn_core::RequestConfig, /// If the current module being parsed is a markdown file, `.markdown` contains the name and /// content of that file pub markdown: Option<(String, String)>, @@ -165,6 +165,7 @@ impl Library2 { ) -> ftd::ftd2021::p1::Result<()> { self.packages_under_process.push(package.name.to_string()); if self + .config .config .all_packages .borrow() @@ -173,15 +174,19 @@ impl Library2 { return Ok(()); } - let package = self.config.resolve_package(package).await.map_err(|_| { - ftd::ftd2021::p1::Error::ParseError { + let package = self + .config + .config + .resolve_package(package) + .await + .map_err(|_| ftd::ftd2021::p1::Error::ParseError { message: format!("Cannot resolve the package: {}", package.name), doc_id: self.document_id.to_string(), line_number: 0, - } - })?; + })?; self.config + .config .all_packages .borrow_mut() .insert(package.name.to_string(), package); @@ -198,6 +203,7 @@ impl Library2 { })?; self.config + .config .all_packages .borrow() .get(current_package_name) @@ -288,7 +294,7 @@ impl Library2 { lib: &mut Library2, ) -> Option { lib.push_package_under_process(package).await.ok()?; - let packages = lib.config.all_packages.borrow(); + let packages = lib.config.config.all_packages.borrow(); let package = packages.get(package.name.as_str()).unwrap_or(package); // Explicit check for the current package. if !name.starts_with(package.name.as_str()) { @@ -296,7 +302,11 @@ impl Library2 { } let new_name = name.replacen(package.name.as_str(), "", 1); let (file_path, data) = package - .resolve_by_id(new_name.as_str(), None, lib.config.package.name.as_str()) + .resolve_by_id( + new_name.as_str(), + None, + lib.config.config.package.name.as_str(), + ) .await .ok()?; if !file_path.ends_with(".ftd") { diff --git a/fastn-core/src/library2022/mod.rs b/fastn-core/src/library2022/mod.rs index f1647820a2..4107921f2f 100644 --- a/fastn-core/src/library2022/mod.rs +++ b/fastn-core/src/library2022/mod.rs @@ -13,17 +13,7 @@ impl KeyValueData { } } -#[derive(Debug)] -pub struct Library2022 { - pub config: fastn_core::Config, - /// If the current module being parsed is a markdown file, `.markdown` contains the name and - /// content of that file - pub markdown: Option<(String, String)>, - pub document_id: String, - pub translated_data: fastn_core::TranslationData, - pub base_url: String, - pub module_package_map: std::collections::BTreeMap, -} +pub type Library2022 = fastn_core::RequestConfig; impl Library2022 { pub async fn get_with_result( @@ -228,65 +218,49 @@ impl Library2022 { "figma-typo-token" => { processor::figma_typography_tokens::process_typography_tokens(value, kind, doc) } - "figma-cs-token" => { - processor::figma_tokens::process_figma_tokens(value, kind, doc, &self.config) - } + "figma-cs-token" => processor::figma_tokens::process_figma_tokens(value, kind, doc), "figma-cs-token-old" => { - processor::figma_tokens::process_figma_tokens_old(value, kind, doc, &self.config) + processor::figma_tokens::process_figma_tokens_old(value, kind, doc) } - "http" => processor::http::process(value, kind, doc, &self.config).await, - "tutor" => fastn_core::tutor::process(value, kind, doc).await, - "toc" => processor::toc::process(value, kind, doc, &self.config), - "get-data" => processor::get_data::process(value, kind, doc, &self.config), - "sitemap" => processor::sitemap::process(value, kind, doc, &self.config), - "full-sitemap" => { - processor::sitemap::full_sitemap_process(value, kind, doc, &self.config) - } - "request-data" => processor::request_data::process(value, kind, doc, &self.config), + "http" => processor::http::process(value, kind, doc, self).await, + "tutor-data" => fastn_core::tutor::process(value, kind, doc).await, + "toc" => processor::toc::process(value, kind, doc), + "get-data" => processor::get_data::process(value, kind, doc, self), + "sitemap" => processor::sitemap::process(value, kind, doc, self), + "full-sitemap" => processor::sitemap::full_sitemap_process(value, kind, doc, self), + "request-data" => processor::request_data::process(value, kind, doc, self), "document-readers" => processor::document::process_readers( value, kind, doc, - &self.config, + self, self.document_id.as_str(), ), "document-writers" => processor::document::process_writers( value, kind, doc, - &self.config, + self, self.document_id.as_str(), ), - "user-groups" => processor::user_group::process(value, kind, doc, &self.config), - "user-group-by-id" => { - processor::user_group::process_by_id(value, kind, doc, &self.config) - } - "get-identities" => { - processor::user_group::get_identities(value, kind, doc, &self.config) - } - "document-id" => processor::document::document_id(value, kind, doc, &self.config), - "document-full-id" => { - processor::document::document_full_id(value, kind, doc, &self.config) - } - "document-suffix" => { - processor::document::document_suffix(value, kind, doc, &self.config) - } - "document-name" => { - processor::document::document_name(value, kind, doc, &self.config).await - } - "fetch-file" => { - processor::fetch_file::fetch_files(value, kind, doc, &self.config).await - } - "user-details" => processor::user_details::process(value, kind, doc, &self.config), - "fastn-apps" => processor::apps::process(value, kind, doc, &self.config), - "is-reader" => processor::user_group::is_reader(value, kind, doc, &self.config).await, - "sql" => processor::sql::process(value, kind, doc, &self.config).await, + "user-groups" => processor::user_group::process(value, kind, doc, self), + "user-group-by-id" => processor::user_group::process_by_id(value, kind, doc, self), + "get-identities" => processor::user_group::get_identities(value, kind, doc, self), + "document-id" => processor::document::document_id(value, kind, doc, self), + "document-full-id" => processor::document::document_full_id(value, kind, doc, self), + "document-suffix" => processor::document::document_suffix(value, kind, doc, self), + "document-name" => processor::document::document_name(value, kind, doc, self).await, + "fetch-file" => processor::fetch_file::fetch_files(value, kind, doc, self).await, + "user-details" => processor::user_details::process(value, kind, doc, self), + "fastn-apps" => processor::apps::process(value, kind, doc, self), + "is-reader" => processor::user_group::is_reader(value, kind, doc, self).await, + "sql" => processor::sql::process(value, kind, doc, self).await, "package-query" => { processor::sqlite::process( value, kind, doc, - &self.config, + self, &fastn_core::library2022::processor::sql::get_db_config()?, ) .await @@ -295,16 +269,7 @@ impl Library2022 { "package-tree" => { processor::package_tree::process(value, kind, doc, &self.config).await } - "query" => { - processor::query::process( - value, - kind, - doc, - &mut self.config, - self.document_id.as_str(), - ) - .await - } + "query" => processor::query::process(value, kind, doc, self).await, t => Err(ftd::interpreter::Error::ParseError { doc_id: self.document_id.to_string(), line_number, diff --git a/fastn-core/src/library2022/processor/apps.rs b/fastn-core/src/library2022/processor/apps.rs index b56b9a2456..d82c52d877 100644 --- a/fastn-core/src/library2022/processor/apps.rs +++ b/fastn-core/src/library2022/processor/apps.rs @@ -2,7 +2,7 @@ pub fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { use itertools::Itertools; #[derive(Debug, serde::Serialize)] @@ -14,7 +14,8 @@ pub fn process( icon: Option, } - let apps = config + let apps = req_config + .config .package .apps .iter() diff --git a/fastn-core/src/library2022/processor/document.rs b/fastn-core/src/library2022/processor/document.rs index e28b83ae68..6ceb04c8c7 100644 --- a/fastn-core/src/library2022/processor/document.rs +++ b/fastn-core/src/library2022/processor/document.rs @@ -2,7 +2,7 @@ pub fn process_readers( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, document_id: &str, ) -> ftd::interpreter::Result { use itertools::Itertools; @@ -17,11 +17,11 @@ pub fn process_readers( .get_optional_string_by_key("document", doc.name, value.line_number())? .unwrap_or_else(|| document_id.to_string()); - let document_name = config.document_name_with_default(document.as_str()); + let document_name = req_config.document_name_with_default(document.as_str()); - let readers = match config.package.sitemap.as_ref() { + let readers = match req_config.config.package.sitemap.as_ref() { Some(s) => s - .readers(document_name.as_str(), &config.package.groups) + .readers(document_name.as_str(), &req_config.config.package.groups) .0 .into_iter() .map(|g| g.to_group_compat()) @@ -36,7 +36,7 @@ pub fn process_writers( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, document_id: &str, ) -> ftd::interpreter::Result { use itertools::Itertools; @@ -52,10 +52,10 @@ pub fn process_writers( .get_optional_string_by_key("document", doc.name, value.line_number())? .unwrap_or_else(|| document_id.to_string()); - let document_name = config.document_name_with_default(document.as_str()); - let writers = match config.package.sitemap.as_ref() { + let document_name = req_config.document_name_with_default(document.as_str()); + let writers = match req_config.config.package.sitemap.as_ref() { Some(s) => s - .writers(document_name.as_str(), &config.package.groups) + .writers(document_name.as_str(), &req_config.config.package.groups) .into_iter() .map(|g| g.to_group_compat()) .collect_vec(), @@ -69,12 +69,12 @@ pub fn document_id( _value: ftd::ast::VariableValue, _kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { - let doc_id = config.doc_id().unwrap_or_else(|| { + let doc_id = req_config.doc_id().unwrap_or_else(|| { doc.name .to_string() - .replace(config.package.name.as_str(), "") + .replace(req_config.config.package.name.as_str(), "") }); let document_id = doc_id @@ -98,10 +98,10 @@ pub fn document_full_id( _value: ftd::ast::VariableValue, _kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { Ok(ftd::interpreter::Value::String { - text: fastn_core::library2022::utils::document_full_id(config, doc)?, + text: fastn_core::library2022::utils::document_full_id(req_config, doc)?, }) } @@ -109,12 +109,12 @@ pub fn document_suffix( _value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { - let doc_id = config.doc_id().unwrap_or_else(|| { + let doc_id = req_config.doc_id().unwrap_or_else(|| { doc.name .to_string() - .replace(config.package.name.as_str(), "") + .replace(req_config.config.package.name.as_str(), "") }); let value = doc_id @@ -136,23 +136,23 @@ pub async fn document_name<'a>( value: ftd::ast::VariableValue, _kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc<'a>, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { - let doc_id = config.doc_id().unwrap_or_else(|| { + let doc_id = req_config.doc_id().unwrap_or_else(|| { doc.name .to_string() - .replace(config.package.name.as_str(), "") + .replace(req_config.config.package.name.as_str(), "") }); - let file_path = - config - .get_file_path(&doc_id) - .await - .map_err(|e| ftd::ftd2021::p1::Error::ParseError { - message: e.to_string(), - doc_id: doc.name.to_string(), - line_number: value.line_number(), - })?; + let file_path = req_config + .config + .get_file_path(&doc_id) + .await + .map_err(|e| ftd::ftd2021::p1::Error::ParseError { + message: e.to_string(), + doc_id: doc.name.to_string(), + line_number: value.line_number(), + })?; Ok(ftd::interpreter::Value::String { text: file_path.trim().to_string(), diff --git a/fastn-core/src/library2022/processor/fetch_file.rs b/fastn-core/src/library2022/processor/fetch_file.rs index 216511cbcc..74d8251104 100644 --- a/fastn-core/src/library2022/processor/fetch_file.rs +++ b/fastn-core/src/library2022/processor/fetch_file.rs @@ -2,7 +2,7 @@ pub async fn fetch_files( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc<'_>, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { if !kind.is_string() { return ftd::interpreter::utils::e2( @@ -24,7 +24,7 @@ pub async fn fetch_files( })?; Ok(ftd::interpreter::Value::String { - text: tokio::fs::read_to_string(config.root.join(path)) + text: tokio::fs::read_to_string(req_config.config.root.join(path)) .await .map_err(|v| ftd::interpreter::Error::ParseError { message: v.to_string(), diff --git a/fastn-core/src/library2022/processor/figma_tokens.rs b/fastn-core/src/library2022/processor/figma_tokens.rs index c1669874f9..1403396cc1 100644 --- a/fastn-core/src/library2022/processor/figma_tokens.rs +++ b/fastn-core/src/library2022/processor/figma_tokens.rs @@ -4,7 +4,6 @@ pub fn process_figma_tokens( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &mut ftd::interpreter::TDoc, - _config: &fastn_core::Config, ) -> ftd::interpreter::Result { let line_number = value.line_number(); let mut variable_name: Option = None; @@ -47,7 +46,6 @@ pub fn process_figma_tokens_old( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &mut ftd::interpreter::TDoc, - _config: &fastn_core::Config, ) -> ftd::interpreter::Result { let line_number = value.line_number(); let mut variable_name: Option = None; diff --git a/fastn-core/src/library2022/processor/get_data.rs b/fastn-core/src/library2022/processor/get_data.rs index 0e83251ba6..b00845230c 100644 --- a/fastn-core/src/library2022/processor/get_data.rs +++ b/fastn-core/src/library2022/processor/get_data.rs @@ -2,7 +2,7 @@ pub fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { let (section_name, headers, body, line_number) = match value.get_record(doc.name) { Ok(val) => ( @@ -30,7 +30,7 @@ pub fn process( }); } - if let Some(data) = config.extra_data.get(key.as_str()) { + if let Some(data) = req_config.extra_data.get(key.as_str()) { return match kind { ftd::interpreter::Kind::Integer => { let value2 = diff --git a/fastn-core/src/library2022/processor/http.rs b/fastn-core/src/library2022/processor/http.rs index 20237911fb..5e5c6fb245 100644 --- a/fastn-core/src/library2022/processor/http.rs +++ b/fastn-core/src/library2022/processor/http.rs @@ -2,7 +2,7 @@ pub async fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc<'_>, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { let (headers, line_number) = if let Ok(val) = value.get_record(doc.name) { (val.2.to_owned(), val.5.to_owned()) @@ -57,12 +57,14 @@ pub async fn process( } }; - let (_, mut url, conf) = fastn_core::config::utils::get_clean_url(config, url.as_str()) - .map_err(|e| ftd::interpreter::Error::ParseError { - message: format!("invalid url: {:?}", e), - doc_id: doc.name.to_string(), - line_number, - })?; + let (_, mut url, conf) = + fastn_core::config::utils::get_clean_url(&req_config.config, url.as_str()).map_err( + |e| ftd::interpreter::Error::ParseError { + message: format!("invalid url: {:?}", e), + doc_id: doc.name.to_string(), + line_number, + }, + )?; let mut body = vec![]; for header in headers.0 { @@ -104,7 +106,7 @@ pub async fn process( let response = if method.as_str().eq("post") { fastn_core::http::http_post_with_cookie( url.as_str(), - config.request.as_ref().and_then(|v| v.cookies_string()), + req_config.request.cookies_string(), &conf, dbg!(format!("{{{}}}", body.join(","))).as_str(), ) @@ -112,7 +114,7 @@ pub async fn process( } else { fastn_core::http::http_get_with_cookie( url.as_str(), - config.request.as_ref().and_then(|v| v.cookies_string()), + req_config.request.cookies_string(), &conf, ) .await diff --git a/fastn-core/src/library2022/processor/query.rs b/fastn-core/src/library2022/processor/query.rs index a3a07ef93a..e4b168b27d 100644 --- a/fastn-core/src/library2022/processor/query.rs +++ b/fastn-core/src/library2022/processor/query.rs @@ -2,8 +2,7 @@ pub async fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc<'_>, - config: &mut fastn_core::Config, - document_id: &str, + req_config: &mut fastn_core::RequestConfig, ) -> ftd::interpreter::Result { // TODO: document key should be optional @@ -14,25 +13,25 @@ pub async fn process( let path = headers .get_optional_string_by_key("file", doc.name, value.line_number())? - .unwrap_or_else(|| document_id.to_string()); + .unwrap_or_else(|| req_config.document_id.to_string()); let stage = headers .get_optional_string_by_key("stage", doc.name, value.line_number())? .unwrap_or_else(|| "ast".to_string()); - let file = config + let file = req_config .get_file_and_package_by_id(path.as_str()) .await .map_err(|e| ftd::interpreter::Error::ParseError { message: format!("Cannot get path: {} {:?}", path.as_str(), e), - doc_id: document_id.to_string(), + doc_id: req_config.document_id.to_string(), line_number: value.line_number(), })?; doc.from_json( &fastn_core::commands::query::get_ftd_json(&file, stage.as_str()).map_err(|e| { ftd::interpreter::Error::ParseError { message: format!("Cannot resolve json for path: {} {:?}", path.as_str(), e), - doc_id: document_id.to_string(), + doc_id: req_config.document_id.to_string(), line_number: value.line_number(), } })?, diff --git a/fastn-core/src/library2022/processor/request_data.rs b/fastn-core/src/library2022/processor/request_data.rs index b581f94a31..1df3663eef 100644 --- a/fastn-core/src/library2022/processor/request_data.rs +++ b/fastn-core/src/library2022/processor/request_data.rs @@ -2,27 +2,11 @@ pub fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { - let req = match config.request.as_ref() { - Some(v) => v, - None if kind.is_optional() => { - return Ok(ftd::interpreter::Value::Optional { - data: Box::new(None), - kind: kind.into_kind_data(), - }); - } - None => { - return ftd::interpreter::utils::e2( - "config does not contain http-request object", - doc.name, - value.line_number(), - ) - } - }; - let mut data = req.query().clone(); + let mut data = req_config.request.query().clone(); - for (name, param_value) in config.named_parameters.iter() { + for (name, param_value) in req_config.named_parameters.iter() { let json_value = param_value .to_serde_value() @@ -34,7 +18,7 @@ pub fn process( data.insert(name.to_string(), json_value); } - match req.body_as_json() { + match req_config.request.body_as_json() { Ok(Some(b)) => { data.extend(b); } @@ -49,7 +33,7 @@ pub fn process( } data.extend( - config + req_config .extra_data .iter() .map(|(k, v)| (k.to_string(), serde_json::Value::String(v.to_string()))), diff --git a/fastn-core/src/library2022/processor/sitemap.rs b/fastn-core/src/library2022/processor/sitemap.rs index f4edd4409e..60dc09bd6e 100644 --- a/fastn-core/src/library2022/processor/sitemap.rs +++ b/fastn-core/src/library2022/processor/sitemap.rs @@ -2,17 +2,17 @@ pub fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { - if let Some(ref sitemap) = config.package.sitemap { - let doc_id = config + if let Some(ref sitemap) = req_config.config.package.sitemap { + let doc_id = req_config .current_document .clone() .map(|v| fastn_core::utils::id_to_path(v.as_str())) .unwrap_or_else(|| { doc.name .to_string() - .replace(config.package.name.as_str(), "") + .replace(req_config.config.package.name.as_str(), "") }) .trim() .replace(std::path::MAIN_SEPARATOR, "/"); @@ -32,17 +32,17 @@ pub fn full_sitemap_process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { - if let Some(ref sitemap) = config.package.sitemap { - let doc_id = config + if let Some(ref sitemap) = req_config.config.package.sitemap { + let doc_id = req_config .current_document .clone() .map(|v| fastn_core::utils::id_to_path(v.as_str())) .unwrap_or_else(|| { doc.name .to_string() - .replace(config.package.name.as_str(), "") + .replace(req_config.config.package.name.as_str(), "") }) .trim() .replace(std::path::MAIN_SEPARATOR, "/"); diff --git a/fastn-core/src/library2022/processor/sql.rs b/fastn-core/src/library2022/processor/sql.rs index 3bdcd7fbcb..f1a3eca3bd 100644 --- a/fastn-core/src/library2022/processor/sql.rs +++ b/fastn-core/src/library2022/processor/sql.rs @@ -23,7 +23,7 @@ pub async fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc<'_>, - config: &fastn_core::Config, + config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { let db_config = fastn_core::library2022::processor::sql::get_db_config()?; let db_type = db_config.db_type.as_str(); diff --git a/fastn-core/src/library2022/processor/sqlite.rs b/fastn-core/src/library2022/processor/sqlite.rs index 7cd593d0a3..91a7e6e8fe 100644 --- a/fastn-core/src/library2022/processor/sqlite.rs +++ b/fastn-core/src/library2022/processor/sqlite.rs @@ -28,7 +28,7 @@ pub async fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc<'_>, - config: &fastn_core::Config, + config: &fastn_core::RequestConfig, db_config: &fastn_core::library2022::processor::sql::DatabaseConfig, ) -> ftd::interpreter::Result { let (headers, query) = get_p1_data("package-data", &value, doc.name)?; @@ -41,7 +41,7 @@ pub async fn process( // for now they wil be ordered // select * from users where - let db_path = config.root.join(&db_config.db_url); + let db_path = config.config.root.join(&db_config.db_url); let query_response = execute_query(&db_path, query.as_str(), doc, headers, value.line_number()).await; diff --git a/fastn-core/src/library2022/processor/toc.rs b/fastn-core/src/library2022/processor/toc.rs index ae2daad439..de794359b5 100644 --- a/fastn-core/src/library2022/processor/toc.rs +++ b/fastn-core/src/library2022/processor/toc.rs @@ -2,7 +2,6 @@ pub fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - _config: &fastn_core::Config, ) -> ftd::interpreter::Result { let (body, line_number) = if let Ok(val) = value.get_record(doc.name) { (val.3.to_owned(), val.5.to_owned()) diff --git a/fastn-core/src/library2022/processor/user_details.rs b/fastn-core/src/library2022/processor/user_details.rs index 4860cb91d5..58f12877ae 100644 --- a/fastn-core/src/library2022/processor/user_details.rs +++ b/fastn-core/src/library2022/processor/user_details.rs @@ -2,20 +2,17 @@ pub fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { let mut found_cookie = false; - let is_login = match &config.request { - Some(req) => { - for auth_provider in fastn_core::auth::AuthProviders::AUTH_ITER.iter() { - if req.cookie(auth_provider.as_str()).is_some() { - found_cookie = true; - break; - } + let is_login = { + for auth_provider in fastn_core::auth::AuthProviders::AUTH_ITER.iter() { + if req_config.request.cookie(auth_provider.as_str()).is_some() { + found_cookie = true; + break; } - found_cookie } - None => false, + found_cookie }; #[derive(Debug, serde::Serialize)] diff --git a/fastn-core/src/library2022/processor/user_group.rs b/fastn-core/src/library2022/processor/user_group.rs index 548cc850d0..9ba9b06d9f 100644 --- a/fastn-core/src/library2022/processor/user_group.rs +++ b/fastn-core/src/library2022/processor/user_group.rs @@ -2,10 +2,11 @@ pub fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { use itertools::Itertools; - let g = config + let g = req_config + .config .package .groups .values() @@ -19,7 +20,7 @@ pub fn process_by_id( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { let headers = match value.get_record(doc.name) { Ok(val) => val.2.to_owned(), @@ -37,7 +38,8 @@ pub fn process_by_id( } }; - let g = config + let g = req_config + .config .package .groups .get(group_id.as_str()) @@ -56,18 +58,20 @@ pub fn get_identities( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { use itertools::Itertools; - let doc_id = fastn_core::library2022::utils::document_full_id(config, doc)?; + let doc_id = fastn_core::library2022::utils::document_full_id(req_config, doc)?; - let identities = fastn_core::user_group::get_identities(config, doc_id.as_str(), true) - .map_err(|e| ftd::ftd2021::p1::Error::ParseError { - message: e.to_string(), - doc_id, - line_number: value.line_number(), - })?; + let identities = + fastn_core::user_group::get_identities(&req_config.config, doc_id.as_str(), true).map_err( + |e| ftd::ftd2021::p1::Error::ParseError { + message: e.to_string(), + doc_id, + line_number: value.line_number(), + }, + )?; Ok(ftd::interpreter::Value::List { data: identities @@ -93,17 +97,16 @@ pub async fn is_reader<'a>( value: ftd::ast::VariableValue, _kind: ftd::interpreter::Kind, doc: &ftd::interpreter::TDoc<'a>, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::interpreter::Result { - let doc_id = fastn_core::library2022::utils::document_full_id(config, doc)?; - let is_reader = config - .can_read(config.request.as_ref().unwrap(), &doc_id, false) - .await - .map_err(|e| ftd::ftd2021::p1::Error::ParseError { + let doc_id = fastn_core::library2022::utils::document_full_id(req_config, doc)?; + let is_reader = req_config.can_read(&doc_id, false).await.map_err(|e| { + ftd::ftd2021::p1::Error::ParseError { message: e.to_string(), doc_id, line_number: value.line_number(), - })?; + } + })?; Ok(ftd::interpreter::Value::Boolean { value: is_reader }) } diff --git a/fastn-core/src/library2022/utils.rs b/fastn-core/src/library2022/utils.rs index 2ee2ba5927..d91b49e0fe 100644 --- a/fastn-core/src/library2022/utils.rs +++ b/fastn-core/src/library2022/utils.rs @@ -1,11 +1,11 @@ pub fn document_full_id( - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, doc: &ftd::interpreter::TDoc, ) -> ftd::ftd2021::p1::Result { - let full_document_id = config.doc_id().unwrap_or_else(|| { + let full_document_id = req_config.doc_id().unwrap_or_else(|| { doc.name .to_string() - .replace(config.package.name.as_str(), "") + .replace(req_config.config.package.name.as_str(), "") }); if full_document_id.trim_matches('/').is_empty() { diff --git a/fastn-core/src/package/app.rs b/fastn-core/src/package/app.rs index 3759801b58..d349f08ae2 100644 --- a/fastn-core/src/package/app.rs +++ b/fastn-core/src/package/app.rs @@ -94,11 +94,12 @@ impl AppTemp { } // Takes the path /-/// or /mount-point// -pub async fn can_read(config: &fastn_core::Config, path: &str) -> fastn_core::Result { +pub async fn can_read(config: &fastn_core::RequestConfig, path: &str) -> fastn_core::Result { use itertools::Itertools; // first get the app - let readers_groups = if let Some((_, _, _, Some(app))) = - config.get_mountpoint_sanitized_path(&config.package, path) + let readers_groups = if let Some((_, _, _, Some(app))) = config + .config + .get_mountpoint_sanitized_path(&config.config.package, path) { app.readers.clone() } else { @@ -110,6 +111,7 @@ pub async fn can_read(config: &fastn_core::Config, path: &str) -> fastn_core::Re } let user_groups = config + .config .package .groups .iter() @@ -124,17 +126,15 @@ pub async fn can_read(config: &fastn_core::Config, path: &str) -> fastn_core::Re let mut app_identities = vec![]; for ug in user_groups.iter() { - app_identities.extend(ug.get_identities(config)?) + app_identities.extend(ug.get_identities(&config.config)?) } - let auth_identities = fastn_core::auth::get_auth_identities( - config.request.as_ref().unwrap().cookies(), - app_identities.as_slice(), - ) - .await?; + let auth_identities = + fastn_core::auth::get_auth_identities(config.request.cookies(), app_identities.as_slice()) + .await?; return fastn_core::user_group::belongs_to( - config, + &config.config, user_groups.as_slice(), auth_identities.iter().collect_vec().as_slice(), ); diff --git a/fastn-core/src/package/package_doc.rs b/fastn-core/src/package/package_doc.rs index f7786720db..184adc2203 100644 --- a/fastn-core/src/package/package_doc.rs +++ b/fastn-core/src/package/package_doc.rs @@ -364,14 +364,14 @@ impl From for fastn_core::http::Response { #[tracing::instrument(skip_all)] pub(crate) async fn read_ftd( - config: &mut fastn_core::Config, + config: &mut fastn_core::RequestConfig, main: &fastn_core::Document, base_url: &str, download_assets: bool, test: bool, ) -> fastn_core::Result { tracing::info!(document = main.id); - match config.ftd_edition { + match config.config.ftd_edition { fastn_core::FTDEdition::FTD2021 => { unimplemented!() } @@ -386,26 +386,25 @@ pub(crate) async fn read_ftd( #[tracing::instrument(name = "read_ftd_2022", skip_all)] pub(crate) async fn read_ftd_2022( - config: &mut fastn_core::Config, + config: &mut fastn_core::RequestConfig, main: &fastn_core::Document, base_url: &str, download_assets: bool, test: bool, ) -> fastn_core::Result { - let lib_config = config.clone(); - let mut all_packages = config.all_packages.borrow_mut(); - let current_package = all_packages + let font_style = config.config.get_font_style(); + let c = &config.config.clone(); + + let current_package = config + .config + .all_packages + .borrow() .get(main.package_name.as_str()) - .unwrap_or(&config.package); - - let mut lib = fastn_core::Library2022 { - config: lib_config, - markdown: None, - document_id: main.id.clone(), - translated_data: Default::default(), - base_url: base_url.to_string(), - module_package_map: Default::default(), - }; + .unwrap_or(&config.config.package) + .to_owned(); + + config.document_id = main.id.clone(); + config.base_url = base_url.to_string(); // Get Prefix Body => [AutoImports + Actual Doc content] let mut doc_content = @@ -417,7 +416,7 @@ pub(crate) async fn read_ftd_2022( let main_ftd_doc = match fastn_core::doc::interpret_helper( main.id_with_package().as_str(), doc_content.as_str(), - &mut lib, + config, base_url, download_assets, line_number, @@ -432,26 +431,19 @@ pub(crate) async fn read_ftd_2022( }); } }; - config.dependencies_during_render = lib.config.dependencies_during_render; + if let Some((url, code)) = main_ftd_doc.get_redirect()? { return Ok(FTDResult::Redirect { url, code }); } + let executor = ftd::executor::ExecuteDoc::from_interpreter(main_ftd_doc)?; let node = ftd::node::NodeData::from_rt(executor); let html_ui = ftd::html::HtmlUI::from_node_data(node, "main", test)?; - config - .downloaded_assets - .extend(lib.config.downloaded_assets); - - all_packages.extend(lib.config.all_packages.into_inner()); - drop(all_packages); - - let font_style = config.get_font_style(); let file_content = fastn_core::utils::replace_markers_2022( fastn_core::ftd_html(), html_ui, - config, + c, main.id_to_path().as_str(), font_style.as_str(), base_url, @@ -463,25 +455,24 @@ pub(crate) async fn read_ftd_2022( #[allow(clippy::await_holding_refcell_ref)] #[tracing::instrument(name = "read_ftd_2023", skip_all)] pub(crate) async fn read_ftd_2023( - config: &mut fastn_core::Config, + config: &mut fastn_core::RequestConfig, main: &fastn_core::Document, base_url: &str, download_assets: bool, ) -> fastn_core::Result { - let lib_config = config.clone(); - let mut all_packages = config.all_packages.borrow_mut(); - let current_package = all_packages + let package_name = config.config.package.name.to_string(); + let c = &config.config.clone(); + + let current_package = config + .config + .all_packages + .borrow() .get(main.package_name.as_str()) - .unwrap_or(&config.package); - - let mut lib = fastn_core::Library2022 { - config: lib_config, - markdown: None, - document_id: main.id.clone(), - translated_data: Default::default(), - base_url: base_url.to_string(), - module_package_map: Default::default(), - }; + .unwrap_or(&config.config.package) + .to_owned(); + + config.document_id = main.id.clone(); + config.base_url = base_url.to_string(); // Get Prefix Body => [AutoImports + Actual Doc content] let mut doc_content = @@ -493,7 +484,7 @@ pub(crate) async fn read_ftd_2023( let main_ftd_doc = match fastn_core::doc::interpret_helper( main.id_with_package().as_str(), doc_content.as_str(), - &mut lib, + config, base_url, download_assets, line_number, @@ -508,55 +499,46 @@ pub(crate) async fn read_ftd_2023( }); } }; - config.dependencies_during_render = lib.config.dependencies_during_render; if let Some((url, code)) = main_ftd_doc.get_redirect()? { return Ok(FTDResult::Redirect { url, code }); } let js_ast_data = ftd::js::document_into_js_ast(main_ftd_doc); - let js_document_script = - fastn_js::to_js(js_ast_data.asts.as_slice(), config.package.name.as_str()); + let js_document_script = fastn_js::to_js(js_ast_data.asts.as_slice(), package_name.as_str()); let js_ftd_script = fastn_js::to_js( ftd::js::default_bag_into_js_ast().as_slice(), - config.package.name.as_str(), + package_name.as_str(), ); let ssr_body = fastn_js::ssr_with_js_string( - &config.package.name, + &package_name, format!("{js_ftd_script}\n{js_document_script}").as_str(), ); - all_packages.extend(lib.config.all_packages.into_inner()); - drop(all_packages); - - config - .downloaded_assets - .extend(lib.config.downloaded_assets); - - let font_style = config.get_font_style(); let file_content = fastn_core::utils::replace_markers_2023( ftd::ftd_js_html(), js_document_script.as_str(), js_ast_data.scripts.join("").as_str(), ssr_body.as_str(), - font_style.as_str(), + config.config.get_font_style().as_str(), ftd::ftd_js_css(), base_url, - config, + c, ); Ok(FTDResult::Html(file_content.into())) } pub(crate) async fn process_ftd( - config: &mut fastn_core::Config, + config: &mut fastn_core::RequestConfig, main: &fastn_core::Document, base_url: &str, build_static_files: bool, test: bool, file_path: &str, ) -> fastn_core::Result { + let build_dir = config.config.build_dir(); let response = read_ftd(config, main, base_url, build_static_files, test).await?; - fastn_core::utils::overwrite(&config.build_dir(), file_path, &response.html()).await?; + fastn_core::utils::overwrite(&build_dir, file_path, &response.html()).await?; Ok(response) } diff --git a/fastn-core/src/package/user_group.rs b/fastn-core/src/package/user_group.rs index de73ca3096..6824c4b5f5 100644 --- a/fastn-core/src/package/user_group.rs +++ b/fastn-core/src/package/user_group.rs @@ -603,16 +603,17 @@ pub mod processor { pub fn get_identities( section: &ftd::ftd2021::p1::Section, doc: &ftd::ftd2021::p2::TDoc, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::ftd2021::p1::Result { - let doc_id = fastn_core::library::document::document_full_id(config, doc)?; - let identities = super::get_identities(config, doc_id.as_str(), true).map_err(|e| { - ftd::ftd2021::p1::Error::ParseError { - message: e.to_string(), - doc_id, - line_number: section.line_number, - } - })?; + let doc_id = fastn_core::library::document::document_full_id(req_config, doc)?; + let identities = + super::get_identities(&req_config.config, doc_id.as_str(), true).map_err(|e| { + ftd::ftd2021::p1::Error::ParseError { + message: e.to_string(), + doc_id, + line_number: section.line_number, + } + })?; Ok(ftd::Value::List { data: identities @@ -641,17 +642,16 @@ pub mod processor { pub async fn is_reader<'a>( section: &ftd::ftd2021::p1::Section, doc: &'a ftd::ftd2021::p2::TDoc<'_>, - config: &fastn_core::Config, + req_config: &fastn_core::RequestConfig, ) -> ftd::ftd2021::p1::Result { - let doc_id = fastn_core::library::document::document_full_id(config, doc)?; - let is_reader = config - .can_read(config.request.as_ref().unwrap(), &doc_id, false) - .await - .map_err(|e| ftd::ftd2021::p1::Error::ParseError { + let doc_id = fastn_core::library::document::document_full_id(req_config, doc)?; + let is_reader = req_config.can_read(&doc_id, false).await.map_err(|e| { + ftd::ftd2021::p1::Error::ParseError { message: e.to_string(), doc_id, line_number: section.line_number, - })?; + } + })?; Ok(ftd::Value::Boolean { value: is_reader }) } diff --git a/fastn-core/src/tutor.rs b/fastn-core/src/tutor.rs index 65c3229a01..d4b5cd635c 100644 --- a/fastn-core/src/tutor.rs +++ b/fastn-core/src/tutor.rs @@ -1,3 +1,20 @@ +pub async fn main() -> fastn_core::Result<()> { + println!("starting TUTOR mode"); + std::env::set_current_dir(std::env::current_dir()?.join(".tutor"))?; + fastn_core::listen( + "127.0.0.1", + Some(2000), + None, + Some("2023".to_string()), + vec![], + vec![], + vec![], + vec![], + "the-tutor".to_string(), + ) + .await +} + pub async fn pwd() -> fastn_core::Result { if !is_tutor() { return Ok(fastn_core::not_found!("this only works in tutor mode")); @@ -6,15 +23,57 @@ pub async fn pwd() -> fastn_core::Result { fastn_core::http::api_ok(std::env::current_dir()?.to_string_lossy()) } -pub async fn shutdown() -> fastn_core::Result { +pub async fn js() -> fastn_core::Result { + Ok(actix_web::HttpResponse::Ok().body(include_bytes!("../tutor.js").to_vec())) +} + +async fn set_tutorial(t: Option) -> fastn_core::Result { if !is_tutor() { return Ok(fastn_core::not_found!("this only works in tutor mode")); } - println!("/-/shutdown/ called, shutting down"); - std::process::exit(0); + *CURRENT_TUTORIAL.write().await = t; + fastn_core::http::api_ok("done") +} + +pub async fn start(t: Tutorial) -> fastn_core::Result { + set_tutorial(Some(t)).await +} + +pub async fn stop() -> fastn_core::Result { + set_tutorial(None).await } +static CURRENT_TUTORIAL: once_cell::sync::Lazy>> = + once_cell::sync::Lazy::new(|| async_lock::RwLock::new(None)); + +#[derive(serde::Deserialize)] +pub struct Tutorial { + id: String, + data: fastn_core::commands::serve::AppData, +} + +pub(crate) async fn config( + app_data: &fastn_core::commands::serve::AppData, +) -> fastn_core::Result<(fastn_core::Config, String)> { + let (root, app_data) = match CURRENT_TUTORIAL.read().await.as_ref() { + Some(context) => (Some(context.id.clone()), context.data.clone()), + None => (None, app_data.clone()), + }; + + let config = fastn_core::Config::read(root, false) + .await + .unwrap() + .add_edition(app_data.edition)? + .add_external_js(app_data.external_js) + .add_inline_js(app_data.inline_js) + .add_external_css(app_data.external_css) + .add_inline_css(app_data.inline_css); + + Ok((config, app_data.package_name)) +} + +/// tutor-data $processor$ pub async fn process( value: ftd::ast::VariableValue, kind: ftd::interpreter::Kind, @@ -26,212 +85,42 @@ pub async fn process( )); } - let state = + let fs_state: TutorStateFS = match tokio::fs::read(dirs::home_dir().unwrap().join(".fastn").join("tutor.json")).await { Ok(v) => serde_json::from_slice(&v)?, - Err(e) => match e.kind() { - std::io::ErrorKind::NotFound => TutorStateFS::default(), - _ => return Err(e.into()), + Err(e) => match dbg!(e.kind()) { + std::io::ErrorKind::NotFound => { + println!("not found, using default"); + TutorStateFS::default() + } + _ => { + println!("error: {:?}, {:?}", e, e.kind()); + return Err(e.into()); + } }, - } - .to_state(std::env::current_dir()?)?; + }; - doc.from_json(&state, &kind, &value) -} + let state = TutorState { + fs_state, + current: CURRENT_TUTORIAL.read().await.as_ref().map(|t| t.id.clone()), + }; -#[derive(Debug, Default, serde::Deserialize)] -struct TutorStateFS { - done: Vec, - current: String, + doc.from_json(&state, &kind, &value) } -#[derive(Debug, serde::Serialize, PartialEq)] +#[derive(Debug, Default, serde::Serialize)] struct TutorState { - workshops: Vec, + fs_state: TutorStateFS, + current: Option, } -impl TutorStateFS { - fn to_state>( - self: TutorStateFS, - path: T, - ) -> ftd::interpreter::Result { - use itertools::Itertools; - - let mut workshops = vec![]; - static RE: once_cell::sync::Lazy = - once_cell::sync::Lazy::new(|| regex::Regex::new(r"^[a-zA-Z]-[a-zA-Z]+.*$").unwrap()); - - for entry in std::fs::read_dir(path)?.sorted_by(sort_path) { - let entry = entry?; - let path = entry.path(); - - if !path.is_dir() { - continue; - } - if !RE.is_match(&path.file_name().unwrap().to_string_lossy()) { - continue; - } - - workshops.push(Workshop::load(&path, &self)?); - } - - Ok(TutorState { workshops }) - } -} - -fn sort_path( - a: &std::io::Result, - b: &std::io::Result, -) -> std::cmp::Ordering { - a.as_ref().unwrap().path().cmp(&b.as_ref().unwrap().path()) -} - -#[derive(Debug, serde::Serialize, PartialEq)] -struct Workshop { - title: String, - url: String, - done: bool, - current: bool, - tutorials: Vec, -} - -impl Workshop { - fn load(path: &std::path::Path, state: &TutorStateFS) -> ftd::interpreter::Result { - use itertools::Itertools; - - let mut tutorials = vec![]; - let id = path.file_name().unwrap().to_string_lossy(); - - static RE: once_cell::sync::Lazy = - once_cell::sync::Lazy::new(|| regex::Regex::new(r"^[0-9][0-9]-[a-zA-Z]+.*$").unwrap()); - - for entry in std::fs::read_dir(path)?.sorted_by(sort_path) { - let entry = entry?; - let path = entry.path(); - - if !path.is_dir() { - continue; - } - if !RE.is_match(&path.file_name().unwrap().to_string_lossy()) { - continue; - } - - tutorials.push(Tutorial::load(&id, &path, state)?); - } - - Ok(Workshop { - title: title_from_readme(path)?, - url: format!("/{id}/"), - done: !tutorials.iter().any(|t| !t.done), - current: tutorials.iter().any(|t| t.current), - tutorials, - }) - } -} - -fn title_from_readme(folder: &std::path::Path) -> ftd::interpreter::Result { - let content = std::fs::read_to_string(folder.join("README.md"))?; - let (title, _about) = match content.split_once("\n\n") { - Some(v) => v, - None => { - return Err(ftd::interpreter::Error::OtherError( - "invalid README.md".into(), - )) - } - }; - Ok(title.replacen("# ", "", 1)) -} - -#[derive(Debug, serde::Serialize, PartialEq)] -struct Tutorial { - id: String, - url: String, - title: String, - done: bool, - current: bool, -} - -impl Tutorial { - fn load( - parent: &str, - path: &std::path::Path, - state: &TutorStateFS, - ) -> ftd::interpreter::Result { - let id = format!("{parent}/{}", path.file_name().unwrap().to_string_lossy()); - - Ok(Tutorial { - title: title_from_readme(path)?, - done: state.done.contains(&id), - current: state.current == id, - url: format!("/{id}/"), - id, - }) - } +#[derive(Debug, Default, serde::Serialize, serde::Deserialize)] +struct TutorStateFS { + done: Vec, } pub fn is_tutor() -> bool { // https://github.com/orgs/fastn-stack/discussions/1414 // with either of these are passed we allow APIs like /-/shutdown/, `/-/start/` etc - std::env::args().any(|e| e == "tutor" || e == "--tutor") -} - -#[cfg(test)] -mod test { - use pretty_assertions::assert_eq; - - #[test] - fn test() { - let mut ts = super::TutorState { - workshops: vec![ - super::Workshop { - title: "Build Websites Using `fastn`".to_string(), - url: "/a-website/".to_string(), - done: false, - current: false, - tutorials: vec![super::Tutorial { - id: "a-website/01-hello-world".to_string(), - url: "/a-website/01-hello-world/".to_string(), - title: "Install and start using `fastn`".to_string(), - done: false, - current: false, - }], - }, - super::Workshop { - title: "Build User Interfaces Using `fastn`".to_string(), - url: "/b-ui/".to_string(), - done: false, - current: false, - tutorials: vec![super::Tutorial { - id: "b-ui/01-hello-world".to_string(), - url: "/b-ui/01-hello-world/".to_string(), - title: "Install and start using `fastn`".to_string(), - done: false, - current: false, - }], - }, - ], - }; - - assert_eq!( - super::TutorStateFS::default() - .to_state("tutor-tests/one") - .unwrap(), - ts, - ); - - ts.workshops[0].tutorials[0].done = true; - ts.workshops[0].done = true; - ts.workshops[1].current = true; - ts.workshops[1].tutorials[0].current = true; - - assert_eq!( - super::TutorStateFS { - done: vec!["a-website/01-hello-world".to_string()], - current: "b-ui/01-hello-world".to_string(), - } - .to_state("tutor-tests/one") - .unwrap(), - ts, - ); - } + std::env::args().any(|e| e == "tutor") } diff --git a/fastn-core/src/utils.rs b/fastn-core/src/utils.rs index 2a237d35dc..95eedeaab7 100644 --- a/fastn-core/src/utils.rs +++ b/fastn-core/src/utils.rs @@ -656,7 +656,7 @@ fn get_extra_css(external_css: &[String], inline_css: &[String], css: &str) -> S pub fn replace_markers_2022( s: &str, html_ui: ftd::html::HtmlUI, - config: &mut fastn_core::Config, + config: &fastn_core::Config, main_id: &str, font_style: &str, base_url: &str, @@ -750,7 +750,7 @@ pub fn replace_markers_2023( font_style: &str, default_css: &str, base_url: &str, - config: &mut fastn_core::Config, + config: &fastn_core::Config, ) -> String { ftd::html::utils::trim_all_lines( s.replace( @@ -793,6 +793,16 @@ pub fn replace_markers_2023( ) .as_str(), ) + .replace( + "__extra_js__", + get_extra_js( + config.ftd_external_js.as_slice(), + config.ftd_inline_js.as_slice(), + "", + "", + ) + .as_str(), + ) .replace("__default_css__", default_css) .replace("__base_url__", base_url) .as_str(), diff --git a/fastn-core/tutor-tests/one/a-website/01-hello-world/README.md b/fastn-core/tutor-tests/one/a-website/01-hello-world/README.md deleted file mode 100644 index 11d9765222..0000000000 --- a/fastn-core/tutor-tests/one/a-website/01-hello-world/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Install and start using `fastn` - -In this exercise we will install fastn and create a basic hello world program. \ No newline at end of file diff --git a/fastn-core/tutor-tests/one/a-website/README.md b/fastn-core/tutor-tests/one/a-website/README.md deleted file mode 100644 index cfd5187ce7..0000000000 --- a/fastn-core/tutor-tests/one/a-website/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Build Websites Using `fastn` - -This workshop teaches you how to build websites using `fastn`. \ No newline at end of file diff --git a/fastn-core/tutor-tests/one/b-ui/01-hello-world/README.md b/fastn-core/tutor-tests/one/b-ui/01-hello-world/README.md deleted file mode 100644 index 11d9765222..0000000000 --- a/fastn-core/tutor-tests/one/b-ui/01-hello-world/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Install and start using `fastn` - -In this exercise we will install fastn and create a basic hello world program. \ No newline at end of file diff --git a/fastn-core/tutor-tests/one/b-ui/README.md b/fastn-core/tutor-tests/one/b-ui/README.md deleted file mode 100644 index 594b45e2af..0000000000 --- a/fastn-core/tutor-tests/one/b-ui/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Build User Interfaces Using `fastn` - -This workshop teaches you how to build user interfaces using `fastn`. \ No newline at end of file diff --git a/fastn-core/tutor.js b/fastn-core/tutor.js new file mode 100644 index 0000000000..8f039b651a --- /dev/null +++ b/fastn-core/tutor.js @@ -0,0 +1,26 @@ +(function() { + document.addEventListener("DOMContentLoaded", function() { + let sidebarWidth = "300px"; + + let iframe = document.createElement('iframe'); + // iframe.src = "/-/tutor/"; + iframe.src = "https://fastn.com/"; + iframe.style.position = "fixed"; + iframe.style.top = "0"; + iframe.style.left = "0"; + iframe.style.width = sidebarWidth; + iframe.style.height = "100vh"; + + document.body.style.paddingLeft = sidebarWidth; + document.body.insertBefore(iframe, document.body.firstChild); + }); + + window.onmessage = function(e) { + if (e.kind === 'navigate') { + document.location.href = e.url; + return; + } + + console.warn('Unknown message', e); + }; +})(); \ No newline at end of file diff --git a/fastn/src/main.rs b/fastn/src/main.rs index df12f1d6eb..a4f982fedf 100644 --- a/fastn/src/main.rs +++ b/fastn/src/main.rs @@ -80,25 +80,13 @@ async fn fastn_core_commands(matches: &clap::ArgMatches) -> fastn_core::Result<( return fastn_core::clone(clone.value_of_("source").unwrap()).await; } - let mut config = fastn_core::Config::read(None, true, None).await?; - let package_name = config.package.name.clone(); - if let Some(_tutor) = matches.subcommand_matches("tutor") { - println!("starting TUTOR mode"); - return fastn_core::listen( - "127.0.0.1", - Some(2000), - None, - Some("2023".to_string()), - vec![], - vec![], - vec![], - vec![], - package_name, - ) - .await; + return fastn_core::tutor::main().await; } + let mut config = fastn_core::Config::read(None, true).await?; + let package_name = config.package.name.clone(); + if let Some(serve) = matches.subcommand_matches("serve") { let port = serve.value_of_("port").map(|p| match p.parse::() { Ok(v) => v, @@ -181,7 +169,7 @@ async fn fastn_core_commands(matches: &clap::ArgMatches) -> fastn_core::Result<( .add_inline_css(inline_css); return fastn_core::build( - &mut config, + &config, build.value_of_("file"), // TODO: handle more than one files build.value_of_("base").unwrap_or("/"), build.get_flag("ignore-failed"), @@ -518,7 +506,6 @@ mod sub_command { .action(clap::ArgAction::Append)) .arg(clap::arg!(--"css" "CSS text added in ftd files") .action(clap::ArgAction::Append)) - .arg(clap::arg!(--"tutor" "Start the server in tutor mode").hide(true)) .arg(clap::arg!(--"download-base-url" "If running without files locally, download needed files from here")); if cfg!(feature = "remote") { serve diff --git a/foo/Cargo.lock b/foo/Cargo.lock new file mode 100644 index 0000000000..0e5399e665 --- /dev/null +++ b/foo/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "foo" +version = "0.1.0" diff --git a/foo/Cargo.toml b/foo/Cargo.toml new file mode 100644 index 0000000000..5a9b78bdf5 --- /dev/null +++ b/foo/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "foo" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = { version = "1.27.0", features = ["full"] } diff --git a/foo/src/main.rs b/foo/src/main.rs new file mode 100644 index 0000000000..24c7704086 --- /dev/null +++ b/foo/src/main.rs @@ -0,0 +1,37 @@ +fn main() { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(outer_main()) +} + +async fn outer_main() { + let mut i = P { x: 10 }; + + yo(&mut i).await; + + let mut q = Q { p: &mut i }; + bo(&mut q).await; + + println!("Hello, world: {}", q.p.x); +} + +struct P { + x: i32, +} + +struct Q<'a> { + p: &'a mut P, +} + +async fn yo(i: &mut P) { + i.x = 20; + println!("yo: {}", i.x); +} + +async fn bo(q: &mut Q<'_>) { + // q.p = &mut P { x: 30 }; + q.p.x = 30; + println!("bo: {}", q.p.x); +} diff --git a/ftd/ftd-js.html b/ftd/ftd-js.html index 8113699d14..d9cad7d961 100644 --- a/ftd/ftd-js.html +++ b/ftd/ftd-js.html @@ -1,3 +1,4 @@ + @@ -9,7 +10,7 @@ __fastn_package__ - __script_file__ + __script_file____extra_js__