diff --git a/server/.sqlx/query-4824bfa1ca8109255aa81c5b9744c4222a914ce2d09a112c4dda259c425b3587.json b/server/.sqlx/query-4824bfa1ca8109255aa81c5b9744c4222a914ce2d09a112c4dda259c425b3587.json new file mode 100644 index 000000000..0db008ca7 --- /dev/null +++ b/server/.sqlx/query-4824bfa1ca8109255aa81c5b9744c4222a914ce2d09a112c4dda259c425b3587.json @@ -0,0 +1,62 @@ +{ + "db_name": "SQLite", + "query": "SELECT * FROM de WHERE key = ?", + "describe": { + "columns": [ + { + "name": "key", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "name", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "tumonline_room_nr", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "type", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "type_common_name", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "lat", + "ordinal": 5, + "type_info": "Float" + }, + { + "name": "lon", + "ordinal": 6, + "type_info": "Float" + }, + { + "name": "data", + "ordinal": 7, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + false, + false + ] + }, + "hash": "4824bfa1ca8109255aa81c5b9744c4222a914ce2d09a112c4dda259c425b3587" +} diff --git a/server/.sqlx/query-84ee8b091b1e17632363a2c7e4efdc195d4716f7857a99494918905ee5bfb868.json b/server/.sqlx/query-84ee8b091b1e17632363a2c7e4efdc195d4716f7857a99494918905ee5bfb868.json new file mode 100644 index 000000000..7863b66c5 --- /dev/null +++ b/server/.sqlx/query-84ee8b091b1e17632363a2c7e4efdc195d4716f7857a99494918905ee5bfb868.json @@ -0,0 +1,62 @@ +{ + "db_name": "SQLite", + "query": "SELECT * FROM en WHERE key = ?", + "describe": { + "columns": [ + { + "name": "key", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "name", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "tumonline_room_nr", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "type", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "type_common_name", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "lat", + "ordinal": 5, + "type_info": "Float" + }, + { + "name": "lon", + "ordinal": 6, + "type_info": "Float" + }, + { + "name": "data", + "ordinal": 7, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + false, + false + ] + }, + "hash": "84ee8b091b1e17632363a2c7e4efdc195d4716f7857a99494918905ee5bfb868" +} diff --git a/server/.sqlx/query-c407c4d9fe1ce6bd90db434750df722d58068d3187a54cdf95dd8361db2d4c26.json b/server/.sqlx/query-c407c4d9fe1ce6bd90db434750df722d58068d3187a54cdf95dd8361db2d4c26.json new file mode 100644 index 000000000..8dd5185d2 --- /dev/null +++ b/server/.sqlx/query-c407c4d9fe1ce6bd90db434750df722d58068d3187a54cdf95dd8361db2d4c26.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT key, visible_id, type\n FROM aliases\n WHERE key = ? OR key = ?\n ", + "describe": { + "columns": [ + { + "name": "key", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "visible_id", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "type", + "ordinal": 2, + "type_info": "Text" + } + ], + "parameters": { + "Right": 2 + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "c407c4d9fe1ce6bd90db434750df722d58068d3187a54cdf95dd8361db2d4c26" +} diff --git a/server/.sqlx/query-ca4c11b14efa6564387df4dbc475539734b181798e2dba867f40bc460c7b9a3a.json b/server/.sqlx/query-ca4c11b14efa6564387df4dbc475539734b181798e2dba867f40bc460c7b9a3a.json new file mode 100644 index 000000000..13d4ee508 --- /dev/null +++ b/server/.sqlx/query-ca4c11b14efa6564387df4dbc475539734b181798e2dba867f40bc460c7b9a3a.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT data FROM en WHERE key = ?", + "describe": { + "columns": [ + { + "name": "data", + "ordinal": 0, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false + ] + }, + "hash": "ca4c11b14efa6564387df4dbc475539734b181798e2dba867f40bc460c7b9a3a" +} diff --git a/server/.sqlx/query-fe271671edc17aafa97e4da7ea8d7097d4186c3eb97649a45abc378fc8a2a83b.json b/server/.sqlx/query-fe271671edc17aafa97e4da7ea8d7097d4186c3eb97649a45abc378fc8a2a83b.json new file mode 100644 index 000000000..3809e3cac --- /dev/null +++ b/server/.sqlx/query-fe271671edc17aafa97e4da7ea8d7097d4186c3eb97649a45abc378fc8a2a83b.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT data FROM de WHERE key = ?", + "describe": { + "columns": [ + { + "name": "data", + "ordinal": 0, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false + ] + }, + "hash": "fe271671edc17aafa97e4da7ea8d7097d4186c3eb97649a45abc378fc8a2a83b" +} diff --git a/server/Cargo.lock b/server/Cargo.lock index a829f7679..e0a9ce7b1 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -856,9 +856,7 @@ dependencies = [ "chrono", "diesel_derives", "itoa", - "libsqlite3-sys", "pq-sys", - "time", ] [[package]] @@ -2125,7 +2123,6 @@ dependencies = [ "actix-web", "actix-web-prom", "cached", - "diesel", "futures", "image", "imageproc", diff --git a/server/Dockerfile b/server/Dockerfile index 1340549b4..b71e97038 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -28,7 +28,6 @@ RUN cargo build --release --workspace \ && rm -fr target/release/deps/navigatum* # second run of the image build (including our code) -COPY main-api/diesel.toml ./main-api/diesel.toml COPY calendar/diesel.toml ./calendar/diesel.toml COPY calendar/migrations calendar/migrations COPY .sqlx .sqlx diff --git a/server/main-api/Cargo.toml b/server/main-api/Cargo.toml index dce235adb..216b6351a 100644 --- a/server/main-api/Cargo.toml +++ b/server/main-api/Cargo.toml @@ -33,7 +33,6 @@ unicode-truncate = "0.2.0" serde_yaml = "0.9" # database -diesel = { version = "2.1.3", features = ["default", "sqlite"] } libsqlite3-sys = { version = "*", features = ["bundled"] } sqlx = { version = "0.7.2", features = ["sqlite", "runtime-tokio", "migrate", "macros"] } diff --git a/server/main-api/diesel.toml b/server/main-api/diesel.toml deleted file mode 100644 index 35a12ff0d..000000000 --- a/server/main-api/diesel.toml +++ /dev/null @@ -1,8 +0,0 @@ -# For documentation on how to configure this file, -# see https://diesel.rs/guides/configuring-diesel-cli - -[print_schema] -file = "src/schema.rs" - -[migrations_directory] -dir = "migrations" diff --git a/server/main-api/src/entries/get.rs b/server/main-api/src/entries/get.rs index 3c843996e..7d884329e 100644 --- a/server/main-api/src/entries/get.rs +++ b/server/main-api/src/entries/get.rs @@ -1,40 +1,37 @@ use crate::models::DBRoomKeyAlias; use crate::utils; use actix_web::{get, web, HttpResponse}; -use diesel::prelude::*; use log::error; +use sqlx::SqlitePool; #[get("/api/get/{id}")] pub async fn get_handler( params: web::Path, web::Query(args): web::Query, + data: web::Data, ) -> HttpResponse { - let conn = &mut utils::establish_connection(); - let (probable_id, redirect_url) = match get_alias_and_redirect(conn, ¶ms.into_inner()) { - Some(alias_and_redirect) => alias_and_redirect, - None => return HttpResponse::NotFound().body("Not found"), - }; + let (probable_id, redirect_url) = + match get_alias_and_redirect(&data.db, ¶ms.into_inner()).await { + Some(alias_and_redirect) => alias_and_redirect, + None => return HttpResponse::NotFound().body("Not found"), + }; let result = match args.should_use_english() { true => { - use crate::schema::en::dsl; - dsl::en - .filter(dsl::key.eq(&probable_id)) - .select(dsl::data) - .load::(conn) + sqlx::query_scalar!("SELECT data FROM en WHERE key = ?", probable_id) + .fetch_optional(&data.db) + .await } false => { - use crate::schema::de::dsl; - dsl::de - .filter(dsl::key.eq(&probable_id)) - .select(dsl::data) - .load::(conn) + sqlx::query_scalar!("SELECT data FROM de WHERE key = ?", probable_id) + .fetch_optional(&data.db) + .await } }; match result { - Ok(d) => match d.len() { - 0 => HttpResponse::NotFound().body("Not found"), - _ => { - let mut response_json = d[0].clone(); + Ok(d) => match d { + None => HttpResponse::NotFound().body("Not found"), + Some(d) => { + let mut response_json = d.clone(); // We don not want to serialise this data at any point in the server. // This just flows through the server, but adding redirect_url to the response is necessary response_json.pop(); // remove last } @@ -53,18 +50,24 @@ pub async fn get_handler( } } -fn get_alias_and_redirect(conn: &mut SqliteConnection, query: &str) -> Option<(String, String)> { - use crate::schema::aliases::dsl::{alias, aliases, key, type_, visible_id}; - let result = aliases - .filter(alias.eq(query).or(key.eq(query))) - .select((key, visible_id, type_)) - .distinct() - .load::(conn); +async fn get_alias_and_redirect(conn: &SqlitePool, query: &str) -> Option<(String, String)> { + let result = sqlx::query_as!( + DBRoomKeyAlias, + r#" + SELECT key, visible_id, type + FROM aliases + WHERE key = ? OR key = ? + "#, + query, + query + ) + .fetch_all(conn) + .await; match result { Ok(d) => { let redirect_url = match d.len() { 0 => return None, // not key or alias - 1 => extract_redirect_exact_match(&d[0].type_, &d[0].visible_id), + 1 => extract_redirect_exact_match(&d[0].r#type, &d[0].visible_id), _ => { let keys = d .clone() diff --git a/server/main-api/src/main.rs b/server/main-api/src/main.rs index 513e35c75..63a4ebf44 100644 --- a/server/main-api/src/main.rs +++ b/server/main-api/src/main.rs @@ -1,7 +1,10 @@ use actix_cors::Cors; use actix_web::{get, middleware, web, App, HttpResponse, HttpServer}; use actix_web_prom::PrometheusMetricsBuilder; -use log::{debug, info}; +use log::{debug, error, info}; +use sqlx::prelude::*; +use sqlx::sqlite::SqlitePoolOptions; +use sqlx::SqlitePool; use std::collections::HashMap; use structured_logger::async_json::new_writer; use structured_logger::Builder; @@ -9,22 +12,34 @@ use structured_logger::Builder; mod entries; mod maps; mod models; -mod schema; mod search; mod setup; mod utils; const MAX_JSON_PAYLOAD: usize = 1024 * 1024; // 1 MB +#[derive(Clone, Debug)] +pub struct AppData { + db: SqlitePool, +} + #[get("/api/status")] -async fn health_status_handler() -> HttpResponse { +async fn health_status_handler(data: web::Data) -> HttpResponse { let github_link = match std::env::var("GIT_COMMIT_SHA") { Ok(hash) => format!("https://github.com/TUM-Dev/navigatum/tree/{hash}"), Err(_) => "unknown commit hash, probably running in development".to_string(), }; - HttpResponse::Ok() - .content_type("text/plain") - .body(format!("healthy\nsource_code: {github_link}")) + return match data.db.execute("SELECT 1").await { + Ok(_) => HttpResponse::Ok() + .content_type("text/plain") + .body(format!("healthy\nsource_code: {github_link}")), + Err(e) => { + error!("database error: {e:?}",); + HttpResponse::InternalServerError() + .content_type("text/plain") + .body(format!("unhealthy\nsource_code: {github_link}")) + } + }; } #[tokio::main] @@ -32,8 +47,10 @@ async fn main() -> Result<(), Box> { Builder::with_level("info") .with_target_writer("*", new_writer(tokio::io::stdout())) .init(); + let uri = std::env::var("DB_LOCATION").unwrap_or_else(|_| "api_data.db".to_string()); + let pool = SqlitePoolOptions::new().connect(&uri).await?; info!("setting up the database"); - setup::database::setup_database().await?; + setup::database::setup_database(&pool).await?; info!("setting up meilisearch"); setup::meilisearch::setup_meilisearch().await?; @@ -62,6 +79,7 @@ async fn main() -> Result<(), Box> { .wrap(middleware::Logger::default().exclude("/api/status")) .wrap(middleware::Compress::default()) .app_data(web::JsonConfig::default().limit(MAX_JSON_PAYLOAD)) + .app_data(web::Data::new(AppData { db: pool.clone() })) .service(health_status_handler) .service(web::scope("/api/preview").configure(maps::configure)) .service(entries::get::get_handler) diff --git a/server/main-api/src/maps/mod.rs b/server/main-api/src/maps/mod.rs index 2ad8ec425..f234db364 100644 --- a/server/main-api/src/maps/mod.rs +++ b/server/main-api/src/maps/mod.rs @@ -8,11 +8,11 @@ use crate::models::DBRoomEntry; use actix_web::{get, web, HttpResponse}; use cached::proc_macro::cached; use cached::SizedCache; -use diesel::prelude::*; use image::Rgba; use std::io::Cursor; use log::{debug, error, warn}; +use sqlx::SqlitePool; use tokio::time::Instant; use unicode_truncate::UnicodeTruncateStr; @@ -27,15 +27,19 @@ pub fn configure(cfg: &mut web::ServiceConfig) { } } -fn get_localised_data(id: &str, should_use_english: bool) -> Result { - let conn = &mut utils::establish_connection(); - +async fn get_localised_data( + conn: &SqlitePool, + id: &str, + should_use_english: bool, +) -> Result { let result = if should_use_english { - use crate::schema::en::dsl; - dsl::en.filter(dsl::key.eq(&id)).load::(conn) + sqlx::query_as!(DBRoomEntry, "SELECT * FROM en WHERE key = ?", id) + .fetch_all(conn) + .await } else { - use crate::schema::de::dsl; - dsl::de.filter(dsl::key.eq(&id)).load::(conn) + sqlx::query_as!(DBRoomEntry, "SELECT * FROM de WHERE key = ?", id) + .fetch_all(conn) + .await }; match result { @@ -124,10 +128,11 @@ fn load_default_image() -> Vec { pub async fn maps_handler( params: web::Path, web::Query(args): web::Query, + data: web::Data, ) -> HttpResponse { let start_time = Instant::now(); let id = params.into_inner(); - let data = match get_localised_data(&id, args.should_use_english()) { + let data = match get_localised_data(&data.db, &id, args.should_use_english()).await { Ok(data) => data, Err(e) => { return e; diff --git a/server/main-api/src/maps/overlay_map.rs b/server/main-api/src/maps/overlay_map.rs index 84f49e3b1..56f84e3f2 100644 --- a/server/main-api/src/maps/overlay_map.rs +++ b/server/main-api/src/maps/overlay_map.rs @@ -4,14 +4,14 @@ use futures::{stream::FuturesUnordered, StreamExt}; use log::warn; pub(crate) struct OverlayMapTask { - pub(crate) x: f32, - pub(crate) y: f32, + pub(crate) x: f64, + pub(crate) y: f64, pub(crate) z: u32, } impl OverlayMapTask { pub fn with(entry: &DBRoomEntry) -> Self { - let zoom = match entry.type_.as_str() { + let zoom = match entry.r#type.as_str() { "campus" => 14, "area" | "site" => 15, "building" | "joined_building" => 16, @@ -77,11 +77,11 @@ impl OverlayMapTask { } } -fn lat_lon_z_to_xyz(lat_deg: f32, lon_deg: f32, zoom: u32) -> (f32, f32, u32) { +fn lat_lon_z_to_xyz(lat_deg: f64, lon_deg: f64, zoom: u32) -> (f64, f64, u32) { let lat_rad = lat_deg.to_radians(); - let n = 2_u32.pow(zoom) as f32; + let n = 2_u32.pow(zoom) as f64; let xtile = (lon_deg + 180.0) / 360.0 * n; - let ytile = (1.0 - lat_rad.tan().asinh() / std::f32::consts::PI) / 2.0 * n; + let ytile = (1.0 - lat_rad.tan().asinh() / std::f64::consts::PI) / 2.0 * n; (xtile, ytile, zoom) } @@ -109,16 +109,16 @@ mod overlay_tests { #[test] fn test_lat_lon_z_to_xyz() { let (x, y, _) = lat_lon_z_to_xyz(52.520_008, 13.404_954, 17); - assert_eq!(x, 70416.59_f32); - assert_eq!(y, 42985.734_f32); + assert_eq!(x, 70416.59480746667_f64); + assert_eq!(y, 42985.734050611834_f64); } #[test] fn test_lat_lon_no_zoom_mut() { for x in -5..5 { - let x = x as f32; + let x = x as f64; for y in -5..5 { - let y = y as f32; + let y = y as f64; for z in 0..20 { let (_, _, zg) = lat_lon_z_to_xyz(x + y / 100.0, y, z); assert_eq!(z, zg); diff --git a/server/main-api/src/models.rs b/server/main-api/src/models.rs index 780967e4e..af8ad25ff 100644 --- a/server/main-api/src/models.rs +++ b/server/main-api/src/models.rs @@ -1,20 +1,18 @@ -use diesel::prelude::*; - -#[derive(Queryable, Debug, Clone)] +#[derive(Debug, Clone)] pub struct DBRoomEntry { pub key: String, pub name: String, - pub tumonline_room_nr: Option, - pub type_: String, + pub tumonline_room_nr: Option, + pub r#type: String, pub type_common_name: String, - pub lat: f32, - pub lon: f32, + pub lat: f64, + pub lon: f64, pub data: String, } -#[derive(Queryable, Debug, Clone)] +#[derive(Debug, Clone)] pub struct DBRoomKeyAlias { pub key: String, pub visible_id: String, - pub type_: String, + pub r#type: String, } diff --git a/server/main-api/src/schema.rs b/server/main-api/src/schema.rs deleted file mode 100644 index 479c54543..000000000 --- a/server/main-api/src/schema.rs +++ /dev/null @@ -1,42 +0,0 @@ -// @generated automatically by Diesel CLI. - -diesel::table! { - aliases (id) { - id -> Integer, - alias -> Text, - key -> Text, - visible_id -> Text, - #[sql_name = "type"] - type_ -> Text, - } -} - -diesel::table! { - de (key) { - key -> Text, - name -> Text, - tumonline_room_nr -> Nullable, - #[sql_name = "type"] - type_ -> Text, - type_common_name -> Text, - lat -> Float, - lon -> Float, - data -> Text, - } -} - -diesel::table! { - en (key) { - key -> Text, - name -> Text, - tumonline_room_nr -> Nullable, - #[sql_name = "type"] - type_ -> Text, - type_common_name -> Text, - lat -> Float, - lon -> Float, - data -> Text, - } -} - -diesel::allow_tables_to_appear_in_same_query!(aliases, de, en,); diff --git a/server/main-api/src/setup/database/mod.rs b/server/main-api/src/setup/database/mod.rs index cb601c090..0d91e6667 100644 --- a/server/main-api/src/setup/database/mod.rs +++ b/server/main-api/src/setup/database/mod.rs @@ -1,25 +1,21 @@ mod alias; mod data; -use sqlx::sqlite::SqlitePoolOptions; -use sqlx::Executor; +use sqlx::{Executor, SqlitePool}; -pub(crate) async fn setup_database() -> Result<(), Box> { - let uri = std::env::var("DB_LOCATION").unwrap_or_else(|_| "api_data.db".to_string()); - let uri = format!("{uri}?mode=rwc"); - let pool = SqlitePoolOptions::new().connect(&uri).await?; - sqlx::migrate!("./migrations").run(&pool).await?; +pub(crate) async fn setup_database(pool: &SqlitePool) -> Result<(), Box> { + sqlx::migrate!("./migrations").run(pool).await?; // this is to setup the database faster // we don't want to use an acid compliant database for this step ;) pool.execute("PRAGMA journal_mode = OFF;"); pool.execute("PRAGMA synchronous = OFF;"); // delete all old data - sqlx::query!("DELETE FROM aliases").execute(&pool).await?; - sqlx::query!("DELETE FROM en").execute(&pool).await?; - sqlx::query!("DELETE FROM de").execute(&pool).await?; + sqlx::query!("DELETE FROM aliases").execute(pool).await?; + sqlx::query!("DELETE FROM en").execute(pool).await?; + sqlx::query!("DELETE FROM de").execute(pool).await?; - data::load_all_to_db(&pool).await?; - alias::load_all_to_db(&pool).await?; + data::load_all_to_db(pool).await?; + alias::load_all_to_db(pool).await?; Ok(()) } diff --git a/server/main-api/src/utils.rs b/server/main-api/src/utils.rs index fabada3e7..98219e562 100644 --- a/server/main-api/src/utils.rs +++ b/server/main-api/src/utils.rs @@ -1,4 +1,3 @@ -use diesel::{Connection, SqliteConnection}; use serde::Deserialize; #[derive(Deserialize)] @@ -11,8 +10,3 @@ impl LangQueryArgs { self.lang.as_ref().map_or(false, |c| c == "en") } } - -pub fn establish_connection() -> SqliteConnection { - let database_loc = std::env::var("DB_LOCATION").unwrap_or_else(|_| "api_data.db".to_string()); - SqliteConnection::establish(&database_loc).expect("Cannot open database") -}