From 5975ed29d9d44efa77dd9fd84c36a8cf772d59ba Mon Sep 17 00:00:00 2001 From: Tommy Yu Date: Fri, 9 Aug 2024 14:27:09 +1200 Subject: [PATCH] Removing `pmrapp_client` and `pmrapp_server` - Thanks `sauron`, and long live `leptos`. --- .github/workflows/build.yml | 29 +- Cargo.toml | 4 - pmrapp.sh | 8 - pmrapp_client/Cargo.toml | 33 --- pmrapp_client/LICENSE | 1 - pmrapp_client/README.md | 29 -- pmrapp_client/src/api.rs | 34 --- pmrapp_client/src/app/content.rs | 285 ------------------- pmrapp_client/src/app/mod.rs | 364 ------------------------ pmrapp_client/src/error.rs | 7 - pmrapp_client/src/lib.rs | 28 -- pmrapp_server/Cargo.toml | 26 -- pmrapp_server/README.md | 26 -- pmrapp_server/src/config.rs | 14 - pmrapp_server/src/http/api/mod.rs | 1 - pmrapp_server/src/http/api/workspace.rs | 119 -------- pmrapp_server/src/http/error.rs | 55 ---- pmrapp_server/src/http/mod.rs | 118 -------- pmrapp_server/src/http/page.rs | 81 ------ pmrapp_server/src/http/workspace.rs | 160 ----------- pmrapp_server/src/lib.rs | 2 - pmrapp_server/src/main.rs | 19 -- 22 files changed, 21 insertions(+), 1422 deletions(-) delete mode 100755 pmrapp.sh delete mode 100644 pmrapp_client/Cargo.toml delete mode 120000 pmrapp_client/LICENSE delete mode 100644 pmrapp_client/README.md delete mode 100644 pmrapp_client/src/api.rs delete mode 100644 pmrapp_client/src/app/content.rs delete mode 100644 pmrapp_client/src/app/mod.rs delete mode 100644 pmrapp_client/src/error.rs delete mode 100644 pmrapp_client/src/lib.rs delete mode 100644 pmrapp_server/Cargo.toml delete mode 100644 pmrapp_server/README.md delete mode 100644 pmrapp_server/src/config.rs delete mode 100644 pmrapp_server/src/http/api/mod.rs delete mode 100644 pmrapp_server/src/http/api/workspace.rs delete mode 100644 pmrapp_server/src/http/error.rs delete mode 100644 pmrapp_server/src/http/mod.rs delete mode 100644 pmrapp_server/src/http/page.rs delete mode 100644 pmrapp_server/src/http/workspace.rs delete mode 100644 pmrapp_server/src/lib.rs delete mode 100644 pmrapp_server/src/main.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ff23ed3..c9f1963 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: push: branches: - "main" + - "testing" pull_request: branches: - "main" @@ -16,14 +17,26 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install wasm-pack - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - name: Build - run: | - cargo build - wasm-pack build pmrapp_client --target web - cargo build --bin pmrapp_server --package pmrapp_server + + - name: Add wasm32-unknown-unknown + run: rustup target add wasm32-unknown-unknown + + - name: Install binstall + uses: cargo-bins/cargo-binstall@main + + - name: Install wasm-bindgen + run: cargo binstall wasm-bindgen-cli --no-confirm + + - name: Install cargo-leptos + run: cargo binstall cargo-leptos --no-confirm + + - name: Build core + run: cargo build + + - name: Build app + run: cargo leptos build + - name: Run tests run: | cargo test --all-features - cargo test --package pmrapp_server --package pmrapp_client + cargo test --package pmrapp diff --git a/Cargo.toml b/Cargo.toml index e548c6a..cf84670 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,6 @@ [workspace] members = [ "pmrapp", - "pmrapp_client", - "pmrapp_server", "pmrctrl", "pmrcore", "pmrmodel", @@ -22,8 +20,6 @@ resolver = "2" [patch.crates-io] pmrapp = { path = "./pmrapp" } -pmrapp_client = { path = "./pmrapp_client" } -pmrapp_server = { path = "./pmrapp_server" } pmrctrl = { path = "./pmrctrl" } pmrmodel = { path = "./pmrmodel" } pmrcore = { path = "./pmrcore" } diff --git a/pmrapp.sh b/pmrapp.sh deleted file mode 100755 index ca9d2a6..0000000 --- a/pmrapp.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -set -e -cd "$(dirname "$0")" - -# This script will build and run the pmrapp_server. - -wasm-pack build pmrapp_client --release --target web -cargo run --release --bin pmrapp_server --package pmrapp_server diff --git a/pmrapp_client/Cargo.toml b/pmrapp_client/Cargo.toml deleted file mode 100644 index c8415bf..0000000 --- a/pmrapp_client/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "pmrapp_client" -version = "0.0.0" -edition = "2021" -authors = ["Tommy Yu "] -license = "AGPL-3.0" - -[dependencies] -chrono = { version = "0.4.19", features = ["serde", "wasmbind"] } -console_log = { version ="0.2", features = ["color"], optional = true } -console_error_panic_hook = { version = "0.1", optional = true} -derive_more = "0.99.16" -log = "0.4" -pmrcore = "0.1.0" -reqwest = { version = "0.11", features = ["json"] } -sauron = "0.51.0" -serde = { version = "1.0", features = ["serde_derive"]} -serde_json = "1.0" -serde-wasm-bindgen = "0.4" -thiserror = "1.0" -wasm-bindgen-futures = { version = "0.4.32", optional = true } -wasm-bindgen = { version = "0.2.82", optional = true} - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -default = ["wasm"] -wasm = ["wasm-bindgen", "wasm-bindgen-futures", "console_error_panic_hook", "console_log"] - -[dependencies.web-sys] -version = "0.3" -features = ["PopStateEvent"] diff --git a/pmrapp_client/LICENSE b/pmrapp_client/LICENSE deleted file mode 120000 index ea5b606..0000000 --- a/pmrapp_client/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE \ No newline at end of file diff --git a/pmrapp_client/README.md b/pmrapp_client/README.md deleted file mode 100644 index a23f3d4..0000000 --- a/pmrapp_client/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# pmrapp\_client - -The client library for the PMR application. - -## Features - -This client library can be used to produce HTML DOM representation of -the underlying models of PMR. - -The library can be compiled to native code and WebAssembly. - -## Build - -In order for `pmrapp_server` be able to provide the instructions to -enable client side rendering for its clients, the Wasm binary must be -built first. The pre-requsite for this is at the minimum `rustc 1.30.0` -and then following [its installation instructions]( -https://rustwasm.github.io/docs/wasm-pack/) - -When `wasm-pack` is available, build using the following command: - -```console -$ wasm-pack build pmrapp_client --release --target web -``` - -## Usage - -Typically, this is used from another application or package, such as -the `pmrapp_server` package. diff --git a/pmrapp_client/src/api.rs b/pmrapp_client/src/api.rs deleted file mode 100644 index ad7e1db..0000000 --- a/pmrapp_client/src/api.rs +++ /dev/null @@ -1,34 +0,0 @@ -use pmrcore::workspace::{ - Workspaces, - // Workspace, -}; -use pmrcore::repo::RepoResult; -use crate::error::ServerError; - -pub async fn request_get_json( - url: &str, -) -> Result { - log::trace!("request_get_json: {}", url); - let response = reqwest::get(url).await?; - Ok(response.json::().await?) -} - -pub async fn get_workspace_listing() -> Result { - let url = format!("{}/api/workspace/", sauron::window().location().origin().expect("must have location")); - Ok(request_get_json::(&url).await?) -} - -pub async fn get_workspace_top(workspace_id: &i64) -> Result { - let url = format!("{}/api/workspace/{}/", sauron::window().location().origin().expect("must have location"), workspace_id); - Ok(request_get_json::(&url).await?) -} - -pub async fn get_workspace_pathinfo(workspace_id: &i64, commit_id: &str, path: Option<&str>) -> Result { - let url = format!("{}/api/workspace/{}/file/{}/{}", - sauron::window().location().origin().expect("must have location"), - workspace_id, - commit_id, - path.unwrap_or(""), - ); - Ok(request_get_json::(&url).await?) -} diff --git a/pmrapp_client/src/app/content.rs b/pmrapp_client/src/app/content.rs deleted file mode 100644 index a97be1c..0000000 --- a/pmrapp_client/src/app/content.rs +++ /dev/null @@ -1,285 +0,0 @@ -use crate::app; -use sauron::prelude::*; -use serde::{Deserialize, Serialize}; - -use pmrcore::{ - workspace::{ - Workspaces, - Workspace, - }, - repo::{ - PathObjectInfo, - RepoResult, - }, -}; - -use crate::app::Resource; -use crate::app::Msg; - -#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)] -pub enum Content { - Homepage, - WorkspaceListing(Workspaces), - WorkspaceTop(RepoResult), - WorkspaceRepoResult(RepoResult), -} - - -impl Content { - pub fn view(&self) -> Node { - match self { - Content::Homepage => { - node! { -
-

"Physiome Model Repository"

-

- "Welcome to the demo of the platform that will \n\ - power the next generation of the Physiome Model \n\ - Repository, written in Rust." -

-

- "The code for this project may be found on " - - "its project page on GitHub" - - "." -

-
-
"Workspace Listing"
-
"Listing of all workspaces in the repository."
-
-
- } - } - Content::WorkspaceListing(workspaces) => { - node! { -
-

"Workspace Listing"

-
- { - for workspace in workspaces.iter() { - self.show_workspace(workspace) - } - } -
-
- } - }, - Content::WorkspaceTop(repo_result) => { - node! { -
-

{ text!("{}", &repo_result.workspace.description.as_ref().unwrap_or( - &format!("Workspace {}", &repo_result.workspace.id))) }

-
-
"Git Repository URI"
-
{ text!("{}", &repo_result.workspace.url) }
-
-
- { - self.show_workspace_file_table(&repo_result) - } -
-
- } - }, - Content::WorkspaceRepoResult(repo_result) => { - let workspace_id = repo_result.workspace.id; - node! { -
-

- - { - text!("{}", &repo_result.workspace.description.as_ref().unwrap_or( - &format!("Workspace {}", &repo_result.workspace.id))) - } - -

-
- { - match &repo_result.target { - PathObjectInfo::TreeInfo(..) => { - self.show_workspace_file_table(&repo_result) - } - PathObjectInfo::FileInfo(file_info) => { - let href = format!( - "/workspace/{}/raw/{}/{}", - &repo_result.workspace.id, - &repo_result.commit.commit_id, - &repo_result.path, - ); - node! { -
- { - text!("{:?}", file_info) - } -
- - { - if &file_info.mime_type[..5] == "image" { - node! { -
-

"Preview"

- -
- } - } - else { - node! { -
- } - } - } - } - } - other => { - text!("unhandled PathObjectInfo {other:?}") - } - } - } -
-
- } - }, - } - } - - fn show_workspace(&self, workspace: &Workspace) -> Node { - let workspace_id = workspace.id; - node! { -
- -
{ text!("{}", workspace.url) }
-
{ text!("{}", workspace.description.as_ref().unwrap_or(&"".to_string())) }
-
- } - } - - fn show_workspace_file_table(&self, repo_result: &RepoResult) -> Node { - node! { - - - - - - - - - { - self.show_workspace_file_table_body(repo_result) - } -
"Filename""Size""Date"
- } - } - - fn show_workspace_file_table_body(&self, repo_result: &RepoResult) -> Node { - match &repo_result.target { - PathObjectInfo::TreeInfo(tree_info) => { - node! { - - { - if repo_result.path != "" { - self.show_workspace_file_row( - repo_result.workspace.id, - repo_result.commit.commit_id.clone(), - repo_result.path.clone(), - "pardir", - "..", - ) - } - else { - node! {} - } - } - { - for info in tree_info.entries.iter() { - self.show_workspace_file_row( - repo_result.workspace.id, - repo_result.commit.commit_id.clone(), - repo_result.path.clone(), - &info.kind, - &info.name, - ) - } - } - - } - }, - _ => node! {}, - } - } - - fn show_workspace_file_row( - &self, - workspace_id: i64, - commit_id: String, - path: String, - kind: &str, - name: &str, - ) -> Node { - let path_name = if name == ".." { - let idx = path[0..path.len() - 1].rfind('/').unwrap_or(0); - if idx == 0 { - "".to_string() - } else { - format!("{}/", &path[0..idx]) - } - } else { - format!("{}{}", path, if kind == "tree" { - format!("{}/", name) - } else { - format!("{}", name) - }) - }; - let href = format!("/workspace/{}/file/{}/{}", workspace_id, commit_id, &path_name); - // Sauron needs this key attribute, otherwise the correct event - // sometimes don't get patched in... - // https://github.com/ivanceras/sauron/issues/63 - let key = path_name.clone(); - // TODO need to test putting a proper key at the table itself... - node! { - - { text!("{}", name) } - - - - - } - } - -} diff --git a/pmrapp_client/src/app/mod.rs b/pmrapp_client/src/app/mod.rs deleted file mode 100644 index 3e4c86a..0000000 --- a/pmrapp_client/src/app/mod.rs +++ /dev/null @@ -1,364 +0,0 @@ -use pmrcore::{ - workspace::{ - Workspaces, - // Workspace, - }, - repo::RepoResult, -}; -use sauron::prelude::*; -use serde::{Deserialize, Serialize}; -#[cfg(feature = "wasm")] -use wasm_bindgen_futures::spawn_local; -use web_sys::PopStateEvent; - -mod content; - -use content::Content; -use crate::error::ServerError; -use crate::api; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum FetchStatus { - Idle, - Complete(T), - Error(String), -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub enum Resource { - Unset, - - Homepage, - WorkspaceListing, - WorkspaceTop(i64), - WorkspaceRepoResult(i64, String, String), -} - -pub enum Msg { - Retrieve(Resource, String), - - // new content and url - ReceivedContent(Resource, Content), - // for dealing with error responses - RequestError(ServerError), - // for the URL push state - UrlChanged(Resource, String), -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct App { - pub content: FetchStatus, - is_loading: bool, - resource: Resource, -} - -impl Default for App { - fn default() -> Self { - Self { - content: FetchStatus::Idle, - is_loading: true, - resource: Resource::Unset, - } - } -} - -impl Application for App { - - #[cfg(feature = "wasm")] - fn init(&mut self) -> Cmd { - // Only calling this init if this is not default, i.e. not the default app - // created when loading failed in client/src/lib.rs main function - if self.resource == Resource::Unset { - return Cmd::none(); - } - let mut commands = vec![]; - let listen_to_url_changes = Window::add_event_listeners(vec![on_popstate(|e| { - log::trace!("pop_state is triggered in sauron add event listener - state: {:#?}", PopStateEvent::from(JsValue::from(&e)).state()); - let url = sauron::window() - .location() - .pathname() - .expect("must have get a pathname"); - // TODO if the state is unsupported, this blows up - // TODO rather than unwrap, if error, trigger a redirect to url - Msg::UrlChanged( - serde_wasm_bindgen::from_value( - PopStateEvent::from(JsValue::from(e)).state() - ).unwrap(), - url, - ) - })]); - - let history = sauron::window().history().expect("must have history"); - log::trace!("setting initial state: {:#?}", self.resource); - history - .replace_state(&serde_wasm_bindgen::to_value(&self.resource).unwrap(), "") - .expect("must push state"); - - commands.push(listen_to_url_changes); - Cmd::batch(commands) - } - - fn view(&self) -> Node { - node! { - -
- "active", - _ => "" - } } - on_click=|e| { - e.prevent_default(); - Msg::Retrieve(Resource::Homepage, "/".to_string()) - }> - "Home" - - "active", - _ => "" - } } - on_click=|e| { - e.prevent_default(); - Msg::Retrieve(Resource::WorkspaceListing, "/workspace/".to_string()) - }> - "Workspace Listing" - -
- { self.loading_indicator() } -
- { self.view_content() } -
- - } - } - - #[cfg(not(feature = "wasm"))] - fn update(&mut self, _msg: Msg) -> Cmd { - Cmd::none() - } - - #[cfg(feature = "wasm")] - fn update(&mut self, msg: Msg) -> Cmd { - let mut update_resource = |resource: Resource| { - self.is_loading = true; - self.resource = resource.clone(); - match resource { - Resource::Homepage => { - self.fetch_homepage(resource) - } - Resource::WorkspaceListing => { - self.fetch_workspace_listing(resource) - } - Resource::WorkspaceTop(workspace_id) => { - self.fetch_workspace(resource, workspace_id) - } - Resource::WorkspaceRepoResult(workspace_id, ref commit_id, ref path) => { - let commit_id = commit_id.clone(); - let path = path.clone(); - self.fetch_workspace_pathinfo( - resource, - workspace_id, - commit_id, - path, - ) - } - Resource::Unset => { - Cmd::none() - } - } - }; - - match msg { - // core application related - Msg::Retrieve(resource, url) => { - Self::push_state(&resource, &url); - update_resource(resource) - } - - // System related - Msg::ReceivedContent(resource, content) => { - if resource == self.resource { - self.content = FetchStatus::Complete(content); - self.is_loading = false; - log::trace!("content prepared for resource {:?}", &resource); - Window::scroll_to_top() - } - else { - log::warn!("fetched resource {:?} not match current resource {:?}; doing nothing", &resource, &self.resource); - Cmd::none() - } - } - Msg::RequestError(server_error) => { - self.is_loading = false; - log::error!("Error: {}", server_error); - Cmd::none() - } - Msg::UrlChanged(resource, url) => { - log::trace!("UrlChanged: {}", url); - update_resource(resource) - } - } - } - - fn style(&self) -> std::string::String { - // TODO figure out how/where should this be integrated/merged with - // existing definitions - "".to_string() - } -} - -impl App { - fn view_content(&self) -> Node { - match &self.content { - FetchStatus::Idle => node! {

