Docs authorization for protected routes

This commit is contained in:
roaming97 2025-04-18 23:59:17 -06:00
parent bc3469fa88
commit 2673f48ade
4 changed files with 30 additions and 9 deletions

View File

@ -4,7 +4,8 @@ Tasks that have been completed in commits before this one have been omitted from
- [x] /video PUT route
- [x] /channel PUT route
- [x] OpenAPI documentation
- [x] OpenAPI docs
- [x] Add authorization for protected routes in API docs
## Planned features

View File

@ -1,4 +1,10 @@
use utoipa::OpenApi;
use utoipa::{
Modify, OpenApi,
openapi::{
Components,
security::{ApiKey, ApiKeyValue, SecurityScheme},
},
};
use utoipa_swagger_ui::SwaggerUi;
// Because apparently the OpenApi macro can't import these on its own
@ -36,10 +42,24 @@ use crate::routes::{
info(
title = "Almond API",
description = "Interface to archive YouTube videos."
)
),
modifiers(&SecurityAddon),
)]
struct ApiDoc;
struct SecurityAddon;
impl Modify for SecurityAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
let components = openapi.components.get_or_insert_with(Components::default);
components.add_security_scheme(
"authKey",
SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::with_description("almond-api-key", "The API key/password that must be sent at the request header for protected routes."))),
);
}
}
pub fn docs_router() -> SwaggerUi {
SwaggerUi::new("/docs").url("/api-doc/openapi.json", ApiDoc::openapi())
}

View File

@ -104,7 +104,7 @@ pub struct UploadChannelQuery {
}
/// Upload a channel's metadata to the database
#[utoipa::path(post, path = "/channel", request_body = UploadChannelQuery, params(("url" = String, Query), ("almond-api-key" = String, description = "API key")), tags = ["channel"], responses((status = 200, description = "Channel already exists, skipping"), (status = 201, description = "Channel uploaded successfully"), (status = 400, description = "Bad request, likely a malformed URL"), (status = 401, description = "Unauthorized"), (status = 500, description = "Failed to upload channel to database")))]
#[utoipa::path(post, path = "/channel", request_body = UploadChannelQuery, params(("url" = String, Query)), tags = ["channel"], responses((status = 200, description = "Channel already exists, skipping"), (status = 201, description = "Channel uploaded successfully"), (status = 400, description = "Bad request, likely a malformed URL"), (status = 401, description = "Unauthorized"), (status = 500, description = "Failed to upload channel to database")), security(("authKey" = [])))]
pub async fn upload_channel(
State(state): State<Instance>,
Query(query): Query<UploadChannelQuery>,
@ -177,7 +177,7 @@ pub async fn upload_channel(
}
/// Update an existing channel from the database
#[utoipa::path(put, path = "/channel/{id}", params(("almond-api-key" = String, description = "API key")), tags = ["channel"], responses((status = 204, description = "Channel updated successfully"), (status = 404, description = "Video with specified ID not found"), (status = 500, description = "Failed to update channel from database")))]
#[utoipa::path(put, path = "/channel/{id}", params(("almond-api-key" = String, description = "API key")), tags = ["channel"], responses((status = 204, description = "Channel updated successfully"), (status = 404, description = "Video with specified ID not found"), (status = 500, description = "Failed to update channel from database")), security(("authKey" = [])))]
pub async fn update_channel(State(state): State<Instance>, Path(id): Path<String>) -> StatusCode {
match sqlx::query_as!(Channel, "SELECT * FROM channel WHERE youtube_id = ?", id)
.fetch_optional(&state.pool)
@ -222,7 +222,7 @@ pub async fn update_channel(State(state): State<Instance>, Path(id): Path<String
}
/// Delete a channel from the database
#[utoipa::path(delete, path = "/channel/{id}", params(("almond-api-key" = String, description = "API key")), tags = ["channel"], responses((status = 204, description = "Channel deleted successfully"), (status = 404, description = "Video with specified ID not found"), (status = 500, description = "Failed to delete channel from database")))]
#[utoipa::path(delete, path = "/channel/{id}", params(("almond-api-key" = String, description = "API key")), tags = ["channel"], responses((status = 204, description = "Channel deleted successfully"), (status = 404, description = "Video with specified ID not found"), (status = 500, description = "Failed to delete channel from database")), security(("authKey" = [])))]
pub async fn delete_channel(State(state): State<Instance>, Path(id): Path<String>) -> StatusCode {
match sqlx::query!("DELETE FROM channel WHERE youtube_id = ?", id)
.fetch_optional(&state.pool)

View File

@ -85,7 +85,7 @@ pub struct UploadVideoQuery {
}
/// Upload a video to the database
#[utoipa::path(post, path = "/video", request_body = UploadVideoQuery, params(("url" = String, Query), ("almond-api-key" = String, description = "API key")), tags = ["video"], responses((status = 200, description = "Video already exists, skipping"), (status = 201, description = "Video uploaded successfully"), (status = 400, description = "Bad request, likely a malformed URL"), (status = 401, description = "Unauthorized"), (status = 500, description = "Failed to upload video to database")))]
#[utoipa::path(post, path = "/video", request_body = UploadVideoQuery, params(("url" = String, Query), ("almond-api-key" = String, description = "API key")), tags = ["video"], responses((status = 200, description = "Video already exists, skipping"), (status = 201, description = "Video uploaded successfully"), (status = 400, description = "Bad request, likely a malformed URL"), (status = 401, description = "Unauthorized"), (status = 500, description = "Failed to upload video to database")), security(("authKey" = [])))]
pub async fn upload_video(
State(state): State<Instance>,
Query(query): Query<UploadVideoQuery>,
@ -162,7 +162,7 @@ pub async fn upload_video(
}
/// Update an existing video from the database
#[utoipa::path(put, path = "/video/{id}", params(("almond-api-key" = String, description = "API key")), tags = ["video"], responses((status = 204, description = "Video updated successfully"), (status = 404, description = "Video with specified ID not found"), (status = 500, description = "Failed to update video from database")))]
#[utoipa::path(put, path = "/video/{id}", params(("almond-api-key" = String, description = "API key")), tags = ["video"], responses((status = 204, description = "Video updated successfully"), (status = 404, description = "Video with specified ID not found"), (status = 500, description = "Failed to update video from database")), security(("authKey" = [])))]
pub async fn update_video(State(state): State<Instance>, Path(id): Path<String>) -> StatusCode {
match sqlx::query_as!(Video, "SELECT * FROM video WHERE youtube_id = ?", id)
.fetch_optional(&state.pool)
@ -205,7 +205,7 @@ pub async fn update_video(State(state): State<Instance>, Path(id): Path<String>)
}
/// Delete a video from the database
#[utoipa::path(delete, path = "/video/{id}", params(("almond-api-key" = String, description = "API key")), tags = ["video"], responses((status = 204, description = "Video deleted successfully"), (status = 404, description = "Video with specified ID not found"), (status = 500, description = "Failed to delete video from database")))]
#[utoipa::path(delete, path = "/video/{id}", params(("almond-api-key" = String, description = "API key")), tags = ["video"], responses((status = 204, description = "Video deleted successfully"), (status = 404, description = "Video with specified ID not found"), (status = 500, description = "Failed to delete video from database")), security(("authKey" = [])))]
pub async fn delete_video(State(state): State<Instance>, Path(id): Path<String>) -> StatusCode {
match sqlx::query!("DELETE FROM video WHERE youtube_id = ?", id)
.fetch_optional(&state.pool)