fix: add OpenAPI documentation to API endpoints and integrate Swagger UI
Co-authored-by: aider (openai/andrew/openrouter/qwen/qwen3-coder) <aider@aider.chat>
This commit is contained in:
@@ -65,6 +65,7 @@ async fn run_server(
|
|||||||
|
|
||||||
// Add API, documentation, and pages routes
|
// Add API, documentation, and pages routes
|
||||||
let app = api::add_routes(app);
|
let app = api::add_routes(app);
|
||||||
|
let app = api::add_docs_routes(app);
|
||||||
let app = docs::add_routes(app);
|
let app = docs::add_routes(app);
|
||||||
let app = pages::add_routes(app);
|
let app = pages::add_routes(app);
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,30 @@ use std::path::PathBuf;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
use crate::compression_engine::{CompressionType, get_compression_engine};
|
use crate::compression_engine::{CompressionType, get_compression_engine};
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::modes::server::common::{AppState, ApiResponse, ItemInfo, TagsQuery, check_auth, ListItemsQuery};
|
use crate::modes::server::common::{AppState, ApiResponse, ItemInfo, TagsQuery, check_auth, ListItemsQuery};
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/item/",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully retrieved list of items", body = ApiResponse<Vec<ItemInfo>>),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by"),
|
||||||
|
("order" = Option<String>, Query, description = "Sort order (newest or oldest)"),
|
||||||
|
("start" = Option<u64>, Query, description = "Starting index for pagination"),
|
||||||
|
("count" = Option<u64>, Query, description = "Number of items to return")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_list_items(
|
pub async fn handle_list_items(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(params): Query<ListItemsQuery>,
|
Query(params): Query<ListItemsQuery>,
|
||||||
@@ -106,6 +125,18 @@ pub async fn handle_list_items(
|
|||||||
Ok(Json(response))
|
Ok(Json(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/item/",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully created item", body = ApiResponse<ItemInfo>),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_post_item(
|
pub async fn handle_post_item(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
@@ -129,6 +160,22 @@ pub async fn handle_post_item(
|
|||||||
Ok(Json(response))
|
Ok(Json(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
delete,
|
||||||
|
path = "/api/item/{item_id}",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully deleted item", body = ApiResponse<()>),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 404, description = "Item not found"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("item_id" = String, Path, description = "ID of the item to delete")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_delete_item(
|
pub async fn handle_delete_item(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(item_id): Path<String>,
|
Path(item_id): Path<String>,
|
||||||
@@ -166,6 +213,22 @@ pub async fn handle_delete_item(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/item/latest",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully retrieved latest item content", body = ApiResponse<String>),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 404, description = "Item not found"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_get_item_latest(
|
pub async fn handle_get_item_latest(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(params): Query<TagsQuery>,
|
Query(params): Query<TagsQuery>,
|
||||||
@@ -218,6 +281,22 @@ pub async fn handle_get_item_latest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/item/{item_id}",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully retrieved item content", body = ApiResponse<String>),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 404, description = "Item not found"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("item_id" = String, Path, description = "ID of the item to retrieve")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_get_item(
|
pub async fn handle_get_item(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(item_id): Path<String>,
|
Path(item_id): Path<String>,
|
||||||
@@ -269,6 +348,22 @@ pub async fn handle_get_item(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/item/latest/content",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully retrieved latest item raw content"),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 404, description = "Item not found"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_get_item_latest_content(
|
pub async fn handle_get_item_latest_content(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(params): Query<TagsQuery>,
|
Query(params): Query<TagsQuery>,
|
||||||
@@ -316,6 +411,22 @@ pub async fn handle_get_item_latest_content(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/item/{item_id}/content",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully retrieved item raw content"),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 404, description = "Item not found"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("item_id" = String, Path, description = "ID of the item to retrieve")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_get_item_content(
|
pub async fn handle_get_item_content(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(item_id): Path<String>,
|
Path(item_id): Path<String>,
|
||||||
@@ -416,6 +527,22 @@ async fn get_item_raw_content(item: &db::Item, data_dir: &PathBuf, conn: &mut ru
|
|||||||
Ok((content, mime_type))
|
Ok((content, mime_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/item/latest/meta",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully retrieved latest item metadata", body = ApiResponse<HashMap<String, String>>),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 404, description = "Item not found"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_get_item_latest_meta(
|
pub async fn handle_get_item_latest_meta(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(params): Query<TagsQuery>,
|
Query(params): Query<TagsQuery>,
|
||||||
@@ -465,6 +592,22 @@ pub async fn handle_get_item_latest_meta(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/item/{item_id}/meta",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully retrieved item metadata", body = ApiResponse<HashMap<String, String>>),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 404, description = "Item not found"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
params(
|
||||||
|
("item_id" = String, Path, description = "ID of the item to retrieve metadata for")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_get_item_meta(
|
pub async fn handle_get_item_meta(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(item_id): Path<String>,
|
Path(item_id): Path<String>,
|
||||||
|
|||||||
@@ -2,11 +2,45 @@ pub mod item;
|
|||||||
pub mod status;
|
pub mod status;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
routing::get,
|
routing::{get, post, delete},
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::modes::server::common::AppState;
|
use crate::modes::server::common::AppState;
|
||||||
|
use utoipa::OpenApi;
|
||||||
|
use utoipa_swagger_ui::SwaggerUi;
|
||||||
|
|
||||||
|
#[derive(OpenApi)]
|
||||||
|
#[openapi(
|
||||||
|
paths(
|
||||||
|
status::handle_status,
|
||||||
|
item::handle_list_items,
|
||||||
|
item::handle_post_item,
|
||||||
|
item::handle_delete_item,
|
||||||
|
item::handle_get_item_latest,
|
||||||
|
item::handle_get_item_latest_meta,
|
||||||
|
item::handle_get_item_latest_content,
|
||||||
|
item::handle_get_item,
|
||||||
|
item::handle_get_item_meta,
|
||||||
|
item::handle_get_item_content,
|
||||||
|
),
|
||||||
|
components(
|
||||||
|
schemas(
|
||||||
|
crate::modes::server::common::ApiResponse<crate::modes::server::api::status::StatusInfo>,
|
||||||
|
crate::modes::server::common::ApiResponse<Vec<crate::modes::server::api::item::ItemInfo>>,
|
||||||
|
crate::modes::server::common::ApiResponse<crate::modes::server::api::item::ItemInfo>,
|
||||||
|
crate::modes::server::common::ApiResponse<HashMap<String, String>>,
|
||||||
|
crate::modes::server::common::ApiResponse<()>,
|
||||||
|
crate::modes::server::api::status::StatusInfo,
|
||||||
|
crate::modes::server::api::item::ItemInfo,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
tags(
|
||||||
|
(name = "status", description = "Status API endpoints"),
|
||||||
|
(name = "items", description = "Item management API endpoints")
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
struct ApiDoc;
|
||||||
|
|
||||||
pub fn add_routes(router: Router<AppState>) -> Router<AppState> {
|
pub fn add_routes(router: Router<AppState>) -> Router<AppState> {
|
||||||
router
|
router
|
||||||
@@ -22,3 +56,8 @@ pub fn add_routes(router: Router<AppState>) -> Router<AppState> {
|
|||||||
.route("/api/item/:id/meta", get(item::handle_get_item_meta))
|
.route("/api/item/:id/meta", get(item::handle_get_item_meta))
|
||||||
.route("/api/item/:id/content", get(item::handle_get_item_content))
|
.route("/api/item/:id/content", get(item::handle_get_item_content))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_docs_routes(router: Router<AppState>) -> Router<AppState> {
|
||||||
|
router
|
||||||
|
.merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi()))
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,24 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
use crate::modes::server::common::{AppState, ApiResponse, check_auth};
|
use crate::modes::server::common::{AppState, ApiResponse, check_auth};
|
||||||
use crate::common::status::{generate_status_info, StatusInfo};
|
use crate::common::status::{generate_status_info, StatusInfo};
|
||||||
use crate::meta_plugin::MetaPluginType;
|
use crate::meta_plugin::MetaPluginType;
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/status",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully retrieved status information", body = ApiResponse<StatusInfo>),
|
||||||
|
(status = 401, description = "Unauthorized"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("bearerAuth" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub async fn handle_status(
|
pub async fn handle_status(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
|||||||
Reference in New Issue
Block a user