Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: add explorer flag and build folder #2997

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/katana/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ tracing.workspace = true
tracing-log.workspace = true
tracing-subscriber.workspace = true
url.workspace = true
tiny_http = "0.12"

[dev-dependencies]
assert_matches.workspace = true
Expand Down
43 changes: 42 additions & 1 deletion crates/katana/cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ use tracing::{info, Subscriber};
use tracing_log::LogTracer;
use tracing_subscriber::{fmt, EnvFilter};
use url::Url;
use katana_rpc::cors::HeaderValue;

use crate::file::NodeArgsConfig;
use crate::options::*;
use crate::utils;
use crate::utils::{parse_seed, LogFormat};
use crate::explorer::ExplorerServer;

pub(crate) const LOG_TARGET: &str = "katana::cli";

Expand Down Expand Up @@ -110,11 +112,17 @@ pub struct NodeArgs {
#[cfg(feature = "slot")]
#[command(flatten)]
pub slot: SlotOptions,

#[command(flatten)]
pub explorer: ExplorerOptions,
}

impl NodeArgs {
pub async fn execute(&self) -> Result<()> {
// Initialize logging first
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Initialize logging first

Those comments don't add any relevant information.

self.init_logging()?;

// Finally start the node
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Finally start the node

self.start_node().await
}

Expand All @@ -130,6 +138,29 @@ impl NodeArgs {
// Launch the node
let handle = node.launch().await.context("failed to launch node")?;

// Then start the explorer if enabled
if self.explorer.explorer {
let build_dir = self.explorer.explorer_build_dir
.clone()
.unwrap_or_else(|| {
PathBuf::from("crates/katana")
.join("explorer")
.join("build")
});

if !build_dir.exists() {
anyhow::bail!("Explorer build directory not found at {:?}. Please build the explorer first or specify a different path with --explorer-build-dir", build_dir);
}

let explorer = ExplorerServer::new(
self.explorer.explorer_port,
build_dir,
)?;

explorer.start()?;
}


// Wait until an OS signal (ie SIGINT, SIGTERM) is received or the node is shutdown.
tokio::select! {
_ = dojo_utils::signal::wait_signals() => {
Expand Down Expand Up @@ -216,12 +247,22 @@ impl NodeArgs {
modules
};

let mut cors_origins = self.server.http_cors_origins.clone();

// Add explorer URL to CORS origins if explorer is enabled
if self.explorer.explorer {
cors_origins.push(
HeaderValue::from_str(&format!("http://127.0.0.1:{}", self.explorer.explorer_port))
.context("Failed to create CORS header")?
);
}

Ok(RpcConfig {
apis: modules,
port: self.server.http_port,
addr: self.server.http_addr,
max_connections: self.server.max_connections,
cors_origins: self.server.http_cors_origins.clone(),
cors_origins: cors_origins,
max_event_page_size: Some(self.server.max_event_page_size),
max_proof_keys: Some(self.server.max_proof_keys),
})
Expand Down
96 changes: 96 additions & 0 deletions crates/katana/cli/src/explorer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// crates/katana/cli/src/explorer.rs
use std::path::PathBuf;
use anyhow::{Result};
use tiny_http::{Server, Response};
use std::thread;
use tracing::info;

pub struct ExplorerServer {
port: u16,
build_dir: PathBuf,
}

impl ExplorerServer {
pub fn new(port: u16, build_dir: PathBuf) -> Result<Self> {
Ok(Self {
port,
build_dir,
})
}

pub fn start(&self) -> Result<()> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible at this point to inject the Katana url (which can be modified by the user) into the env somehow, which would make the explorer always indexing by default the katana that has just started?

// Create the server
let addr = format!("127.0.0.1:{}", self.port);
let server = Server::http(&addr)
.map_err(|e| anyhow::anyhow!("Failed to start explorer server: {}", e))?;

// Handle requests in a separate thread
let build_dir = self.build_dir.clone();
thread::spawn(move || {
info!(
target: "katana",
"Explorer server started. addr=http://{}",
addr,
);

for request in server.incoming_requests() {
let path = request.url().to_string();
info!(
target: "katana::explorer",
"Received request for: {}",
path
);

let file_path = if path == "/" {
build_dir.join("index.html")
} else {
build_dir.join(&path[1..])
};

if let Ok(content) = std::fs::read(&file_path) {
let content_type = match file_path.extension().and_then(|s| s.to_str()) {
Some("html") => "text/html",
Some("js") => "application/javascript",
Some("css") => "text/css",
Some("png") => "image/png",
Some("svg") => "image/svg+xml",
Some("json") => "application/json",
_ => "application/octet-stream",
};

let response = Response::from_data(content)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even with those headers, when I start Katana without providing the build path into explorer repo (/dist), I have those errors:
image

.with_header(tiny_http::Header {
field: "Content-Type".parse().unwrap(),
value: content_type.parse().unwrap(),
})
.with_header(tiny_http::Header {
field: "Access-Control-Allow-Origin".parse().unwrap(),
value: "*".parse().unwrap(),
})
.with_header(tiny_http::Header {
field: "Access-Control-Allow-Methods".parse().unwrap(),
value: "GET, POST, OPTIONS".parse().unwrap(),
})
.with_header(tiny_http::Header {
field: "Access-Control-Allow-Headers".parse().unwrap(),
value: "Content-Type".parse().unwrap(),
});

let _ = request.respond(response);
} else {
// If file not found, serve index.html for SPA routing
if let Ok(content) = std::fs::read(build_dir.join("index.html")) {
let response = Response::from_data(content)
.with_header(tiny_http::Header {
field: "Content-Type".parse().unwrap(),
value: "text/html".parse().unwrap(),
});
let _ = request.respond(response);
}
}
}
});

Ok(())
}
}
1 change: 1 addition & 0 deletions crates/katana/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod args;
pub mod file;
pub mod options;
pub mod utils;
pub mod explorer;

pub use args::NodeArgs;
pub use options::*;
18 changes: 18 additions & 0 deletions crates/katana/cli/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use url::Url;
#[cfg(feature = "server")]
use crate::utils::{deserialize_cors_origins, serialize_cors_origins};
use crate::utils::{parse_block_hash_or_number, parse_genesis, LogFormat};
use std::path::PathBuf;

const DEFAULT_DEV_SEED: &str = "0";
const DEFAULT_DEV_ACCOUNTS: u16 = 10;
Expand Down Expand Up @@ -357,6 +358,23 @@ pub struct SlotOptions {
pub controller: bool,
}

#[derive(Debug, Args, Clone, Default,Serialize, Deserialize)]
#[command(next_help_heading = "Explorer options")]
pub struct ExplorerOptions {
/// Enable and launch the explorer frontend
#[arg(long)]
#[serde(default)]
pub explorer: bool,

/// The port to run the explorer frontend on
#[arg(long = "explorer-port", default_value_t = 3001)]
pub explorer_port: u16,

/// Path to the explorer's build directory
#[arg(long = "explorer-build-dir")]
pub explorer_build_dir: Option<PathBuf>,
}

// ** Default functions to setup serde of the configuration file **
fn default_seed() -> String {
DEFAULT_DEV_SEED.to_string()
Expand Down
Loading