added swagget-ui
This commit is contained in:
1 parent
78ca22e1b7
commit
9f0681563e
17 files changed
+544
-19
No files matched your search
Generated
+181
@@ -32,6 +32,15 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
@@ -1066,6 +1075,8 @@ checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1279,6 +1290,8 @@ dependencies = [
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"utoipa",
|
||||
"utoipa-swagger-ui",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -1604,6 +1617,30 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
@@ -1696,6 +1733,35 @@ dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.7.1"
|
||||
@@ -1727,6 +1793,40 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.111",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.18.0"
|
||||
@@ -1762,6 +1862,15 @@ version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "schannel"
|
||||
version = "0.1.28"
|
||||
@@ -2801,6 +2910,47 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utoipa"
|
||||
version = "4.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-gen"
|
||||
version = "4.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20c24e8ab68ff9ee746aad22d39b5535601e6416d1b0feeabf78be986a5c4392"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-swagger-ui"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b39868d43c011961e04b41623e050aedf2cc93652562ff7935ce0f819aaf2da"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"mime_guess",
|
||||
"regex",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
@@ -2830,6 +2980,16 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
@@ -2912,6 +3072,15 @@ dependencies = [
|
||||
"wasite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
@@ -3244,6 +3413,18 @@ dependencies = [
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
"flate2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
|
||||
@@ -20,3 +20,5 @@ uuid = { version = "1.0", features = ["v4"] }
|
||||
image = "0.24"
|
||||
config = "0.13"
|
||||
symphonia = { version = "0.5", features = ["all"] }
|
||||
utoipa = { version = "4.2", features = ["axum_extras", "chrono"] }
|
||||
utoipa-swagger-ui = { version = "6.0", features = ["axum"] }
|
||||
@@ -6,6 +6,15 @@ use sqlx::Row;
|
||||
use std::sync::Arc;
|
||||
|
||||
// УПРОЩЕННАЯ версия получения чатов
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/chats",
|
||||
responses(
|
||||
(status = 200, description = "List of chat partners", body = Vec<ChatPartner>),
|
||||
(status = 401, description = "Unauthorized", body = Vec<ChatPartner>)
|
||||
),
|
||||
security(("bearerAuth" = []))
|
||||
)]
|
||||
pub async fn get_chats_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
headers: axum::http::HeaderMap,
|
||||
|
||||
@@ -7,6 +7,15 @@ use chrono::{Duration, Utc};
|
||||
use sqlx::Row;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/login",
|
||||
request_body = LoginRequest,
|
||||
responses(
|
||||
(status = 200, description = "Login successful", body = AuthResponse),
|
||||
(status = 400, description = "Invalid credentials", body = AuthResponse)
|
||||
)
|
||||
)]
|
||||
pub async fn login_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
Json(payload): Json<model::LoginRequest>,
|
||||
|
||||
@@ -6,6 +6,16 @@ use axum::extract::{Json, State};
|
||||
use std::sync::Arc;
|
||||
|
||||
// Mark messages as read endpoint
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/mark-read",
|
||||
request_body = MarkReadRequest,
|
||||
responses(
|
||||
(status = 200, description = "Messages marked as read", body = serde_json::Value),
|
||||
(status = 401, description = "Unauthorized", body = serde_json::Value)
|
||||
),
|
||||
security(("bearerAuth" = []))
|
||||
)]
|
||||
pub async fn mark_read_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
headers: axum::http::HeaderMap,
|
||||
|
||||
@@ -7,6 +7,16 @@ use std::sync::Arc;
|
||||
use tokio::fs;
|
||||
|
||||
// Delete message endpoint
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/delete-message",
|
||||
request_body = DeleteMessageRequest,
|
||||
responses(
|
||||
(status = 200, description = "Message deletion result", body = DeleteMessageResponse),
|
||||
(status = 401, description = "Unauthorized", body = DeleteMessageResponse)
|
||||
),
|
||||
security(("bearerAuth" = []))
|
||||
)]
|
||||
pub async fn delete_message_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
headers: axum::http::HeaderMap,
|
||||
|
||||
@@ -6,6 +6,16 @@ use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
// Upload avatar endpoint
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/upload-avatar",
|
||||
request_body(content = Vec<u8>, description = "Avatar image file", content_type = "multipart/form-data"),
|
||||
responses(
|
||||
(status = 200, description = "Avatar upload result", body = UploadAvatarResponse),
|
||||
(status = 401, description = "Unauthorized", body = UploadAvatarResponse)
|
||||
),
|
||||
security(("bearerAuth" = []))
|
||||
)]
|
||||
pub async fn upload_avatar_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
headers: axum::http::HeaderMap,
|
||||
|
||||
@@ -5,6 +5,15 @@ use axum::extract::{Json, State};
|
||||
use sqlx::Row;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/profile",
|
||||
responses(
|
||||
(status = 200, description = "User profile information", body = UserProfile),
|
||||
(status = 401, description = "Unauthorized", body = UserProfile)
|
||||
),
|
||||
security(("bearerAuth" = []))
|
||||
)]
|
||||
pub async fn profile_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
headers: axum::http::HeaderMap,
|
||||
|
||||
@@ -6,6 +6,15 @@ use bcrypt::{DEFAULT_COST, hash};
|
||||
use chrono::{Duration, Utc};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/register",
|
||||
request_body = RegisterRequest,
|
||||
responses(
|
||||
(status = 200, description = "Registration successful", body = AuthResponse),
|
||||
(status = 400, description = "Registration failed", body = AuthResponse)
|
||||
)
|
||||
)]
|
||||
pub async fn register_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
Json(payload): Json<model::RegisterRequest>,
|
||||
|
||||
@@ -5,6 +5,16 @@ use sqlx::Row;
|
||||
use std::sync::Arc;
|
||||
|
||||
// New endpoint to search users
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/search-users",
|
||||
params(
|
||||
("q" = String, Query, description = "Search query string")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "List of matching users", body = Vec<ChatPartner>)
|
||||
)
|
||||
)]
|
||||
pub async fn search_users_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
Query(params): Query<model::SearchQuery>,
|
||||
|
||||
@@ -5,6 +5,15 @@ use axum::extract::{Json, State};
|
||||
use sqlx::Row;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/validate-token",
|
||||
request_body = ValidateTokenRequest,
|
||||
responses(
|
||||
(status = 200, description = "Token validation result", body = ValidateTokenResponse),
|
||||
(status = 400, description = "Invalid token", body = ValidateTokenResponse)
|
||||
)
|
||||
)]
|
||||
pub async fn validate_token_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
Json(payload): Json<model::ValidateTokenRequest>,
|
||||
|
||||
@@ -12,6 +12,16 @@ use symphonia::core::probe::Hint;
|
||||
use tokio::fs;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/upload",
|
||||
request_body(content = Vec<u8>, description = "File to upload", content_type = "multipart/form-data"),
|
||||
responses(
|
||||
(status = 200, description = "File upload result", body = serde_json::Value),
|
||||
(status = 401, description = "Unauthorized", body = serde_json::Value)
|
||||
),
|
||||
security(("bearerAuth" = []))
|
||||
)]
|
||||
pub async fn upload_file_handler(
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
headers: axum::http::HeaderMap,
|
||||
|
||||
+21
-1
@@ -12,7 +12,27 @@ use futures_util::{SinkExt, StreamExt};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
// WebSocket handlers
|
||||
/// WebSocket endpoint for real-time chat messaging
|
||||
///
|
||||
/// ## Connection Flow
|
||||
/// 1. Client connects to `/ws`
|
||||
/// 2. Client sends authentication message: `{"type": "auth", "token": "your_token"}`
|
||||
/// 3. Server responds with system message on success or error message on failure
|
||||
/// 4. After successful authentication, client can send/receive chat messages
|
||||
///
|
||||
/// ## Message Types
|
||||
/// - Authentication: `{"type": "auth", "token": "jwt_token"}`
|
||||
/// - Chat message: `{"id": "...", "from_user": "...", "to_user": "...", "content": "...", "timestamp": 12345, "message_type": "text"}`
|
||||
/// - System message: `{"type": "system", "content": "Connected successfully"}`
|
||||
/// - Error message: `{"type": "error", "content": "Invalid token"}`
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/ws",
|
||||
responses(
|
||||
(status = 101, description = "WebSocket upgrade successful")
|
||||
),
|
||||
tag = "WebSocket"
|
||||
)]
|
||||
pub async fn websocket_handler(
|
||||
ws: WebSocketUpgrade,
|
||||
State(state): State<Arc<state::AppState>>,
|
||||
|
||||
@@ -6,13 +6,17 @@ use axum::{
|
||||
use std::{collections::HashMap, net::SocketAddr, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
use tower_http::services::ServeDir;
|
||||
use utoipa::OpenApi;
|
||||
use utoipa_swagger_ui::SwaggerUi;
|
||||
|
||||
mod api;
|
||||
mod conf;
|
||||
mod db;
|
||||
mod model;
|
||||
mod openapi;
|
||||
mod state;
|
||||
mod utils;
|
||||
mod websocket_doc;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -64,6 +68,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
)
|
||||
.route("/api/upload", post(api::upload::upload_file_handler))
|
||||
.route("/api/profile", get(api::profile::get::profile_handler))
|
||||
.merge(
|
||||
SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi::ApiDoc::openapi()),
|
||||
)
|
||||
.nest_service("/", ServeDir::new(&state.config.files.web_dir))
|
||||
.layer(DefaultBodyLimit::max(state.config.files.max_upload_size))
|
||||
.with_state(state.clone());
|
||||
|
||||
+136
-18
@@ -1,108 +1,154 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct AuthResponse {
|
||||
/// Indicates if the operation was successful
|
||||
pub success: bool,
|
||||
/// Message describing the result
|
||||
pub message: String,
|
||||
/// Username of the authenticated user
|
||||
pub username: Option<String>,
|
||||
/// Authentication token
|
||||
pub token: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct RegisterRequest {
|
||||
/// Phone number for registration
|
||||
pub phone: String,
|
||||
/// Desired username
|
||||
pub username: String,
|
||||
/// Password for account
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct LoginRequest {
|
||||
/// Phone number associated with the account
|
||||
pub phone: String,
|
||||
/// Password for the account
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct ValidateTokenResponse {
|
||||
/// Indicates if the token is valid
|
||||
pub success: bool,
|
||||
/// Username associated with the token
|
||||
pub username: Option<String>,
|
||||
/// Phone number associated with the token
|
||||
pub phone: Option<String>,
|
||||
/// URL to the user's avatar
|
||||
pub avatar_url: Option<String>,
|
||||
/// Additional message about the validation
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct ValidateTokenRequest {
|
||||
/// Authentication token to validate
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct ChatPartner {
|
||||
/// Username of the chat partner
|
||||
pub username: String,
|
||||
/// Phone number of the chat partner
|
||||
pub phone: String,
|
||||
/// URL to the partner's avatar
|
||||
pub avatar_url: Option<String>,
|
||||
/// Content of the last message
|
||||
pub last_message: Option<String>,
|
||||
/// Timestamp of the last message
|
||||
pub last_message_time: Option<i64>,
|
||||
/// Number of unread messages
|
||||
pub unread_count: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct SearchQuery {
|
||||
/// Search query string
|
||||
pub q: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct MarkReadRequest {
|
||||
/// User who sent the messages
|
||||
pub from_user: String,
|
||||
/// User who received the messages
|
||||
pub to_user: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct DeleteMessageRequest {
|
||||
/// ID of the message to delete
|
||||
pub message_id: String,
|
||||
/// Whether to delete for everyone
|
||||
pub for_everyone: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct DeleteMessageResponse {
|
||||
/// Indicates if the deletion was successful
|
||||
pub success: bool,
|
||||
/// Message describing the result
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct UploadAvatarResponse {
|
||||
/// Indicates if the upload was successful
|
||||
pub success: bool,
|
||||
/// URL to the uploaded avatar
|
||||
pub avatar_url: Option<String>,
|
||||
/// Message describing the result
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct UserProfile {
|
||||
/// Username of the profile
|
||||
pub username: String,
|
||||
/// Phone number of the profile
|
||||
pub phone: String,
|
||||
/// URL to the user's avatar
|
||||
pub avatar_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub enum MessageType {
|
||||
/// Text message
|
||||
#[serde(rename = "text")]
|
||||
Text,
|
||||
/// File attachment
|
||||
#[serde(rename = "file")]
|
||||
File,
|
||||
/// Photo attachment
|
||||
#[serde(rename = "photo")]
|
||||
Photo,
|
||||
/// Video attachment
|
||||
#[serde(rename = "video")]
|
||||
Video,
|
||||
/// Audio attachment
|
||||
#[serde(rename = "audio")]
|
||||
Audio,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct ChatMessage {
|
||||
/// Unique identifier for the message
|
||||
pub id: String,
|
||||
/// User who sent the message
|
||||
pub from_user: String,
|
||||
/// User who received the message
|
||||
pub to_user: String,
|
||||
/// Content of the message
|
||||
pub content: String, // Store serialized content as JSON string
|
||||
/// Timestamp of when the message was sent
|
||||
pub timestamp: i64,
|
||||
/// Type of the message
|
||||
pub message_type: MessageType,
|
||||
}
|
||||
|
||||
@@ -214,38 +260,110 @@ pub struct ChatMessage {
|
||||
// Video(VideoInfo),
|
||||
// }
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct FileInfo {
|
||||
/// Original filename
|
||||
pub filename: String,
|
||||
/// Path to the stored file
|
||||
pub filepath: String,
|
||||
/// Size of the file in bytes
|
||||
pub size: u64,
|
||||
/// MIME type of the file
|
||||
pub mime_type: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct PhotoInfo {
|
||||
/// Original filename
|
||||
pub filename: String,
|
||||
/// Path to the stored photo
|
||||
pub filepath: String,
|
||||
/// Size of the photo in bytes
|
||||
pub size: u64,
|
||||
/// Width of the photo in pixels
|
||||
pub width: u32,
|
||||
/// Height of the photo in pixels
|
||||
pub height: u32,
|
||||
/// MIME type of the photo
|
||||
pub mime_type: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct VideoInfo {
|
||||
/// Original filename
|
||||
pub filename: String,
|
||||
/// Path to the stored video
|
||||
pub filepath: String,
|
||||
/// Size of the video in bytes
|
||||
pub size: u64,
|
||||
/// Duration of the video in seconds
|
||||
pub duration: Option<f64>, // in seconds
|
||||
/// MIME type of the video
|
||||
pub mime_type: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct AudioInfo {
|
||||
/// Original filename
|
||||
pub filename: String,
|
||||
/// Path to the stored audio
|
||||
pub filepath: String,
|
||||
/// Size of the audio in bytes
|
||||
pub size: u64,
|
||||
/// Duration of the audio in seconds
|
||||
pub duration: Option<f64>, // in seconds
|
||||
/// MIME type of the audio
|
||||
pub mime_type: String,
|
||||
}
|
||||
|
||||
// WebSocket message types
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct WsAuthMessage {
|
||||
/// Type of the message (should be "auth")
|
||||
#[schema(example = "auth")]
|
||||
pub r#type: String,
|
||||
/// Authentication token
|
||||
#[schema(example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")]
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct WsChatMessage {
|
||||
/// Unique identifier for the message
|
||||
#[schema(example = "123e4567-e89b-12d3-a456-426614174000")]
|
||||
pub id: String,
|
||||
/// User who sent the message
|
||||
#[schema(example = "john_doe")]
|
||||
pub from_user: String,
|
||||
/// User who received the message
|
||||
#[schema(example = "jane_doe")]
|
||||
pub to_user: String,
|
||||
/// Content of the message
|
||||
#[schema(example = "Hello, world!")]
|
||||
pub content: String,
|
||||
/// Timestamp of when the message was sent
|
||||
#[schema(example = 1678886400)]
|
||||
pub timestamp: i64,
|
||||
/// Type of the message
|
||||
pub message_type: MessageType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct WsSystemMessage {
|
||||
/// Type of the system message
|
||||
#[schema(example = "system")]
|
||||
pub r#type: String,
|
||||
/// Content of the system message
|
||||
#[schema(example = "Connected successfully")]
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct WsErrorMessage {
|
||||
/// Type of the error message
|
||||
#[schema(example = "error")]
|
||||
pub r#type: String,
|
||||
/// Content of the error message
|
||||
#[schema(example = "Invalid or expired token")]
|
||||
pub content: String,
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
use crate::api;
|
||||
use crate::model::*;
|
||||
use crate::websocket_doc::WebSocketApiDoc;
|
||||
use utoipa::OpenApi;
|
||||
|
||||
/// Chat messaging API
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
info(
|
||||
title = "Chat Messaging API",
|
||||
description = "A simple chat messaging API with user registration, login, and message handling.",
|
||||
version = "1.0.0"
|
||||
),
|
||||
paths(
|
||||
api::websock::websocket_handler,
|
||||
api::login::login_handler,
|
||||
api::register::register_handler,
|
||||
api::security::validate_token_handler,
|
||||
api::chats::get_chats_handler,
|
||||
api::search::search_users_handler,
|
||||
api::messages::mark_read_handler,
|
||||
api::messages::delete::delete_message_handler,
|
||||
api::profile::get::profile_handler,
|
||||
api::profile::avatar::upload_avatar_handler,
|
||||
api::upload::upload_file_handler
|
||||
),
|
||||
components(
|
||||
schemas(
|
||||
AuthResponse,
|
||||
RegisterRequest,
|
||||
LoginRequest,
|
||||
ValidateTokenResponse,
|
||||
ValidateTokenRequest,
|
||||
ChatPartner,
|
||||
SearchQuery,
|
||||
MarkReadRequest,
|
||||
DeleteMessageRequest,
|
||||
DeleteMessageResponse,
|
||||
UploadAvatarResponse,
|
||||
UserProfile,
|
||||
MessageType,
|
||||
ChatMessage,
|
||||
FileInfo,
|
||||
PhotoInfo,
|
||||
VideoInfo,
|
||||
AudioInfo,
|
||||
WsAuthMessage,
|
||||
WsChatMessage,
|
||||
WsSystemMessage,
|
||||
WsErrorMessage
|
||||
)
|
||||
),
|
||||
modifiers(&SecurityAddon, &WebSocketApiDoc)
|
||||
)]
|
||||
pub struct ApiDoc;
|
||||
|
||||
struct SecurityAddon;
|
||||
|
||||
impl utoipa::Modify for SecurityAddon {
|
||||
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
|
||||
if let Some(components) = openapi.components.as_mut() {
|
||||
use utoipa::openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme};
|
||||
components.add_security_scheme(
|
||||
"bearerAuth",
|
||||
SecurityScheme::Http(
|
||||
HttpBuilder::new()
|
||||
.scheme(HttpAuthScheme::Bearer)
|
||||
.bearer_format("JWT")
|
||||
.build(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use utoipa::Modify;
|
||||
|
||||
/// Расширенная документация для WebSocket API
|
||||
pub struct WebSocketApiDoc;
|
||||
|
||||
impl Modify for WebSocketApiDoc {
|
||||
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
|
||||
// Обновляем описание, добавляя информацию о WebSocket
|
||||
let current_description = openapi.info.description.clone().unwrap_or_default();
|
||||
let extended_description = format!(
|
||||
"{}\n\n## WebSocket API\n\n### Connection Flow\n1. Connect to `/ws`\n2. Send auth message: `{{\"type\": \"auth\", \"token\": \"your_jwt\"}}`\n3. Receive system/error message\n4. Send/receive chat messages\n\n### Message Types:\n- **Auth**: `{{\"type\": \"auth\", \"token\": \"...\"}}`\n- **Chat**: `{{\"id\": \"...\", \"from_user\": \"...\", \"to_user\": \"...\", \"content\": \"...\", \"timestamp\": 12345, \"message_type\": \"text\"}}`\n- **System**: `{{\"type\": \"system\", \"content\": \"...\"}}`\n- **Error**: `{{\"type\": \"error\", \"content\": \"...\"}}`",
|
||||
current_description
|
||||
);
|
||||
openapi.info.description = Some(extended_description);
|
||||
|
||||
// Добавляем тег для WebSocket
|
||||
let websocket_tag = utoipa::openapi::tag::TagBuilder::new()
|
||||
.name("WebSocket")
|
||||
.description(Some("Real-time chat messaging via WebSocket"))
|
||||
.build();
|
||||
|
||||
if let Some(tags) = &mut openapi.tags {
|
||||
tags.push(websocket_tag);
|
||||
} else {
|
||||
openapi.tags = Some(vec![websocket_tag]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user