What am I even doing?
This commit is contained in:
parent
04ca51c927
commit
0751408450
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
api_keys.json
|
api_keys.json
|
||||||
|
*.sqlite-*
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS events (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name VARCHAR NOT NULL,
|
||||||
|
event_type VARCHAR NOT NULL,
|
||||||
|
event_id INTEGER NOT NULL
|
||||||
|
);
|
|
@ -0,0 +1,72 @@
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use okapi::openapi3::{Object, SecurityRequirement, SecurityScheme, SecuritySchemeData};
|
||||||
|
use rocket::{
|
||||||
|
http::Status,
|
||||||
|
request::{self, FromRequest},
|
||||||
|
Request,
|
||||||
|
};
|
||||||
|
use rocket_okapi::{
|
||||||
|
gen::OpenApiGenerator,
|
||||||
|
request::{OpenApiFromRequest, RequestHeaderInput},
|
||||||
|
};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref API_KEYS: Vec<String> = {
|
||||||
|
serde_json::from_str(
|
||||||
|
&std::fs::read_to_string("api_keys.json").expect("api_keys.json does not exist"),
|
||||||
|
)
|
||||||
|
.expect("api_keys.json is not valid")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ApiKey;
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for ApiKey {
|
||||||
|
type Error = ApiKey;
|
||||||
|
|
||||||
|
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
if req
|
||||||
|
.headers()
|
||||||
|
.get("X-API-Key")
|
||||||
|
.any(|k| API_KEYS.contains(&String::from(k)))
|
||||||
|
{
|
||||||
|
request::Outcome::Success(ApiKey)
|
||||||
|
} else {
|
||||||
|
request::Outcome::Failure((Status::Unauthorized, ApiKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> OpenApiFromRequest<'a> for ApiKey {
|
||||||
|
fn from_request_input(
|
||||||
|
_gen: &mut OpenApiGenerator,
|
||||||
|
_name: String,
|
||||||
|
_required: bool,
|
||||||
|
) -> rocket_okapi::Result<RequestHeaderInput> {
|
||||||
|
// Setup global requirement for Security scheme
|
||||||
|
let security_scheme = SecurityScheme {
|
||||||
|
description: Some("Requires an API key to access".to_owned()),
|
||||||
|
// Setup data requirements.
|
||||||
|
// This can be part of the `header`, `query` or `cookie`.
|
||||||
|
// In this case the header `x-api-key: mykey` needs to be set.
|
||||||
|
data: SecuritySchemeData::ApiKey {
|
||||||
|
name: "x-api-key".to_owned(),
|
||||||
|
location: "header".to_owned(),
|
||||||
|
},
|
||||||
|
extensions: Object::default(),
|
||||||
|
};
|
||||||
|
// Add the requirement for this route/endpoint
|
||||||
|
// This can change between routes.
|
||||||
|
let mut security_req = SecurityRequirement::new();
|
||||||
|
// Each security requirement needs to be met before access is allowed.
|
||||||
|
security_req.insert("ApiKeyAuth".to_owned(), Vec::new());
|
||||||
|
// These vvvvvvv-----^^^^^^^^^^ values need to match exactly!
|
||||||
|
Ok(RequestHeaderInput::Security(
|
||||||
|
"ApiKeyAuth".to_owned(),
|
||||||
|
security_scheme,
|
||||||
|
security_req,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use sqlx::FromRow;
|
||||||
|
|
||||||
|
use super::{prelude::*, util::PartyError};
|
||||||
|
|
||||||
|
api_routes!();
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct EventOutcome {
|
||||||
|
points: HashMap<i64, i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, FromRow)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct FreeForAllGame {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, FromRow)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct TeamGame {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, FromRow)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Test {}
|
||||||
|
|
||||||
|
// # Event
|
||||||
|
//
|
||||||
|
// An event in which participants can win or lose points
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum Event {
|
||||||
|
FreeForAllGame(FreeForAllGame),
|
||||||
|
TeamGame(TeamGame),
|
||||||
|
Test(Test),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventRecord {
|
||||||
|
id: i64,
|
||||||
|
name: String,
|
||||||
|
event_type: String,
|
||||||
|
event_id: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! dispatch {
|
||||||
|
($event:ident) => {
|
||||||
|
dispatch!($event,
|
||||||
|
free_for_all_game => FreeForAllGame,
|
||||||
|
team_game => TeamGame,
|
||||||
|
test => Test,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
($event:ident, $($event_type:ident => $event_struct:ident),* $(,)?) => {
|
||||||
|
match $event.event_type.as_str() {
|
||||||
|
$(stringify!($event_type) => dispatch_run!($event_type, $event_struct),)*
|
||||||
|
_ => return Err(PartyError::Unknown("invalid event type".into())),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reverse_dispatch {
|
||||||
|
($event:ident) => {
|
||||||
|
reverse_dispatch!($event,
|
||||||
|
FreeForAllGame => free_for_all_game,
|
||||||
|
TeamGame => team_game,
|
||||||
|
Test => test,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
($event:ident, $($event_struct:ident => $event_type:ident),* $(,)?) => {
|
||||||
|
match $event {
|
||||||
|
$(Event::$event_struct(e) => reverse_dispatch_run!($event_type, $event_struct, e),)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventRecord {
|
||||||
|
pub async fn get(db: &mut Connection<Db>, id: i64) -> Result<Self, PartyError> {
|
||||||
|
Ok(sqlx::query_as!(
|
||||||
|
EventRecord,
|
||||||
|
"SELECT id, name, event_type, event_id FROM events WHERE id = ?",
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.fetch_one(&mut **db)
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove(&self, db: &mut Connection<Db>) -> Result<(), PartyError> {
|
||||||
|
macro_rules! dispatch_run {
|
||||||
|
($event_type:ident, $event_struct:ident) => {{
|
||||||
|
sqlx::query(&format!(
|
||||||
|
"DELETE FROM events_{} WHERE id = {}",
|
||||||
|
stringify!($event_type),
|
||||||
|
self.event_id
|
||||||
|
))
|
||||||
|
.fetch_one(&mut **db)
|
||||||
|
.await?;
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch!(self);
|
||||||
|
|
||||||
|
sqlx::query!("DELETE FROM events WHERE id = ?", self.id)
|
||||||
|
.execute(&mut **db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event {
|
||||||
|
pub async fn register(
|
||||||
|
&self,
|
||||||
|
db: &mut Connection<Db>,
|
||||||
|
name: String,
|
||||||
|
) -> Result<EventRecord, PartyError> {
|
||||||
|
let event_id = match self {
|
||||||
|
Self::FreeForAllGame(e) => {
|
||||||
|
unimplemented!()
|
||||||
|
/*
|
||||||
|
sqlx::query!("INSERT INTO events_free_for_all_game () VALUES ()")
|
||||||
|
.execute(&mut **db)
|
||||||
|
.await?
|
||||||
|
.last_insert_rowid()
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
Self::TeamGame(e) => {
|
||||||
|
unimplemented!()
|
||||||
|
/*
|
||||||
|
sqlx::query!("INSERT INTO events_team_game () VALUES ()")
|
||||||
|
.execute(&mut **db)
|
||||||
|
.await?
|
||||||
|
.last_insert_rowid()
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
Self::Test(e) => sqlx::query!("INSERT INTO events_test () VALUES ()")
|
||||||
|
.execute(&mut **db)
|
||||||
|
.await?
|
||||||
|
.last_insert_rowid(),
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! reverse_dispatch_run {
|
||||||
|
($event_type:ident, $event_struct:ident, $inner:ident) => {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO events (name, event_type, event_id) VALUES (?, ?, ?)",
|
||||||
|
stringify!($event_type),
|
||||||
|
name,
|
||||||
|
event_id
|
||||||
|
)
|
||||||
|
.execute(&mut **db)
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = reverse_dispatch!(self).last_insert_rowid();
|
||||||
|
Ok(EventRecord::get(db, id).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get(db: &mut Connection<Db>, record: EventRecord) -> Result<Self, PartyError> {
|
||||||
|
macro_rules! dispatch_run {
|
||||||
|
($event_type:ident, $event_struct:ident) => {
|
||||||
|
Event::$event_struct(
|
||||||
|
sqlx::query_as::<_, $event_struct>(&format!(
|
||||||
|
"SELECT id, name, event_type, event_id FROM events_{} WHERE id = {}",
|
||||||
|
stringify!($event_type),
|
||||||
|
record.event_id
|
||||||
|
))
|
||||||
|
.fetch_one(&mut **db)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(dispatch!(record))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[openapi(tag = "Event")]
|
||||||
|
#[post("/<id>/stop")]
|
||||||
|
pub fn stop_event(id: i64) -> Result<Json<EventOutcome>, PartyError> {
|
||||||
|
todo!()
|
||||||
|
}
|
|
@ -1 +1,50 @@
|
||||||
pub mod user;
|
mod auth;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
|
pub use auth::ApiKey;
|
||||||
|
use rocket::{Build, Rocket};
|
||||||
|
use rocket_okapi::{mount_endpoints_and_merged_docs, settings::OpenApiSettings};
|
||||||
|
|
||||||
|
mod prelude {
|
||||||
|
pub use super::{util, ApiKey};
|
||||||
|
pub use crate::{api_routes, Db};
|
||||||
|
pub use rocket::{
|
||||||
|
http::Status,
|
||||||
|
response::status,
|
||||||
|
serde::{json::Json, Deserialize, Serialize},
|
||||||
|
};
|
||||||
|
pub use rocket_db_pools::{sqlx, Connection};
|
||||||
|
pub use rocket_okapi::{openapi, JsonSchema};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! api_routes {
|
||||||
|
($($route:ident),* $(,)?) => {
|
||||||
|
pub fn get_routes_and_docs(
|
||||||
|
settings: &rocket_okapi::settings::OpenApiSettings
|
||||||
|
) -> (Vec<rocket::Route>, okapi::openapi3::OpenApi) {
|
||||||
|
rocket_okapi::openapi_get_routes_spec![
|
||||||
|
settings: $($route,)*
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! mount_endpoints {
|
||||||
|
($($endpoint:ident),* $(,)?) => {
|
||||||
|
$(pub mod $endpoint;)*
|
||||||
|
|
||||||
|
pub fn mount_endpoints(
|
||||||
|
mut building_rocket: Rocket<Build>,
|
||||||
|
openapi_settings: &OpenApiSettings,
|
||||||
|
) -> Rocket<Build> {
|
||||||
|
mount_endpoints_and_merged_docs! {
|
||||||
|
building_rocket, "/api".to_owned(), openapi_settings,
|
||||||
|
$(stringify!("/", $endpoint) => $endpoint::get_routes_and_docs(&openapi_settings),)*
|
||||||
|
};
|
||||||
|
building_rocket
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mount_endpoints!(user, event);
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use crate::{ApiKey, Db, Ordering, PartyError};
|
use super::prelude::*;
|
||||||
use okapi::openapi3::OpenApi;
|
|
||||||
use rocket::{
|
|
||||||
fairing::AdHoc,
|
|
||||||
http::Status,
|
|
||||||
response::status::Created,
|
|
||||||
serde::{json::Json, Deserialize, Serialize},
|
|
||||||
};
|
|
||||||
use rocket_db_pools::{sqlx, Connection};
|
|
||||||
use rocket_okapi::{
|
|
||||||
openapi, openapi_get_routes, openapi_get_routes_spec, settings::OpenApiSettings, JsonSchema,
|
|
||||||
};
|
|
||||||
use sqlx::FromRow;
|
use sqlx::FromRow;
|
||||||
|
use util::{Ordering, PartyError};
|
||||||
|
|
||||||
|
api_routes!(
|
||||||
|
add_user,
|
||||||
|
get_user,
|
||||||
|
get_all_users,
|
||||||
|
delete_user,
|
||||||
|
set_score,
|
||||||
|
get_score
|
||||||
|
);
|
||||||
|
|
||||||
/// # User
|
/// # User
|
||||||
///
|
///
|
||||||
|
@ -37,7 +36,7 @@ pub async fn add_user(
|
||||||
_api_key: ApiKey,
|
_api_key: ApiKey,
|
||||||
mut db: Connection<Db>,
|
mut db: Connection<Db>,
|
||||||
name: Json<&str>,
|
name: Json<&str>,
|
||||||
) -> Result<Created<Json<User>>, PartyError> {
|
) -> Result<status::Created<Json<User>>, PartyError> {
|
||||||
let result = sqlx::query!("INSERT INTO users (name) VALUES (?)", *name)
|
let result = sqlx::query!("INSERT INTO users (name) VALUES (?)", *name)
|
||||||
.execute(&mut *db)
|
.execute(&mut *db)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -48,7 +47,7 @@ pub async fn add_user(
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Created::new("/").body(Json(user)))
|
Ok(status::Created::new("/").body(Json(user)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Delete user by id
|
/// # Delete user by id
|
||||||
|
@ -159,14 +158,3 @@ pub async fn get_score(
|
||||||
.await?;
|
.await?;
|
||||||
Ok(Json(score))
|
Ok(Json(score))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_routes_and_docs(settings: &OpenApiSettings) -> (Vec<rocket::Route>, OpenApi) {
|
|
||||||
openapi_get_routes_spec![
|
|
||||||
settings: add_user,
|
|
||||||
get_user,
|
|
||||||
get_all_users,
|
|
||||||
delete_user,
|
|
||||||
set_score,
|
|
||||||
get_score
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
use rocket::{http::Status, response, response::Responder, Request};
|
||||||
|
use rocket_okapi::response::OpenApiResponderInner;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// # Ordering
|
||||||
|
///
|
||||||
|
/// Ordering of data in an array, ascending or descending
|
||||||
|
#[derive(Clone, Debug, FromFormField, JsonSchema)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Ordering {
|
||||||
|
#[field(value = "desc")]
|
||||||
|
Desc,
|
||||||
|
#[field(value = "asc")]
|
||||||
|
Asc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for Ordering {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Desc => "DESC",
|
||||||
|
Self::Asc => "ASC",
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PartyError {
|
||||||
|
#[error("user `{0}` does not exist")]
|
||||||
|
UserNotFound(i64),
|
||||||
|
#[error("unknown error: {0}")]
|
||||||
|
Unknown(String),
|
||||||
|
#[error("invalid parameter: {0}")]
|
||||||
|
InvalidParameter(String),
|
||||||
|
#[error("uuid error {source:?}")]
|
||||||
|
UuidError {
|
||||||
|
#[from]
|
||||||
|
source: uuid::Error,
|
||||||
|
},
|
||||||
|
#[error("sqlx error {source:?}")]
|
||||||
|
SqlxError {
|
||||||
|
#[from]
|
||||||
|
source: sqlx::Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OpenApiResponderInner for PartyError {
|
||||||
|
fn responses(
|
||||||
|
_gen: &mut rocket_okapi::gen::OpenApiGenerator,
|
||||||
|
) -> rocket_okapi::Result<okapi::openapi3::Responses> {
|
||||||
|
Ok(okapi::openapi3::Responses::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r, 'o: 'r> Responder<'r, 'o> for PartyError {
|
||||||
|
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
|
||||||
|
match self {
|
||||||
|
Self::UserNotFound(_) => Status::NotFound,
|
||||||
|
_ => Status::InternalServerError,
|
||||||
|
}
|
||||||
|
.respond_to(req)
|
||||||
|
}
|
||||||
|
}
|
168
src/main.rs
168
src/main.rs
|
@ -1,18 +1,10 @@
|
||||||
mod api;
|
mod api;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use rocket::{fairing, fairing::AdHoc, Build, Rocket};
|
||||||
use okapi::openapi3::{Object, Responses, SecurityRequirement, SecurityScheme, SecuritySchemeData};
|
|
||||||
use rocket::{
|
|
||||||
fairing, fairing::AdHoc, http::Status, request, request::FromRequest, response,
|
|
||||||
response::Responder, serde, Build, Request, Rocket,
|
|
||||||
};
|
|
||||||
use rocket_db_pools::{sqlx, Database};
|
use rocket_db_pools::{sqlx, Database};
|
||||||
use rocket_okapi::{
|
use rocket_okapi::{
|
||||||
gen::OpenApiGenerator,
|
mount_endpoints_and_merged_docs, openapi,
|
||||||
mount_endpoints_and_merged_docs, openapi, openapi_get_routes,
|
|
||||||
rapidoc::*,
|
rapidoc::*,
|
||||||
request::{OpenApiFromRequest, RequestHeaderInput},
|
|
||||||
response::OpenApiResponderInner,
|
|
||||||
settings::{OpenApiSettings, UrlObject},
|
settings::{OpenApiSettings, UrlObject},
|
||||||
swagger_ui::{make_swagger_ui, SwaggerUIConfig},
|
swagger_ui::{make_swagger_ui, SwaggerUIConfig},
|
||||||
};
|
};
|
||||||
|
@ -20,155 +12,16 @@ use rocket_okapi::{
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
/*
|
|
||||||
const API_KEYS: [&'static str; 3] = [
|
|
||||||
"7de10bf6-278d-11ed-ad60-a8a15919d1b3",
|
|
||||||
"89eb06e0-278d-11ed-9b29-a8a15919d1b3",
|
|
||||||
"8a35ba14-278d-11ed-a200-a8a15919d1b3",
|
|
||||||
];
|
|
||||||
*/
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref API_KEYS: Vec<String> = {
|
|
||||||
serde_json::from_str(
|
|
||||||
&std::fs::read_to_string("api_keys.json").expect("api_keys.json does not exist"),
|
|
||||||
)
|
|
||||||
.expect("api_keys.json is not valid")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum PartyError {
|
|
||||||
#[error("user `{0}` does not exist")]
|
|
||||||
UserNotFound(i64),
|
|
||||||
#[error("unknown error: {0}")]
|
|
||||||
Unknown(String),
|
|
||||||
#[error("invalid parameter: {0}")]
|
|
||||||
InvalidParameter(String),
|
|
||||||
#[error("uuid error {source:?}")]
|
|
||||||
UuidError {
|
|
||||||
#[from]
|
|
||||||
source: uuid::Error,
|
|
||||||
},
|
|
||||||
#[error("sqlx error {source:?}")]
|
|
||||||
SqlxError {
|
|
||||||
#[from]
|
|
||||||
source: sqlx::Error,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OpenApiResponderInner for PartyError {
|
|
||||||
fn responses(
|
|
||||||
_gen: &mut rocket_okapi::gen::OpenApiGenerator,
|
|
||||||
) -> rocket_okapi::Result<okapi::openapi3::Responses> {
|
|
||||||
Ok(okapi::openapi3::Responses::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'r, 'o: 'r> Responder<'r, 'o> for PartyError {
|
|
||||||
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
|
|
||||||
match self {
|
|
||||||
Self::UserNotFound(_) => Status::NotFound,
|
|
||||||
_ => Status::InternalServerError,
|
|
||||||
}
|
|
||||||
.respond_to(req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Ordering
|
|
||||||
///
|
|
||||||
/// Ordering of data in an array, ascending or descending
|
|
||||||
#[derive(Clone, Debug, FromFormField, JsonSchema)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum Ordering {
|
|
||||||
#[field(value = "desc")]
|
|
||||||
Desc,
|
|
||||||
#[field(value = "asc")]
|
|
||||||
Asc,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for Ordering {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::Desc => "DESC",
|
|
||||||
Self::Asc => "ASC",
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Database)]
|
#[derive(Database)]
|
||||||
#[database("party")]
|
#[database("party")]
|
||||||
pub struct Db(sqlx::SqlitePool);
|
pub struct Db(sqlx::SqlitePool);
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct ApiKey;
|
|
||||||
|
|
||||||
#[rocket::async_trait]
|
|
||||||
impl<'r> FromRequest<'r> for ApiKey {
|
|
||||||
type Error = ApiKey;
|
|
||||||
|
|
||||||
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
|
||||||
if req
|
|
||||||
.headers()
|
|
||||||
.get("X-API-Key")
|
|
||||||
.any(|k| API_KEYS.contains(&String::from(k)))
|
|
||||||
{
|
|
||||||
request::Outcome::Success(ApiKey)
|
|
||||||
} else {
|
|
||||||
request::Outcome::Failure((Status::Unauthorized, ApiKey))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> OpenApiFromRequest<'a> for ApiKey {
|
|
||||||
fn from_request_input(
|
|
||||||
_gen: &mut OpenApiGenerator,
|
|
||||||
_name: String,
|
|
||||||
_required: bool,
|
|
||||||
) -> rocket_okapi::Result<RequestHeaderInput> {
|
|
||||||
// Setup global requirement for Security scheme
|
|
||||||
let security_scheme = SecurityScheme {
|
|
||||||
description: Some("Requires an API key to access".to_owned()),
|
|
||||||
// Setup data requirements.
|
|
||||||
// This can be part of the `header`, `query` or `cookie`.
|
|
||||||
// In this case the header `x-api-key: mykey` needs to be set.
|
|
||||||
data: SecuritySchemeData::ApiKey {
|
|
||||||
name: "x-api-key".to_owned(),
|
|
||||||
location: "header".to_owned(),
|
|
||||||
},
|
|
||||||
extensions: Object::default(),
|
|
||||||
};
|
|
||||||
// Add the requirement for this route/endpoint
|
|
||||||
// This can change between routes.
|
|
||||||
let mut security_req = SecurityRequirement::new();
|
|
||||||
// Each security requirement needs to be met before access is allowed.
|
|
||||||
security_req.insert("ApiKeyAuth".to_owned(), Vec::new());
|
|
||||||
// These vvvvvvv-----^^^^^^^^^^ values need to match exactly!
|
|
||||||
Ok(RequestHeaderInput::Security(
|
|
||||||
"ApiKeyAuth".to_owned(),
|
|
||||||
security_scheme,
|
|
||||||
security_req,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[openapi]
|
#[openapi]
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn index() -> String {
|
fn index() -> String {
|
||||||
format!("Hello, world!")
|
format!("Hello, world!")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_docs() -> SwaggerUIConfig {
|
|
||||||
SwaggerUIConfig {
|
|
||||||
url: "../api/openapi.json".to_owned(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
|
async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
|
||||||
match Db::fetch(&rocket) {
|
match Db::fetch(&rocket) {
|
||||||
Some(db) => match sqlx::migrate!("db/migrations").run(&**db).await {
|
Some(db) => match sqlx::migrate!("db/migrations").run(&**db).await {
|
||||||
|
@ -187,12 +40,17 @@ async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> _ {
|
fn rocket() -> _ {
|
||||||
println!("{:#?}", API_KEYS.len());
|
let building_rocket = rocket::build()
|
||||||
let mut building_rocket = rocket::build()
|
|
||||||
.attach(Db::init())
|
.attach(Db::init())
|
||||||
.attach(AdHoc::try_on_ignite("SQLx Migrations", run_migrations))
|
.attach(AdHoc::try_on_ignite("SQLx Migrations", run_migrations))
|
||||||
.mount("/", routes![index])
|
.mount("/", routes![index])
|
||||||
.mount("/swagger", make_swagger_ui(&get_docs()))
|
.mount(
|
||||||
|
"/swagger",
|
||||||
|
make_swagger_ui(&SwaggerUIConfig {
|
||||||
|
url: "../api/openapi.json".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
.mount(
|
.mount(
|
||||||
"/rapidoc/",
|
"/rapidoc/",
|
||||||
make_rapidoc(&RapiDocConfig {
|
make_rapidoc(&RapiDocConfig {
|
||||||
|
@ -210,10 +68,6 @@ fn rocket() -> _ {
|
||||||
);
|
);
|
||||||
|
|
||||||
let openapi_settings = OpenApiSettings::default();
|
let openapi_settings = OpenApiSettings::default();
|
||||||
mount_endpoints_and_merged_docs! {
|
|
||||||
building_rocket, "/api".to_owned(), openapi_settings,
|
|
||||||
"/user" => api::user::get_routes_and_docs(&openapi_settings),
|
|
||||||
};
|
|
||||||
|
|
||||||
building_rocket
|
api::mount_endpoints(building_rocket, &openapi_settings)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue