diff --git a/ceres/src/lfs/lfs_structs.rs b/ceres/src/lfs/lfs_structs.rs index b2d670037..d9e3cc467 100644 --- a/ceres/src/lfs/lfs_structs.rs +++ b/ceres/src/lfs/lfs_structs.rs @@ -75,7 +75,7 @@ impl MetaObject { oid: req_obj.oid.to_string(), size: req_obj.size, exist: true, - splited: if config.storage_type == StorageTypeEnum::AwsS3 { + splited: if StorageTypeEnum::AwsS3 == config.storage_type.clone().into() { false } else { splited diff --git a/common/src/config.rs b/common/src/config.rs index cad025ea9..c985f1e98 100644 --- a/common/src/config.rs +++ b/common/src/config.rs @@ -485,16 +485,44 @@ impl PackConfig { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct LFSConfig { - pub storage_type: StorageTypeEnum, + pub storage_type: StorageType, pub local: LFSLocalConfig, pub aws: LFSAwsConfig, pub ssh: LFSSshConfig, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum StorageType { + Database, + LocalFs, + AwsS3, +} + +impl From for StorageType { + fn from(value: StorageTypeEnum) -> Self { + match value { + StorageTypeEnum::Database => StorageType::Database, + StorageTypeEnum::LocalFs => StorageType::LocalFs, + StorageTypeEnum::AwsS3 => StorageType::AwsS3, + } + } +} + +impl From for StorageTypeEnum { + fn from(value: StorageType) -> Self { + match value { + StorageType::Database => StorageTypeEnum::Database, + StorageType::LocalFs => StorageTypeEnum::LocalFs, + StorageType::AwsS3 => StorageTypeEnum::AwsS3, + } + } +} + impl Default for LFSConfig { fn default() -> Self { Self { - storage_type: StorageTypeEnum::LocalFs, + storage_type: StorageType::LocalFs, local: LFSLocalConfig::default(), aws: LFSAwsConfig::default(), ssh: LFSSshConfig::default(), diff --git a/jupiter/callisto/src/sea_orm_active_enums.rs b/jupiter/callisto/src/sea_orm_active_enums.rs index 7bd000b40..cc8b9505f 100644 --- a/jupiter/callisto/src/sea_orm_active_enums.rs +++ b/jupiter/callisto/src/sea_orm_active_enums.rs @@ -2,9 +2,8 @@ use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; -use utoipa::ToSchema; -#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize, ToSchema)] +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)] #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "conv_type_enum")] pub enum ConvTypeEnum { #[sea_orm(string_value = "comment")] @@ -34,7 +33,7 @@ pub enum ConvTypeEnum { #[sea_orm(string_value = "assignee")] Assignee, } -#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize, ToSchema)] +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)] #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "merge_status_enum")] pub enum MergeStatusEnum { #[sea_orm(string_value = "open")] @@ -53,7 +52,6 @@ pub enum RefTypeEnum { Tag, } #[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "storage_type_enum")] pub enum StorageTypeEnum { #[sea_orm(string_value = "database")] diff --git a/jupiter/src/lfs_storage/mod.rs b/jupiter/src/lfs_storage/mod.rs index 53b32d67e..1d6b4c44a 100644 --- a/jupiter/src/lfs_storage/mod.rs +++ b/jupiter/src/lfs_storage/mod.rs @@ -7,9 +7,8 @@ use async_trait::async_trait; use aws_s3_storage::AwsS3Storage; use bytes::Bytes; -use callisto::sea_orm_active_enums::StorageTypeEnum; use common::{ - config::LFSConfig, + config::{LFSConfig, StorageType}, errors::{GitLFSError, MegaError}, }; use sea_orm::DatabaseConnection; @@ -66,8 +65,8 @@ pub async fn init( connection: Arc, ) -> Arc { match lfs_config.storage_type { - StorageTypeEnum::LocalFs => Arc::new(LocalStorage::init(lfs_config.local, connection)), - StorageTypeEnum::AwsS3 => Arc::new(AwsS3Storage::init(lfs_config.aws).await), + StorageType::LocalFs => Arc::new(LocalStorage::init(lfs_config.local, connection)), + StorageType::AwsS3 => Arc::new(AwsS3Storage::init(lfs_config.aws).await), _ => unreachable!("Not supported value of config `storage_type`, support value can be 'local_fs' or 'aws_s3'"), } } diff --git a/mono/src/api/conversation/mod.rs b/mono/src/api/conversation/mod.rs index bad13e66b..19438d8b3 100644 --- a/mono/src/api/conversation/mod.rs +++ b/mono/src/api/conversation/mod.rs @@ -11,7 +11,7 @@ pub mod conv_router; pub struct ConversationItem { pub id: i64, pub username: String, - pub conv_type: ConvTypeEnum, + pub conv_type: ConvType, pub comment: Option, pub created_at: i64, pub updated_at: i64, @@ -27,7 +27,7 @@ impl ConversationItem { let mut item = Self { id: conversation.id, username: conversation.username, - conv_type: conversation.conv_type, + conv_type: conversation.conv_type.into(), comment: conversation.comment, created_at: conversation.created_at.and_utc().timestamp(), updated_at: conversation.updated_at.and_utc().timestamp(), @@ -82,3 +82,40 @@ pub struct ReactionRequest { pub content: String, pub comment_type: String, } + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +pub enum ConvType { + Comment, + Deploy, + Commit, + ForcePush, + Edit, + Review, + Approve, + MergeQueue, + Merged, + Closed, + Reopen, + Label, + Assignee, +} + +impl From for ConvType { + fn from(value: ConvTypeEnum) -> Self { + match value { + ConvTypeEnum::Comment => ConvType::Comment, + ConvTypeEnum::Deploy => ConvType::Deploy, + ConvTypeEnum::Commit => ConvType::Commit, + ConvTypeEnum::ForcePush => ConvType::ForcePush, + ConvTypeEnum::Edit => ConvType::Edit, + ConvTypeEnum::Review => ConvType::Review, + ConvTypeEnum::Approve => ConvType::Approve, + ConvTypeEnum::MergeQueue => ConvType::MergeQueue, + ConvTypeEnum::Merged => ConvType::Merged, + ConvTypeEnum::Closed => ConvType::Closed, + ConvTypeEnum::Reopen => ConvType::Reopen, + ConvTypeEnum::Label => ConvType::Label, + ConvTypeEnum::Assignee => ConvType::Assignee, + } + } +} diff --git a/mono/src/api/mr/mod.rs b/mono/src/api/mr/mod.rs index 352293c31..c351cee7e 100644 --- a/mono/src/api/mr/mod.rs +++ b/mono/src/api/mr/mod.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use ceres::model::mr::MrDiffFile; use jupiter::model::mr_dto::MRDetails; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use uuid::Uuid; @@ -17,7 +17,7 @@ pub struct MRDetailRes { pub id: i64, pub link: String, pub title: String, - pub status: MergeStatusEnum, + pub status: MergeStatus, pub open_timestamp: i64, pub merge_timestamp: Option, pub conversations: Vec, @@ -31,7 +31,7 @@ impl From for MRDetailRes { id: value.mr.id, link: value.mr.link, title: value.mr.title, - status: value.mr.status, + status: value.mr.status.into(), open_timestamp: value.mr.created_at.and_utc().timestamp(), merge_timestamp: value.mr.merge_date.map(|dt| dt.and_utc().timestamp()), conversations: value @@ -122,3 +122,30 @@ impl From for MrFilesRes { } } } + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +pub enum MergeStatus { + Open, + Merged, + Closed, +} + +impl From for MergeStatus { + fn from(value: MergeStatusEnum) -> Self { + match value { + MergeStatusEnum::Open => MergeStatus::Open, + MergeStatusEnum::Merged => MergeStatus::Merged, + MergeStatusEnum::Closed => MergeStatus::Closed, + } + } +} + +impl From for MergeStatusEnum { + fn from(value: MergeStatus) -> Self { + match value { + MergeStatus::Open => MergeStatusEnum::Open, + MergeStatus::Merged => MergeStatusEnum::Merged, + MergeStatus::Closed => MergeStatusEnum::Closed, + } + } +} diff --git a/mono/src/api/user/model.rs b/mono/src/api/user/model.rs index 9c2c338f8..edee9c765 100644 --- a/mono/src/api/user/model.rs +++ b/mono/src/api/user/model.rs @@ -1,20 +1,20 @@ use callisto::{access_token, ssh_keys}; -use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, ToSchema)] pub struct AddSSHKey { pub title: String, pub ssh_key: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct ListSSHKey { pub id: i64, pub title: String, pub ssh_key: String, pub finger: String, - pub created_at: NaiveDateTime, + pub created_at: i64, } impl From for ListSSHKey { @@ -24,16 +24,16 @@ impl From for ListSSHKey { title: value.title, ssh_key: value.ssh_key, finger: value.finger, - created_at: value.created_at, + created_at: value.created_at.and_utc().timestamp(), } } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct ListToken { pub id: i64, pub token: String, - pub created_at: NaiveDateTime, + pub created_at: i64, } impl From for ListToken { @@ -43,7 +43,7 @@ impl From for ListToken { Self { id: value.id, token: mask_token, - created_at: value.created_at, + created_at: value.created_at.and_utc().timestamp(), } } } diff --git a/mono/src/api/user/user_router.rs b/mono/src/api/user/user_router.rs index 1782ea967..abd6d8161 100644 --- a/mono/src/api/user/user_router.rs +++ b/mono/src/api/user/user_router.rs @@ -2,31 +2,31 @@ use std::collections::HashMap; use axum::{ extract::{Path, Query, State}, - routing::{get, post}, + routing::get, Json, }; use russh::keys::{parse_public_key_base64, HashAlg}; -use utoipa_axum::router::OpenApiRouter; +use utoipa_axum::{router::OpenApiRouter, routes}; use common::model::CommonResult; -use crate::api::user::model::AddSSHKey; use crate::api::user::model::ListSSHKey; use crate::api::user::model::ListToken; use crate::api::MonoApiServiceState; use crate::api::{error::ApiError, oauth::model::LoginUser, util}; +use crate::{api::user::model::AddSSHKey, server::https_server::USER_TAG}; pub fn routers() -> OpenApiRouter { OpenApiRouter::new().nest( "/user", OpenApiRouter::new() .route("/", get(user)) - .route("/ssh", get(list_key)) - .route("/ssh", post(add_key)) - .route("/ssh/{key_id}/delete", post(remove_key)) - .route("/token/generate", post(generate_token)) - .route("/token/list", get(list_token)) - .route("/token/{key_id}/delete", post(remove_token)) + .routes(routes!(list_key)) + .routes(routes!(add_key)) + .routes(routes!(remove_key)) + .routes(routes!(generate_token)) + .routes(routes!(list_token)) + .routes(routes!(remove_token)) .route("/repo-permissions", get(repo_permissions)), ) } @@ -38,6 +38,16 @@ async fn user( Ok(Json(CommonResult::success(Some(user)))) } +/// Add SSH Key +#[utoipa::path( + post, + path = "/ssh", + request_body = AddSSHKey, + responses( + (status = 200, body = CommonResult, content_type = "application/json") + ), + tag = USER_TAG +)] async fn add_key( user: LoginUser, state: State, @@ -68,6 +78,18 @@ async fn add_key( Ok(Json(CommonResult::success(None))) } +/// Delete SSH Key +#[utoipa::path( + delete, + params( + ("key_id", description = "A numeric ID representing a SSH"), + ), + path = "/ssh/{key_id}", + responses( + (status = 200, body = CommonResult, content_type = "application/json") + ), + tag = USER_TAG +)] async fn remove_key( user: LoginUser, state: State, @@ -80,6 +102,15 @@ async fn remove_key( Ok(Json(CommonResult::success(None))) } +/// Get User's SSH key list +#[utoipa::path( + get, + path = "/ssh/list", + responses( + (status = 200, body = CommonResult>, content_type = "application/json") + ), + tag = USER_TAG +)] async fn list_key( user: LoginUser, state: State, @@ -93,6 +124,15 @@ async fn list_key( )))) } +/// Generate Token For http push +#[utoipa::path( + post, + path = "/token/generate", + responses( + (status = 200, body = CommonResult, content_type = "application/json") + ), + tag = USER_TAG +)] async fn generate_token( user: LoginUser, state: State, @@ -104,6 +144,18 @@ async fn generate_token( Ok(Json(CommonResult::success(Some(res)))) } +/// Delete User's http push token +#[utoipa::path( + delete, + params( + ("key_id", description = "A numeric ID representing a User Token"), + ), + path = "/token/{key_id}", + responses( + (status = 200, body = CommonResult, content_type = "application/json") + ), + tag = USER_TAG +)] async fn remove_token( user: LoginUser, state: State, @@ -116,6 +168,15 @@ async fn remove_token( Ok(Json(CommonResult::success(None))) } +/// Get User's push token list +#[utoipa::path( + get, + path = "/token/list", + responses( + (status = 200, body = CommonResult>, content_type = "application/json") + ), + tag = USER_TAG +)] async fn list_token( user: LoginUser, state: State, diff --git a/mono/src/server/https_server.rs b/mono/src/server/https_server.rs index 9a6d9455c..1c55be4ba 100644 --- a/mono/src/server/https_server.rs +++ b/mono/src/server/https_server.rs @@ -51,6 +51,8 @@ pub async fn start_http(ctx: AppContext, options: CommonHttpOptions) { let server_url = format!("{host}:{port}"); let addr = SocketAddr::from_str(&server_url).unwrap(); + tracing::info!("http server start up!"); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); axum::serve(listener, app.into_make_service()) .await @@ -206,6 +208,7 @@ pub const ISSUE_TAG: &str = "issue"; pub const LABEL_TAG: &str = "label"; pub const CONV_TAG: &str = "conversation"; pub const SYNC_NOTES_STATE_TAG: &str = "sync-notes-state"; +pub const USER_TAG: &str = "user"; #[derive(OpenApi)] #[openapi( tags( diff --git a/moon/packages/types/generated.ts b/moon/packages/types/generated.ts index b8a446df8..aa61d6375 100644 --- a/moon/packages/types/generated.ts +++ b/moon/packages/types/generated.ts @@ -3033,6 +3033,11 @@ export type FigmaKeyPair = { write_key: string } +export type AddSSHKey = { + ssh_key: string + title: string +} + export type AssigneeUpdatePayload = { assignees: string[] /** @format int64 */ @@ -3129,7 +3134,7 @@ export type CommonResultMRDetailRes = { merge_timestamp?: number | null /** @format int64 */ open_timestamp: number - status: MergeStatusEnum + status: MergeStatus title: string } err_message: string @@ -3163,6 +3168,32 @@ export type CommonResultVecIssueSuggestions = { req_result: boolean } +export type CommonResultVecListSSHKey = { + data?: { + /** @format int64 */ + created_at: number + finger: string + /** @format int64 */ + id: number + ssh_key: string + title: string + }[] + err_message: string + req_result: boolean +} + +export type CommonResultVecListToken = { + data?: { + /** @format int64 */ + created_at: number + /** @format int64 */ + id: number + token: string + }[] + err_message: string + req_result: boolean +} + export type CommonResultVecMrFilesRes = { data?: { action: string @@ -3205,7 +3236,7 @@ export type ContentPayload = { content: string } -export enum ConvTypeEnum { +export enum ConvType { Comment = 'Comment', Deploy = 'Deploy', Commit = 'Commit', @@ -3223,7 +3254,7 @@ export enum ConvTypeEnum { export type ConversationItem = { comment?: string | null - conv_type: ConvTypeEnum + conv_type: ConvType /** @format int64 */ created_at: number grouped_reactions: ReactionItem[] @@ -3329,6 +3360,24 @@ export type ListPayload = { status: string } +export type ListSSHKey = { + /** @format int64 */ + created_at: number + finger: string + /** @format int64 */ + id: number + ssh_key: string + title: string +} + +export type ListToken = { + /** @format int64 */ + created_at: number + /** @format int64 */ + id: number + token: string +} + export type MRDetailRes = { assignees: string[] conversations: ConversationItem[] @@ -3340,11 +3389,11 @@ export type MRDetailRes = { merge_timestamp?: number | null /** @format int64 */ open_timestamp: number - status: MergeStatusEnum + status: MergeStatus title: string } -export enum MergeStatusEnum { +export enum MergeStatus { Open = 'Open', Merged = 'Merged', Closed = 'Closed' @@ -3417,6 +3466,14 @@ export type ReactionRequest = { content: string } +export type ShowResponse = { + description_html: string + /** @format int32 */ + description_schema_version: number + description_state?: string | null + id: string +} + export type TreeBriefItem = { content_type: string name: string @@ -3442,6 +3499,13 @@ export type TreeResponse = { tree_items: TreeBriefItem[] } +export type UpdateRequest = { + description_html: string + /** @format int32 */ + description_schema_version: number + description_state: string +} + export type UserInfo = { avatar_url: string display_name: string @@ -4653,6 +4717,10 @@ export type PostApiMrReopenData = CommonResultString export type PostApiMrTitleData = CommonResultString +export type GetApiOrganizationsNotesSyncStateData = ShowResponse + +export type PatchApiOrganizationsNotesSyncStateData = any + export type GetApiStatusData = string export type GetApiTreeParams = { @@ -4689,6 +4757,18 @@ export type GetApiTreePathCanCloneParams = { export type GetApiTreePathCanCloneData = CommonResultBool +export type PostApiUserSshData = CommonResultString + +export type GetApiUserSshListData = CommonResultVecListSSHKey + +export type DeleteApiUserSshByKeyIdData = CommonResultString + +export type PostApiUserTokenGenerateData = CommonResultString + +export type GetApiUserTokenListData = CommonResultVecListToken + +export type DeleteApiUserTokenByKeyIdData = CommonResultString + export type QueryParamsType = Record export type ResponseFormat = keyof Omit @@ -13694,6 +13774,54 @@ export class Api extends HttpClient { + const base = 'GET:/api/v1/organizations/{org_slug}/notes/{id}/sync_state' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (orgSlug: number, id: string) => + dataTaggedQueryKey([base, orgSlug, id]), + request: (orgSlug: number, id: string, params: RequestParams = {}) => + this.request({ + path: `/api/v1/organizations/${orgSlug}/notes/${id}/sync_state`, + method: 'GET', + ...params + }) + } + }, + + /** + * No description + * + * @tags sync-notes-state + * @name PatchApiOrganizationsNotesSyncState + * @request PATCH:/api/v1/organizations/{org_slug}/notes/{id}/sync_state + */ + patchApiOrganizationsNotesSyncState: () => { + const base = 'PATCH:/api/v1/organizations/{org_slug}/notes/{id}/sync_state' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (orgSlug: number, id: string) => + dataTaggedQueryKey([base, orgSlug, id]), + request: (orgSlug: number, id: string, data: UpdateRequest, params: RequestParams = {}) => + this.request({ + path: `/api/v1/organizations/${orgSlug}/notes/${id}/sync_state`, + method: 'PATCH', + body: data, + type: ContentType.Json, + ...params + }) + } + }, + /** * No description * @@ -13838,6 +13966,146 @@ export class Api extends HttpClient { + const base = 'POST:/api/v1/user/ssh' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: () => dataTaggedQueryKey([base]), + request: (data: AddSSHKey, params: RequestParams = {}) => + this.request({ + path: `/api/v1/user/ssh`, + method: 'POST', + body: data, + type: ContentType.Json, + ...params + }) + } + }, + + /** + * No description + * + * @tags user + * @name GetApiUserSshList + * @summary Get User's SSH key list + * @request GET:/api/v1/user/ssh/list + */ + getApiUserSshList: () => { + const base = 'GET:/api/v1/user/ssh/list' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: () => dataTaggedQueryKey([base]), + request: (params: RequestParams = {}) => + this.request({ + path: `/api/v1/user/ssh/list`, + method: 'GET', + ...params + }) + } + }, + + /** + * No description + * + * @tags user + * @name DeleteApiUserSshByKeyId + * @summary Delete SSH Key + * @request DELETE:/api/v1/user/ssh/{key_id} + */ + deleteApiUserSshByKeyId: () => { + const base = 'DELETE:/api/v1/user/ssh/{key_id}' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (keyId: number) => dataTaggedQueryKey([base, keyId]), + request: (keyId: number, params: RequestParams = {}) => + this.request({ + path: `/api/v1/user/ssh/${keyId}`, + method: 'DELETE', + ...params + }) + } + }, + + /** + * No description + * + * @tags user + * @name PostApiUserTokenGenerate + * @summary Generate Token For http push + * @request POST:/api/v1/user/token/generate + */ + postApiUserTokenGenerate: () => { + const base = 'POST:/api/v1/user/token/generate' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: () => dataTaggedQueryKey([base]), + request: (params: RequestParams = {}) => + this.request({ + path: `/api/v1/user/token/generate`, + method: 'POST', + ...params + }) + } + }, + + /** + * No description + * + * @tags user + * @name GetApiUserTokenList + * @summary Get User's push token list + * @request GET:/api/v1/user/token/list + */ + getApiUserTokenList: () => { + const base = 'GET:/api/v1/user/token/list' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: () => dataTaggedQueryKey([base]), + request: (params: RequestParams = {}) => + this.request({ + path: `/api/v1/user/token/list`, + method: 'GET', + ...params + }) + } + }, + + /** + * No description + * + * @tags user + * @name DeleteApiUserTokenByKeyId + * @summary Delete User's http push token + * @request DELETE:/api/v1/user/token/{key_id} + */ + deleteApiUserTokenByKeyId: () => { + const base = 'DELETE:/api/v1/user/token/{key_id}' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (keyId: number) => dataTaggedQueryKey([base, keyId]), + request: (keyId: number, params: RequestParams = {}) => + this.request({ + path: `/api/v1/user/token/${keyId}`, + method: 'DELETE', + ...params + }) + } } } }