feat: Chat request implementation #14
11 changed files with 147 additions and 106 deletions
|
@ -20,7 +20,7 @@ use oxidetalis_core::types::PublicKey;
|
||||||
use oxidetalis_entities::prelude::*;
|
use oxidetalis_entities::prelude::*;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
use crate::errors::ApiResult;
|
use crate::errors::ServerResult;
|
||||||
|
|
||||||
/// Extension trait for the `DatabaseConnection` to work with the blacklist
|
/// Extension trait for the `DatabaseConnection` to work with the blacklist
|
||||||
/// table
|
/// table
|
||||||
|
@ -31,7 +31,7 @@ pub trait BlackListExt {
|
||||||
&self,
|
&self,
|
||||||
blacklister: &UserModel,
|
blacklister: &UserModel,
|
||||||
awiteb marked this conversation as resolved
|
|||||||
target_public_key: &PublicKey,
|
target_public_key: &PublicKey,
|
||||||
) -> ApiResult<bool>;
|
) -> ServerResult<bool>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlackListExt for DatabaseConnection {
|
impl BlackListExt for DatabaseConnection {
|
||||||
|
@ -40,7 +40,7 @@ impl BlackListExt for DatabaseConnection {
|
||||||
&self,
|
&self,
|
||||||
blacklister: &UserModel,
|
blacklister: &UserModel,
|
||||||
target_public_key: &PublicKey,
|
target_public_key: &PublicKey,
|
||||||
) -> ApiResult<bool> {
|
) -> ServerResult<bool> {
|
||||||
blacklister
|
blacklister
|
||||||
.find_related(BlacklistEntity)
|
.find_related(BlacklistEntity)
|
||||||
.filter(BlacklistColumn::Target.eq(target_public_key.to_string()))
|
.filter(BlacklistColumn::Target.eq(target_public_key.to_string()))
|
||||||
|
|
|
@ -21,7 +21,7 @@ use oxidetalis_core::types::PublicKey;
|
||||||
use oxidetalis_entities::prelude::*;
|
use oxidetalis_entities::prelude::*;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
use crate::websocket::errors::{WsError, WsResult};
|
use crate::errors::ServerResult;
|
||||||
|
|
||||||
/// Extension trait for the `in_chat_requests` table.
|
/// Extension trait for the `in_chat_requests` table.
|
||||||
pub trait InChatRequestsExt {
|
pub trait InChatRequestsExt {
|
||||||
|
@ -30,7 +30,7 @@ pub trait InChatRequestsExt {
|
||||||
&self,
|
&self,
|
||||||
requester: &PublicKey,
|
requester: &PublicKey,
|
||||||
recipient: &UserModel,
|
recipient: &UserModel,
|
||||||
) -> WsResult<()>;
|
) -> ServerResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InChatRequestsExt for DatabaseConnection {
|
impl InChatRequestsExt for DatabaseConnection {
|
||||||
|
@ -39,16 +39,12 @@ impl InChatRequestsExt for DatabaseConnection {
|
||||||
&self,
|
&self,
|
||||||
sender: &PublicKey,
|
sender: &PublicKey,
|
||||||
recipient: &UserModel,
|
recipient: &UserModel,
|
||||||
) -> WsResult<()> {
|
) -> ServerResult<()> {
|
||||||
if sender.to_string() == recipient.public_key {
|
|
||||||
return Err(WsError::CannotSendChatRequestToSelf);
|
|
||||||
}
|
|
||||||
if recipient
|
if recipient
|
||||||
.find_related(InChatRequestsEntity)
|
.find_related(InChatRequestsEntity)
|
||||||
.filter(InChatRequestsColumn::Sender.eq(sender.to_string()))
|
.filter(InChatRequestsColumn::Sender.eq(sender.to_string()))
|
||||||
.one(self)
|
.one(self)
|
||||||
.await
|
.await?
|
||||||
.map_err(|_| WsError::InternalServerError)?
|
|
||||||
.is_none()
|
.is_none()
|
||||||
awiteb marked this conversation as resolved
Amjad50
commented
since we are not dealing with the result. maybe a one statement approach to do
here i'm updating since we are not dealing with the result. maybe a one statement approach to do
```rust
InChatRequestsEntity::insert(InChatRequestsActiveModel {
recipient_id: Set(recipient.id),
sender: Set(sender.to_string()),
in_on: Set(Utc::now()),
..Default::default()
})
.on_conflict(
OnConflict::columns([
InChatRequestsColumn::RecipientId,
InChatRequestsColumn::Sender,
])
.update_column(InChatRequestsColumn::InOn)
.to_owned(),
)
.exec(self)
```
here i'm updating `InOn` if needed, but can also be changed to `do_nothing()`
|
|||||||
{
|
{
|
||||||
InChatRequestsActiveModel {
|
InChatRequestsActiveModel {
|
||||||
|
@ -58,8 +54,7 @@ impl InChatRequestsExt for DatabaseConnection {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.save(self)
|
.save(self)
|
||||||
.await
|
.await?;
|
||||||
.map_err(|_| WsError::InternalServerError)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,7 @@ use oxidetalis_core::types::PublicKey;
|
||||||
use oxidetalis_entities::prelude::*;
|
use oxidetalis_entities::prelude::*;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
use crate::{
|
use crate::{errors::ServerResult, websocket::errors::WsError};
|
||||||
errors::ApiResult,
|
|
||||||
websocket::errors::{WsError, WsResult},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Extension trait for the `out_chat_requests` table.
|
/// Extension trait for the `out_chat_requests` table.
|
||||||
pub trait OutChatRequestsExt {
|
pub trait OutChatRequestsExt {
|
||||||
|
@ -33,14 +30,14 @@ pub trait OutChatRequestsExt {
|
||||||
&self,
|
&self,
|
||||||
requester: &UserModel,
|
requester: &UserModel,
|
||||||
recipient: &PublicKey,
|
recipient: &PublicKey,
|
||||||
) -> ApiResult<bool>;
|
) -> ServerResult<bool>;
|
||||||
awiteb marked this conversation as resolved
Amjad50
commented
should return should return `true` based on the name and description of the func
Amjad50
commented
small nitpick (also applies to small nitpick (also applies to `have_chat_request_to` below)
I would name it `get_chat_request_to`, as `have` seems that it would be boolean
|
|||||||
|
|
||||||
/// Save the chat request in the requester table
|
/// Save the chat request in the requester table
|
||||||
async fn save_out_chat_request(
|
async fn save_out_chat_request(
|
||||||
&self,
|
&self,
|
||||||
requester: &UserModel,
|
requester: &UserModel,
|
||||||
recipient: &PublicKey,
|
recipient: &PublicKey,
|
||||||
) -> WsResult<()>;
|
) -> ServerResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutChatRequestsExt for DatabaseConnection {
|
impl OutChatRequestsExt for DatabaseConnection {
|
||||||
|
@ -49,7 +46,7 @@ impl OutChatRequestsExt for DatabaseConnection {
|
||||||
&self,
|
&self,
|
||||||
requester: &UserModel,
|
requester: &UserModel,
|
||||||
recipient: &PublicKey,
|
recipient: &PublicKey,
|
||||||
) -> ApiResult<bool> {
|
) -> ServerResult<bool> {
|
||||||
requester
|
requester
|
||||||
.find_related(OutChatRequestsEntity)
|
.find_related(OutChatRequestsEntity)
|
||||||
.filter(OutChatRequestsColumn::Recipient.eq(recipient.to_string()))
|
.filter(OutChatRequestsColumn::Recipient.eq(recipient.to_string()))
|
||||||
|
@ -64,13 +61,9 @@ impl OutChatRequestsExt for DatabaseConnection {
|
||||||
&self,
|
&self,
|
||||||
requester: &UserModel,
|
requester: &UserModel,
|
||||||
recipient: &PublicKey,
|
recipient: &PublicKey,
|
||||||
) -> WsResult<()> {
|
) -> ServerResult<()> {
|
||||||
if self
|
if self.have_chat_request_to(requester, recipient).await? {
|
||||||
.have_chat_request_to(requester, recipient)
|
return Err(WsError::AlreadySendChatRequest.into());
|
||||||
.await
|
|
||||||
.map_err(|_| WsError::InternalServerError)?
|
|
||||||
{
|
|
||||||
return Err(WsError::AlreadySendChatRequest);
|
|
||||||
}
|
}
|
||||||
OutChatRequestsActiveModel {
|
OutChatRequestsActiveModel {
|
||||||
sender_id: Set(requester.id),
|
sender_id: Set(requester.id),
|
||||||
|
@ -79,8 +72,7 @@ impl OutChatRequestsExt for DatabaseConnection {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.save(self)
|
.save(self)
|
||||||
.await
|
.await?;
|
||||||
.map_err(|_| WsError::InternalServerError)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,29 +21,29 @@ use oxidetalis_core::types::PublicKey;
|
||||||
use oxidetalis_entities::prelude::*;
|
use oxidetalis_entities::prelude::*;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
use crate::errors::{ApiError, ApiResult};
|
use crate::{errors::ServerResult, routes::ApiError};
|
||||||
|
|
||||||
pub trait UserTableExt {
|
pub trait UserTableExt {
|
||||||
/// Returns true if there is users in the database
|
/// Returns true if there is users in the database
|
||||||
async fn users_exists_in_database(&self) -> ApiResult<bool>;
|
async fn users_exists_in_database(&self) -> ServerResult<bool>;
|
||||||
/// Register new user
|
/// Register new user
|
||||||
async fn register_user(&self, public_key: &PublicKey, is_admin: bool) -> ApiResult<()>;
|
async fn register_user(&self, public_key: &PublicKey, is_admin: bool) -> ServerResult<()>;
|
||||||
/// Returns user by its public key
|
/// Returns user by its public key
|
||||||
async fn get_user_by_pubk(&self, public_key: &PublicKey) -> ApiResult<Option<UserModel>>;
|
async fn get_user_by_pubk(&self, public_key: &PublicKey) -> ServerResult<Option<UserModel>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserTableExt for DatabaseConnection {
|
impl UserTableExt for DatabaseConnection {
|
||||||
#[logcall]
|
#[logcall]
|
||||||
async fn users_exists_in_database(&self) -> ApiResult<bool> {
|
async fn users_exists_in_database(&self) -> ServerResult<bool> {
|
||||||
UserEntity::find()
|
UserEntity::find()
|
||||||
.one(self)
|
.one(self)
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
|
||||||
.map(|u| u.is_some())
|
.map(|u| u.is_some())
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[logcall]
|
#[logcall]
|
||||||
async fn register_user(&self, public_key: &PublicKey, is_admin: bool) -> ApiResult<()> {
|
async fn register_user(&self, public_key: &PublicKey, is_admin: bool) -> ServerResult<()> {
|
||||||
if let Err(err) = (UserActiveModel {
|
if let Err(err) = (UserActiveModel {
|
||||||
public_key: Set(public_key.to_string()),
|
public_key: Set(public_key.to_string()),
|
||||||
is_admin: Set(is_admin),
|
is_admin: Set(is_admin),
|
||||||
|
@ -53,7 +53,7 @@ impl UserTableExt for DatabaseConnection {
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
if let Some(SqlErr::UniqueConstraintViolation(_)) = err.sql_err() {
|
if let Some(SqlErr::UniqueConstraintViolation(_)) = err.sql_err() {
|
||||||
return Err(ApiError::AlreadyRegistered);
|
return Err(ApiError::AlreadyRegistered.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,11 +61,11 @@ impl UserTableExt for DatabaseConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[logcall]
|
#[logcall]
|
||||||
async fn get_user_by_pubk(&self, public_key: &PublicKey) -> ApiResult<Option<UserModel>> {
|
async fn get_user_by_pubk(&self, public_key: &PublicKey) -> ServerResult<Option<UserModel>> {
|
||||||
UserEntity::find()
|
UserEntity::find()
|
||||||
.filter(UserColumn::PublicKey.eq(public_key.to_string()))
|
.filter(UserColumn::PublicKey.eq(public_key.to_string()))
|
||||||
.one(self)
|
.one(self)
|
||||||
.await
|
.await
|
||||||
.map_err(ApiError::SeaOrm)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,7 @@ use oxidetalis_core::types::PublicKey;
|
||||||
use oxidetalis_entities::prelude::*;
|
use oxidetalis_entities::prelude::*;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
use crate::{
|
use crate::{errors::ServerResult, websocket::errors::WsError};
|
||||||
errors::ApiResult,
|
|
||||||
websocket::errors::{WsError, WsResult},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Extension trait for the `DatabaseConnection` to work with the whitelist
|
/// Extension trait for the `DatabaseConnection` to work with the whitelist
|
||||||
/// table
|
/// table
|
||||||
|
@ -35,14 +32,14 @@ pub trait WhiteListExt {
|
||||||
&self,
|
&self,
|
||||||
awiteb marked this conversation as resolved
Amjad50
commented
ture ture
The whitelister has whitelisted
|
|||||||
whitelister: &UserModel,
|
whitelister: &UserModel,
|
||||||
target_public_key: &PublicKey,
|
target_public_key: &PublicKey,
|
||||||
) -> ApiResult<bool>;
|
) -> ServerResult<bool>;
|
||||||
|
|
||||||
/// Add the `target_public_key` to the whitelist of the `whitelister`
|
/// Add the `target_public_key` to the whitelist of the `whitelister`
|
||||||
async fn add_to_whitelist(
|
async fn add_to_whitelist(
|
||||||
&self,
|
&self,
|
||||||
whitelister: &UserModel,
|
whitelister: &UserModel,
|
||||||
target_public_key: &PublicKey,
|
target_public_key: &PublicKey,
|
||||||
) -> WsResult<()>;
|
) -> ServerResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WhiteListExt for DatabaseConnection {
|
impl WhiteListExt for DatabaseConnection {
|
||||||
|
@ -50,7 +47,7 @@ impl WhiteListExt for DatabaseConnection {
|
||||||
&self,
|
&self,
|
||||||
whitelister: &UserModel,
|
whitelister: &UserModel,
|
||||||
target_public_key: &PublicKey,
|
target_public_key: &PublicKey,
|
||||||
) -> ApiResult<bool> {
|
) -> ServerResult<bool> {
|
||||||
whitelister
|
whitelister
|
||||||
.find_related(WhitelistEntity)
|
.find_related(WhitelistEntity)
|
||||||
.filter(WhitelistColumn::Target.eq(target_public_key.to_string()))
|
.filter(WhitelistColumn::Target.eq(target_public_key.to_string()))
|
||||||
|
@ -64,16 +61,12 @@ impl WhiteListExt for DatabaseConnection {
|
||||||
&self,
|
&self,
|
||||||
whitelister: &UserModel,
|
whitelister: &UserModel,
|
||||||
target_public_key: &PublicKey,
|
target_public_key: &PublicKey,
|
||||||
) -> WsResult<()> {
|
) -> ServerResult<()> {
|
||||||
if self
|
if self.is_whitelisted(whitelister, target_public_key).await? {
|
||||||
.is_whitelisted(whitelister, target_public_key)
|
return Err(WsError::AlreadyOnTheWhitelist.into());
|
||||||
.await
|
|
||||||
.map_err(|_| WsError::InternalServerError)?
|
|
||||||
{
|
|
||||||
return Err(WsError::AlreadyOnTheWhitelist);
|
|
||||||
}
|
}
|
||||||
if whitelister.public_key == target_public_key.to_string() {
|
if whitelister.public_key == target_public_key.to_string() {
|
||||||
return Err(WsError::CannotAddSelfToWhitelist);
|
return Err(WsError::CannotAddSelfToWhitelist.into());
|
||||||
}
|
}
|
||||||
WhitelistActiveModel {
|
WhitelistActiveModel {
|
||||||
user_id: Set(whitelister.id),
|
user_id: Set(whitelister.id),
|
||||||
|
@ -82,8 +75,7 @@ impl WhiteListExt for DatabaseConnection {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.save(self)
|
.save(self)
|
||||||
.await
|
.await?;
|
||||||
.map_err(|_| WsError::InternalServerError)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,24 +14,16 @@
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://gnu.org/licenses/agpl-3.0>.
|
// along with this program. If not, see <https://gnu.org/licenses/agpl-3.0>.
|
||||||
|
|
||||||
use salvo::{
|
use sea_orm::DbErr;
|
||||||
http::StatusCode,
|
|
||||||
oapi::{Components as OapiComponents, EndpointOutRegister, Operation as OapiOperation},
|
|
||||||
Response,
|
|
||||||
Scribe,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{routes::write_json_body, schemas::MessageSchema};
|
use crate::{routes::ApiError, websocket::errors::WsError};
|
||||||
|
|
||||||
/// Result type of the homeserver
|
/// Result type of the homeserver
|
||||||
#[allow(clippy::absolute_paths)]
|
pub(crate) type ServerResult<T> = Result<T, ServerError>;
|
||||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
|
||||||
#[allow(clippy::absolute_paths)]
|
|
||||||
pub type ApiResult<T> = std::result::Result<T, ApiError>;
|
|
||||||
|
|
||||||
/// The homeserver errors
|
/// The homeserver errors
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub(crate) enum Error {
|
pub enum InternalError {
|
||||||
#[error("Database Error: {0}")]
|
#[error("Database Error: {0}")]
|
||||||
Database(#[from] sea_orm::DbErr),
|
Database(#[from] sea_orm::DbErr),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
|
@ -39,43 +31,39 @@ pub(crate) enum Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum ApiError {
|
/// The homeserver errors
|
||||||
/// Error from the database (500 Internal Server Error)
|
pub enum ServerError {
|
||||||
#[error("Internal server error")]
|
/// Internal server error, should not be exposed to the user
|
||||||
SeaOrm(#[from] sea_orm::DbErr),
|
#[error("{0}")]
|
||||||
/// The server registration is closed (403 Forbidden)
|
Internal(#[from] InternalError),
|
||||||
#[error("Server registration is closed")]
|
/// API error, errors happening in the API
|
||||||
RegistrationClosed,
|
#[error("{0}")]
|
||||||
/// The entered public key is already registered (400 Bad Request)
|
Api(#[from] ApiError),
|
||||||
#[error("The entered public key is already registered")]
|
/// WebSocket error, errors happening in the WebSocket
|
||||||
AlreadyRegistered,
|
#[error("{0}")]
|
||||||
/// The user entered two different public keys
|
Ws(#[from] WsError),
|
||||||
/// one in the header and other in the request body
|
|
||||||
/// (400 Bad Request)
|
|
||||||
#[error("You entered two different public keys")]
|
|
||||||
TwoDifferentKeys,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApiError {
|
impl From<DbErr> for ServerError {
|
||||||
/// Status code of the error
|
fn from(err: DbErr) -> Self {
|
||||||
pub const fn status_code(&self) -> StatusCode {
|
Self::Internal(err.into())
|
||||||
match self {
|
}
|
||||||
Self::SeaOrm(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
}
|
||||||
Self::RegistrationClosed => StatusCode::FORBIDDEN,
|
|
||||||
Self::AlreadyRegistered | Self::TwoDifferentKeys => StatusCode::BAD_REQUEST,
|
impl From<ServerError> for WsError {
|
||||||
|
fn from(err: ServerError) -> Self {
|
||||||
|
match err {
|
||||||
|
ServerError::Internal(_) | ServerError::Api(_) => WsError::InternalServerError,
|
||||||
|
ServerError::Ws(err) => err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EndpointOutRegister for ApiError {
|
impl From<ServerError> for ApiError {
|
||||||
fn register(_: &mut OapiComponents, _: &mut OapiOperation) {}
|
fn from(err: ServerError) -> Self {
|
||||||
}
|
match err {
|
||||||
|
ServerError::Internal(_) | ServerError::Ws(_) => ApiError::Internal,
|
||||||
impl Scribe for ApiError {
|
ServerError::Api(err) => err,
|
||||||
fn render(self, res: &mut Response) {
|
}
|
||||||
log::error!("Error: {self}");
|
|
||||||
|
|
||||||
res.status_code(self.status_code());
|
|
||||||
write_json_body(res, MessageSchema::new(self.to_string()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,9 +39,9 @@ macro_rules! try_ws {
|
||||||
match $result_expr {
|
match $result_expr {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("Error in try_ws macro: {err:?}");
|
log::error!("{err}");
|
||||||
return $crate::websocket::ServerEvent::<$crate::websocket::Unsigned>::from(
|
return $crate::websocket::ServerEvent::<$crate::websocket::Unsigned>::from(
|
||||||
$crate::websocket::errors::WsError::InternalServerError,
|
$crate::websocket::errors::WsError::from(err),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,11 +60,12 @@ macro_rules! try_ws {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! ws_errors {
|
macro_rules! ws_errors {
|
||||||
($($name:ident = $reason:tt),+ $(,)?) => {
|
($($name:ident = $reason:tt),+ $(,)?) => {
|
||||||
#[derive(Debug)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[doc = "Websocket errors, returned in the websocket communication"]
|
#[doc = "Websocket errors, returned in the websocket communication"]
|
||||||
pub enum WsError {
|
pub enum WsError {
|
||||||
$(
|
$(
|
||||||
#[doc = $reason]
|
#[doc = $reason]
|
||||||
|
#[error($reason)]
|
||||||
$name
|
$name
|
||||||
),+
|
),+
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
|
||||||
|
use errors::ServerError;
|
||||||
use oxidetalis_config::{CliArgs, Parser};
|
use oxidetalis_config::{CliArgs, Parser};
|
||||||
use oxidetalis_migrations::MigratorTrait;
|
use oxidetalis_migrations::MigratorTrait;
|
||||||
use salvo::{conn::TcpListener, Listener, Server};
|
use salvo::{conn::TcpListener, Listener, Server};
|
||||||
|
@ -34,11 +35,12 @@ mod schemas;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod websocket;
|
mod websocket;
|
||||||
|
|
||||||
async fn try_main() -> errors::Result<()> {
|
async fn try_main() -> errors::ServerResult<()> {
|
||||||
pretty_env_logger::init_timed();
|
pretty_env_logger::init_timed();
|
||||||
|
|
||||||
log::info!("Parsing configuration");
|
log::info!("Parsing configuration");
|
||||||
let config = oxidetalis_config::Config::load(CliArgs::parse())?;
|
let config = oxidetalis_config::Config::load(CliArgs::parse())
|
||||||
|
.map_err(|err| ServerError::Internal(err.into()))?;
|
||||||
log::info!("Configuration parsed successfully");
|
log::info!("Configuration parsed successfully");
|
||||||
log::info!("Connecting to the database");
|
log::info!("Connecting to the database");
|
||||||
let connection = sea_orm::Database::connect(utils::postgres_url(&config.postgresdb)).await?;
|
let connection = sea_orm::Database::connect(utils::postgres_url(&config.postgresdb)).await?;
|
||||||
|
|
68
crates/oxidetalis/src/routes/errors.rs
Normal file
68
crates/oxidetalis/src/routes/errors.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// OxideTalis Messaging Protocol homeserver implementation
|
||||||
|
// Copyright (C) 2024 OxideTalis Developers <otmp@4rs.nl>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://gnu.org/licenses/agpl-3.0>.
|
||||||
|
|
||||||
|
use salvo::{
|
||||||
|
http::StatusCode,
|
||||||
|
oapi::{Components as OapiComponents, EndpointOutRegister, Operation as OapiOperation},
|
||||||
|
Response,
|
||||||
|
Scribe,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{routes::write_json_body, schemas::MessageSchema};
|
||||||
|
|
||||||
|
/// Result type of the API
|
||||||
|
pub type ApiResult<T> = Result<T, ApiError>;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ApiError {
|
||||||
|
#[error("Internal server error")]
|
||||||
|
Internal,
|
||||||
|
/// The server registration is closed (403 Forbidden)
|
||||||
|
#[error("Server registration is closed")]
|
||||||
|
RegistrationClosed,
|
||||||
|
/// The entered public key is already registered (400 Bad Request)
|
||||||
|
#[error("The entered public key is already registered")]
|
||||||
|
AlreadyRegistered,
|
||||||
|
/// The user entered two different public keys
|
||||||
|
/// one in the header and other in the request body
|
||||||
|
/// (400 Bad Request)
|
||||||
|
#[error("You entered two different public keys")]
|
||||||
|
TwoDifferentKeys,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiError {
|
||||||
|
/// Status code of the error
|
||||||
|
pub const fn status_code(&self) -> StatusCode {
|
||||||
|
match self {
|
||||||
|
Self::Internal => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
Self::RegistrationClosed => StatusCode::FORBIDDEN,
|
||||||
|
Self::AlreadyRegistered | Self::TwoDifferentKeys => StatusCode::BAD_REQUEST,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EndpointOutRegister for ApiError {
|
||||||
|
fn register(_: &mut OapiComponents, _: &mut OapiOperation) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scribe for ApiError {
|
||||||
|
fn render(self, res: &mut Response) {
|
||||||
|
log::error!("Error: {self}");
|
||||||
|
|
||||||
|
res.status_code(self.status_code());
|
||||||
|
write_json_body(res, MessageSchema::new(self.to_string()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,8 +27,11 @@ use crate::nonce::NonceCache;
|
||||||
use crate::schemas::MessageSchema;
|
use crate::schemas::MessageSchema;
|
||||||
use crate::{middlewares, websocket};
|
use crate::{middlewares, websocket};
|
||||||
|
|
||||||
|
mod errors;
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
|
pub use errors::*;
|
||||||
|
|
||||||
pub fn write_json_body(res: &mut Response, json_body: impl serde::Serialize) {
|
pub fn write_json_body(res: &mut Response, json_body: impl serde::Serialize) {
|
||||||
res.write_body(serde_json::to_string(&json_body).expect("Json serialization can't be fail"))
|
res.write_body(serde_json::to_string(&json_body).expect("Json serialization can't be fail"))
|
||||||
.ok();
|
.ok();
|
||||||
|
|
|
@ -26,9 +26,9 @@ use salvo::{
|
||||||
Writer,
|
Writer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::{ApiError, ApiResult};
|
||||||
use crate::{
|
use crate::{
|
||||||
database::UserTableExt,
|
database::UserTableExt,
|
||||||
errors::{ApiError, ApiResult},
|
|
||||||
extensions::DepotExt,
|
extensions::DepotExt,
|
||||||
middlewares,
|
middlewares,
|
||||||
schemas::{EmptySchema, MessageSchema, RegisterUserBody},
|
schemas::{EmptySchema, MessageSchema, RegisterUserBody},
|
||||||
|
|
Loading…
Reference in a new issue
ture
->true
(use find and replace all, there are multiple of these)blacklister has blacklisted