rustbasic_core/
inertia.rs1use crate::requests::Request;
2use crate::{IntoResponse, Response};
3use crate::http::{header, StatusCode, HeaderValue};
4use crate::serde_json::{json, Value};
5use std::fs;
6
7pub fn inertia(req: &Request, component: &str, props: Value) -> Response {
9 let is_inertia = req.headers.get("x-inertia").map(|v| v == "true").unwrap_or(false);
10 let url = req.path.clone();
11
12 let version = "";
14
15 let errors: std::collections::HashMap<String, String> = req.session.get("errors").unwrap_or_default();
16 req.session.remove("errors");
17
18 let success: Option<String> = req.session.get("success");
19 req.session.remove("success");
20
21 let error: Option<String> = req.session.get("error");
22 req.session.remove("error");
23
24 let warning: Option<String> = req.session.get("warning");
25 req.session.remove("warning");
26
27 let info: Option<String> = req.session.get("info");
28 req.session.remove("info");
29
30 let mut props = props;
31 if let Value::Object(ref mut map) = props {
32 map.insert("errors".to_string(), json!(errors));
33 map.insert("flash".to_string(), json!({
34 "success": success,
35 "error": error,
36 "warning": warning,
37 "info": info
38 }));
39 let named_routes = crate::router::get_named_routes();
40 map.insert("routes".to_string(), json!(named_routes));
41 let cfg = crate::Config::load();
42 map.insert("app_url".to_string(), json!(cfg.app_url));
43 }
44
45 let page_object = json!({
46 "component": component,
47 "props": props,
48 "url": url,
49 "version": version
50 });
51
52 if is_inertia {
53 let body = crate::serde_json::to_string(&page_object).unwrap_or_default();
55 crate::http::Response::builder()
56 .status(StatusCode::OK)
57 .header(header::CONTENT_TYPE, "application/json")
58 .header("X-Inertia", "true")
59 .header(header::VARY, "X-Inertia")
60 .body(body.into_bytes())
61 .unwrap()
62 .into_response()
63 } else {
64 let vite_assets = get_vite_assets(req);
66 let ctx = crate::serde_json::json!({
67 "page": page_object,
68 "vite_assets": vite_assets,
69 });
70
71 let mut response = crate::view::view(req, "app.rb.html", ctx).into_response();
72 response.headers_mut().insert(
73 header::VARY,
74 HeaderValue::from_static("X-Inertia"),
75 );
76 response
77 }
78}
79
80pub fn get_vite_assets(req: &Request) -> String {
82 let cfg = crate::Config::load();
83 let debug = cfg.app_debug;
84
85 if debug {
86 let port = cfg.vite_port;
87 let mut display_host = "localhost".to_string();
89 if let Some(host_hdr) = req.headers.get("host") {
90 let parts: Vec<&str> = host_hdr.split(':').collect();
91 if !parts.is_empty() {
92 let ip_or_domain = parts[0];
93 if ip_or_domain != "localhost" && ip_or_domain != "127.0.0.1" && !ip_or_domain.is_empty() {
94 display_host = ip_or_domain.to_string();
95 }
96 }
97 }
98
99 if display_host == "localhost" {
100 let host = &cfg.app_host;
101 if host != "0.0.0.0" && !host.is_empty() {
102 display_host = host.clone();
103 }
104 }
105
106 format!(
108 r#"
109 <!-- Vite Dev Server Integration -->
110 <script type="module">
111 import RefreshRuntime from 'http://{host}:{port}/@react-refresh';
112 RefreshRuntime.injectIntoGlobalHook(window);
113 window.$RefreshReg$ = () => {{}};
114 window.$RefreshSig$ = () => (type) => type;
115 window.__vite_plugin_react_preamble_installed__ = true;
116 </script>
117 <script type="module" src="http://{host}:{port}/src/resources/js/main.tsx"></script>
118 "#,
119 host = display_host,
120 port = port
121 )
122 } else {
123 let mut manifest_content = String::new();
125 let paths = ["src/dist/.vite/manifest.json", "src/dist/manifest.json"];
126 for path in &paths {
127 if let Ok(content) = fs::read_to_string(path) {
128 manifest_content = content;
129 break;
130 }
131 }
132
133 if manifest_content.is_empty() {
135 if let Some(f) = crate::server::get_embedded_public_fn() {
136 if let Some(file) = f(".vite/manifest.json")
137 && let Ok(content) = String::from_utf8(file.data.to_vec()) {
138 manifest_content = content;
139 } else if let Some(file) = f("manifest.json")
140 && let Ok(content) = String::from_utf8(file.data.to_vec()) {
141 manifest_content = content;
142 }
143 }
144 }
145
146 if !manifest_content.is_empty()
147 && let Ok(manifest) = crate::serde_json::from_str::<Value>(&manifest_content)
148 && let Some(entry) = manifest.get("src/resources/js/main.tsx") {
149 let file = entry.get("file").and_then(|f| f.as_str()).unwrap_or("assets/main.js");
150 let mut assets_html = format!(r#"<script type="module" src="/{}"></script>"#, file);
151
152 if let Some(css_arr) = entry.get("css").and_then(|c| c.as_array()) {
153 for css in css_arr {
154 if let Some(css_str) = css.as_str() {
155 assets_html = format!(r#"<link rel="stylesheet" href="/{}" />"#, css_str) + &assets_html;
156 }
157 }
158 }
159 return assets_html;
160 }
161
162 r#"<script type="module" src="/assets/main.js"></script>"#.to_string()
164 }
165}