execute query from admin page and display the result
This commit is contained in:
51
src/db.rs
51
src/db.rs
@ -1,5 +1,7 @@
|
||||
use crate::data::{Game, Player, Team};
|
||||
use actix_web::{Error, error, web};
|
||||
use rusqlite::{Row, params};
|
||||
use serde_json::{Map, Value};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub type Pool = r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>;
|
||||
@ -12,6 +14,37 @@ const GAMES_QUERY: &str =
|
||||
const TEAMS_QUERY: &str =
|
||||
"select game, team, min(color), group_concat(player) from teams group by game, team";
|
||||
|
||||
pub async fn execute_query(pool: &Pool, query: &str) -> Result<String, Error> {
|
||||
let pool = pool.clone();
|
||||
let conn = web::block(move || pool.get())
|
||||
.await?
|
||||
.map_err(error::ErrorInternalServerError)?;
|
||||
match conn.execute(query, params![]) {
|
||||
Ok(val) => println!("{val:?}"),
|
||||
Err(er) => println!("error: {er:?}"),
|
||||
}
|
||||
Ok(String::from("success"))
|
||||
}
|
||||
|
||||
pub async fn execute_stmt(pool: &Pool, query: &str) -> Result<String, Error> {
|
||||
let pool = pool.clone();
|
||||
let conn = web::block(move || pool.get())
|
||||
.await?
|
||||
.map_err(|e| error::ErrorInternalServerError(e))?;
|
||||
let mut stmt = conn
|
||||
.prepare(query)
|
||||
.map_err(|e| error::ErrorInternalServerError(e))?;
|
||||
let column_names: Vec<String> = stmt.column_names().iter().map(|s| s.to_string()).collect();
|
||||
let rows = stmt
|
||||
.query_map([], |row| row_to_json(row, &column_names))
|
||||
.map_err(|e| error::ErrorInternalServerError(e))?;
|
||||
let mut res = vec![];
|
||||
for row in rows {
|
||||
res.push(row.map_err(|e| error::ErrorInternalServerError(e))?);
|
||||
}
|
||||
Ok(serde_json::to_string(&res).unwrap())
|
||||
}
|
||||
|
||||
pub async fn games(pool: &Pool) -> Result<Vec<Game>, Error> {
|
||||
let pool = pool.clone();
|
||||
let teams_result = teams(&pool).await?;
|
||||
@ -83,3 +116,21 @@ fn get_all_teams(conn: Connection) -> TeamsResult {
|
||||
})
|
||||
.and_then(Iterator::collect)
|
||||
}
|
||||
|
||||
fn row_to_json(row: &Row, column_names: &[String]) -> rusqlite::Result<Value> {
|
||||
let mut obj = Map::new();
|
||||
|
||||
for (i, name) in column_names.iter().enumerate() {
|
||||
let value: rusqlite::types::Value = row.get(i)?;
|
||||
let json_value = match value {
|
||||
rusqlite::types::Value::Null => Value::Null,
|
||||
rusqlite::types::Value::Integer(i) => Value::from(i),
|
||||
rusqlite::types::Value::Real(f) => Value::from(f),
|
||||
rusqlite::types::Value::Text(t) => Value::from(t),
|
||||
rusqlite::types::Value::Blob(_) => panic!("blob not supported"),
|
||||
};
|
||||
obj.insert(name.clone(), json_value);
|
||||
}
|
||||
|
||||
Ok(Value::Object(obj))
|
||||
}
|
||||
|
||||
27
src/main.rs
27
src/main.rs
@ -15,11 +15,12 @@ use actix::WrapFuture;
|
||||
use actix::dev::MessageResponse;
|
||||
use actix::dev::OneshotSender;
|
||||
use actix::prelude::Actor;
|
||||
use actix_web::{App, HttpResponse, HttpServer, Responder, get, web};
|
||||
use actix_web::{App, HttpResponse, HttpServer, Responder, get, post, web};
|
||||
use chrono::NaiveDateTime;
|
||||
use db::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use reqwest::Client;
|
||||
use serde::Deserialize;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@ -61,6 +62,12 @@ struct GamesActor {
|
||||
games: Arc<Mutex<Vec<Game>>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct QueryForm {
|
||||
query: String,
|
||||
is_read: bool,
|
||||
}
|
||||
|
||||
impl Actor for GamesActor {
|
||||
type Context = actix::Context<Self>;
|
||||
}
|
||||
@ -171,6 +178,19 @@ async fn list_of_games(games: web::Data<Arc<Mutex<Vec<Game>>>>) -> impl Responde
|
||||
.body(games_json)
|
||||
}
|
||||
|
||||
#[post("/admin/query")]
|
||||
async fn admin_query(pool: web::Data<Pool>, form: web::Form<QueryForm>) -> impl Responder {
|
||||
let res: String;
|
||||
if form.is_read {
|
||||
res = db::execute_stmt(&pool, &form.query).await.unwrap();
|
||||
} else {
|
||||
res = db::execute_query(&pool, &form.query).await.unwrap();
|
||||
}
|
||||
HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
.body(res)
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
unsafe {
|
||||
@ -197,8 +217,9 @@ async fn main() -> std::io::Result<()> {
|
||||
.service(game_page)
|
||||
.service(geojson_endpoint)
|
||||
.service(legend_data)
|
||||
.service(admin)
|
||||
.service(list_of_games)
|
||||
//.service(admin)
|
||||
//.service(admin_query)
|
||||
//.service(list_of_games)
|
||||
})
|
||||
.bind(("0.0.0.0", 8080))?
|
||||
.run()
|
||||
|
||||
@ -46,6 +46,13 @@
|
||||
</tr></thead>
|
||||
<tbody id="games_table"></tbody>
|
||||
</table>
|
||||
<form id="query_form">
|
||||
<p><label for="query">Compose a sql query:</label></p>
|
||||
<textarea id="query" name="query" rows="6" cols="60">select * from games limit 10;</textarea>
|
||||
<br>
|
||||
<input type="submit" value="Execute">
|
||||
</form>
|
||||
<pre id="query_result"></pre>
|
||||
<dialog id="team_dialog">
|
||||
<p><bold>Name: <span id="td_name"></span></bold></p>
|
||||
<p>Color: <span id="td_color"></span></p>
|
||||
@ -58,6 +65,34 @@
|
||||
const td_name = document.getElementById("td_name");
|
||||
const td_color = document.getElementById("td_color");
|
||||
const td_players = document.getElementById("td_players");
|
||||
const query_form = document.getElementById("query_form");
|
||||
|
||||
games_form.addEventListener("submit", refresh_games);
|
||||
query_form.addEventListener("submit", submit_query);
|
||||
|
||||
function submit_query(event) {
|
||||
event.preventDefault();
|
||||
fetch("/admin/query", {
|
||||
method: "POST",
|
||||
body: new URLSearchParams({
|
||||
'query': document.getElementById("query").value,
|
||||
'is_read': true,
|
||||
})
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`Response status: ${res.status}`);
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
document.getElementById("query_result").textContent = JSON.stringify(data, null, 2);
|
||||
console.log(data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Fetching data failed: ", error);
|
||||
});
|
||||
}
|
||||
function refresh_games(event) {
|
||||
event.preventDefault();
|
||||
fetch("/admin/games")
|
||||
@ -102,10 +137,9 @@
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Fetching legend data failed:", error);
|
||||
console.error("Fetching data failed:", error);
|
||||
});
|
||||
}
|
||||
games_form.addEventListener("submit", refresh_games);
|
||||
function showModal(name, color, players) {
|
||||
td_name.textContent = name;
|
||||
td_color.textContent = color;
|
||||
|
||||
Reference in New Issue
Block a user