get homepage/register mixup sorted out; serve app data on homepage api route
@ -2,8 +2,8 @@
|
||||
use kinode_process_lib::{
|
||||
await_message, call_init, get_blob,
|
||||
http::{
|
||||
bind_http_path, bind_http_static_path, get_mime_type, serve_index_html, serve_ui,
|
||||
HttpServerError,
|
||||
bind_http_path, bind_http_static_path, get_mime_type, send_response, serve_index_html,
|
||||
serve_ui, HttpServerError, HttpServerRequest, IncomingHttpRequest, StatusCode,
|
||||
},
|
||||
println,
|
||||
vfs::{FileType, VfsAction, VfsRequest, VfsResponse},
|
||||
@ -27,6 +27,14 @@ enum HomepageRequest {
|
||||
Remove,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct HomepageApp {
|
||||
package_name: String,
|
||||
path: String,
|
||||
label: String,
|
||||
base64_icon: String,
|
||||
}
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "wit",
|
||||
world: "process",
|
||||
@ -35,7 +43,7 @@ wit_bindgen::generate!({
|
||||
},
|
||||
});
|
||||
|
||||
const HOME_PAGE: &str = include_str!("../../pkg/ui/index.html");
|
||||
const HOME_PAGE: &str = include_str!("index.html");
|
||||
|
||||
const APP_TEMPLATE: &str = r#"
|
||||
<a class="app-link" id="${package_name}" href="/${path}">
|
||||
@ -46,95 +54,121 @@ const APP_TEMPLATE: &str = r#"
|
||||
|
||||
call_init!(init);
|
||||
|
||||
// Copied in from process_lib serve_ui. see https://github.com/kinode-dao/process_lib/blob/main/src/http.rs
|
||||
fn static_serve_dir(
|
||||
our: &Address,
|
||||
directory: &str,
|
||||
authenticated: bool,
|
||||
local_only: bool,
|
||||
paths: Vec<&str>,
|
||||
) -> anyhow::Result<()> {
|
||||
serve_index_html(our, directory, authenticated, local_only, paths)?;
|
||||
|
||||
let initial_path = format!("{}/pkg/{}", our.package_id(), directory);
|
||||
println!("initial path: {}", initial_path);
|
||||
|
||||
let mut queue = VecDeque::new();
|
||||
queue.push_back(initial_path.clone());
|
||||
|
||||
while let Some(path) = queue.pop_front() {
|
||||
let Ok(directory_response) = KiRequest::to(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&VfsRequest {
|
||||
path,
|
||||
action: VfsAction::ReadDir,
|
||||
})?)
|
||||
.send_and_await_response(5)?
|
||||
else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"serve_ui: no response for path: {}",
|
||||
initial_path
|
||||
));
|
||||
};
|
||||
|
||||
let directory_body = serde_json::from_slice::<VfsResponse>(directory_response.body())?;
|
||||
|
||||
// Determine if it's a file or a directory and handle appropriately
|
||||
match directory_body {
|
||||
VfsResponse::ReadDir(directory_info) => {
|
||||
for entry in directory_info {
|
||||
match entry.file_type {
|
||||
// If it's a file, serve it statically
|
||||
FileType::File => {
|
||||
KiRequest::to(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&VfsRequest {
|
||||
path: entry.path.clone(),
|
||||
action: VfsAction::Read,
|
||||
})?)
|
||||
.send_and_await_response(5)??;
|
||||
|
||||
let Some(blob) = get_blob() else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"serve_ui: no blob for {}",
|
||||
entry.path
|
||||
));
|
||||
};
|
||||
|
||||
let content_type = get_mime_type(&entry.path);
|
||||
|
||||
println!("binding {}", entry.path.replace(&initial_path, ""));
|
||||
|
||||
bind_http_static_path(
|
||||
entry.path.replace(&initial_path, ""),
|
||||
authenticated, // Must be authenticated
|
||||
local_only, // Is not local-only
|
||||
Some(content_type),
|
||||
blob.bytes,
|
||||
)?;
|
||||
}
|
||||
FileType::Directory => {
|
||||
// Push the directory onto the queue
|
||||
queue.push_back(entry.path);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"serve_ui: unexpected response for path: {:?}",
|
||||
directory_body
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
/// bind to root path on http_server (we have special dispensation to do so!)
|
||||
fn bind_index(our: &str, apps: &HashMap<ProcessId, String>) {
|
||||
bind_http_static_path(
|
||||
"/",
|
||||
true,
|
||||
false,
|
||||
Some("text/html".to_string()),
|
||||
HOME_PAGE
|
||||
.replace("${our}", our)
|
||||
.replace(
|
||||
"${apps}",
|
||||
&apps
|
||||
.values()
|
||||
.map(String::as_str)
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n"),
|
||||
)
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.expect("failed to bind to /");
|
||||
}
|
||||
|
||||
// // Copied in from process_lib serve_ui. see https://github.com/kinode-dao/process_lib/blob/main/src/http.rs
|
||||
// fn static_serve_dir(
|
||||
// our: &Address,
|
||||
// directory: &str,
|
||||
// authenticated: bool,
|
||||
// local_only: bool,
|
||||
// paths: Vec<&str>,
|
||||
// ) -> anyhow::Result<()> {
|
||||
// serve_index_html(our, directory, authenticated, local_only, paths)?;
|
||||
|
||||
// let initial_path = format!("{}/pkg/{}", our.package_id(), directory);
|
||||
// println!("initial path: {}", initial_path);
|
||||
|
||||
// let mut queue = VecDeque::new();
|
||||
// queue.push_back(initial_path.clone());
|
||||
|
||||
// while let Some(path) = queue.pop_front() {
|
||||
// let Ok(directory_response) = KiRequest::to(("our", "vfs", "distro", "sys"))
|
||||
// .body(serde_json::to_vec(&VfsRequest {
|
||||
// path,
|
||||
// action: VfsAction::ReadDir,
|
||||
// })?)
|
||||
// .send_and_await_response(5)?
|
||||
// else {
|
||||
// return Err(anyhow::anyhow!(
|
||||
// "serve_ui: no response for path: {}",
|
||||
// initial_path
|
||||
// ));
|
||||
// };
|
||||
|
||||
// let directory_body = serde_json::from_slice::<VfsResponse>(directory_response.body())?;
|
||||
|
||||
// // Determine if it's a file or a directory and handle appropriately
|
||||
// match directory_body {
|
||||
// VfsResponse::ReadDir(directory_info) => {
|
||||
// for entry in directory_info {
|
||||
// match entry.file_type {
|
||||
// // If it's a file, serve it statically
|
||||
// FileType::File => {
|
||||
// KiRequest::to(("our", "vfs", "distro", "sys"))
|
||||
// .body(serde_json::to_vec(&VfsRequest {
|
||||
// path: entry.path.clone(),
|
||||
// action: VfsAction::Read,
|
||||
// })?)
|
||||
// .send_and_await_response(5)??;
|
||||
|
||||
// let Some(blob) = get_blob() else {
|
||||
// return Err(anyhow::anyhow!(
|
||||
// "serve_ui: no blob for {}",
|
||||
// entry.path
|
||||
// ));
|
||||
// };
|
||||
|
||||
// let content_type = get_mime_type(&entry.path);
|
||||
|
||||
// println!("binding {}", entry.path.replace(&initial_path, ""));
|
||||
|
||||
// bind_http_static_path(
|
||||
// entry.path.replace(&initial_path, ""),
|
||||
// authenticated, // Must be authenticated
|
||||
// local_only, // Is not local-only
|
||||
// Some(content_type),
|
||||
// blob.bytes,
|
||||
// )?;
|
||||
// }
|
||||
// FileType::Directory => {
|
||||
// // Push the directory onto the queue
|
||||
// queue.push_back(entry.path);
|
||||
// }
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// _ => {
|
||||
// return Err(anyhow::anyhow!(
|
||||
// "serve_ui: unexpected response for path: {:?}",
|
||||
// directory_body
|
||||
// ))
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
fn init(our: Address) {
|
||||
let mut apps: HashMap<ProcessId, String> = HashMap::new();
|
||||
let mut app_data: HashMap<ProcessId, HomepageApp> = HashMap::new();
|
||||
|
||||
static_serve_dir(&our, "ui", true, false, vec!["/", "/login"]);
|
||||
// static_serve_dir(&our, "index.html", true, false, vec!["/"]);
|
||||
bind_index(&our.node, &apps);
|
||||
|
||||
bind_http_static_path(
|
||||
"/our",
|
||||
@ -156,6 +190,8 @@ fn init(our: Address) {
|
||||
)
|
||||
.expect("failed to bind to /our.js");
|
||||
|
||||
bind_http_path("/apps", true, true).expect("failed to bind /apps");
|
||||
|
||||
loop {
|
||||
let Ok(ref message) = await_message() else {
|
||||
// we never send requests, so this will never happen
|
||||
@ -175,6 +211,15 @@ fn init(our: Address) {
|
||||
if let Ok(request) = serde_json::from_slice::<HomepageRequest>(message.body()) {
|
||||
match request {
|
||||
HomepageRequest::Add { label, icon, path } => {
|
||||
app_data.insert(
|
||||
message.source().process.clone(),
|
||||
HomepageApp {
|
||||
package_name: message.source().clone().package().to_string(),
|
||||
path: path.clone(),
|
||||
label: label.clone(),
|
||||
base64_icon: icon.clone(),
|
||||
},
|
||||
);
|
||||
apps.insert(
|
||||
message.source().process.clone(),
|
||||
APP_TEMPLATE
|
||||
@ -197,13 +242,43 @@ fn init(our: Address) {
|
||||
.replace("${label}", &label)
|
||||
.replace("${base64_icon}", &icon),
|
||||
);
|
||||
// bind_index(&our.node, &apps);
|
||||
bind_index(&our.node, &apps);
|
||||
}
|
||||
HomepageRequest::Remove => {
|
||||
apps.remove(&message.source().process);
|
||||
// bind_index(&our.node, &apps);
|
||||
bind_index(&our.node, &apps);
|
||||
}
|
||||
}
|
||||
} else if let Ok(request) = serde_json::from_slice::<HttpServerRequest>(message.body())
|
||||
{
|
||||
match request {
|
||||
HttpServerRequest::Http(incoming) => {
|
||||
let path = incoming.bound_path(None);
|
||||
println!("on path: {}", path);
|
||||
if path == "/apps" {
|
||||
send_response(
|
||||
StatusCode::OK,
|
||||
Some(HashMap::from([(
|
||||
"Content-Type".to_string(),
|
||||
"application/json".to_string(),
|
||||
)])),
|
||||
app_data
|
||||
.values()
|
||||
.map(|app| serde_json::to_string(app).unwrap())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
);
|
||||
}
|
||||
send_response(
|
||||
StatusCode::OK,
|
||||
Some(HashMap::new()),
|
||||
"hello".as_bytes().to_vec(),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,10 @@
|
||||
"on_exit": "Restart",
|
||||
"request_networking": false,
|
||||
"request_capabilities": [
|
||||
"http_server:distro:sys",
|
||||
"vfs:distro:sys"
|
||||
"http_server:distro:sys"
|
||||
],
|
||||
"grant_capabilities": [
|
||||
"http_server:distro:sys",
|
||||
"vfs:distro:sys"
|
||||
"http_server:distro:sys"
|
||||
],
|
||||
"public": false
|
||||
}
|
||||
|
@ -38,8 +38,7 @@
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "GENERATE_SOURCEMAP=false react-scripts build",
|
||||
"build:copy": "npm run build && npm run copy",
|
||||
"copy": "mkdir -p ../../../src/register-ui/build && rm -rf ../../../src/register-ui/build/* && cp -r build/* ../../../src/register-ui/build/",
|
||||
"build:copy": "npm run build",
|
||||
"inline": "node ./add-inline-tags.js && cd build && inline-source ./index.html > ./inline-index.html && cd ..",
|
||||
"build-inline": "npm run build && npm run inline",
|
||||
"test": "react-scripts test",
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 431 B After Width: | Height: | Size: 431 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |