@@ -576,6 +576,21 @@ void HttpSession::process_request()
576576 std::string file_path = target;
577577 if (file_path == " /" || file_path.empty ()) file_path = " /loading.html" ;
578578
579+ // During startup, redirect all HTML pages to loading.html
580+ // (except loading.html itself). Only loading.html + sync_status
581+ // + currency_info work during early startup.
582+ if (file_path != " /loading.html" && file_path.find (" .html" ) != std::string::npos) {
583+ bool node_ready = mining_interface_->is_node_ready ();
584+ if (!node_ready) {
585+ response.result (http::status::temporary_redirect);
586+ response.set (http::field::location, " /loading.html" );
587+ response.body () = " " ;
588+ response.prepare_payload ();
589+ send_response (std::move (response));
590+ return ;
591+ }
592+ }
593+
579594 std::error_code fec;
580595 std::filesystem::path base = std::filesystem::weakly_canonical (dashboard_dir);
581596 std::filesystem::path requested = base / file_path.substr (1 );
@@ -3486,7 +3501,7 @@ nlohmann::json MiningInterface::get_pplns_for_tip(const std::string& tip_hash)
34863501std::pair<std::string, std::string> MiningInterface::get_cached_window_response ()
34873502{
34883503 std::lock_guard<std::mutex> lock (m_window_cache_mutex);
3489- // Check if cache is valid (has etag = current tip )
3504+ // Check if cache is valid (has real tip hash, not empty/stub )
34903505 if (!m_window_cache_etag.empty ())
34913506 return {m_window_cache_json, m_window_cache_etag};
34923507
@@ -3497,8 +3512,14 @@ std::pair<std::string, std::string> MiningInterface::get_cached_window_response(
34973512 tip = data[" best_hash" ].get <std::string>();
34983513 if (tip.size () > 16 ) tip = tip.substr (0 , 16 );
34993514 }
3515+ // Don't cache stub responses (empty best_hash) — during early startup
3516+ // the sharechain_window_fn may not be wired yet or return empty data.
3517+ // Return the stub without caching so next request retries.
3518+ if (tip.empty ()) {
3519+ return {data.dump (), " " };
3520+ }
35003521 m_window_cache_json = data.dump ();
3501- m_window_cache_etag = tip. empty () ? " none " : tip ;
3522+ m_window_cache_etag = tip;
35023523 return {m_window_cache_json, m_window_cache_etag};
35033524}
35043525
@@ -4369,6 +4390,36 @@ nlohmann::json MiningInterface::rest_sync_status()
43694390 return result;
43704391}
43714392
4393+ bool MiningInterface::is_node_ready ()
4394+ {
4395+ // Check sharechain has verified shares
4396+ if (m_sharechain_stats_fn) {
4397+ auto sc = m_sharechain_stats_fn ();
4398+ if (sc.value (" verified_count" , 0 ) <= 0 )
4399+ return false ;
4400+ } else {
4401+ return false ;
4402+ }
4403+
4404+ // Check work template exists
4405+ {
4406+ std::lock_guard<std::mutex> lock (m_work_mutex);
4407+ if (m_cached_template.is_null () ||
4408+ m_cached_template.value (" coinbasevalue" , uint64_t (0 )) == 0 )
4409+ return false ;
4410+ }
4411+
4412+ // Check sharechain window has data (not a stub)
4413+ if (m_sharechain_window_fn) {
4414+ // Don't call the fn here (expensive) — just check if it's wired
4415+ // The window cache will be populated on first real request
4416+ } else {
4417+ return false ;
4418+ }
4419+
4420+ return true ;
4421+ }
4422+
43724423// ── Log endpoints (read directly from debug.log) ───────────────────────
43734424
43744425static std::string tail_file (const std::filesystem::path& path, size_t max_lines)
0 commit comments