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