"idling"

}, - FetchStatus::Error(e) => { - node! { -
-

"Error: "

- {text(e)} -
- } - } - FetchStatus::Complete(content) => content.view(), - } - } - - fn loading_indicator(&self) -> Node { - node! { -
-
-
- } - } -} - -impl App { - pub fn with_homepage() -> Self { - Self { - content: FetchStatus::Complete(Content::Homepage), - is_loading: false, - resource: Resource::Homepage, - } - } - - pub fn with_workspace_listing(workspaces: Workspaces) -> Self { - Self { - content: FetchStatus::Complete(Content::WorkspaceListing(workspaces)), - is_loading: false, - resource: Resource::WorkspaceListing, - } - } - - pub fn with_workspace_top( - workspace_id: i64, - repo_result: RepoResult, - ) -> Self { - Self { - content: FetchStatus::Complete(Content::WorkspaceTop(repo_result)), - is_loading: false, - resource: Resource::WorkspaceTop(workspace_id), - } - } - - pub fn with_workspace_pathinfo( - workspace_id: i64, - commit_id: String, - filepath: String, - object_info: RepoResult, - ) -> Self { - Self { - content: FetchStatus::Complete(Content::WorkspaceRepoResult(object_info)), - is_loading: false, - resource: Resource::WorkspaceRepoResult(workspace_id, commit_id, filepath), - } - } -} - -#[cfg(feature = "wasm")] -impl App { - fn fetch_homepage(&self, resource: Resource) -> Cmd { - Cmd::new(move|program| { - program.dispatch(Msg::ReceivedContent(resource, Content::Homepage)); - }) - } - - fn fetch_workspace_listing(&self, resource: Resource) -> Cmd { - Cmd::new(move|program| { - let async_fetch = |program:Program| async move{ - match api::get_workspace_listing().await { - Ok(workspaces) => { - program.dispatch(Msg::ReceivedContent(resource, Content::WorkspaceListing( - workspaces, - ))); - } - Err(e) => { - program.dispatch(Msg::RequestError(e)); - } - } - }; - spawn_local(async_fetch(program)) - }) - } - - fn fetch_workspace(&self, resource: Resource, workspace_id: i64) -> Cmd { - Cmd::new(move|program| { - let async_fetch = |program:Program| async move{ - match api::get_workspace_top(&workspace_id).await { - Ok(repo_result) => { - program.dispatch(Msg::ReceivedContent( - resource, - Content::WorkspaceTop(repo_result), - )); - } - Err(e) => { - program.dispatch(Msg::RequestError(e)); - } - } - }; - spawn_local(async_fetch(program)) - }) - } - - fn fetch_workspace_pathinfo( - &self, - resource: Resource, - workspace_id: i64, - commit_id: String, - path: String, - ) -> Cmd { - Cmd::new(move|program| { - let async_fetch = |program:Program| async move{ - match api::get_workspace_pathinfo( - &workspace_id, - &commit_id, - Some(&path), - ).await { - Ok(repo_result) => { - program.dispatch(Msg::ReceivedContent(resource, Content::WorkspaceRepoResult( - repo_result, - ))); - } - Err(e) => { - program.dispatch(Msg::RequestError(e)); - } - } - }; - spawn_local(async_fetch(program)) - }) - } - - fn push_state(resource: &Resource, url: &str) { - let history = sauron::window().history().expect("must have history"); - log::trace!("pushing to state: {}", url); - history - .push_state_with_url(&serde_wasm_bindgen::to_value(&resource).unwrap(), "", Some(url)) - .expect("must push state"); - } -} diff --git a/pmrapp_client/src/error.rs b/pmrapp_client/src/error.rs deleted file mode 100644 index a26e45e..0000000 --- a/pmrapp_client/src/error.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[derive(thiserror::Error, Debug)] -pub enum ServerError { - #[error("reqwest error: {0}")] - Reqwest(#[from] reqwest::Error), - #[error("json error: {0}")] - SerdeJson(#[from] serde_json::Error), -} diff --git a/pmrapp_client/src/lib.rs b/pmrapp_client/src/lib.rs deleted file mode 100644 index 78dc613..0000000 --- a/pmrapp_client/src/lib.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub use app::{App, Msg}; -use sauron::prelude::*; -pub use sauron; - -mod api; -mod app; -mod error; - -#[cfg_attr(feature = "wasm", wasm_bindgen)] -pub async fn main(serialized_state: String) { - #[cfg(feature = "wasm-bindgen")] - { - console_log::init_with_level(log::Level::Trace).ok(); - console_error_panic_hook::set_once(); - } - if serialized_state == "" { - return; - } - - let app = match serde_json::from_str::(&serialized_state) { - Ok(app_state) => app_state, - Err(e) => { - log::warn!("error: {}", e); - App::default() - } - }; - Program::replace_mount(app, &document().query_selector("body").unwrap().unwrap()); -} diff --git a/pmrapp_server/Cargo.toml b/pmrapp_server/Cargo.toml deleted file mode 100644 index b93cf1b..0000000 --- a/pmrapp_server/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "pmrapp_server" -version = "0.0.0" -edition = "2021" -authors = ["Tommy Yu "] -license = "AGPL-3.0" - -[dependencies] -anyhow = "1.0" -axum-macros = "0.3.0" -axum = "0.6.1" -clap = { version = "3.2", features = [ "derive", "env" ] } -dotenvy = "0.15.0" -log = { version = "0.4", features = ["std", "serde"] } -pmrapp_client = "0.0.0" -pmrmodel = "0.1.0" -pmrcore = "0.1.0" -pmrrepo = "0.1.0" -tokio = { version = "1.20", features = [ "full" ] } -tower = "0.4" -tower-http = { version = "0.3", features = ["trace"] } -serde = "1.0" -serde_json = "1.0" -sqlx = { version = "0.7.0", features = [ "runtime-async-std-native-tls" ] } -stderrlog = "0.5.1" -thiserror = "1.0" diff --git a/pmrapp_server/README.md b/pmrapp_server/README.md deleted file mode 100644 index 7329a90..0000000 --- a/pmrapp_server/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# pmrapp\_server - -This package provides the PMR application server. - -## Features - -This package provides a binary that will contain everything necessary -to serve the Physiome Model Repository web application. - -## Build - -In order for this to build, please ensure the Wasm binary from the -accompanied `pmrapp_client` package with the Cargo workspace was built. -Once that's done, the following build command may be issued from the -root of the Cargo workspace: - -```console -cargo build --release --bin pmrapp_server --package pmrapp_server -``` - -Note that `--package` must be specified as this is not one of the default -members of the PMR3 Cargo workspace. - -## Usage - -Simply run the binary and connect to the application end point. diff --git a/pmrapp_server/src/config.rs b/pmrapp_server/src/config.rs deleted file mode 100644 index 9d4ad60..0000000 --- a/pmrapp_server/src/config.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[derive(clap::Parser)] -pub struct Config { - /// The database URL - #[clap(long, env)] - pub pmrapp_db_url: String, - - /// The root for the git repos - #[clap(long, env)] - pub pmr_repo_root: String, - - /// The http port for the server - #[clap(long, env)] - pub http_port: u16, -} diff --git a/pmrapp_server/src/http/api/mod.rs b/pmrapp_server/src/http/api/mod.rs deleted file mode 100644 index b91220b..0000000 --- a/pmrapp_server/src/http/api/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod workspace; diff --git a/pmrapp_server/src/http/api/workspace.rs b/pmrapp_server/src/http/api/workspace.rs deleted file mode 100644 index 032b0a1..0000000 --- a/pmrapp_server/src/http/api/workspace.rs +++ /dev/null @@ -1,119 +0,0 @@ -use axum::{ - extract::{Extension, Path}, - Json, - routing::get, - Router, -}; -use pmrrepo::backend::Backend; -use pmrcore::{ - repo::RepoResult, - workspace::{ - Workspaces, - traits::WorkspaceBackend, - } -}; - -use crate::http::AppContext; -use crate::http::{Error, Result}; - - -pub fn router() -> Router { - Router::new() - .route("/", get(api_workspace)) - .route("/:workspace_id/", - get(api_workspace_top)) - .route("/:workspace_id/file/", - get(api_workspace_pathinfo_workspace_id)) - .route("/:workspace_id/file/:commit_id/", - get(api_workspace_pathinfo_workspace_id_commit_id)) - .route("/:workspace_id/file/:commit_id/*path", - get(api_workspace_pathinfo_workspace_id_commit_id_path)) - .route("/:workspace_id/raw/:commit_id/*path", - get(api_workspace_pathinfo_workspace_id_commit_id_path)) -} - -pub async fn api_workspace(ctx: Extension) -> Result> { - let workspaces = WorkspaceBackend::list_workspaces(ctx.db.as_ref()).await?; - Ok(Json(workspaces)) -} - -pub async fn api_workspace_top( - ctx: Extension, - Path(workspace_id): Path, -) -> Result> { - let backend = Backend::new(ctx.db.clone(), (&ctx.config.pmr_repo_root).into()); - let handle = backend.git_handle(workspace_id).await?; - let pathinfo = handle.pathinfo::(None, None)?; - Ok(Json(pathinfo.into())) -} - -/* -pub async fn api_workspace_top_ssr( - ctx: Extension, - Path(workspace_id): Path, -) -> Result<(JsonWorkspaceRecord, Option)> { - let backend = Backend::new(ctx.db.clone(), (&ctx.config.pmr_repo_root).into()); - let handle = backend.git_handle(workspace_id).await?; - - let pathinfo = match handle.pathinfo(None, None) { - Ok(result) => ( - Some(format!("{}", result.commit.id())), - Some(result.into()), - ), - Err(_) => (None, None) - }; - Ok(( - JsonWorkspaceRecord { - workspace: workspace, - head_commit: head_commit, - }, - path_info, - )) -} -*/ - -async fn api_workspace_pathinfo( - ctx: Extension, - workspace_id: i64, - commit_id: Option, - path: Option, -) -> Result> { - let backend = Backend::new(ctx.db.clone(), (&ctx.config.pmr_repo_root).into()); - let handle = backend.git_handle(workspace_id).await?; - - let result = match handle.pathinfo( - commit_id.as_deref(), - path.as_deref(), - ) { - Ok(pathinfo) => Ok(Json(pathinfo.into())), - Err(e) => { - // TODO log the URI triggering these messages? - log::info!("handle.pathinfo error: {:?}", e); - Err(Error::NotFound) - } - }; - result -} - -pub async fn api_workspace_pathinfo_workspace_id( - ctx: Extension, - Path(workspace_id): Path, -) -> Result> { - api_workspace_pathinfo(ctx, workspace_id, None, None).await -} - -pub async fn api_workspace_pathinfo_workspace_id_commit_id( - ctx: Extension, - Path((workspace_id, commit_id)): Path<(i64, String)>, -) -> Result> { - api_workspace_pathinfo(ctx, workspace_id, Some(commit_id), None).await -} - - -pub async fn api_workspace_pathinfo_workspace_id_commit_id_path( - ctx: Extension, - Path((workspace_id, commit_id, path)): Path<(i64, String, String)>, -) -> Result> { - api_workspace_pathinfo(ctx, workspace_id, Some(commit_id), Some(path)).await -} - diff --git a/pmrapp_server/src/http/error.rs b/pmrapp_server/src/http/error.rs deleted file mode 100644 index 525a990..0000000 --- a/pmrapp_server/src/http/error.rs +++ /dev/null @@ -1,55 +0,0 @@ -use axum::{ - http::StatusCode, - response::{IntoResponse, Response}, -}; - - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("400 Bad Request")] - BadRequest, - #[error("401 Unauthorized")] - Unauthorized, - #[error("403 Forbidden")] - Forbidden, - #[error("404 Not Found")] - NotFound, - - #[error("500 Internal Server Error")] - Error, - #[error("500 Internal Server Error")] - Backend(#[from] pmrcore::error::BackendError), - #[error("500 Internal Server Error")] - SerdeJson(#[from] serde_json::error::Error), - #[error("500 Internal Server Error")] - Sqlx(#[from] sqlx::Error), - #[error("500 Internal Server Error")] - GixError(#[from] pmrrepo::error::GixError), - #[error("500 Internal Server Error")] - PmrRepoError(#[from] pmrrepo::error::PmrRepoError), -} - - -impl Error { - fn status_code(&self) -> StatusCode { - match self { - Self::Unauthorized => StatusCode::UNAUTHORIZED, - Self::Forbidden => StatusCode::FORBIDDEN, - Self::NotFound => StatusCode::NOT_FOUND, - _ => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - - -impl IntoResponse for Error { - fn into_response(self) -> Response { - let body = match self { - Self::PmrRepoError(ref e) => { - log::info!("repo error: {:?}", e); - }, - _ => (), - }; - (self.status_code(), body).into_response() - } -} diff --git a/pmrapp_server/src/http/mod.rs b/pmrapp_server/src/http/mod.rs deleted file mode 100644 index 8f395a1..0000000 --- a/pmrapp_server/src/http/mod.rs +++ /dev/null @@ -1,118 +0,0 @@ -use axum::{ - extract::Extension, - http::header::{ - HeaderMap, - HeaderName, - HeaderValue, - }, - Json, - response::{ - Html, - IntoResponse, - Response, - }, - routing::get, - Router, -}; -use pmrmodel::backend::db::SqliteBackend; -use serde::Serialize; -use std::{ - net::SocketAddr, - sync::Arc, -}; -use tower::ServiceBuilder; -use tower_http::trace::TraceLayer; - -use crate::config::Config; -pub use pmrapp_client::sauron; -use pmrapp_client::App; -use pmrapp_client::sauron::Render; - -mod page; -mod error; -mod api; -mod workspace; - -pub use error::Error; - -pub type Result = std::result::Result; - - -// TODO figure out if this can actually be pub -#[derive(Clone)] -pub struct AppContext { - config: Arc, - db: Arc, -} - - -pub async fn serve(config: Config, db: SqliteBackend) { - let socket: SocketAddr = ([0, 0, 0, 0], config.http_port).into(); - let app = router() - .nest("/workspace/", workspace::router()) - .nest("/api/workspace/", api::workspace::router()) - .layer( - ServiceBuilder::new() - .layer(Extension(AppContext { - config: Arc::new(config), - db: Arc::new(db), - })) - .layer(TraceLayer::new_for_http()) - ); - - println!("serving at: http://{}", socket); - axum::Server::bind(&socket) - .serve(app.into_make_service()) - .await - .unwrap(); -} - - -fn router() -> Router { - Router::new() - .route("/", get(index_root)) - .route("/api/", get(api_root)) - .route("/style/main.css", get(style_main)) - .route("/pkg/pmrapp_client.js", get(client_js)) - .route("/pkg/pmrapp_client_bg.wasm", get(client_bg_wasm)) -} - -// placeholder thingers -async fn index_root() -> Response { - let app = App::with_homepage(); - let content = page::index(&app).render_to_string(); - Html(content).into_response() -} - -#[derive(Serialize)] -struct Page { - name: String, -} - -async fn api_root() -> Response { - let resp = Page { name: "index".to_string() }; - Json(resp).into_response() -} - -async fn style_main() -> (HeaderMap, String) { - let mut headers = HeaderMap::new(); - headers.insert(HeaderName::from_static("content-type"), - HeaderValue::from_static("text/css")); - (headers, page::style()) -} - -async fn client_js() -> (HeaderMap, String) { - let mut headers = HeaderMap::new(); - headers.insert(HeaderName::from_static("content-type"), - HeaderValue::from_static("text/javascript")); - - (headers, include_str!("../../../pmrapp_client/pkg/pmrapp_client.js").to_string()) -} - -async fn client_bg_wasm() -> (HeaderMap, Vec) { - let mut headers = HeaderMap::new(); - headers.insert(HeaderName::from_static("content-type"), - HeaderValue::from_static("application/wasm")); - - (headers, include_bytes!("../../../pmrapp_client/pkg/pmrapp_client_bg.wasm").to_vec()) -} diff --git a/pmrapp_server/src/http/page.rs b/pmrapp_server/src/http/page.rs deleted file mode 100644 index 4d1a274..0000000 --- a/pmrapp_server/src/http/page.rs +++ /dev/null @@ -1,81 +0,0 @@ -use pmrapp_client::sauron; -use pmrapp_client::sauron::prelude::*; -use pmrapp_client::{App, Msg}; -use crate::http::page::jss::jss; - -pub fn index(app: &App) -> Node { - println!("app: {:#?}", app); - let serialized_state = serde_json::to_string(&app).unwrap(); - node! { - - - - "Physiome Model Repository" - - - - { app.view() } - - } -} - -pub fn style() -> String { - jss! { - "body": { - font_family: "'Arial', sans-serif", - margin: "0 auto", - max_width: "90rem", - }, - "header": { - display: "flex", - background_color: "#333", - color: "#fff", - }, - "header a": { - color: "#fff", - padding: "0.5em 2em", - text_decoration: "none", - }, - "header a.active": { - background_color: "#c00", - }, - "table.file-listing": { - width: "100%", - }, - "table.file-listing td.gitobj-tree span::before": { - content: "'\\1f4c1 '", - }, - "table.file-listing td.gitobj-blob span::before": { - content: "'\\1f4c4 '", - }, - "#indicator, #indicator div": { - height: "10px", - }, - "#indicator div.loading": { - background: "repeating-linear-gradient(-45deg, #c00 0, #c00 33%, #333 0, #333 66%)", - background_size: "20px", - animation: "loading_indicator 1s infinite linear", - }, - ".workspace-pathinfo img": { - max_width: "100%", - }, - "@keyframes loading_indicator": { - from: { - background_position: "0px 0px", - }, - to: { - background_position: "-40px 0px", - } - }, - } -} diff --git a/pmrapp_server/src/http/workspace.rs b/pmrapp_server/src/http/workspace.rs deleted file mode 100644 index aa207a7..0000000 --- a/pmrapp_server/src/http/workspace.rs +++ /dev/null @@ -1,160 +0,0 @@ -use axum::{ - Json, - Router, - extract::{Extension, Path}, - http::header, - response::{IntoResponse, Redirect, Response}, - routing::get, -}; -use pmrcore::{ - repo::{ - PathObjectInfo, - RemoteInfo, - }, -}; -use pmrrepo::{ - backend::Backend, - handle::GitResultTarget, -}; -use std::io::Write; - -use crate::http::{ - api, - AppContext, - Error, - Html, - page, -}; -use pmrapp_client::App; -use pmrapp_client::sauron::Render; - - -pub fn router() -> Router { - Router::new() - .route("/", get(render_workspace_listing)) - .route("/:workspace_id/", get(render_workspace)) - .route("/:workspace_id/file/:commit_id/", get(render_workspace_pathinfo_workspace_id_commit_id)) - .route("/:workspace_id/file/:commit_id/*path", get(render_workspace_pathinfo_workspace_id_commit_id_path)) - .route("/:workspace_id/raw/:commit_id/*path", get(raw_workspace_pathinfo_workspace_id_commit_id_path)) -} - -async fn render_workspace_listing(ctx: Extension) -> Response { - match api::workspace::api_workspace(ctx).await { - Ok(Json(workspace_listing)) => { - let app = App::with_workspace_listing(workspace_listing); - let content = page::index(&app).render_to_string(); - Html(content).into_response() - }, - Err(e) => Error::from(e).into_response() - } -} - -async fn render_workspace( - ctx: Extension, - path: Path, -) -> Response { - let workspace_id = path.0; - match api::workspace::api_workspace_top(ctx, path).await { - Ok(Json(repo_result)) => { - let app = App::with_workspace_top(workspace_id, repo_result); - let content = page::index(&app).render_to_string(); - Html(content).into_response() - }, - Err(e) => Error::from(e).into_response() - } -} - -async fn render_workspace_pathinfo_workspace_id_commit_id( - ctx: Extension, - path: Path<(i64, String)>, -) -> Response { - let workspace_id = path.0.0; - let commit_id = path.1.clone(); - match api::workspace::api_workspace_pathinfo_workspace_id_commit_id(ctx, path).await { - Ok(Json(path_info)) => { - // XXX instead of None we have an empty string for path... - let app = App::with_workspace_pathinfo(workspace_id, commit_id, "".to_string(), path_info); - let content = page::index(&app).render_to_string(); - Html(content).into_response() - }, - Err(e) => Error::from(e).into_response() - } -} - -async fn render_workspace_pathinfo_workspace_id_commit_id_path( - ctx: Extension, - path: Path<(i64, String, String)>, -) -> Response { - let workspace_id = path.0.0; - let commit_id = path.1.clone(); - let filepath = path.2.clone(); - match api::workspace::api_workspace_pathinfo_workspace_id_commit_id_path(ctx, path).await { - Ok(Json(path_info)) => { - let app = App::with_workspace_pathinfo(workspace_id, commit_id, filepath, path_info); - let content = page::index(&app).render_to_string(); - Html(content).into_response() - }, - Err(e) => Error::from(e).into_response() - } -} - -async fn raw_workspace_pathinfo_workspace_id_commit_id_path( - ctx: Extension, - path: Path<(i64, String, String)>, -) -> Response { - let workspace_id = path.0.0; - let commit_id = path.1.clone(); - let filepath = path.2.clone(); - - let backend = Backend::new(ctx.db.clone(), (&ctx.config.pmr_repo_root).into()); - let handle = match backend.git_handle(workspace_id).await { - Ok(handle) => handle, - Err(e) => return Error::from(e).into_response() - }; - - let result = match handle.pathinfo( - Some(&commit_id), - Some(&filepath), - ) { - Ok(result) => { - let mut buffer = >::new(); - // The following is a !Send Future (async) so.... - // handle.stream_result_blob(&mut blob, &result).await?; - // Ok(blob) - - match &result.target() { - GitResultTarget::Object(object) => { - let info: PathObjectInfo = object.into(); - match info { - PathObjectInfo::FileInfo(info) => { - // possible to avoid copying these bytes? - match (&mut buffer).write(&object.object.data) { - Ok(_) => Ok(( - [(header::CONTENT_TYPE, info.mime_type)], - buffer - ).into_response()), - Err(_) => Err(Error::Error), - } - }, - _ => { - log::info!("failed to get blob from object"); - Err(Error::NotFound) - } - } - } - GitResultTarget::RemoteInfo(RemoteInfo { location, commit, subpath, .. }) => { - // XXX this should be a redirect - Ok(Redirect::temporary( - &format!("{}/raw/{}/{}", location, commit, subpath) - ).into_response()) - }, - } - }, - Err(e) => { - // TODO log the URI triggering these messages? - log::info!("handle.pathinfo error: {:?}", e); - Err(Error::NotFound) - } - }; - result.unwrap_or_else(|e| Error::from(e).into_response()) -} diff --git a/pmrapp_server/src/lib.rs b/pmrapp_server/src/lib.rs deleted file mode 100644 index 31da9a4..0000000 --- a/pmrapp_server/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod config; -pub mod http; diff --git a/pmrapp_server/src/main.rs b/pmrapp_server/src/main.rs deleted file mode 100644 index 82c5f74..0000000 --- a/pmrapp_server/src/main.rs +++ /dev/null @@ -1,19 +0,0 @@ -use clap::Parser; -use pmrmodel::backend::db::SqliteBackend; -use pmrapp_server::config::Config; -use pmrapp_server::http; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - dotenvy::dotenv().ok(); - stderrlog::new() - .module(module_path!()) - .verbosity(2) - .timestamp(stderrlog::Timestamp::Second) - .init() - .unwrap(); - let config = Config::parse(); - let backend = SqliteBackend::from_url(&config.pmrapp_db_url).await?; - http::serve(config, backend).await; - Ok(()) -}