add init utoipa crate openapi doc generation
This commit is contained in:
parent
6e887bfd8b
commit
f9c724f53c
5 changed files with 256 additions and 13 deletions
163
Cargo.lock
generated
163
Cargo.lock
generated
|
@ -409,6 +409,15 @@ version = "1.0.97"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.7.1"
|
version = "1.7.1"
|
||||||
|
@ -631,6 +640,8 @@ dependencies = [
|
||||||
"temp-env",
|
"temp-env",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"tracing-actix-web",
|
"tracing-actix-web",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
"uuid",
|
"uuid",
|
||||||
"validator",
|
"validator",
|
||||||
]
|
]
|
||||||
|
@ -1102,6 +1113,17 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.100",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.19"
|
version = "0.99.19"
|
||||||
|
@ -1826,6 +1848,7 @@ checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.15.2",
|
"hashbrown 0.15.2",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2695,6 +2718,40 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed"
|
||||||
|
version = "8.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a"
|
||||||
|
dependencies = [
|
||||||
|
"rust-embed-impl",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-impl"
|
||||||
|
version = "8.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"syn 2.0.100",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-utils"
|
||||||
|
version = "8.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594"
|
||||||
|
dependencies = [
|
||||||
|
"sha2",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_decimal"
|
name = "rust_decimal"
|
||||||
version = "1.36.0"
|
version = "1.36.0"
|
||||||
|
@ -2804,6 +2861,15 @@ version = "1.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scc"
|
name = "scc"
|
||||||
version = "2.3.4"
|
version = "2.3.4"
|
||||||
|
@ -3121,6 +3187,12 @@ dependencies = [
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simdutf8"
|
name = "simdutf8"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
@ -3810,6 +3882,49 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utoipa"
|
||||||
|
version = "5.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "435c6f69ef38c9017b4b4eea965dfb91e71e53d869e896db40d1cf2441dd75c0"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"utoipa-gen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utoipa-gen"
|
||||||
|
version = "5.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a77d306bc75294fd52f3e99b13ece67c02c1a2789190a6f31d32f736624326f7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn 2.0.100",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utoipa-swagger-ui"
|
||||||
|
version = "9.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "161166ec520c50144922a625d8bc4925cc801b2dda958ab69878527c0e5c5d61"
|
||||||
|
dependencies = [
|
||||||
|
"actix-web",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"mime_guess",
|
||||||
|
"regex",
|
||||||
|
"rust-embed",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"url",
|
||||||
|
"utoipa",
|
||||||
|
"zip",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
@ -3874,6 +3989,16 @@ version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
@ -3995,6 +4120,15 @@ dependencies = [
|
||||||
"wasite",
|
"wasite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
|
@ -4296,6 +4430,35 @@ dependencies = [
|
||||||
"syn 2.0.100",
|
"syn 2.0.100",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "2.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"displaydoc",
|
||||||
|
"flate2",
|
||||||
|
"indexmap",
|
||||||
|
"memchr",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"zopfli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zopfli"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"crc32fast",
|
||||||
|
"log",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zstd"
|
name = "zstd"
|
||||||
version = "0.13.3"
|
version = "0.13.3"
|
||||||
|
|
|
@ -27,6 +27,10 @@ sea-orm = { version = "1.1", features = [
|
||||||
] }
|
] }
|
||||||
uuid = "1"
|
uuid = "1"
|
||||||
|
|
||||||
|
utoipa = { version = "*", features = ["actix_extras", "chrono", "uuid"] }
|
||||||
|
# utoipa-actix-web = "*" # TODO: Test this for better actix-web integration
|
||||||
|
utoipa-swagger-ui = { version = "*", features = ["actix-web"] }
|
||||||
|
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use actix_web::web::{self, ServiceConfig};
|
use actix_web::web::{self, ServiceConfig};
|
||||||
|
|
||||||
mod auth;
|
pub mod auth; // TODO: Refactor to use re-exports instead of making module public
|
||||||
mod class;
|
mod class;
|
||||||
mod group;
|
mod group;
|
||||||
mod project;
|
mod project;
|
||||||
|
|
|
@ -4,22 +4,53 @@ use actix_web::{
|
||||||
web::{self, ServiceConfig},
|
web::{self, ServiceConfig},
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
use crate::{Database, error::ApiError};
|
use crate::{Database, error::ApiError};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize, ToSchema)]
|
||||||
struct LoginRequest {
|
pub struct LoginRequest {
|
||||||
username: String,
|
/// Username for authentication
|
||||||
password: String,
|
pub username: String,
|
||||||
|
/// Password for authentication
|
||||||
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema)]
|
||||||
|
pub struct LoginResponse {
|
||||||
|
/// Success message
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema)]
|
||||||
|
pub struct LogoutResponse {
|
||||||
|
/// Logout confirmation message
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement generic ApiResponse<T> type to reduce boilerplate
|
||||||
|
|
||||||
pub fn setup(cfg: &mut ServiceConfig) {
|
pub fn setup(cfg: &mut ServiceConfig) {
|
||||||
cfg.service(login).service(logout);
|
cfg.service(login).service(logout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/v1/login",
|
||||||
|
tag = "auth",
|
||||||
|
summary = "User login",
|
||||||
|
description = "Authenticate a user with username and password",
|
||||||
|
request_body = LoginRequest,
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Login successful", body = LoginResponse),
|
||||||
|
(status = 400, description = "Invalid credentials"),
|
||||||
|
(status = 409, description = "User already logged in"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
)
|
||||||
|
)]
|
||||||
#[post("/login")]
|
#[post("/login")]
|
||||||
async fn login(
|
pub async fn login(
|
||||||
db: web::Data<Database>,
|
db: web::Data<Database>,
|
||||||
login_request: web::Json<LoginRequest>,
|
login_request: web::Json<LoginRequest>,
|
||||||
session: Session,
|
session: Session,
|
||||||
|
@ -36,14 +67,29 @@ async fn login(
|
||||||
|
|
||||||
session.insert("user", user_id)?;
|
session.insert("user", user_id)?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok())
|
Ok(HttpResponse::Ok().json(LoginResponse {
|
||||||
|
message: "Login successful".to_string(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/v1/logout",
|
||||||
|
tag = "auth",
|
||||||
|
summary = "User logout",
|
||||||
|
description = "Log out the currently authenticated user and clear session",
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Logout successful", body = LogoutResponse),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
)
|
||||||
|
)]
|
||||||
#[post("/logout")]
|
#[post("/logout")]
|
||||||
async fn logout(session: Session, request: HttpRequest) -> Result<impl Responder, ApiError> {
|
pub async fn logout(session: Session, request: HttpRequest) -> Result<impl Responder, ApiError> {
|
||||||
debug!("request cookies: {:?}", request.cookies());
|
debug!("request cookies: {:?}", request.cookies());
|
||||||
debug!("Session entries: {:?}", session.entries());
|
debug!("Session entries: {:?}", session.entries());
|
||||||
session.purge();
|
session.purge();
|
||||||
debug!("Session entries after purge: {:?}", session.entries());
|
debug!("Session entries after purge: {:?}", session.entries());
|
||||||
Ok(HttpResponse::Ok().body("Logged out successfully"))
|
Ok(HttpResponse::Ok().json(LogoutResponse {
|
||||||
|
message: "Logged out successfully".to_string(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use actix_files::NamedFile;
|
use actix_files::NamedFile;
|
||||||
use actix_session::Session;
|
|
||||||
use actix_session::{SessionMiddleware, storage::RedisSessionStore};
|
use actix_session::{SessionMiddleware, storage::RedisSessionStore};
|
||||||
use actix_web::cookie::SameSite;
|
use actix_web::cookie::SameSite;
|
||||||
use actix_web::{App, HttpResponse, HttpServer, cookie::Key, middleware::Logger, web};
|
use actix_web::{App, HttpServer, cookie::Key, middleware::Logger, web};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
use utoipa::OpenApi;
|
||||||
|
use utoipa_swagger_ui::SwaggerUi;
|
||||||
|
|
||||||
mod controller;
|
mod controller;
|
||||||
mod db;
|
mod db;
|
||||||
|
@ -20,6 +21,31 @@ struct AppConfig {
|
||||||
ldap_auth: bool,
|
ldap_auth: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(OpenApi)]
|
||||||
|
#[openapi(
|
||||||
|
info(
|
||||||
|
title = "PGG API",
|
||||||
|
description = "API for the PGG (Paket Verfolgungs Programm) application",
|
||||||
|
version = "1.0.0",
|
||||||
|
),
|
||||||
|
paths(
|
||||||
|
controller::auth::login,
|
||||||
|
controller::auth::logout,
|
||||||
|
),
|
||||||
|
components(schemas(
|
||||||
|
controller::auth::LoginRequest,
|
||||||
|
controller::auth::LoginResponse,
|
||||||
|
controller::auth::LogoutResponse,
|
||||||
|
)),
|
||||||
|
tags(
|
||||||
|
(name = "auth", description = "Authentication endpoints"),
|
||||||
|
(name = "users", description = "User management endpoints"),
|
||||||
|
(name = "projects", description = "Project management endpoints"),
|
||||||
|
(name = "groups", description = "Group management endpoints"),
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
struct ApiDoc;
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
dotenvy::dotenv().ok();
|
dotenvy::dotenv().ok();
|
||||||
|
@ -59,7 +85,11 @@ async fn main() -> std::io::Result<()> {
|
||||||
.app_data(web::Data::new(app_config.clone()))
|
.app_data(web::Data::new(app_config.clone()))
|
||||||
.wrap(Logger::default())
|
.wrap(Logger::default())
|
||||||
.wrap(session_middleware)
|
.wrap(session_middleware)
|
||||||
.service(web::scope("/api/v1").configure(controller::register_controllers));
|
.service(web::scope("/api/v1").configure(controller::register_controllers))
|
||||||
|
.service(
|
||||||
|
SwaggerUi::new("/swagger-ui/{_:.*}")
|
||||||
|
.url("/api-docs/openapi.json", ApiDoc::openapi()),
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(feature = "serve")]
|
#[cfg(feature = "serve")]
|
||||||
let app = {
|
let app = {
|
||||||
|
|
Loading…
Add table
Reference in a new issue