Compare commits
13 commits
Author | SHA1 | Date | |
---|---|---|---|
e8f931392e | |||
36709b6b80 | |||
cd79f47caa | |||
48143979e5 | |||
717e3fda49 | |||
22fa59334c | |||
5c0c03fd7a | |||
c8ed40c3cc | |||
8373b278cc | |||
c0799484bb | |||
83a1af59d9 | |||
c01ed85e7a | |||
12938d1ee4 |
8 changed files with 101 additions and 76 deletions
|
@ -7,12 +7,25 @@ on:
|
||||||
- '[0-9]+.[0-9]+.[0-9]+'
|
- '[0-9]+.[0-9]+.[0-9]+'
|
||||||
- '[0-9]+.[0-9]+.[0-9]+rc[0-9]+'
|
- '[0-9]+.[0-9]+.[0-9]+rc[0-9]+'
|
||||||
jobs:
|
jobs:
|
||||||
run-tests:
|
test:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
steps:
|
steps:
|
||||||
|
-
|
||||||
|
name: Checking Out Repository Code
|
||||||
|
uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
-
|
-
|
||||||
name: Placeholder
|
name: Placeholder
|
||||||
run: echo Placeholder Job
|
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:
|
build:
|
||||||
needs: test
|
needs: test
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -397,7 +397,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chellaris-rust-api"
|
name = "chellaris-rust-api"
|
||||||
version = "1.0.3"
|
version = "1.2.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chellaris-rust-api"
|
name = "chellaris-rust-api"
|
||||||
version = "1.0.3"
|
version = "1.2.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -13,6 +13,7 @@ use chrono::Local;
|
||||||
use actix_web::{middleware::Logger, web, App, HttpServer};
|
use actix_web::{middleware::Logger, web, App, HttpServer};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{PgPool, Pool, Postgres, Connection};
|
use sqlx::{PgPool, Pool, Postgres, Connection};
|
||||||
|
use tokio::signal::unix::SignalKind;
|
||||||
use utoipa::{OpenApi, openapi::security::{SecurityScheme, ApiKey, ApiKeyValue}, Modify};
|
use utoipa::{OpenApi, openapi::security::{SecurityScheme, ApiKey, ApiKeyValue}, Modify};
|
||||||
use utoipa_swagger_ui::{Config, SwaggerUi, Url};
|
use utoipa_swagger_ui::{Config, SwaggerUi, Url};
|
||||||
|
|
||||||
|
@ -220,13 +221,11 @@ async fn main() {
|
||||||
struct ApiDocV1;
|
struct ApiDocV1;
|
||||||
|
|
||||||
let openapi_urls = vec![
|
let openapi_urls = vec![
|
||||||
Url::new("v1", concat!(api_base_1!(), "/openapi.json")),
|
Url::new("v1", concat!(api_base!(), "-docs/openapi1.json")),
|
||||||
Url::new("v2-L", concat!(api_base_2!(), "/openapi.json")),
|
Url::new("v2-L", concat!(api_base!(), "-docs/openapi2l.json")),
|
||||||
Url::new("v3-L", concat!(api_base_3!(), "/openapi.json")),
|
Url::new("v3-L", concat!(api_base!(), "-docs/openapi3l.json")),
|
||||||
];
|
];
|
||||||
|
|
||||||
let is_alive: Arc<AtomicBool> = Arc::new(AtomicBool::new(true));
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let db_auth_tokens = config.auth.clone();
|
let db_auth_tokens = config.auth.clone();
|
||||||
let pool = PgPool::connect(dotenv!("DATABASE_URL")).await.unwrap();
|
let pool = PgPool::connect(dotenv!("DATABASE_URL")).await.unwrap();
|
||||||
|
@ -249,14 +248,19 @@ async fn main() {
|
||||||
println!(" -> http://[{}]:{}", Ipv6Addr::UNSPECIFIED, 8080);
|
println!(" -> http://[{}]:{}", Ipv6Addr::UNSPECIFIED, 8080);
|
||||||
println!(" -> http://{}:{}", Ipv4Addr::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 {
|
tokio::spawn(async move {
|
||||||
actix_web::rt::signal::ctrl_c().await.unwrap();
|
actix_web::rt::signal::unix::signal(SignalKind::terminate()).unwrap().recv().await;
|
||||||
println!("Ctrl-C received, killing Server");
|
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()
|
abort()
|
||||||
});
|
});
|
||||||
|
|
||||||
let server = HttpServer::new(move || {
|
let _ = HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.app_data(web::Data::new(AppState { db: pool.clone(), auth_tokens: db_auth_tokens.clone() }))
|
.app_data(web::Data::new(AppState { db: pool.clone(), auth_tokens: db_auth_tokens.clone() }))
|
||||||
.wrap(Logger::default())
|
.wrap(Logger::default())
|
||||||
|
@ -295,15 +299,15 @@ async fn main() {
|
||||||
SwaggerUi::new(concat!(api_base!(), "/swagger/{_:.*}"))
|
SwaggerUi::new(concat!(api_base!(), "/swagger/{_:.*}"))
|
||||||
.urls(vec![
|
.urls(vec![
|
||||||
(
|
(
|
||||||
Url::new("v1", concat!(api_base_1!(), "/openapi.json")),
|
Url::new("v1", concat!(api_base!(), "-docs/openapi1.json")),
|
||||||
openapi_v1.clone(),
|
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(),
|
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(),
|
openapi_v3_l.clone(),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{db, AppState};
|
use crate::{db, AppState};
|
||||||
use actix_web::web::Json;
|
|
||||||
use actix_web::{delete, get, post, put, web, HttpRequest, HttpResponse, Responder};
|
use actix_web::{delete, get, post, put, web, HttpRequest, HttpResponse, Responder};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -9,7 +8,7 @@ use sqlx::QueryBuilder;
|
||||||
|
|
||||||
pub(crate) mod schemas;
|
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()
|
req.headers().get("x-api-key")?.to_str().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +48,7 @@ async fn verify_user_auth(data: &web::Data<AppState>, auth_token: &str, user_tok
|
||||||
|
|
||||||
// User Endpoints
|
// User Endpoints
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
request_body = schemas::GetUserParams,
|
request_body = GetUserParams,
|
||||||
responses(
|
responses(
|
||||||
(status = 200, description = "OK", body = User),
|
(status = 200, description = "OK", body = User),
|
||||||
(status = 403, description = "Unauthorized"),
|
(status = 403, description = "Unauthorized"),
|
||||||
|
@ -59,8 +58,8 @@ async fn verify_user_auth(data: &web::Data<AppState>, auth_token: &str, user_tok
|
||||||
("api_key" = [])
|
("api_key" = [])
|
||||||
),
|
),
|
||||||
)]
|
)]
|
||||||
#[get("/api/v1/user")]
|
#[post("/api/v1/user")]
|
||||||
async fn get_user(
|
pub(crate) async fn get_user(
|
||||||
data: web::Data<AppState>,
|
data: web::Data<AppState>,
|
||||||
params: web::Json<schemas::GetUserParams>,
|
params: web::Json<schemas::GetUserParams>,
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
|
@ -90,18 +89,18 @@ async fn get_user(
|
||||||
Err(_) => return HttpResponse::InternalServerError().finish(),
|
Err(_) => return HttpResponse::InternalServerError().finish(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut permissions: HashMap<String, bool> = HashMap::new();
|
let mut user_permissions: HashMap<String, bool> = HashMap::new();
|
||||||
|
|
||||||
permissions.insert("game_permissions".to_string(), user.game_permissions);
|
user_permissions.insert("game_permissions".to_string(), user.game_permissions);
|
||||||
permissions.insert("empire_permissions".to_string(), user.empire_permissions);
|
user_permissions.insert("empire_permissions".to_string(), user.empire_permissions);
|
||||||
permissions.insert("data_permissions".to_string(), user.data_permissions);
|
user_permissions.insert("data_permissions".to_string(), user.data_permissions);
|
||||||
permissions.insert("user_permissions".to_string(), user.user_permissions);
|
user_permissions.insert("user_permissions".to_string(), user.user_permissions);
|
||||||
|
|
||||||
let return_data = schemas::User {
|
let return_data = schemas::User {
|
||||||
user_token: user.token,
|
user_token: user.token,
|
||||||
discord_handle: user.discord_id,
|
discord_handle: user.discord_id,
|
||||||
profile_picture: user.picture_url,
|
profile_picture: user.picture_url,
|
||||||
permissions: permissions
|
permissions: user_permissions
|
||||||
};
|
};
|
||||||
|
|
||||||
return HttpResponse::Ok().json(return_data);
|
return HttpResponse::Ok().json(return_data);
|
||||||
|
@ -117,7 +116,7 @@ async fn get_user(
|
||||||
(status = 500, description = "Internal Server Error")
|
(status = 500, description = "Internal Server Error")
|
||||||
),
|
),
|
||||||
)]
|
)]
|
||||||
#[post("/api/v1/user")]
|
#[post("/api/v1/user/create")]
|
||||||
pub(crate) async fn create_user(
|
pub(crate) async fn create_user(
|
||||||
data: web::Data<AppState>,
|
data: web::Data<AppState>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
|
@ -196,10 +195,18 @@ pub(crate) async fn update_user(
|
||||||
None => return HttpResponse::Unauthorized().finish(),
|
None => return HttpResponse::Unauthorized().finish(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut user_permissions: HashMap<String, bool> = HashMap::new();
|
||||||
|
match params.permissions {
|
||||||
|
Some(data) => {user_permissions = data.clone()},
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
|
||||||
let mut elevated_auth = false;
|
let mut elevated_auth = false;
|
||||||
if params.permissions["game_permissions"] || params.permissions["empire_permissions"] || params.permissions["data_permissions"] || params.permissions["user_permissions"] {
|
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;
|
elevated_auth = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let auth = verify_user_auth(&data, &auth_token, ¶ms.user_token, schemas::TablePermission::User, elevated_auth).await;
|
let auth = verify_user_auth(&data, &auth_token, ¶ms.user_token, schemas::TablePermission::User, elevated_auth).await;
|
||||||
|
|
||||||
|
@ -226,7 +233,8 @@ pub(crate) async fn update_user(
|
||||||
any_param_present = true;
|
any_param_present = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entry, value) in params.permissions.iter() {
|
if user_permissions.len() != 0 {
|
||||||
|
for (entry, value) in user_permissions.iter() {
|
||||||
match entry.deref() {
|
match entry.deref() {
|
||||||
"game_permissions" => {
|
"game_permissions" => {
|
||||||
user_query_separated.push( " game_permissions = ");
|
user_query_separated.push( " game_permissions = ");
|
||||||
|
@ -263,6 +271,7 @@ pub(crate) async fn update_user(
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if any_param_present {
|
if any_param_present {
|
||||||
user_query_separated.push_unseparated(" WHERE token = ").push_bind_unseparated(params.user_token);
|
user_query_separated.push_unseparated(" WHERE token = ").push_bind_unseparated(params.user_token);
|
||||||
|
@ -290,17 +299,17 @@ pub(crate) async fn update_user(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut permissions: HashMap<String, bool> = HashMap::new();
|
let mut user_permissions: HashMap<String, bool> = HashMap::new();
|
||||||
permissions.insert("game_permissions".to_string(), user.game_permissions);
|
user_permissions.insert("game_permissions".to_string(), user.game_permissions);
|
||||||
permissions.insert("empire_permissions".to_string(), user.empire_permissions);
|
user_permissions.insert("empire_permissions".to_string(), user.empire_permissions);
|
||||||
permissions.insert("data_permissions".to_string(), user.data_permissions);
|
user_permissions.insert("data_permissions".to_string(), user.data_permissions);
|
||||||
permissions.insert("user_permissions".to_string(), user.user_permissions);
|
user_permissions.insert("user_permissions".to_string(), user.user_permissions);
|
||||||
|
|
||||||
let return_data = schemas::User {
|
let return_data = schemas::User {
|
||||||
user_token: user.token,
|
user_token: user.token,
|
||||||
discord_handle: user.discord_id,
|
discord_handle: user.discord_id,
|
||||||
profile_picture: user.picture_url,
|
profile_picture: user.picture_url,
|
||||||
permissions: permissions
|
permissions: user_permissions
|
||||||
};
|
};
|
||||||
return HttpResponse::Ok().json(return_data);
|
return HttpResponse::Ok().json(return_data);
|
||||||
} else {
|
} else {
|
||||||
|
@ -309,7 +318,7 @@ pub(crate) async fn update_user(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
request_body = schemas::DeleteUserParams,
|
request_body = DeleteUserParams,
|
||||||
responses(
|
responses(
|
||||||
(status = 200, description = "OK"),
|
(status = 200, description = "OK"),
|
||||||
(status = 403, description = "Unauthorized"),
|
(status = 403, description = "Unauthorized"),
|
||||||
|
@ -348,7 +357,7 @@ pub(crate) async fn delete_user(
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
return HttpResponse::InternalServerError().finish();
|
return HttpResponse::InternalServerError().finish();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use utoipa::{IntoParams, ToSchema};
|
use utoipa::{IntoParams, ToSchema};
|
||||||
use crate::v3::schemas::ChellarisGameLegacy;
|
|
||||||
|
|
||||||
// DB Permission Enums
|
// DB Permission Enums
|
||||||
|
|
||||||
|
@ -55,7 +54,7 @@ pub struct UpdateUserParams {
|
||||||
[\"user_permissions\"]: false,
|
[\"user_permissions\"]: false,
|
||||||
}\
|
}\
|
||||||
")]
|
")]
|
||||||
pub permissions: HashMap<String, bool>,
|
pub permissions: Option<HashMap<String, bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
||||||
|
|
|
@ -31,7 +31,7 @@ fn verify_auth(token: Option<&str>, data: &AppState) -> schemas::AuthReturn {
|
||||||
return auth_return;
|
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()
|
req.headers().get("x-api-key")?.to_str().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, hash::Hash};
|
use std::{collections::HashMap};
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use utoipa::{ToSchema, IntoParams};
|
use utoipa::{ToSchema, IntoParams};
|
||||||
|
|
Reference in a new issue