reifydb_sub_server_admin/
handlers.rs1use axum::{
15 Json,
16 body::Body,
17 extract::{Path, State},
18 http::{Response, StatusCode, header},
19 response::IntoResponse,
20};
21use serde::{Deserialize, Serialize};
22use serde_json::json;
23
24use crate::{assets, state::AdminState};
25
26#[derive(Debug, Deserialize)]
28pub struct LoginRequest {
29 pub token: String,
30}
31
32#[derive(Debug, Serialize)]
34pub struct LoginResponse {
35 pub success: bool,
36 pub message: Option<String>,
37 pub session_token: Option<String>,
38}
39
40#[derive(Debug, Serialize)]
42pub struct AuthStatusResponse {
43 pub auth_required: bool,
44 pub authenticated: bool,
45}
46
47pub async fn handle_login(State(state): State<AdminState>, Json(request): Json<LoginRequest>) -> impl IntoResponse {
49 if !state.auth_required() {
50 return (
51 StatusCode::OK,
52 Json(LoginResponse {
53 success: true,
54 message: Some("Auth not required".to_string()),
55 session_token: None,
56 }),
57 );
58 }
59
60 if state.auth_token() == Some(&request.token) {
61 (
63 StatusCode::OK,
64 Json(LoginResponse {
65 success: true,
66 message: None,
67 session_token: Some("temp_session_token".to_string()),
68 }),
69 )
70 } else {
71 (
72 StatusCode::BAD_REQUEST,
73 Json(LoginResponse {
74 success: false,
75 message: Some("Invalid token".to_string()),
76 session_token: None,
77 }),
78 )
79 }
80}
81
82pub async fn handle_logout() -> impl IntoResponse {
84 (
85 StatusCode::OK,
86 Json(json!({
87 "success": true,
88 "message": "Logged out"
89 })),
90 )
91}
92
93pub async fn handle_auth_status(State(state): State<AdminState>) -> impl IntoResponse {
95 (
96 StatusCode::OK,
97 Json(AuthStatusResponse {
98 auth_required: state.auth_required(),
99 authenticated: !state.auth_required(),
101 }),
102 )
103}
104
105#[derive(Debug, Deserialize)]
107pub struct ExecuteRequest {
108 pub query: String,
109}
110
111pub async fn handle_execute(
113 State(_state): State<AdminState>,
114 Json(request): Json<ExecuteRequest>,
115) -> impl IntoResponse {
116 (
118 StatusCode::OK,
119 Json(json!({
120 "success": true,
121 "message": "Query execution not yet implemented",
122 "query": request.query
123 })),
124 )
125}
126
127const FALLBACK_HTML: &str = r#"<!DOCTYPE html>
128<html>
129<head>
130 <title>ReifyDB Admin</title>
131 <style>
132 body { font-family: system-ui; max-width: 800px; margin: 50px auto; padding: 20px; }
133 .error { background: #fee; padding: 20px; border-radius: 5px; }
134 </style>
135</head>
136<body>
137 <h1>ReifyDB Admin Console</h1>
138 <div class="error">
139 <p>React app not found. Please build the webapp first.</p>
140 </div>
141</body>
142</html>"#;
143
144pub async fn serve_index() -> impl IntoResponse {
146 if let Some(file) = assets::get_embedded_file("index.html") {
147 Response::builder()
148 .status(StatusCode::OK)
149 .header(header::CONTENT_TYPE, file.mime_type)
150 .body(Body::from(file.content.to_vec()))
151 .unwrap()
152 } else {
153 Response::builder()
154 .status(StatusCode::OK)
155 .header(header::CONTENT_TYPE, "text/html")
156 .body(Body::from(FALLBACK_HTML))
157 .unwrap()
158 }
159}
160
161pub async fn serve_static(Path(path): Path<String>) -> impl IntoResponse {
163 let clean_path = path.strip_prefix('/').unwrap_or(&path);
166 let full_path = format!("assets/{}", clean_path);
167
168 if let Some(file) = assets::get_embedded_file(&full_path) {
169 Response::builder()
170 .status(StatusCode::OK)
171 .header(header::CONTENT_TYPE, file.mime_type)
172 .header(header::CACHE_CONTROL, "public, max-age=31536000")
173 .body(Body::from(file.content.to_vec()))
174 .unwrap()
175 } else {
176 Response::builder()
177 .status(StatusCode::NOT_FOUND)
178 .header(header::CONTENT_TYPE, "text/plain")
179 .body(Body::from(format!("Static file not found: {}", full_path)))
180 .unwrap()
181 }
182}