feat(api): expand API endpoints for repo, PR, user, workspace management
- Add git operation endpoints: archive, compare branches, diff, tree, repository extras - Add repo endpoints: contributors, delete fork, get branch/commit status/deploy key/invitation/member/release/tag/webhook, topics, release assets, webhook deliveries/retry - Add PR endpoints: review requests, templates - Add user endpoints: block/unblock, follow/unfollow, presence, personal access tokens, account restore - Add workspace endpoints: billing history, approvals, domains, integrations, invitations, members, webhooks, restore - Add internal API, notification API, IM API modules - Update route configuration and OpenAPI spec
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::channels::ChannelCategory;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::service::im::categories::CreateCategoryParams;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/categories",
|
||||
tag = "IM",
|
||||
operation_id = "imCategoryCreate",
|
||||
params(PathParams),
|
||||
request_body(
|
||||
content = CreateCategoryParams,
|
||||
description = "Category creation parameters",
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 201, description = "Category created successfully", body = ApiResponse<ChannelCategory>),
|
||||
(status = 400, description = "Invalid parameters", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn category_create(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
params: web::Json<CreateCategoryParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let result = service
|
||||
.im
|
||||
.category_create(&im_session, &path.workspace_name, params.into_inner())
|
||||
.await?;
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(result)))
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiEmptyResponse, ApiErrorResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub category_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Delete a category
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/categories/{category_id}",
|
||||
tag = "IM",
|
||||
operation_id = "imCategoryDelete",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Category deleted successfully", body = ApiEmptyResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or category not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn category_delete(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
service
|
||||
.im
|
||||
.category_delete(&im_session, &path.workspace_name, path.category_id)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiEmptyResponse::ok("Category deleted")))
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::channels::ChannelCategory;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
}
|
||||
|
||||
/// List categories
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/categories",
|
||||
tag = "IM",
|
||||
operation_id = "imCategoryList",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Categories listed successfully", body = ApiResponse<Vec<ChannelCategory>>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn category_list(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let result = service
|
||||
.im
|
||||
.category_list(&im_session, &path.workspace_name)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::channels::ChannelCategory;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::service::im::categories::UpdateCategoryParams;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub category_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Update a category
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/categories/{category_id}",
|
||||
tag = "IM",
|
||||
operation_id = "imCategoryUpdate",
|
||||
params(PathParams),
|
||||
request_body(
|
||||
content = UpdateCategoryParams,
|
||||
description = "Category update parameters",
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Category updated successfully", body = ApiResponse<ChannelCategory>),
|
||||
(status = 400, description = "Invalid parameters", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or category not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn category_update(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
params: web::Json<UpdateCategoryParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let result = service
|
||||
.im
|
||||
.category_update(
|
||||
&im_session,
|
||||
&path.workspace_name,
|
||||
path.category_id,
|
||||
params.into_inner(),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::base_info::resolve_users;
|
||||
use crate::models::channels::ChannelDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::service::im::channels::CreateChannelParams;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
}
|
||||
|
||||
/// Create a channel
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels",
|
||||
tag = "IM",
|
||||
operation_id = "imChannelCreate",
|
||||
params(PathParams),
|
||||
request_body(
|
||||
content = CreateChannelParams,
|
||||
description = "Channel creation parameters",
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 201, description = "Channel created successfully", body = ApiResponse<ChannelDetail>),
|
||||
(status = 400, description = "Invalid parameters", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn channel_create(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
params: web::Json<CreateChannelParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let request_id = uuid::Uuid::now_v7();
|
||||
let channel = service
|
||||
.im
|
||||
.channel_create(
|
||||
&im_session,
|
||||
&path.workspace_name,
|
||||
params.into_inner(),
|
||||
request_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let db = &service.ctx.db;
|
||||
let users = resolve_users(db, &[channel.created_by]).await?;
|
||||
let creator = users.get(&channel.created_by).cloned().unwrap_or_default();
|
||||
let detail = channel.into_detail(creator);
|
||||
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(detail)))
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiEmptyResponse, ApiErrorResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub channel_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Delete a channel
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels/{channel_id}",
|
||||
tag = "IM",
|
||||
operation_id = "imChannelDelete",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Channel deleted successfully", body = ApiEmptyResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or channel not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn channel_delete(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let request_id = uuid::Uuid::now_v7();
|
||||
service
|
||||
.im
|
||||
.channel_delete(
|
||||
&im_session,
|
||||
&path.workspace_name,
|
||||
path.channel_id,
|
||||
request_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiEmptyResponse::ok("Channel deleted")))
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::base_info::resolve_users;
|
||||
use crate::models::channels::ChannelDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub channel_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Get a channel
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels/{channel_id}",
|
||||
tag = "IM",
|
||||
operation_id = "imChannelGet",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Channel retrieved successfully", body = ApiResponse<ChannelDetail>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or channel not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn channel_get(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let channel = service
|
||||
.im
|
||||
.channel_get(&im_session, &path.workspace_name, path.channel_id)
|
||||
.await?;
|
||||
|
||||
let db = &service.ctx.db;
|
||||
let users = resolve_users(db, &[channel.created_by]).await?;
|
||||
let creator = users.get(&channel.created_by).cloned().unwrap_or_default();
|
||||
let detail = channel.into_detail(creator);
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(detail)))
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::base_info::resolve_users;
|
||||
use crate::models::channels::ChannelDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::service::im::channels::ChannelListFilters;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct QueryParams {
|
||||
pub channel_type: Option<String>,
|
||||
pub channel_kind: Option<String>,
|
||||
pub category_id: Option<uuid::Uuid>,
|
||||
pub archived: Option<bool>,
|
||||
pub limit: Option<i64>,
|
||||
pub offset: Option<i64>,
|
||||
}
|
||||
|
||||
/// List channels
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels",
|
||||
tag = "IM",
|
||||
operation_id = "imChannelList",
|
||||
params(PathParams, QueryParams),
|
||||
responses(
|
||||
(status = 200, description = "Channels listed successfully", body = ApiResponse<Vec<ChannelDetail>>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn channel_list(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
query: web::Query<QueryParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let filters = ChannelListFilters {
|
||||
channel_type: query.channel_type.clone(),
|
||||
channel_kind: query.channel_kind.clone(),
|
||||
category_id: query.category_id,
|
||||
archived: query.archived,
|
||||
};
|
||||
let result = service
|
||||
.im
|
||||
.channel_list(
|
||||
&im_session,
|
||||
&path.workspace_name,
|
||||
filters,
|
||||
query.limit.unwrap_or(50),
|
||||
query.offset.unwrap_or(0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let db = &service.ctx.db;
|
||||
let creator_ids: Vec<Uuid> = result.iter().map(|c| c.created_by).collect();
|
||||
let users = resolve_users(db, &creator_ids).await?;
|
||||
let details: Vec<ChannelDetail> = result
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
let creator = users.get(&c.created_by).cloned().unwrap_or_default();
|
||||
c.into_detail(creator)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(details)))
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::base_info::resolve_users;
|
||||
use crate::models::channels::ChannelDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::service::im::channels::UpdateChannelParams;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub channel_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Update a channel
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels/{channel_id}",
|
||||
tag = "IM",
|
||||
operation_id = "imChannelUpdate",
|
||||
params(PathParams),
|
||||
request_body(
|
||||
content = UpdateChannelParams,
|
||||
description = "Channel update parameters",
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Channel updated successfully", body = ApiResponse<ChannelDetail>),
|
||||
(status = 400, description = "Invalid parameters", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or channel not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn channel_update(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
params: web::Json<UpdateChannelParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let request_id = uuid::Uuid::now_v7();
|
||||
let channel = service
|
||||
.im
|
||||
.channel_update(
|
||||
&im_session,
|
||||
&path.workspace_name,
|
||||
path.channel_id,
|
||||
params.into_inner(),
|
||||
request_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let db = &service.ctx.db;
|
||||
let users = resolve_users(db, &[channel.created_by]).await?;
|
||||
let creator = users.get(&channel.created_by).cloned().unwrap_or_default();
|
||||
let detail = channel.into_detail(creator);
|
||||
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(detail)))
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::channels::ChannelMember;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::service::im::members::InviteMemberParams;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub channel_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Invite a member
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels/{channel_id}/members",
|
||||
tag = "IM",
|
||||
operation_id = "imMemberInvite",
|
||||
params(PathParams),
|
||||
request_body(
|
||||
content = InviteMemberParams,
|
||||
description = "Invitation parameters",
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 201, description = "Member invited successfully", body = ApiResponse<ChannelMember>),
|
||||
(status = 400, description = "Invalid parameters", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or channel not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn member_invite(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
params: web::Json<InviteMemberParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let result = service
|
||||
.im
|
||||
.member_invite(
|
||||
&im_session,
|
||||
&path.workspace_name,
|
||||
path.channel_id,
|
||||
params.into_inner(),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(result)))
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::channels::ChannelMember;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub channel_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Join a channel
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels/{channel_id}/join",
|
||||
tag = "IM",
|
||||
operation_id = "imMemberJoin",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Joined channel successfully", body = ApiResponse<ChannelMember>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or channel not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn member_join(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let result = service
|
||||
.im
|
||||
.member_join(&im_session, &path.workspace_name, path.channel_id)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiEmptyResponse, ApiErrorResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub channel_id: uuid::Uuid,
|
||||
pub user_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Kick a member
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels/{channel_id}/members/{user_id}",
|
||||
tag = "IM",
|
||||
operation_id = "imMemberKick",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Member kicked successfully", body = ApiEmptyResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace, channel or member not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn member_kick(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
service
|
||||
.im
|
||||
.member_kick(
|
||||
&im_session,
|
||||
&path.workspace_name,
|
||||
path.channel_id,
|
||||
path.user_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiEmptyResponse::ok("Member kicked")))
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiEmptyResponse, ApiErrorResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub channel_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Leave a channel
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels/{channel_id}/leave",
|
||||
tag = "IM",
|
||||
operation_id = "imMemberLeave",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Left channel successfully", body = ApiEmptyResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or channel not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn member_leave(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
service
|
||||
.im
|
||||
.member_leave(&im_session, &path.workspace_name, path.channel_id)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiEmptyResponse::ok("Left channel")))
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::channels::ChannelMember;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub channel_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct QueryParams {
|
||||
pub limit: Option<i64>,
|
||||
pub offset: Option<i64>,
|
||||
}
|
||||
|
||||
/// List channel members
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels/{channel_id}/members",
|
||||
tag = "IM",
|
||||
operation_id = "imMemberList",
|
||||
params(PathParams, QueryParams),
|
||||
responses(
|
||||
(status = 200, description = "Members listed successfully", body = ApiResponse<Vec<ChannelMember>>),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or channel not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn member_list(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
query: web::Query<QueryParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let result = service
|
||||
.im
|
||||
.member_list(
|
||||
&im_session,
|
||||
&path.workspace_name,
|
||||
path.channel_id,
|
||||
query.limit.unwrap_or(50),
|
||||
query.offset.unwrap_or(0),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use actix_web::{HttpResponse, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::channels::ChannelMember;
|
||||
use crate::service::AppService;
|
||||
use crate::service::im::ImSession;
|
||||
use crate::service::im::members::UpdateMemberParams;
|
||||
use crate::session::Session;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct PathParams {
|
||||
pub workspace_name: String,
|
||||
pub channel_id: uuid::Uuid,
|
||||
pub user_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
/// Update member role
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/api/v1/im/workspaces/{workspace_name}/channels/{channel_id}/members/{user_id}",
|
||||
tag = "IM",
|
||||
operation_id = "imMemberUpdate",
|
||||
params(PathParams),
|
||||
request_body(
|
||||
content = UpdateMemberParams,
|
||||
description = "Member update parameters",
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Member updated successfully", body = ApiResponse<ChannelMember>),
|
||||
(status = 400, description = "Invalid parameters", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace, channel or member not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
),
|
||||
security(("session_cookie" = []))
|
||||
)]
|
||||
pub async fn member_update(
|
||||
service: web::Data<AppService>,
|
||||
session: Session,
|
||||
path: web::Path<PathParams>,
|
||||
params: web::Json<UpdateMemberParams>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let user_id = session.user().ok_or(AppError::Unauthorized)?;
|
||||
let im_session = ImSession::new(user_id);
|
||||
let result = service
|
||||
.im
|
||||
.member_update(
|
||||
&im_session,
|
||||
&path.workspace_name,
|
||||
path.channel_id,
|
||||
path.user_id,
|
||||
params.into_inner(),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(result)))
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
pub mod category_create;
|
||||
pub mod category_delete;
|
||||
pub mod category_list;
|
||||
pub mod category_update;
|
||||
pub mod channel_create;
|
||||
pub mod channel_delete;
|
||||
pub mod channel_get;
|
||||
pub mod channel_list;
|
||||
pub mod channel_update;
|
||||
pub mod member_invite;
|
||||
pub mod member_join;
|
||||
pub mod member_kick;
|
||||
pub mod member_leave;
|
||||
pub mod member_list;
|
||||
pub mod member_update;
|
||||
|
||||
use actix_web::web;
|
||||
|
||||
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("/im/workspaces/{workspace_name}")
|
||||
// Channels
|
||||
.route("/channels", web::get().to(channel_list::channel_list))
|
||||
.route("/channels", web::post().to(channel_create::channel_create))
|
||||
.route(
|
||||
"/channels/{channel_id}",
|
||||
web::get().to(channel_get::channel_get),
|
||||
)
|
||||
.route(
|
||||
"/channels/{channel_id}",
|
||||
web::put().to(channel_update::channel_update),
|
||||
)
|
||||
.route(
|
||||
"/channels/{channel_id}",
|
||||
web::delete().to(channel_delete::channel_delete),
|
||||
)
|
||||
// Members
|
||||
.route(
|
||||
"/channels/{channel_id}/members",
|
||||
web::get().to(member_list::member_list),
|
||||
)
|
||||
.route(
|
||||
"/channels/{channel_id}/members",
|
||||
web::post().to(member_invite::member_invite),
|
||||
)
|
||||
.route(
|
||||
"/channels/{channel_id}/members/{user_id}",
|
||||
web::put().to(member_update::member_update),
|
||||
)
|
||||
.route(
|
||||
"/channels/{channel_id}/members/{user_id}",
|
||||
web::delete().to(member_kick::member_kick),
|
||||
)
|
||||
.route(
|
||||
"/channels/{channel_id}/join",
|
||||
web::post().to(member_join::member_join),
|
||||
)
|
||||
.route(
|
||||
"/channels/{channel_id}/leave",
|
||||
web::post().to(member_leave::member_leave),
|
||||
)
|
||||
// Categories
|
||||
.route("/categories", web::get().to(category_list::category_list))
|
||||
.route(
|
||||
"/categories",
|
||||
web::post().to(category_create::category_create),
|
||||
)
|
||||
.route(
|
||||
"/categories/{category_id}",
|
||||
web::put().to(category_update::category_update),
|
||||
)
|
||||
.route(
|
||||
"/categories/{category_id}",
|
||||
web::delete().to(category_delete::category_delete),
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user