mirror of
https://github.com/Govor-team/Govor.git
synced 2026-07-21 11:44:56 +00:00
GitBook: No commit message
This commit is contained in:
committed by
gitbook-bot
parent
53ea25f9fd
commit
19f1ccdfd5
+28
@@ -0,0 +1,28 @@
|
||||
# Table of contents
|
||||
|
||||
* [Page](README.md)
|
||||
|
||||
## Endpoints
|
||||
|
||||
* [Authentication](endpoints/authentication/README.md)
|
||||
* [AuthController](endpoints/authentication/authcontroller.md)
|
||||
* [RefreshController](endpoints/authentication/refreshcontroller.md)
|
||||
* [FriendshipController](endpoints/friendshipcontroller.md)
|
||||
* [FriendsRequestQueryController](endpoints/friendsrequestquerycontroller.md)
|
||||
* [MediaController](endpoints/mediacontroller.md)
|
||||
* [ChatLoadController](endpoints/chatloadcontroller.md)
|
||||
* [OnlinePingingController(не работает)](endpoints/onlinepingingcontroller-ne-rabotaet.md)
|
||||
|
||||
## SignalR
|
||||
|
||||
* [PresenceHub](signalr/presencehub.md)
|
||||
* [ChatHub](signalr/chathub.md)
|
||||
|
||||
***
|
||||
|
||||
* [FriendsHub](friendshub/README.md)
|
||||
* [FriendsHub Client (Java)](friendshub/friendshub-client-java.md)
|
||||
|
||||
## Code Docs 
|
||||
|
||||
* [HubResult\<T>](code-docs/hubresult-less-than-t-greater-than.md)
|
||||
@@ -0,0 +1,164 @@
|
||||
---
|
||||
description: >-
|
||||
Description: A generic class used to encapsulate the result of SignalR hub
|
||||
operations, providing a standardized way to return status, data, and error
|
||||
messages. It supports various HTTP-like status cod
|
||||
---
|
||||
|
||||
# HubResult\<T>
|
||||
|
||||
**Namespace**: `Govor.Contracts.Responses.SignalR`
|
||||
|
||||
### <mark style="color:$info;">Properties</mark>
|
||||
|
||||
* **Status**: (`HubResultStatus`) Indicates the status of the hub operation, corresponding to HTTP-like status codes (e.g., Success, BadRequest).
|
||||
* **Result**: (`T?`) The data returned by the hub operation, if applicable. Can be null for error cases or when no data is returned.
|
||||
* **ErrorMessage**: (`string?`) A message describing the error, if the operation failed. Null for successful operations.
|
||||
|
||||
### <mark style="color:$info;">Static Methods</mark>
|
||||
|
||||
#### Ok
|
||||
|
||||
**Description**: Creates a successful result with an optional data payload.\
|
||||
**Parameters**:
|
||||
|
||||
* `result`: (`T?`, optional) The data to include in the response (default: `null`).\
|
||||
**Returns**: A `HubResult<T>` with `Status` set to `Success` (200) and the provided `result`.\
|
||||
**Example**:
|
||||
|
||||
```csharp
|
||||
HubResult<string> result = HubResult<string>.Ok("Operation completed");
|
||||
```
|
||||
|
||||
#### Created
|
||||
|
||||
**Description**: Creates a result indicating a resource was created, with an optional data payload.\
|
||||
**Parameters**:
|
||||
|
||||
* `result`: (`T?`, optional) The data to include in the response (default: `null`).\
|
||||
**Returns**: A `HubResult<T>` with `Status` set to `Created` (201) and the provided `result`.\
|
||||
**Example**:
|
||||
|
||||
```csharp
|
||||
HubResult<string> result = HubResult<string>.Created("Resource created");
|
||||
```
|
||||
|
||||
#### NoContent
|
||||
|
||||
**Description**: Creates a result indicating the operation was successful but no data is returned.\
|
||||
**Parameters**: None\
|
||||
**Returns**: A `HubResult<T>` with `Status` set to `NoContent` (204) and `Result` set to `null`.\
|
||||
**Example**:
|
||||
|
||||
```csharp
|
||||
HubResult<string> result = HubResult<string>.NoContent();
|
||||
```
|
||||
|
||||
#### BadRequest
|
||||
|
||||
**Description**: Creates a result indicating a client error due to invalid input.\
|
||||
**Parameters**:
|
||||
|
||||
* `message`: (`string`) The error message describing the issue.
|
||||
* `details`: (`T?`, optional) Additional details about the error (default: `null`).\
|
||||
**Returns**: A `HubResult<T>` with `Status` set to `BadRequest` (400), the provided `message`, and optional `details`.\
|
||||
**Example**:
|
||||
|
||||
```csharp
|
||||
HubResult<string> result = HubResult<string>.BadRequest("Invalid input provided");
|
||||
```
|
||||
|
||||
#### NotFound
|
||||
|
||||
**Description**: Creates a result indicating a requested resource was not found.\
|
||||
**Parameters**:
|
||||
|
||||
* `message`: (`string`) The error message describing the issue.
|
||||
* `details`: (`T?`, optional) Additional details about the error (default: `null`).\
|
||||
**Returns**: A `HubResult<T>` with `Status` set to `NotFound` (404), the provided `message`, and optional `details`.\
|
||||
**Example**:
|
||||
|
||||
```csharp
|
||||
HubResult<string> result = HubResult<string>.NotFound("User not found");
|
||||
```
|
||||
|
||||
#### Unauthorized
|
||||
|
||||
**Description**: Creates a result indicating the client is not authorized to perform the operation.\
|
||||
**Parameters**:
|
||||
|
||||
* `message`: (`string`) The error message describing the issue.\
|
||||
**Returns**: A `HubResult<T>` with `Status` set to `Unauthorized` (401) and the provided `message`.\
|
||||
**Example**:
|
||||
|
||||
```csharp
|
||||
HubResult<string> result = HubResult<string>.Unauthorized("Authentication required");
|
||||
```
|
||||
|
||||
#### Conflict
|
||||
|
||||
**Description**: Creates a result indicating a conflict, such as a duplicate resource.\
|
||||
**Parameters**:
|
||||
|
||||
* `message`: (`string`) The error message describing the issue.\
|
||||
**Returns**: A `HubResult<T>` with `Status` set to `Conflict` (409) and the provided `message`.\
|
||||
**Example**:
|
||||
|
||||
```csharp
|
||||
HubResult<string> result = HubResult<string>.Conflict("Resource already exists");
|
||||
```
|
||||
|
||||
#### UnprocessableEntity
|
||||
|
||||
**Description**: Creates a result indicating the request could not be processed due to semantic errors.\
|
||||
**Parameters**:
|
||||
|
||||
* `message`: (`string`) The error message describing the issue.\
|
||||
**Returns**: A `HubResult<T>` with `Status` set to `UnprocessableEntity` (422) and the provided `message`.\
|
||||
**Example**:
|
||||
|
||||
```csharp
|
||||
HubResult<string> result = HubResult<string>.UnprocessableEntity("Invalid data format");
|
||||
```
|
||||
|
||||
#### Error
|
||||
|
||||
**Description**: Creates a result indicating an internal server error.\
|
||||
**Parameters**:
|
||||
|
||||
* `message`: (`string`) The error message describing the issue.\
|
||||
**Returns**: A `HubResult<T>` with `Status` set to `ServerError` (500) and the provided `message`.\
|
||||
**Example**:
|
||||
|
||||
```csharp
|
||||
HubResult<string> result = HubResult<string>.Error("Unexpected server error");
|
||||
```
|
||||
|
||||
### <mark style="color:$info;">Enum: HubResultStatus</mark>
|
||||
|
||||
**Description**: Enumerates the possible status codes for a `HubResult<T>` response, aligning with HTTP status codes for consistency in SignalR hub operations.
|
||||
|
||||
**Values**:
|
||||
|
||||
* <mark style="color:$success;">**Success**</mark> (200): The operation was successful.
|
||||
* <mark style="color:$success;">**Created**</mark> (201): A resource was successfully created.
|
||||
* <mark style="color:$success;">**NoContent**</mark> (204): The operation was successful, but no content is returned.
|
||||
* <mark style="color:$danger;">**BadRequest**</mark> (400): The request was invalid or malformed.
|
||||
* <mark style="color:$danger;">**Unauthorized**</mark> (401): The client is not authorized to perform the operation.
|
||||
* <mark style="color:$danger;">**NotFound**</mark> (404): The requested resource was not found.
|
||||
* <mark style="color:$danger;">**Conflict**</mark> (409): The request conflicts with an existing resource.
|
||||
* <mark style="color:$danger;">**UnprocessableEntity**</mark> (422): The request could not be processed due to semantic errors.
|
||||
* <mark style="color:$warning;">**ServerError**</mark> (500): An unexpected server error occurred.
|
||||
|
||||
### Example Usage
|
||||
|
||||
```csharp
|
||||
// Successful operation with data
|
||||
var successResult = HubResult<UserDto>.Ok(new UserDto { Id = Guid.NewGuid(), Username = "example" });
|
||||
|
||||
// Error due to invalid input
|
||||
var errorResult = HubResult<UserDto>.BadRequest("Invalid username provided");
|
||||
|
||||
// Not found scenario
|
||||
var notFoundResult = HubResult<UserDto>.NotFound("User not found in database");
|
||||
```
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
description: How to work with jwt tokens
|
||||
icon: user
|
||||
---
|
||||
|
||||
# Authentication
|
||||
|
||||
### <mark style="color:$info;">Components</mark>
|
||||
|
||||
* **AuthController**: Handles registration and login.
|
||||
* **Route**: `/api/auth`
|
||||
* **RefreshController**: Manages token refresh.
|
||||
* **Route**: `/api/auth/token`
|
||||
|
||||
### <mark style="color:$info;">Authentication Process</mark>
|
||||
|
||||
* **Registration**:
|
||||
* **Endpoint**: `POST /api/auth/register`
|
||||
* **Input**: `RegistrationRequest` (name, password, inviteLink, deviceInfo)
|
||||
* **Output**: Access token
|
||||
* **Action**: Validates invite, registers user, opens session.
|
||||
* **Login**:
|
||||
* **Endpoint**: `POST /api/auth/login`
|
||||
* **Input**: `LoginRequest` (name, password, deviceInfo)
|
||||
* **Output**: Access token
|
||||
* **Action**: Authenticates user, opens session.
|
||||
* **Token Refresh**:
|
||||
* **Endpoint**: `POST /api/auth/token/refresh`
|
||||
* **Input**: `RefreshTokenRequest` (refreshToken)
|
||||
* **Output**: `RefreshTokenResponse` (accessToken, refreshToken)
|
||||
* **Action**: Refreshes expired access token.
|
||||
|
||||
### <mark style="color:$info;">Token Usage</mark>
|
||||
|
||||
* **Access Token**:
|
||||
* **Request**: Obtain during registration or login.
|
||||
* **Refresh**: Request via `/api/auth/token/refresh` when expired (e.g., 02:12 PM CEST, July 21, 2025).
|
||||
* **Refresh Token**:
|
||||
* **Request**: Received during registration or login.
|
||||
* **Refresh**: Use to obtain new access token when current one expires.
|
||||
|
||||
### Security
|
||||
|
||||
* **Protocol**: Requires HTTPS.
|
||||
* **Storage**: Store refresh token in HTTP-only cookie with `Secure` and `SameSite=Strict`.
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
description: >-
|
||||
Controller for handling user authentication operations, including registration
|
||||
and login, with session management and invite-based registration.
|
||||
---
|
||||
|
||||
# AuthController
|
||||
|
||||
### Controller Description
|
||||
|
||||
* **Route**: `api/auth`
|
||||
* **Authorize**: Allows anonymous access (`[AllowAnonymous]`).
|
||||
|
||||
### Endpoints
|
||||
|
||||
#### <mark style="color:$info;">Register</mark>
|
||||
|
||||
* **Description**: Registers a new user using a valid invite link and opens a user session, returning an authentication token.
|
||||
* **Route**: `[POST] api/auth/register`
|
||||
* **HTTP Method**: `POST`
|
||||
* **Request**:
|
||||
* **Content-Type**: `application/json`
|
||||
* **Request Body**: `RegistrationRequest` object with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "string",
|
||||
"password": "string",
|
||||
"inviteLink": "string",
|
||||
"deviceInfo": "string"
|
||||
}
|
||||
```
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns the authentication token upon successful registration and session creation.
|
||||
|
||||
```json
|
||||
{
|
||||
"refreshToken": "string",
|
||||
"accessToken": "string"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$danger;">**400 Bad Request**</mark>:
|
||||
* If model validation fails.
|
||||
* If the user already exists: `"Registration failed: user already exists."`
|
||||
* If the invite link is invalid: `"Invite link invalid."`
|
||||
* If the username is invalid: `"Invalid username: <error_message>"`
|
||||
* <mark style="color:$warning;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
"An unexpected error occurred. Please try again later."
|
||||
```
|
||||
|
||||
#### <mark style="color:$info;">Login</mark>
|
||||
|
||||
* **Description**: Authenticates an existing user and opens a session, returning an authentication token.
|
||||
* **Route**: `[POST] api/auth/login`
|
||||
* **HTTP Method**: `POST`
|
||||
* **Request**:
|
||||
* **Content-Type**: `application/json`
|
||||
* **Request Body**: `LoginRequest` object with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "string",
|
||||
"password": "string",
|
||||
"deviceInfo": "string"
|
||||
}
|
||||
```
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns the authentication token upon successful login and session creation.
|
||||
|
||||
```json
|
||||
{
|
||||
"refreshToken": "string",
|
||||
"accessToken": "string"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$danger;">**400 Bad Request**</mark>:
|
||||
* If model validation fails.
|
||||
* If the user does not exist: `"Login failed: user does not exist."`
|
||||
* If the username or password is incorrect: `"Login failed: username or password is incorrect."`
|
||||
* <mark style="color:$warning;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
"An unexpected error occurred. Please try again later."
|
||||
```
|
||||
|
||||
### Data Models
|
||||
|
||||
#### RegistrationRequest
|
||||
|
||||
* **Description**: Model for registration request data.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "string",
|
||||
"password": "string",
|
||||
"inviteLink": "string",
|
||||
"deviceInfo": "string"
|
||||
}
|
||||
```
|
||||
|
||||
#### LoginRequest
|
||||
|
||||
* **Description**: Model for login request data.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "string",
|
||||
"password": "string",
|
||||
"deviceInfo": "string"
|
||||
}
|
||||
```
|
||||
|
||||
#### RefreshResult
|
||||
|
||||
* **Description**: Model for refresh token response data.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"refreshToken": "string",
|
||||
"accessToken": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
* **Invalid User ID**: Not applicable (handled by session service).
|
||||
* **Invalid Operations**: Registration fails if the user already exists, invite link is invalid, or username is invalid, returning `400 Bad Request`.
|
||||
* **Unauthorized Access**: Not applicable (endpoint is `[AllowAnonymous]`).
|
||||
* **Unexpected Errors**: General exceptions are caught, logged, and returned as `500 Internal Server Error`.
|
||||
|
||||
### Logging
|
||||
|
||||
* Logs successful registration and login events with username and user ID.
|
||||
* Logs session opening events with username and user ID.
|
||||
* Logs warnings for specific exceptions (e.g., user already exists, invalid invite link, login failures).
|
||||
* Logs errors for unexpected exceptions during registration and login.
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
description: >-
|
||||
Controller for managing token refresh operations, allowing users to refresh
|
||||
their access tokens using a valid refresh token.
|
||||
---
|
||||
|
||||
# RefreshController
|
||||
|
||||
### Controller Description
|
||||
|
||||
* **Route**: `api/auth/token`
|
||||
* **Authorize**: Allows anonymous access (`[AllowAnonymous]`).
|
||||
|
||||
### Endpoints
|
||||
|
||||
#### <mark style="color:$info;">Refresh</mark>
|
||||
|
||||
* **Description**: Refreshes an access token using a provided refresh token and returns a new access token and refresh token pair.
|
||||
* **Route**: `[POST] api/auth/token/refresh`
|
||||
* **HTTP Method**: `POST`
|
||||
* **Request**:
|
||||
* **Content-Type**: `application/json`
|
||||
* **Request Body**: `RefreshTokenRequest` object with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"refreshToken": "string"
|
||||
}
|
||||
```
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns a new access token and refresh token upon successful refresh.
|
||||
|
||||
```json
|
||||
{
|
||||
"accessToken": "string",
|
||||
"refreshToken": "string"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$warning;">**400 Bad Request**</mark>: If model validation fails or the refresh token is empty.
|
||||
|
||||
```json
|
||||
"Refresh token cant be empty."
|
||||
```
|
||||
* <mark style="color:$warning;">**401 Unauthorized**</mark>: If the refresh token is invalid or authorization fails.
|
||||
|
||||
```json
|
||||
"Invalid refresh token"
|
||||
```
|
||||
* <mark style="color:$danger;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
"An unexpected error occurred."
|
||||
```
|
||||
|
||||
### Data Models
|
||||
|
||||
#### RefreshTokenRequest
|
||||
|
||||
* **Description**: Model for refresh token request data.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"refreshToken": "string"
|
||||
}
|
||||
```
|
||||
|
||||
#### RefreshTokenResponse
|
||||
|
||||
* **Description**: Model for refresh token response data.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"accessToken": "string",
|
||||
"refreshToken": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
* **Invalid User ID**: Not applicable (handled by session service).
|
||||
* **Invalid Operations**: Returns `400 Bad Request` if the refresh token is empty or invalid.
|
||||
* **Unauthorized Access**: Returns `401 Unauthorized` if the refresh token fails authorization.
|
||||
* **Unexpected Errors**: General exceptions are caught, logged, and returned as `500 Internal Server Error`.
|
||||
|
||||
### Logging
|
||||
|
||||
* Logs warnings for invalid or failed refresh token attempts.
|
||||
* Logs errors for unexpected exceptions during token refresh.
|
||||
@@ -0,0 +1,259 @@
|
||||
---
|
||||
description: Controller for loading chat messages for users and groups with pagination.
|
||||
---
|
||||
|
||||
# ChatLoadController
|
||||
|
||||
### Controller Description
|
||||
|
||||
* **Route**: `api/chats`
|
||||
* **Authorize**: Requires authenticated user with roles "Admin" or "User" (`[Authorize(Roles = "Admin, User")]`).
|
||||
|
||||
### Endpoints
|
||||
|
||||
#### <mark style="color:$info;">GetGroupMessages</mark>
|
||||
|
||||
* **Description**: Retrieves messages from a specified group chat with pagination.
|
||||
* **Route**: `[GET] api/chats/group-messages`
|
||||
* **HTTP Method**: `GET`
|
||||
* **Request**:
|
||||
* **Path Parameter**: `chatId` (Guid, required): ID of the group chat.
|
||||
* **Query Parameter**: `query` (`MessageQuery`, required) with:
|
||||
* `startMessageId` (Guid?, optional): ID of the starting message.
|
||||
* `before` (int, default=20): Number of messages before the start.
|
||||
* `after` (int, default=2): Number of messages after the start.
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns a list of group chat messages.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "Guid",
|
||||
"senderId": "Guid",
|
||||
"recipientId": "Guid",
|
||||
"recipientType": "string",
|
||||
"encryptedContent": "string",
|
||||
"sentAt": "DateTime",
|
||||
"isEdited": "boolean",
|
||||
"editedAt": "DateTime?",
|
||||
"replyToMessageId": "Guid?",
|
||||
"mediaAttachments": [
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"mediaFileId": "Guid"
|
||||
}
|
||||
],
|
||||
"reactions": [
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"userId": "Guid",
|
||||
"reactionCode": "string",
|
||||
"reactedAt": "DateTime"
|
||||
}
|
||||
],
|
||||
"messageViews": [
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"userId": "Guid",
|
||||
"viewedAt": "DateTime"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
* <mark style="color:$danger;">**400 Bad Request**</mark>:
|
||||
* If `before` or `after` is negative or total exceeds 100: `"Values must be non-negative and total must not exceed 100."`
|
||||
* If an argument or resource is invalid: `"string"`
|
||||
* <mark style="color:$danger;">**403 Forbidden**</mark>: If user lacks authorization.
|
||||
|
||||
```json
|
||||
"string"
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
"Unexpected Error! Please try again later."
|
||||
```
|
||||
|
||||
#### <mark style="color:$info;">GetUserMessages</mark>
|
||||
|
||||
* **Description**: Retrieves messages from a specified user chat with pagination.
|
||||
* **Route**: `[GET] api/chats/user-messages`
|
||||
* **HTTP Method**: `GET`
|
||||
* **Request**:
|
||||
* **Path Parameter**: `userId` (Guid, required): ID of the target user.
|
||||
* **Query Parameter**: `query` (`MessageQuery`, required) with:
|
||||
* `startMessageId` (Guid?, optional): ID of the starting message.
|
||||
* `before` (int, default=20): Number of messages before the start.
|
||||
* `after` (int, default=2): Number of messages after the start.
|
||||
* **Responses**:
|
||||
* **200 OK**: Returns a list of user chat messages.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "Guid",
|
||||
"senderId": "Guid",
|
||||
"recipientId": "Guid",
|
||||
"recipientType": "string",
|
||||
"encryptedContent": "string",
|
||||
"sentAt": "DateTime",
|
||||
"isEdited": "boolean",
|
||||
"editedAt": "DateTime?",
|
||||
"replyToMessageId": "Guid?",
|
||||
"mediaAttachments": [
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"mediaFileId": "Guid"
|
||||
}
|
||||
],
|
||||
"reactions": [
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"userId": "Guid",
|
||||
"reactionCode": "string",
|
||||
"reactedAt": "DateTime"
|
||||
}
|
||||
],
|
||||
"messageViews": [
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"userId": "Guid",
|
||||
"viewedAt": "DateTime"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
* **400 Bad Request**:
|
||||
* If `before` or `after` is negative or total exceeds 100: `"Values must be non-negative and total must not exceed 100."`
|
||||
* If an operation or resource is invalid: `"string"`
|
||||
* **403 Forbidden**: If user lacks authorization.
|
||||
|
||||
```json
|
||||
"string"
|
||||
```
|
||||
* **500 Internal Server Error**: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
"Unexpected Error! Please try again later."
|
||||
```
|
||||
|
||||
### Data Models
|
||||
|
||||
#### MessageQuery
|
||||
|
||||
* **Description**: Model for pagination query parameters.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"startMessageId": "Guid?",
|
||||
"before": "int",
|
||||
"after": "int"
|
||||
}
|
||||
```
|
||||
|
||||
#### MessageResponse
|
||||
|
||||
* **Description**: Data transfer object for chat message details.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "Guid",
|
||||
"senderId": "Guid",
|
||||
"recipientId": "Guid",
|
||||
"recipientType": "string",
|
||||
"encryptedContent": "string",
|
||||
"sentAt": "DateTime",
|
||||
"isEdited": "boolean",
|
||||
"editedAt": "DateTime?",
|
||||
"replyToMessageId": "Guid?",
|
||||
"mediaAttachments": [
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"mediaFileId": "Guid"
|
||||
}
|
||||
],
|
||||
"reactions": [
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"userId": "Guid",
|
||||
"reactionCode": "string",
|
||||
"reactedAt": "DateTime"
|
||||
}
|
||||
],
|
||||
"messageViews": [
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"userId": "Guid",
|
||||
"viewedAt": "DateTime"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### MediaAttachmentResponse
|
||||
|
||||
* **Description**: Data transfer object for media attachments in messages.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"mediaFileId": "Guid"
|
||||
}
|
||||
```
|
||||
|
||||
#### MessageReactionResponse
|
||||
|
||||
* **Description**: Data transfer object for message reactions.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"userId": "Guid",
|
||||
"reactionCode": "string",
|
||||
"reactedAt": "DateTime"
|
||||
}
|
||||
```
|
||||
|
||||
#### MessageViewResponse
|
||||
|
||||
* **Description**: Data transfer object for message view tracking.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "Guid",
|
||||
"messageId": "Guid",
|
||||
"userId": "Guid",
|
||||
"viewedAt": "DateTime"
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
* **Invalid User ID**: Handled by `ICurrentUserService`, aborts if invalid.
|
||||
* **Invalid Operations**: Returns `400 Bad Request` for negative values, total exceeding 100, or `InvalidOperationException`.
|
||||
* **Unauthorized Access**: Returns `403 Forbidden` for `UnauthorizedAccessException`.
|
||||
* **Resource Not Found**: Returns `400 Bad Request` for `NotFoundException`.
|
||||
* **Unexpected Errors**: Caught and returned as `500 Internal Server Error`.
|
||||
|
||||
### Logging
|
||||
|
||||
* Logs warnings for `UnauthorizedAccessException`, `NotFoundException`, `ArgumentException`, and `InvalidOperationException`.
|
||||
* Logs errors for unexpected exceptions.
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
description: >-
|
||||
Description: Controller for managing friendship-related operations, including
|
||||
searching for users and retrieving the list of friends for the current user.
|
||||
---
|
||||
|
||||
# FriendshipController
|
||||
|
||||
### Controller Description
|
||||
|
||||
* **Route**: `api/friends`
|
||||
* **Authorize**: Requires authenticated user (`[Authorize]`).
|
||||
|
||||
### Endpoints
|
||||
|
||||
#### <mark style="color:$info;">Search</mark>
|
||||
|
||||
* **Description**: Searches for users based on a query string.
|
||||
* **Route**: `[GET] api/friends/search?query=`
|
||||
* **HTTP Method**: `GET`
|
||||
* **Request**:
|
||||
* **Query Parameter**: `query` (string, required)
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns a list of matching users.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "Guid",
|
||||
"username": "string",
|
||||
"description": "string",
|
||||
"wasOnline": "DateTime",
|
||||
"iconId": "Guid"
|
||||
}
|
||||
]
|
||||
```
|
||||
* <mark style="color:$danger;">**400 Bad Request**</mark>: If the query is empty.
|
||||
|
||||
```json
|
||||
"Query cannot be empty"
|
||||
```
|
||||
* <mark style="color:$danger;">**403 Forbidden**</mark>: If user lacks authorization.
|
||||
|
||||
```json
|
||||
"string"
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Internal error during user search."
|
||||
}
|
||||
```
|
||||
|
||||
#### <mark style="color:$info;">GetFriends</mark>
|
||||
|
||||
* **Description**: Retrieves the list of friends for the authenticated user.
|
||||
* **Route**: `[GET] api/friends`
|
||||
* **HTTP Method**: `GET`
|
||||
* **Request**: None
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns a list of the user's friends or an empty list if none.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "Guid",
|
||||
"username": "string",
|
||||
"description": "string",
|
||||
"wasOnline": "DateTime",
|
||||
"iconId": "Guid"
|
||||
}
|
||||
]
|
||||
```
|
||||
* <mark style="color:$danger;">**403 Forbidden**</mark>: If user lacks authorization.
|
||||
|
||||
```json
|
||||
"string"
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Internal server error."
|
||||
}
|
||||
```
|
||||
|
||||
### Data Models
|
||||
|
||||
#### UserDto
|
||||
|
||||
* **Description**: Data transfer object for user information.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "Guid",
|
||||
"username": "string",
|
||||
"description": "string",
|
||||
"wasOnline": "DateTime",
|
||||
"iconId": "Guid"
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
* **Invalid User ID**: Handled by `ICurrentUserService`, aborts if invalid.
|
||||
* **Invalid Operations**: Returns `400 Bad Request` for empty query.
|
||||
* **Unexpected Errors**: Caught and returned as `500 Internal Server Error`.
|
||||
|
||||
### Logging
|
||||
|
||||
* Logs warnings for `SearchUsersException`.
|
||||
* Logs errors for unexpected exceptions and `InvalidOperationException`.
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
description: >-
|
||||
Description: Controller for querying friend request-related data, including
|
||||
incoming friend requests and responses to sent friend requests.
|
||||
---
|
||||
|
||||
# FriendsRequestQueryController
|
||||
|
||||
### Controller Description
|
||||
|
||||
* **Route**: `api/friends`
|
||||
* **Authorize**: Requires authenticated user (`[Authorize]`).
|
||||
|
||||
### Endpoints
|
||||
|
||||
#### <mark style="color:$info;">GetIncomingRequests</mark>
|
||||
|
||||
* **Description**: Retrieves a list of incoming friend requests for the authenticated user.
|
||||
* **Route**: `[GET] api/friends/requests`
|
||||
* **HTTP Method**: `GET`
|
||||
* **Request**: None
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns a list of incoming friend requests or an empty list if none.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "Guid",
|
||||
"senderId": "Guid",
|
||||
"receiverId": "Guid",
|
||||
"status": "string",
|
||||
"createdAt": "DateTime"
|
||||
}
|
||||
]
|
||||
```
|
||||
* <mark style="color:$danger;">**403 Forbidden**</mark>: If user lacks authorization.
|
||||
|
||||
```json
|
||||
"string"
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Internal server error."
|
||||
}
|
||||
```
|
||||
|
||||
#### <mark style="color:$info;">GetResponses</mark>
|
||||
|
||||
* **Description**: Retrieves a list of responses to the authenticated user's sent friend requests.
|
||||
* **Route**: `[GET] api/friends/responses`
|
||||
* **HTTP Method**: `GET`
|
||||
* **Request**: None
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns a list of friend request responses or an empty list if none.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "Guid",
|
||||
"senderId": "Guid",
|
||||
"receiverId": "Guid",
|
||||
"status": "string",
|
||||
"createdAt": "DateTime"
|
||||
}
|
||||
]
|
||||
```
|
||||
* <mark style="color:$danger;">**403 Forbidden**</mark>: If user lacks authorization.
|
||||
|
||||
```json
|
||||
"string"
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Internal server error."
|
||||
}
|
||||
```
|
||||
|
||||
### Data Models
|
||||
|
||||
#### FriendshipDto
|
||||
|
||||
* **Description**: Data transfer object for friend request information.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "Guid",
|
||||
"senderId": "Guid",
|
||||
"receiverId": "Guid",
|
||||
"status": "string",
|
||||
"createdAt": "DateTime"
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
* **Invalid User ID**: Handled by `ICurrentUserService`, aborts if invalid.
|
||||
* **Invalid Operations**: Returns `200 OK` with an empty list for `InvalidOperationException`.
|
||||
* **Unexpected Errors**: Caught and returned as `500 Internal Server Error`.
|
||||
|
||||
### Logging
|
||||
|
||||
* Logs warnings for `InvalidOperationException`.
|
||||
* Logs information for successful response retrieval.
|
||||
* Logs errors for unexpected exceptions.
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
description: Controller for uploading and downloading media files.
|
||||
---
|
||||
|
||||
# MediaController
|
||||
|
||||
### Controller Description
|
||||
|
||||
* **Route**: `api/media`
|
||||
* **Authorize**: Requires authenticated user with roles "User" or "Admin" (`[Authorize(Roles = "User,Admin")]`).
|
||||
|
||||
### Endpoints
|
||||
|
||||
#### <mark style="color:$info;">Upload</mark>
|
||||
|
||||
* **Description**: Uploads a media file with associated metadata.
|
||||
* **Route**: `[POST] api/media/upload`
|
||||
* **HTTP Method**: `POST`
|
||||
* **Request**:
|
||||
* **Content-Type**: `multipart/form-data`
|
||||
* **Request Body**: `MediaUploadRequest` object with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"fromFile": "IFormFile",
|
||||
"type": "MediaType",
|
||||
"mimeType": "string",
|
||||
"encryptedKey": "string"
|
||||
}
|
||||
```
|
||||
* **Constraints**: File size limit of 20 MB.
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns the upload result.
|
||||
|
||||
```json
|
||||
"string" // Media ID or similar result
|
||||
```
|
||||
* <mark style="color:$danger;">**400 Bad Request**</mark>:
|
||||
* If model validation fails.
|
||||
* If no file is uploaded: `"No file uploaded"`
|
||||
* If file exceeds 20 MB: `"File is too large"`
|
||||
* If MIME type is missing: `"Missing MIME type"`
|
||||
* If operation is invalid: `"string"`
|
||||
* <mark style="color:$danger;">**403 Forbidden**</mark>: If user lacks authorization.
|
||||
|
||||
```json
|
||||
"string"
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
"Internal server error"
|
||||
```
|
||||
|
||||
#### <mark style="color:$info;">Download</mark>
|
||||
|
||||
* **Description**: Downloads a media file by its ID.
|
||||
* **Route**: `[GET] api/media/download/{id}`
|
||||
* **HTTP Method**: `GET`
|
||||
* **Request**:
|
||||
* **Path Parameter**: `id` (Guid, required): ID of the media file.
|
||||
* **Responses**:
|
||||
* <mark style="color:$success;">**200 OK**</mark>: Returns the media file as a stream.
|
||||
* **Content-Type**: Matches `MimeType` of the media.
|
||||
* **Content-Disposition**: Includes `filename` from `FileName`.
|
||||
* <mark style="color:$danger;">**403 Forbidden**</mark>: If user lacks access.
|
||||
* No body.
|
||||
* <mark style="color:$danger;">**404 Not Found**</mark>: If media is not found.
|
||||
|
||||
```json
|
||||
"Media not found"
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Internal Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
"Internal server error"
|
||||
```
|
||||
|
||||
### Data Models
|
||||
|
||||
#### MediaUploadRequest
|
||||
|
||||
* **Description**: Model for media upload request data.
|
||||
* **Structure**:
|
||||
|
||||
```json
|
||||
{
|
||||
"fromFile": "IFormFile",
|
||||
"type": "MediaType",
|
||||
"mimeType": "string",
|
||||
"encryptedKey": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
* **Invalid User ID**: Handled by `ICurrentUserService`, aborts if invalid.
|
||||
* **Invalid Operations**: Returns `400 Bad Request` for `InvalidOperationException`.
|
||||
* **Unauthorized Access**: Returns `403 Forbidden` for `UnauthorizedAccessException`.
|
||||
* **Resource Not Found**: Returns `404 Not Found` for `KeyNotFoundException`.
|
||||
* **Unexpected Errors**: Caught and returned as `500 Internal Server Error`.
|
||||
|
||||
### Logging
|
||||
|
||||
* Logs warnings for `UnauthorizedAccessException`, `InvalidOperationException`, and `KeyNotFoundException`.
|
||||
* Logs information for successful file uploads.
|
||||
* Logs errors for unexpected exceptions during upload and download.
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
description: >-
|
||||
Description: Controller for handling user online status updates by processing
|
||||
ping requests.
|
||||
---
|
||||
|
||||
# OnlinePingingController(не работает)
|
||||
|
||||
**Route**: `api/online`\
|
||||
**Authorize**: Requires "User" or "Admin" role
|
||||
|
||||
### <mark style="color:purple;">End Point {PATCH}</mark>
|
||||
|
||||
`[PATCH] api/online/ping`
|
||||
|
||||
#### Description
|
||||
|
||||
Updates the online status of the current user by sending a ping request.
|
||||
|
||||
**Request**
|
||||
|
||||
* **Content-Type**: `application/json`
|
||||
* **Request Body**: None
|
||||
|
||||
**Responses**
|
||||
|
||||
* **200 OK**: Ping processed successfully.
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
* **400 Bad Request**: If the user cannot be found in the database.
|
||||
|
||||
```json
|
||||
"User can't be found in our database."
|
||||
```
|
||||
* **403 Forbidden**: If the user is not authorized to perform the action.
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Not authorized to perform this action."
|
||||
}
|
||||
```
|
||||
* **500 Internal Server Error**: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Failed to send friend request."
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,235 @@
|
||||
---
|
||||
description: >-
|
||||
Description: SignalR hub for managing real-time friend request operations,
|
||||
including sending, accepting, and rejecting friend requests.
|
||||
icon: wifi
|
||||
---
|
||||
|
||||
# FriendsHub
|
||||
|
||||
**Route** `hubs/friends`
|
||||
|
||||
**Authorize**: Requires authentication (user must be logged in)
|
||||
|
||||
### <mark style="color:$info;">Hub Methods</mark>
|
||||
|
||||
#### <mark style="color:$primary;">OnConnectedAsync</mark>
|
||||
|
||||
**Description**: Automatically invoked when a client establishes a connection to the hub. Adds the user to their personal group for receiving friend request notifications.
|
||||
|
||||
**Behavior**:
|
||||
|
||||
* Retrieves the user's ID from the connection context.
|
||||
* If the user ID is invalid (`Guid.Empty`), logs a warning and aborts the connection.
|
||||
* Adds the user to a group named after their user ID (e.g., `userId.ToString()`).
|
||||
* Logs the connection event with the user ID and connection ID.
|
||||
|
||||
***
|
||||
|
||||
#### <mark style="color:$primary;">OnDisconnectedAsync</mark>
|
||||
|
||||
**Description**: Automatically invoked when a client disconnects from the hub. Removes the user from their personal group.
|
||||
|
||||
**Behavior**:
|
||||
|
||||
* Retrieves the user's ID from the connection context, suppressing exceptions if the ID is unavailable (e.g., due to an early abort).
|
||||
* If the user ID is valid, removes the user from their personal group.
|
||||
* Logs the disconnection event with the user ID and connection ID.
|
||||
* If an exception occurs, logs it as a warning along with the connection ID.
|
||||
* If no exception occurs but the user ID is invalid, logs the disconnection with the connection ID.
|
||||
|
||||
***
|
||||
|
||||
#### <mark style="color:$info;">SendRequest</mark>
|
||||
|
||||
**Description**: Sends a friend request to a specified user and notifies the recipient in real-time.
|
||||
|
||||
**Request**:
|
||||
|
||||
* **Method Name**: `SendRequest`
|
||||
* **Parameters**:
|
||||
* `targetUserId`: (`Guid`) The ID of the user to whom the friend request is sent.
|
||||
|
||||
**Responses**:
|
||||
|
||||
* <mark style="color:$success;">**Success (201 Created)**</mark>: Friend request sent successfully.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 201,
|
||||
"result": null,
|
||||
"errorMessage": null
|
||||
}
|
||||
```
|
||||
* <mark style="color:$danger;">**400 Bad Request**</mark>: If the operation is invalid (e.g., sending a request to oneself).
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 400,
|
||||
"result": null,
|
||||
"errorMessage": "<error_message>"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$danger;">**401 Unauthorized**</mark>: If the user is not authorized.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 401,
|
||||
"result": null,
|
||||
"errorMessage": "<error_message>"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$danger;">**409 Conflict**</mark>: If a friend request was already sent.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 409,
|
||||
"result": null,
|
||||
"errorMessage": "<error_message>"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 500,
|
||||
"result": null,
|
||||
"errorMessage": "Unexpected error! Please try later!"
|
||||
}
|
||||
```
|
||||
|
||||
**Client-Side Events**:
|
||||
|
||||
* **FriendRequestReceived**: Triggered on the recipient's client to notify them of a new friend request.
|
||||
* Payload: `userId` (`Guid`) - The ID of the user who sent the request.
|
||||
|
||||
***
|
||||
|
||||
#### <mark style="color:purple;">AcceptRequest</mark>
|
||||
|
||||
**Description**: Accepts a friend request and notifies the user in real-time.
|
||||
|
||||
**Request**:
|
||||
|
||||
* **Method Name**: `AcceptRequest`
|
||||
* **Parameters**:
|
||||
* `friendshipId`: (`Guid`) The ID of the friend request to accept.
|
||||
|
||||
**Responses**:
|
||||
|
||||
* <mark style="color:$success;">**Success (200 OK)**</mark>: Friend request accepted successfully.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"result": null,
|
||||
"errorMessage": null
|
||||
}
|
||||
```
|
||||
* <mark style="color:$danger;">**400 Bad Request**</mark>: If the operation is invalid (e.g., invalid friendship ID).
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 400,
|
||||
"result": null,
|
||||
"errorMessage": "<error_message>"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$danger;">**401 Unauthorized**</mark>: If the user is not authorized to accept the request.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 401,
|
||||
"result": null,
|
||||
"errorMessage": "<error_message>"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 500,
|
||||
"result": null,
|
||||
"errorMessage": "Unexpected error! Please try later!"
|
||||
}
|
||||
```
|
||||
|
||||
**Client-Side Events**:
|
||||
|
||||
* **FriendRequestAccepted**: Triggered on the user's client to confirm the friend request was accepted.
|
||||
* Payload: `friendshipId` (`Guid`) - The ID of the accepted friend request.
|
||||
|
||||
***
|
||||
|
||||
#### <mark style="color:purple;">RejectRequest</mark>
|
||||
|
||||
**Description**: Rejects a friend request and notifies the user in real-time.
|
||||
|
||||
**Request**:
|
||||
|
||||
* **Method Name**: `RejectRequest`
|
||||
* **Parameters**:
|
||||
* `friendshipId`: (`Guid`) The ID of the friend request to reject.
|
||||
|
||||
**Responses**:
|
||||
|
||||
* <mark style="color:$success;">**Success (200 OK)**</mark>: Friend request rejected successfully.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"result": null,
|
||||
"errorMessage": null
|
||||
}
|
||||
```
|
||||
* <mark style="color:$danger;">**400 Bad Request**</mark>: If the operation is invalid (e.g., invalid friendship ID).
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 400,
|
||||
"result": null,
|
||||
"errorMessage": "<error_message>"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$danger;">**401 Unauthorized**</mark>: If the user is not authorized to reject the request.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 401,
|
||||
"result": null,
|
||||
"errorMessage": "<error_message>"
|
||||
}
|
||||
```
|
||||
* <mark style="color:$warning;">**500 Server Error**</mark>: Indicates an unexpected error.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 500,
|
||||
"result": null,
|
||||
"errorMessage": "Unexpected error! Please try later!"
|
||||
}
|
||||
```
|
||||
|
||||
**Client-Side Events**:
|
||||
|
||||
* **FriendRequestRejected**: Triggered on the user's client to confirm the friend request was rejected.
|
||||
* Payload: `friendshipId` (`Guid`) - The ID of the rejected friend request.
|
||||
|
||||
***
|
||||
|
||||
### <mark style="color:$info;">Error Handling</mark>
|
||||
|
||||
* **Invalid User ID**: If the user ID is `Guid.Empty` during connection, the connection is aborted with a logged warning.
|
||||
* **Invalid Operations**: Operations like sending a request to oneself or accepting/rejecting an invalid request return a `BadRequest` result.
|
||||
* **Unauthorized Access**: Unauthorized actions return an `Unauthorized` result with a logged warning.
|
||||
* **Conflicts**: Attempting to send a duplicate friend request returns a `Conflict` result.
|
||||
* **Unexpected Errors**: General exceptions are caught, logged, and returned as a `ServerError` result.
|
||||
|
||||
***
|
||||
|
||||
### Logging
|
||||
|
||||
* Logs connection and disconnection events with user ID and connection ID.
|
||||
* Logs friend request operations (send, accept, reject) with success or failure details for monitoring and debugging.
|
||||
|
||||
***
|
||||
@@ -0,0 +1,384 @@
|
||||
---
|
||||
description: >-
|
||||
Description: This sub-documentation provides example Java client code for
|
||||
interacting with the FriendsHub SignalR hub using the microsoft/signalr
|
||||
library. It covers connecting to the hub, handling con
|
||||
icon: java
|
||||
---
|
||||
|
||||
# FriendsHub Client (Java)
|
||||
|
||||
**Prerequisites**:
|
||||
|
||||
* Include the SignalR Java client library in your project. For Gradle, add the following dependency:
|
||||
|
||||
```gradle
|
||||
implementation("com.microsoft.signalr:signalr:9.0.7")
|
||||
```
|
||||
* Ensure the client has a valid authentication token (JWT) for connecting to the hub, as it requires authentication.
|
||||
|
||||
**Hub Route**: `hubs/friends`\
|
||||
**Authorize**: Requires a valid JWT token with a `userId` claim.
|
||||
|
||||
### <mark style="color:purple;">Setup and Connection</mark>
|
||||
|
||||
#### Connecting to the Hub
|
||||
|
||||
This example demonstrates how to establish a connection to the `FriendsHub` with authentication and handle connection events.
|
||||
|
||||
```java
|
||||
import com.microsoft.signalr.HubConnection;
|
||||
import com.microsoft.signalr.HubConnectionBuilder;
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
|
||||
public class FriendsHubClient {
|
||||
private final HubConnection hubConnection;
|
||||
private final String accessToken; // JWT token with userId claim
|
||||
|
||||
public FriendsHubClient(String hubUrl, String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
this.hubConnection = HubConnectionBuilder.create(hubUrl)
|
||||
.withAccessTokenProvider(Single.just(accessToken))
|
||||
.build();
|
||||
|
||||
// Handle connection established
|
||||
hubConnection.onConnected(() -> {
|
||||
System.out.println("Connected to FriendsHub with connection ID: " + hubConnection.getConnectionId());
|
||||
});
|
||||
|
||||
// Handle connection closed
|
||||
hubConnection.onClosed((exception) -> {
|
||||
if (exception != null) {
|
||||
System.err.println("Connection closed with error: " + exception.getMessage());
|
||||
} else {
|
||||
System.out.println("Connection closed normally");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Completable startConnection() {
|
||||
return hubConnection.start();
|
||||
}
|
||||
|
||||
public Completable stopConnection() {
|
||||
return hubConnection.stop();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
|
||||
```java
|
||||
String hubUrl = "https://your-api-url/hubs/friends";
|
||||
String jwtToken = "your-jwt-token-here";
|
||||
FriendsHubClient client = new FriendsHubClient(hubUrl, jwtToken);
|
||||
client.startConnection()
|
||||
.blockingAwait(); // Wait for connection to establish
|
||||
```
|
||||
|
||||
**Notes**:
|
||||
|
||||
* Replace `your-api-url` with the actual server URL.
|
||||
* The `accessToken` must include a valid JWT with the `userId` claim to avoid connection abortion.
|
||||
* Use `stopConnection()` to gracefully disconnect when done.
|
||||
|
||||
### <mark style="color:purple;">Subscribing to Hub Events</mark>
|
||||
|
||||
The `FriendsHub` sends three client-side events: `FriendRequestReceived`, `FriendRequestAccepted`, and `FriendRequestRejected`. Below is an example of subscribing to these events.
|
||||
|
||||
```java
|
||||
public class FriendsHubClient {
|
||||
// ... (constructor and connection methods as above)
|
||||
|
||||
public void subscribeToEvents() {
|
||||
// Handle FriendRequestReceived event
|
||||
hubConnection.on("FriendRequestReceived", (userId) -> {
|
||||
System.out.println("Received friend request from user ID: " + userId);
|
||||
// Handle the friend request (e.g., update UI or notify user)
|
||||
}, String.class);
|
||||
|
||||
// Handle FriendRequestAccepted event
|
||||
hubConnection.on("FriendRequestAccepted", (friendshipId) -> {
|
||||
System.out.println("Friend request accepted, friendship ID: " + friendshipId);
|
||||
// Update UI or local state to reflect new friendship
|
||||
}, String.class);
|
||||
|
||||
// Handle FriendRequestRejected event
|
||||
hubConnection.on("FriendRequestRejected", (friendshipId) -> {
|
||||
System.out.println("Friend request rejected, friendship ID: " + friendshipId);
|
||||
// Update UI or notify user of rejection
|
||||
}, String.class);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
|
||||
```java
|
||||
FriendsHubClient client = new FriendsHubClient(hubUrl, jwtToken);
|
||||
client.subscribeToEvents();
|
||||
client.startConnection().blockingAwait();
|
||||
```
|
||||
|
||||
**Notes**:
|
||||
|
||||
* The `userId` and `friendshipId` parameters are received as strings (representing `Guid` values) due to JSON serialization. Parse them to `UUID` if needed:
|
||||
|
||||
```java
|
||||
UUID uuid = UUID.fromString(userId);
|
||||
```
|
||||
* Call `subscribeToEvents()` before starting the connection to ensure event handlers are registered.
|
||||
|
||||
### <mark style="color:purple;">Invoking Hub Methods</mark>
|
||||
|
||||
#### SendRequest
|
||||
|
||||
Sends a friend request to another user.
|
||||
|
||||
```java
|
||||
public Completable sendFriendRequest(String targetUserId) {
|
||||
return hubConnection.invoke(HubResult.class, "SendRequest", targetUserId)
|
||||
.doOnSuccess(result -> {
|
||||
switch (result.getStatus()) {
|
||||
case 201: // Created
|
||||
System.out.println("Friend request sent successfully");
|
||||
break;
|
||||
case 400: // BadRequest
|
||||
System.err.println("Bad request: " + result.getErrorMessage());
|
||||
break;
|
||||
case 401: // Unauthorized
|
||||
System.err.println("Unauthorized: " + result.getErrorMessage());
|
||||
break;
|
||||
case 409: // Conflict
|
||||
System.err.println("Conflict: " + result.getErrorMessage());
|
||||
break;
|
||||
case 500: // ServerError
|
||||
System.err.println("Server error: " + result.getErrorMessage());
|
||||
break;
|
||||
default:
|
||||
System.err.println("Unexpected status: " + result.getStatus());
|
||||
}
|
||||
})
|
||||
.ignoreElement();
|
||||
}
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
|
||||
```java
|
||||
client.sendFriendRequest("target-user-guid-here")
|
||||
.blockingAwait();
|
||||
```
|
||||
|
||||
**Notes**:
|
||||
|
||||
* The `targetUserId` should be a valid `Guid` string (e.g., "550e8400-e29b-41d4-a716-446655440000").
|
||||
* The response is a `HubResult<object>` with a status code and optional error message.
|
||||
|
||||
#### AcceptRequest
|
||||
|
||||
Accepts a friend request by its friendship ID.
|
||||
|
||||
```java
|
||||
public Completable acceptFriendRequest(String friendshipId) {
|
||||
return hubConnection.invoke(HubResult.class, "AcceptRequest", friendshipId)
|
||||
.doOnSuccess(result -> {
|
||||
switch (result.getStatus()) {
|
||||
case 200: // OK
|
||||
System.out.println("Friend request accepted successfully");
|
||||
break;
|
||||
case 400: // BadRequest
|
||||
System.err.println("Bad request: " + result.getErrorMessage());
|
||||
break;
|
||||
case 401: // Unauthorized
|
||||
System.err.println("Unauthorized: " + result.getErrorMessage());
|
||||
break;
|
||||
case 500: // ServerError
|
||||
System.err.println("Server error: " + result.getErrorMessage());
|
||||
break;
|
||||
default:
|
||||
System.err.println("Unexpected status: " + result.getStatus());
|
||||
}
|
||||
})
|
||||
.ignoreElement();
|
||||
}
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
|
||||
```java
|
||||
client.acceptFriendRequest("friendship-guid-here")
|
||||
.blockingAwait();
|
||||
```
|
||||
|
||||
#### RejectRequest
|
||||
|
||||
Rejects a friend request by its friendship ID.
|
||||
|
||||
```java
|
||||
public Completable rejectFriendRequest(String friendshipId) {
|
||||
return hubConnection.invoke(HubResult.class, "RejectRequest", friendshipId)
|
||||
.doOnSuccess(result -> {
|
||||
switch (result.getStatus()) {
|
||||
case 200: // OK
|
||||
System.out.println("Friend request rejected successfully");
|
||||
break;
|
||||
case 400: // BadRequest
|
||||
System.err.println("Bad request: " + result.getErrorMessage());
|
||||
break;
|
||||
case 401: // Unauthorized
|
||||
System.err.println("Unauthorized: " + result.getErrorMessage());
|
||||
break;
|
||||
case 500: // ServerError
|
||||
System.err.println("Server error: " + result.getErrorMessage());
|
||||
break;
|
||||
default:
|
||||
System.err.println("Unexpected status: " + result.getStatus());
|
||||
}
|
||||
})
|
||||
.ignoreElement();
|
||||
}
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
|
||||
```java
|
||||
client.rejectFriendRequest("friendship-guid-here")
|
||||
.blockingAwait();
|
||||
```
|
||||
|
||||
### <mark style="color:purple;">Data Model</mark>
|
||||
|
||||
The client expects a `HubResult` response from hub methods, which is serialized as JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "number", // Corresponds to HubResultStatus (e.g., 200, 400, 401, etc.)
|
||||
"result": null, // Always null for FriendsHub methods
|
||||
"errorMessage": "string" // Null for success, error message for failures
|
||||
}
|
||||
```
|
||||
|
||||
### <mark style="color:purple;">Error Handling</mark>
|
||||
|
||||
* **Connection Errors**: Handle connection failures in `onClosed` or by catching exceptions from `startConnection()`.
|
||||
* **Invalid User ID**: If the JWT lacks a valid `userId` claim, the hub aborts the connection, and `onClosed` is triggered with an error.
|
||||
* **Hub Method Errors**: Check the `status` and `errorMessage` fields in the `HubResult` response to handle specific errors (e.g., `BadRequest`, `Unauthorized`, `Conflict`).
|
||||
|
||||
### <mark style="color:purple;">Example Full Client</mark>
|
||||
|
||||
Below is a complete example combining connection, event subscription, and method invocation.
|
||||
|
||||
```java
|
||||
import com.microsoft.signalr.HubConnection;
|
||||
import com.microsoft.signalr.HubConnectionBuilder;
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
|
||||
public class FriendsHubClient {
|
||||
private final HubConnection hubConnection;
|
||||
private final String accessToken;
|
||||
|
||||
public FriendsHubClient(String hubUrl, String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
this.hubConnection = HubConnectionBuilder.create(hubUrl)
|
||||
.withAccessTokenProvider(Single.just(accessToken))
|
||||
.build();
|
||||
|
||||
// Connection events
|
||||
hubConnection.onConnected(() -> {
|
||||
System.out.println("Connected to FriendsHub with connection ID: " + hubConnection.getConnectionId());
|
||||
});
|
||||
hubConnection.onClosed((exception) -> {
|
||||
System.err.println(exception != null ? "Connection closed with error: " + exception.getMessage() : "Connection closed normally");
|
||||
});
|
||||
|
||||
// Subscribe to hub events
|
||||
hubConnection.on("FriendRequestReceived", (userId) -> {
|
||||
System.out.println("Received friend request from user ID: " + userId);
|
||||
}, String.class);
|
||||
hubConnection.on("FriendRequestAccepted", (friendshipId) -> {
|
||||
System.out.println("Friend request accepted, friendship ID: " + friendshipId);
|
||||
}, String.class);
|
||||
hubConnection.on("FriendRequestRejected", (friendshipId) -> {
|
||||
System.out.println("Friend request rejected, friendship ID: " + friendshipId);
|
||||
}, String.class);
|
||||
}
|
||||
|
||||
public Completable startConnection() {
|
||||
return hubConnection.start();
|
||||
}
|
||||
|
||||
public Completable stopConnection() {
|
||||
return hubConnection.stop();
|
||||
}
|
||||
|
||||
public Completable sendFriendRequest(String targetUserId) {
|
||||
return hubConnection.invoke(HubResult.class, "SendRequest", targetUserId)
|
||||
.doOnSuccess(this::handleHubResult)
|
||||
.ignoreElement();
|
||||
}
|
||||
|
||||
public Completable acceptFriendRequest(String friendshipId) {
|
||||
return hubConnection.invoke(HubResult.class, "AcceptRequest", friendshipId)
|
||||
.doOnSuccess(this::handleHubResult)
|
||||
.ignoreElement();
|
||||
}
|
||||
|
||||
public Completable rejectFriendRequest(String friendshipId) {
|
||||
return hubConnection.invoke(HubResult.class, "RejectRequest", friendshipId)
|
||||
.doOnSuccess(this::handleHubResult)
|
||||
.ignoreElement();
|
||||
}
|
||||
|
||||
private void handleHubResult(HubResult result) {
|
||||
switch (result.getStatus()) {
|
||||
case 200:
|
||||
System.out.println("Operation successful");
|
||||
break;
|
||||
case 201:
|
||||
System.out.println("Resource created successfully");
|
||||
break;
|
||||
case 400:
|
||||
System.err.println("Bad request: " + result.getErrorMessage());
|
||||
break;
|
||||
case 401:
|
||||
System.err.println("Unauthorized: " + result.getErrorMessage());
|
||||
break;
|
||||
case 409:
|
||||
System.err.println("Conflict: " + result.getErrorMessage());
|
||||
break;
|
||||
case 500:
|
||||
System.err.println("Server error: " + result.getErrorMessage());
|
||||
break;
|
||||
default:
|
||||
System.err.println("Unexpected status: " + result.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String hubUrl = "https://your-api-url/hubs/friends";
|
||||
String jwtToken = "your-jwt-token-here";
|
||||
FriendsHubClient client = new FriendsHubClient(hubUrl, jwtToken);
|
||||
|
||||
// Start connection and send a friend request
|
||||
client.startConnection()
|
||||
.andThen(client.sendFriendRequest("550e8400-e29b-41d4-a716-446655440000"))
|
||||
.blockingAwait();
|
||||
|
||||
// Simulate accepting a friend request
|
||||
client.acceptFriendRequest("friendship-guid-here")
|
||||
.blockingAwait();
|
||||
|
||||
// Stop connection
|
||||
client.stopConnection().blockingAwait();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Notes**:
|
||||
|
||||
* Replace `your-api-url` and `your-jwt-token-here` with actual values.
|
||||
* The `main` method demonstrates a complete workflow, but in a real application, you would integrate this with your UI or event loop.
|
||||
* Use `UUID.fromString()` to convert string GUIDs to `UUID` objects if needed for internal logic.
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
icon: wifi
|
||||
---
|
||||
|
||||
# ChatHub
|
||||
|
||||
**Route**: `hubs/chats`\
|
||||
**Authorize**: Requires authentication (user must be logged in)
|
||||
|
||||
***
|
||||
|
||||
### Hub Methods
|
||||
|
||||
#### <mark style="color:$primary;">OnConnectedAsync</mark>
|
||||
|
||||
**Description**: Automatically invoked when a client establishes a connection to the hub. Adds the user to their personal group for private messages and notifications.
|
||||
|
||||
**Behavior**:
|
||||
|
||||
* Retrieves the user's ID from the connection context.
|
||||
* If the user ID is invalid (`Guid.Empty`), logs a warning and aborts the connection.
|
||||
* Adds the user to a group named after their user ID (e.g., `userId.ToString()`).
|
||||
* Logs the connection event with the user ID and connection ID.
|
||||
|
||||
**Notes**:
|
||||
|
||||
* Future enhancements may include adding the user to their chat groups (currently a TODO in the code).
|
||||
|
||||
***
|
||||
|
||||
#### <mark style="color:$primary;">OnDisconnectedAsync</mark>
|
||||
|
||||
**Description**: Automatically invoked when a client disconnects from the hub. Removes the user from their personal group.
|
||||
|
||||
**Behavior**:
|
||||
|
||||
* Retrieves the user's ID from the connection context, suppressing exceptions if the ID is unavailable (e.g., due to an early abort).
|
||||
* If the user ID is valid, removes the user from their personal group.
|
||||
* Logs the disconnection event with the user ID and connection ID.
|
||||
* If an exception occurs, logs it as a warning along with the connection ID.
|
||||
* If no exception occurs but the user ID is invalid, logs the disconnection with the connection ID.
|
||||
|
||||
**Notes**:
|
||||
|
||||
* Future enhancements may include removing the user from their chat groups (currently a TODO in the code).
|
||||
|
||||
***
|
||||
|
||||
#### <mark style="color:$info;">Send</mark>
|
||||
|
||||
**Description**: Allows a client to send a message to a recipient, which can be either a user or a group.
|
||||
|
||||
**Request**:
|
||||
|
||||
* **Method Name**: `Send`
|
||||
* **Parameters**:
|
||||
* `request`: An object of type `MessageRequest` with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"encryptedContent": "string",
|
||||
"replyToMessageId": "Guid",
|
||||
"recipientId": "Guid",
|
||||
"recipientType": "string", // "User" or "Group"
|
||||
"mediaAttachments": [
|
||||
{
|
||||
"mediaId": "Guid",
|
||||
"encryptedKey": "string",
|
||||
"type": "string",
|
||||
"mimeType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Responses**:
|
||||
|
||||
* <mark style="color:$success;">**Success**</mark>:
|
||||
* Sends the message to the recipient (user or group) via the `ReceiveMessage` event.
|
||||
* Notifies the sender with a confirmation via the `MessageSent` event.
|
||||
* Logs the successful message send with the message ID, sender ID, recipient ID, and recipient type.
|
||||
* <mark style="color:$danger;">**Error**</mark>:
|
||||
* If the message is empty (no `encryptedContent` and no `mediaAttachments`), logs a warning and throws an `ArgumentException`.
|
||||
* If the message fails to send (e.g., due to an internal error), logs the error and throws a `HubException`.
|
||||
|
||||
**Client-Side Events**:
|
||||
|
||||
* <mark style="color:$primary;">**ReceiveMessage**</mark>: Triggered on the recipient's client(s) to deliver the message.
|
||||
* Payload: `UserMessageResponse` object.
|
||||
* <mark style="color:$primary;">**MessageSent**</mark>: Triggered on the sender's client to confirm the message was sent.
|
||||
* Payload: `UserMessageResponse` object.
|
||||
|
||||
**Notes**:
|
||||
|
||||
* The message must contain either `encryptedContent` or `mediaAttachments`; otherwise, it is invalid.
|
||||
* The recipient is determined by `recipientType`:
|
||||
* `"User"`: Sends to the recipient’s personal group (e.g., `recipientId.ToString()`).
|
||||
* `"Group"`: Sends to all members of the group (e.g., `group_{recipientId}`).
|
||||
|
||||
***
|
||||
|
||||
### Data Models
|
||||
|
||||
#### MessageRequest
|
||||
|
||||
```json
|
||||
{
|
||||
"encryptedContent": "string",
|
||||
"replyToMessageId": "Guid",
|
||||
"recipientId": "Guid",
|
||||
"recipientType": "string", // "User" or "Group"
|
||||
"mediaAttachments": [
|
||||
{
|
||||
"mediaId": "Guid",
|
||||
"encryptedKey": "string",
|
||||
"type": "string",
|
||||
"mimeType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### UserMessageResponse
|
||||
|
||||
```json
|
||||
{
|
||||
"messageId": "Guid",
|
||||
"senderId": "Guid",
|
||||
"recipientId": "Guid",
|
||||
"recipientType": "string",
|
||||
"encryptedContent": "string",
|
||||
"sentAt": "DateTime",
|
||||
"isEdited": "bool",
|
||||
"mediaAttachments": [
|
||||
{
|
||||
"mediaId": "Guid",
|
||||
"encryptedKey": "string",
|
||||
"type": "string",
|
||||
"mimeType": "string"
|
||||
}
|
||||
],
|
||||
"replyToMessageId": "Guid"
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### Error Handling
|
||||
|
||||
* **Invalid User ID**: If the user ID is `Guid.Empty` during connection, the connection is aborted with a logged warning.
|
||||
* **Empty Message**: If a message lacks both content and attachments, an `ArgumentException` is thrown with a logged warning.
|
||||
* **Message Send Failure**: If sending fails (e.g., due to a service error), a `HubException` is thrown with the error logged.
|
||||
|
||||
***
|
||||
|
||||
### Logging
|
||||
|
||||
* Logs connection events with user ID and connection ID.
|
||||
* Logs disconnection events, including exceptions if present.
|
||||
* Logs message send attempts, successes, and failures for monitoring and debugging.
|
||||
|
||||
***
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
description: SignalR hub for managing user online presence and notifications.
|
||||
icon: wifi
|
||||
---
|
||||
|
||||
# PresenceHub
|
||||
|
||||
### Controller Description
|
||||
|
||||
* **Route**: `hubs/presence`
|
||||
* **Authorize**: Requires authenticated user with roles "Admin" or "User" (`[Authorize(Roles = "Admin, User")]`).
|
||||
|
||||
### Hub Methods
|
||||
|
||||
#### <mark style="color:$info;">OnConnectedAsync</mark>
|
||||
|
||||
* **Description**: Invoked when a client connects, updates online status and notifies friends.
|
||||
* **Behavior**:
|
||||
* Retrieves user ID from context.
|
||||
* Aborts connection if user ID is `Guid.Empty` or user does not exist.
|
||||
* Adds user to online store and their personal group.
|
||||
* Notifies friends of user going online via `UserOnline` event.
|
||||
* **Client-Side Events**:
|
||||
* **UserOnline**: Triggered for friends with payload `userId` (Guid).
|
||||
* **Responses**: None (connection aborted on invalid user ID).
|
||||
|
||||
#### <mark style="color:$info;">OnDisconnectedAsync</mark>
|
||||
|
||||
* **Description**: Invoked when a client disconnects, updates offline status and notifies friends.
|
||||
* **Behavior**:
|
||||
* Retrieves user ID from context, suppressing exceptions.
|
||||
* Removes user from personal group if ID is valid.
|
||||
* Updates user's `WasOnline` timestamp.
|
||||
* Sets user as offline in store.
|
||||
* Notifies friends of user going offline via `UserOffline` event.
|
||||
* **Client-Side Events**:
|
||||
* **UserOffline**: Triggered for friends with payload `userId` (Guid).
|
||||
* **Responses**: None.
|
||||
|
||||
### Data Models
|
||||
|
||||
* **N/A**: No specific data models defined for hub methods.
|
||||
|
||||
### Error Handling
|
||||
|
||||
* **Invalid User ID**: Aborts connection with logged warning if `Guid.Empty` or user not found.
|
||||
* **Unexpected Errors**: Logged, but no specific response (handled by base method).
|
||||
|
||||
### Logging
|
||||
|
||||
* Logs warnings for invalid user ID on connection.
|
||||
* Logs information for disconnections with user ID and connection ID.
|
||||
* Logs errors for unexpected exceptions during disconnection.
|
||||
Reference in New Issue
Block a user