diff --git a/.forgejo/workflows/build+release.yml b/.forgejo/workflows/build+release.yml index dd35a7c..c473e32 100644 --- a/.forgejo/workflows/build+release.yml +++ b/.forgejo/workflows/build+release.yml @@ -7,12 +7,25 @@ on: - '[0-9]+.[0-9]+.[0-9]+' - '[0-9]+.[0-9]+.[0-9]+rc[0-9]+' jobs: - run-tests: + test: runs-on: docker steps: + - + name: Checking Out Repository Code + uses: https://code.forgejo.org/actions/checkout@v3 - name: Placeholder run: echo Placeholder Job + - + name: Check if Version in Cargo.toml matches Tag + run: | + VERSION=$(cat Cargo.toml | grep -E "(^|\|)version =" | cut -f2- -d= | tr -d \" | tr -d " ") + if test $VERSION != "${{ github.ref_name }}"; then + echo "Expected Version is: '${{ github.ref_name }}' actual Version is: '$VERSION'"; + exit 1 + else + echo "Version is: '$VERSION'"; + fi build: needs: test diff --git a/Cargo.lock b/Cargo.lock index 53947cb..ca617a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -397,7 +397,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chellaris-rust-api" -version = "1.0.3" +version = "1.2.4" dependencies = [ "actix-web", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 7a9019a..f901372 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chellaris-rust-api" -version = "1.0.3" +version = "1.2.4" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/main.rs b/src/main.rs index 51e8abb..6a3a574 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use chrono::Local; use actix_web::{middleware::Logger, web, App, HttpServer}; use serde::{Deserialize, Serialize}; use sqlx::{PgPool, Pool, Postgres, Connection}; +use tokio::signal::unix::SignalKind; use utoipa::{OpenApi, openapi::security::{SecurityScheme, ApiKey, ApiKeyValue}, Modify}; use utoipa_swagger_ui::{Config, SwaggerUi, Url}; @@ -220,13 +221,11 @@ async fn main() { struct ApiDocV1; let openapi_urls = vec![ - Url::new("v1", concat!(api_base_1!(), "/openapi.json")), - Url::new("v2-L", concat!(api_base_2!(), "/openapi.json")), - Url::new("v3-L", concat!(api_base_3!(), "/openapi.json")), + Url::new("v1", concat!(api_base!(), "-docs/openapi1.json")), + Url::new("v2-L", concat!(api_base!(), "-docs/openapi2l.json")), + Url::new("v3-L", concat!(api_base!(), "-docs/openapi3l.json")), ]; - let is_alive: Arc = Arc::new(AtomicBool::new(true)); - loop { let db_auth_tokens = config.auth.clone(); let pool = PgPool::connect(dotenv!("DATABASE_URL")).await.unwrap(); @@ -249,14 +248,19 @@ async fn main() { println!(" -> http://[{}]:{}", Ipv6Addr::UNSPECIFIED, 8080); println!(" -> http://{}:{}", Ipv4Addr::UNSPECIFIED, 8080); - let watchdog_thread = tokio::spawn(async move { postgres_watchdog(pool_copy, shutdown_clone) }); + let watchdog_thread = tokio::spawn(async move { postgres_watchdog(pool_copy, shutdown_clone).await }); tokio::spawn(async move { - actix_web::rt::signal::ctrl_c().await.unwrap(); - println!("Ctrl-C received, killing Server"); + actix_web::rt::signal::unix::signal(SignalKind::terminate()).unwrap().recv().await; + println!("SIGTERM received, killing Server"); + abort() + }); + tokio::spawn(async move { + actix_web::rt::signal::unix::signal(SignalKind::interrupt()).unwrap().recv().await; + println!("SIGINT received, killing Server"); abort() }); - let server = HttpServer::new(move || { + let _ = HttpServer::new(move || { App::new() .app_data(web::Data::new(AppState { db: pool.clone(), auth_tokens: db_auth_tokens.clone() })) .wrap(Logger::default()) @@ -295,15 +299,15 @@ async fn main() { SwaggerUi::new(concat!(api_base!(), "/swagger/{_:.*}")) .urls(vec![ ( - Url::new("v1", concat!(api_base_1!(), "/openapi.json")), + Url::new("v1", concat!(api_base!(), "-docs/openapi1.json")), openapi_v1.clone(), ), ( - Url::new("v2-l", concat!(api_base_2!(), "/openapi.json")), + Url::new("v2-l", concat!(api_base!(), "-docs/openapi2l.json")), openapi_v2_l.clone(), ), ( - Url::new("v3-l", concat!(api_base_3!(), "/openapi.json")), + Url::new("v3-l", concat!(api_base!(), "-docs/openapi3l.json")), openapi_v3_l.clone(), ), ]) diff --git a/src/v1/mod.rs b/src/v1/mod.rs index ad7c47a..4c0fb1b 100644 --- a/src/v1/mod.rs +++ b/src/v1/mod.rs @@ -1,5 +1,4 @@ use crate::{db, AppState}; -use actix_web::web::Json; use actix_web::{delete, get, post, put, web, HttpRequest, HttpResponse, Responder}; use std::collections::HashMap; use std::ops::Deref; @@ -9,7 +8,7 @@ use sqlx::QueryBuilder; pub(crate) mod schemas; -fn get_auth_header<'a>(req: &'a HttpRequest) -> Option<&'a str> { +fn get_auth_header(req: &HttpRequest) -> Option<&str> { req.headers().get("x-api-key")?.to_str().ok() } @@ -49,7 +48,7 @@ async fn verify_user_auth(data: &web::Data, auth_token: &str, user_tok // User Endpoints #[utoipa::path( - request_body = schemas::GetUserParams, + request_body = GetUserParams, responses( (status = 200, description = "OK", body = User), (status = 403, description = "Unauthorized"), @@ -59,8 +58,8 @@ async fn verify_user_auth(data: &web::Data, auth_token: &str, user_tok ("api_key" = []) ), )] -#[get("/api/v1/user")] -async fn get_user( +#[post("/api/v1/user")] +pub(crate) async fn get_user( data: web::Data, params: web::Json, req: HttpRequest, @@ -90,18 +89,18 @@ async fn get_user( Err(_) => return HttpResponse::InternalServerError().finish(), }; - let mut permissions: HashMap = HashMap::new(); + let mut user_permissions: HashMap = HashMap::new(); - permissions.insert("game_permissions".to_string(), user.game_permissions); - permissions.insert("empire_permissions".to_string(), user.empire_permissions); - permissions.insert("data_permissions".to_string(), user.data_permissions); - permissions.insert("user_permissions".to_string(), user.user_permissions); + user_permissions.insert("game_permissions".to_string(), user.game_permissions); + user_permissions.insert("empire_permissions".to_string(), user.empire_permissions); + user_permissions.insert("data_permissions".to_string(), user.data_permissions); + user_permissions.insert("user_permissions".to_string(), user.user_permissions); let return_data = schemas::User { user_token: user.token, discord_handle: user.discord_id, profile_picture: user.picture_url, - permissions: permissions + permissions: user_permissions }; return HttpResponse::Ok().json(return_data); @@ -117,7 +116,7 @@ async fn get_user( (status = 500, description = "Internal Server Error") ), )] -#[post("/api/v1/user")] +#[post("/api/v1/user/create")] pub(crate) async fn create_user( data: web::Data, ) -> impl Responder { @@ -196,9 +195,17 @@ pub(crate) async fn update_user( None => return HttpResponse::Unauthorized().finish(), }; + let mut user_permissions: HashMap = HashMap::new(); + match params.permissions { + Some(data) => {user_permissions = data.clone()}, + None => {}, + } + let mut elevated_auth = false; - if params.permissions["game_permissions"] || params.permissions["empire_permissions"] || params.permissions["data_permissions"] || params.permissions["user_permissions"] { - elevated_auth = true; + if user_permissions.len() != 0 { + if user_permissions["game_permissions"] || user_permissions["empire_permissions"] || user_permissions["data_permissions"] || user_permissions["user_permissions"] { + elevated_auth = true; + } } let auth = verify_user_auth(&data, &auth_token, ¶ms.user_token, schemas::TablePermission::User, elevated_auth).await; @@ -226,41 +233,43 @@ pub(crate) async fn update_user( any_param_present = true; } - for (entry, value) in params.permissions.iter() { - match entry.deref() { - "game_permissions" => { - user_query_separated.push( " game_permissions = "); - match any_param_present { - true => user_query_separated.push_bind(value), - false => user_query_separated.push_bind_unseparated(value) - }; - any_param_present = true; - }, - "empire_permissions" => { - user_query_separated.push( " empire_permissions = "); - match any_param_present { - true => user_query_separated.push_bind(value), - false => user_query_separated.push_bind_unseparated(value) - }; - any_param_present = true; - }, - "data_permissions" => { - user_query_separated.push( " data_permissions = "); - match any_param_present { - true => user_query_separated.push_bind(value), - false => user_query_separated.push_bind_unseparated(value) - }; - any_param_present = true; - }, - "user_permissions" => { - user_query_separated.push( " user_permissions = "); - match any_param_present { - true => user_query_separated.push_bind(value), - false => user_query_separated.push_bind_unseparated(value) - }; - any_param_present = true; - }, - _ => {} + if user_permissions.len() != 0 { + for (entry, value) in user_permissions.iter() { + match entry.deref() { + "game_permissions" => { + user_query_separated.push( " game_permissions = "); + match any_param_present { + true => user_query_separated.push_bind(value), + false => user_query_separated.push_bind_unseparated(value) + }; + any_param_present = true; + }, + "empire_permissions" => { + user_query_separated.push( " empire_permissions = "); + match any_param_present { + true => user_query_separated.push_bind(value), + false => user_query_separated.push_bind_unseparated(value) + }; + any_param_present = true; + }, + "data_permissions" => { + user_query_separated.push( " data_permissions = "); + match any_param_present { + true => user_query_separated.push_bind(value), + false => user_query_separated.push_bind_unseparated(value) + }; + any_param_present = true; + }, + "user_permissions" => { + user_query_separated.push( " user_permissions = "); + match any_param_present { + true => user_query_separated.push_bind(value), + false => user_query_separated.push_bind_unseparated(value) + }; + any_param_present = true; + }, + _ => {} + } } } @@ -290,17 +299,17 @@ pub(crate) async fn update_user( }; } - let mut permissions: HashMap = HashMap::new(); - permissions.insert("game_permissions".to_string(), user.game_permissions); - permissions.insert("empire_permissions".to_string(), user.empire_permissions); - permissions.insert("data_permissions".to_string(), user.data_permissions); - permissions.insert("user_permissions".to_string(), user.user_permissions); + let mut user_permissions: HashMap = HashMap::new(); + user_permissions.insert("game_permissions".to_string(), user.game_permissions); + user_permissions.insert("empire_permissions".to_string(), user.empire_permissions); + user_permissions.insert("data_permissions".to_string(), user.data_permissions); + user_permissions.insert("user_permissions".to_string(), user.user_permissions); let return_data = schemas::User { user_token: user.token, discord_handle: user.discord_id, profile_picture: user.picture_url, - permissions: permissions + permissions: user_permissions }; return HttpResponse::Ok().json(return_data); } else { @@ -309,7 +318,7 @@ pub(crate) async fn update_user( } #[utoipa::path( - request_body = schemas::DeleteUserParams, + request_body = DeleteUserParams, responses( (status = 200, description = "OK"), (status = 403, description = "Unauthorized"), @@ -348,7 +357,7 @@ pub(crate) async fn delete_user( .await { Ok(_) => {} - Err(e) => { + Err(_) => { return HttpResponse::InternalServerError().finish(); } }; diff --git a/src/v1/schemas.rs b/src/v1/schemas.rs index e561cce..81c23c3 100644 --- a/src/v1/schemas.rs +++ b/src/v1/schemas.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use utoipa::{IntoParams, ToSchema}; -use crate::v3::schemas::ChellarisGameLegacy; // DB Permission Enums @@ -55,7 +54,7 @@ pub struct UpdateUserParams { [\"user_permissions\"]: false, }\ ")] - pub permissions: HashMap, + pub permissions: Option>, } #[derive(Serialize, Deserialize, ToSchema, Debug)] diff --git a/src/v3/mod.rs b/src/v3/mod.rs index 9d904a3..f1c414c 100644 --- a/src/v3/mod.rs +++ b/src/v3/mod.rs @@ -31,7 +31,7 @@ fn verify_auth(token: Option<&str>, data: &AppState) -> schemas::AuthReturn { return auth_return; } -fn get_auth_header<'a>(req: &'a HttpRequest) -> Option<&'a str> { +fn get_auth_header(req: &HttpRequest) -> Option<&str> { req.headers().get("x-api-key")?.to_str().ok() } diff --git a/src/v3/schemas.rs b/src/v3/schemas.rs index 847bccc..709a7b2 100644 --- a/src/v3/schemas.rs +++ b/src/v3/schemas.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, hash::Hash}; +use std::{collections::HashMap}; use serde::{Serialize, Deserialize}; use utoipa::{ToSchema, IntoParams};