diff --git a/ceres/src/api_service/mono_api_service.rs b/ceres/src/api_service/mono_api_service.rs index 18ad703f0..424f58571 100644 --- a/ceres/src/api_service/mono_api_service.rs +++ b/ceres/src/api_service/mono_api_service.rs @@ -233,7 +233,7 @@ impl ApiHandler for MonoApiService { } impl MonoApiService { - pub async fn merge_mr(&self, mr: &mut MergeRequest) -> Result<(), MegaError> { + pub async fn merge_mr(&self, user_id: String, mr: &mut MergeRequest) -> Result<(), MegaError> { let storage = self.context.services.mono_storage.clone(); let refs = storage.get_ref(&mr.path).await.unwrap().unwrap(); @@ -264,7 +264,7 @@ impl MonoApiService { // add conversation self.context .mr_stg() - .add_mr_conversation(&mr.link, 0, ConvTypeEnum::Merged, None) + .add_mr_conversation(&mr.link, user_id, ConvTypeEnum::Merged, None) .await .unwrap(); // update mr status last diff --git a/ceres/src/pack/monorepo.rs b/ceres/src/pack/monorepo.rs index e67f37a44..b342c4e3d 100644 --- a/ceres/src/pack/monorepo.rs +++ b/ceres/src/pack/monorepo.rs @@ -368,7 +368,12 @@ impl MonoRepo { let comment = self.comment_for_force_update(&mr.to_hash, &self.to_hash); mr.to_hash = self.to_hash.clone(); storage - .add_mr_conversation(&mr.link, 0, ConvTypeEnum::ForcePush, Some(comment)) + .add_mr_conversation( + &mr.link, + String::new(), + ConvTypeEnum::ForcePush, + Some(comment), + ) .await .unwrap(); } else { @@ -379,7 +384,7 @@ impl MonoRepo { storage .add_mr_conversation( &mr.link, - 0, + String::new(), ConvTypeEnum::Closed, Some("Mega closed MR due to conflict".to_string()), ) diff --git a/common/src/config.rs b/common/src/config.rs index b79f66943..7b0827bfe 100644 --- a/common/src/config.rs +++ b/common/src/config.rs @@ -539,6 +539,8 @@ pub struct OauthConfig { pub github_client_secret: String, pub ui_domain: String, pub cookie_domain: String, + pub campsite_api_domain: String, + pub allowed_cors_origins: Vec, } #[cfg(test)] diff --git a/config/config.toml b/config/config.toml index 3ca9641fa..11e54db77 100644 --- a/config/config.toml +++ b/config/config.toml @@ -114,3 +114,9 @@ ui_domain = "http://local.gitmega.com" # Set your own domain here, for example: .gitmono.com cookie_domain = "localhost" + +# Used for call api from campsite server +campsite_api_domain = "http://api.gitmega.com" + +# allowed cors origins +allowed_cors_origins = ["http://local.gitmega.com", "http://app.gitmega.com"] \ No newline at end of file diff --git a/jupiter/callisto/src/access_token.rs b/jupiter/callisto/src/access_token.rs index 21b6e497f..a2f8e46bb 100644 --- a/jupiter/callisto/src/access_token.rs +++ b/jupiter/callisto/src/access_token.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: i64, - pub user_id: i64, + pub user_id: String, #[sea_orm(column_type = "Text")] pub token: String, pub created_at: DateTime, diff --git a/jupiter/callisto/src/mega_conversation.rs b/jupiter/callisto/src/mega_conversation.rs index 3441f87c5..a1f189cac 100644 --- a/jupiter/callisto/src/mega_conversation.rs +++ b/jupiter/callisto/src/mega_conversation.rs @@ -10,7 +10,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: i64, pub link: String, - pub user_id: i64, + pub user_id: String, pub conv_type: ConvTypeEnum, #[sea_orm(column_type = "Text", nullable)] pub comment: Option, diff --git a/jupiter/callisto/src/mega_mr.rs b/jupiter/callisto/src/mega_mr.rs index 9f03a6f07..7ebf15901 100644 --- a/jupiter/callisto/src/mega_mr.rs +++ b/jupiter/callisto/src/mega_mr.rs @@ -14,7 +14,7 @@ pub struct Model { pub title: String, pub merge_date: Option, pub status: MergeStatusEnum, - #[sea_orm(column_type = "Text", unique)] + #[sea_orm(column_type = "Text")] pub path: String, pub from_hash: String, pub to_hash: String, diff --git a/jupiter/callisto/src/ssh_keys.rs b/jupiter/callisto/src/ssh_keys.rs index 1e76dacd5..f2e48e101 100644 --- a/jupiter/callisto/src/ssh_keys.rs +++ b/jupiter/callisto/src/ssh_keys.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: i64, - pub user_id: i64, + pub user_id: String, #[sea_orm(column_type = "Text")] pub title: String, #[sea_orm(column_type = "Text")] diff --git a/jupiter/migration/src/lib.rs b/jupiter/migration/src/lib.rs index 7571a0ba9..7cdc11f6a 100644 --- a/jupiter/migration/src/lib.rs +++ b/jupiter/migration/src/lib.rs @@ -4,6 +4,7 @@ use sea_orm_migration::schema::big_integer; mod m20250314_025943_init; mod m20250427_031332_add_mr_refs_tag; mod m20250605_013340_alter_mega_mr_index; +mod m20250613_033821_alter_user_id; pub struct Migrator; @@ -14,6 +15,7 @@ impl MigratorTrait for Migrator { Box::new(m20250314_025943_init::Migration), Box::new(m20250427_031332_add_mr_refs_tag::Migration), Box::new(m20250605_013340_alter_mega_mr_index::Migration), + Box::new(m20250613_033821_alter_user_id::Migration), ] } } diff --git a/jupiter/migration/src/m20250613_033821_alter_user_id.rs b/jupiter/migration/src/m20250613_033821_alter_user_id.rs new file mode 100644 index 000000000..43e1780f0 --- /dev/null +++ b/jupiter/migration/src/m20250613_033821_alter_user_id.rs @@ -0,0 +1,82 @@ +use sea_orm_migration::{prelude::*, sea_orm::DatabaseBackend}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let backend = manager.get_database_backend(); + + match backend { + DatabaseBackend::Postgres | DatabaseBackend::MySql => { + manager + .alter_table( + Table::alter() + .table(MegaConversation::Table) + .modify_column( + ColumnDef::new(Alias::new("user_id")) + .string() + .not_null() + .to_owned(), + ) + .to_owned(), + ) + .await?; + + manager + .alter_table( + Table::alter() + .table(AccessToken::Table) + .modify_column( + ColumnDef::new(Alias::new("user_id")) + .string() + .not_null() + .to_owned(), + ) + .to_owned(), + ) + .await?; + + manager + .alter_table( + Table::alter() + .table(SshKeys::Table) + .modify_column( + ColumnDef::new(Alias::new("user_id")) + .string() + .not_null() + .to_owned(), + ) + .to_owned(), + ) + .await?; + } + + DatabaseBackend::Sqlite => { + + } + } + + Ok(()) + } + + async fn down(&self, _: &SchemaManager) -> Result<(), DbErr> { + Ok(()) + } +} + +#[derive(DeriveIden)] +enum MegaConversation { + Table, +} + +#[derive(DeriveIden)] +enum SshKeys { + Table, +} + +#[derive(DeriveIden)] +enum AccessToken { + Table, +} diff --git a/jupiter/src/storage/issue_storage.rs b/jupiter/src/storage/issue_storage.rs index 111ae4e65..69acf760f 100644 --- a/jupiter/src/storage/issue_storage.rs +++ b/jupiter/src/storage/issue_storage.rs @@ -120,7 +120,7 @@ impl IssueStorage { pub async fn add_issue_conversation( &self, link: &str, - user_id: i64, + user_id: String, comment: Option, ) -> Result { let conversation = mega_conversation::Model { diff --git a/jupiter/src/storage/mr_storage.rs b/jupiter/src/storage/mr_storage.rs index be0ad50d8..39cdb9d10 100644 --- a/jupiter/src/storage/mr_storage.rs +++ b/jupiter/src/storage/mr_storage.rs @@ -78,7 +78,7 @@ impl MrStorage { pub async fn close_mr( &self, model: mega_mr::Model, - user_id: i64, + user_id: String, username: &str, ) -> Result<(), MegaError> { self.update_mr(model.clone()).await.unwrap(); @@ -96,7 +96,7 @@ impl MrStorage { pub async fn reopen_mr( &self, model: mega_mr::Model, - user_id: i64, + user_id: String, username: &str, ) -> Result<(), MegaError> { self.update_mr(model.clone()).await.unwrap(); @@ -141,7 +141,7 @@ impl MrStorage { pub async fn add_mr_conversation( &self, link: &str, - user_id: i64, + user_id: String, conv_type: ConvTypeEnum, comment: Option, ) -> Result { diff --git a/jupiter/src/storage/user_storage.rs b/jupiter/src/storage/user_storage.rs index 4287eca67..820bbae4e 100644 --- a/jupiter/src/storage/user_storage.rs +++ b/jupiter/src/storage/user_storage.rs @@ -53,7 +53,7 @@ impl UserStorage { pub async fn save_ssh_key( &self, - user_id: i64, + user_id: String, title: &str, ssh_key: &str, finger: &str, @@ -71,7 +71,7 @@ impl UserStorage { Ok(()) } - pub async fn list_user_ssh(&self, user_id: i64) -> Result, MegaError> { + pub async fn list_user_ssh(&self, user_id: String) -> Result, MegaError> { let res = ssh_keys::Entity::find() .filter(ssh_keys::Column::UserId.eq(user_id)) .all(self.get_connection()) @@ -79,7 +79,7 @@ impl UserStorage { Ok(res) } - pub async fn delete_ssh_key(&self, user_id: i64, id: i64) -> Result<(), MegaError> { + pub async fn delete_ssh_key(&self, user_id: String, id: i64) -> Result<(), MegaError> { let res = ssh_keys::Entity::find() .filter(ssh_keys::Column::Id.eq(id)) .filter(ssh_keys::Column::UserId.eq(user_id)) @@ -102,7 +102,7 @@ impl UserStorage { Ok(res) } - pub async fn generate_token(&self, user_id: i64) -> Result { + pub async fn generate_token(&self, user_id: String) -> Result { let token_str = Uuid::new_v4().to_string(); let model = access_token::Model { id: generate_id(), @@ -115,7 +115,7 @@ impl UserStorage { Ok(token_str.to_owned()) } - pub async fn delete_token(&self, user_id: i64, id: i64) -> Result<(), MegaError> { + pub async fn delete_token(&self, user_id: String, id: i64) -> Result<(), MegaError> { let res = access_token::Entity::find() .filter(access_token::Column::Id.eq(id)) .filter(access_token::Column::UserId.eq(user_id)) @@ -127,7 +127,7 @@ impl UserStorage { Ok(()) } - pub async fn list_token(&self, user_id: i64) -> Result, MegaError> { + pub async fn list_token(&self, user_id: String) -> Result, MegaError> { let res = access_token::Entity::find() .filter(access_token::Column::UserId.eq(user_id)) .all(self.get_connection()) @@ -135,7 +135,7 @@ impl UserStorage { Ok(res) } - pub async fn check_token(&self, user_id: i64, token: &str) -> Result { + pub async fn check_token(&self, user_id: String, token: &str) -> Result { let res = access_token::Entity::find() .filter(access_token::Column::UserId.eq(user_id)) .filter(access_token::Column::Token.eq(token)) diff --git a/mono/src/api/api_router.rs b/mono/src/api/api_router.rs index 37c31674a..ba56cb471 100644 --- a/mono/src/api/api_router.rs +++ b/mono/src/api/api_router.rs @@ -12,7 +12,8 @@ use http::StatusCode; use ceres::{ api_service::ApiHandler, model::git::{ - BlobContentQuery, CodePreviewQuery, CreateFileInfo, LatestCommitInfo, TreeBriefItem, TreeCommitItem, TreeHashItem, TreeQuery + BlobContentQuery, CodePreviewQuery, CreateFileInfo, LatestCommitInfo, TreeBriefItem, + TreeCommitItem, TreeHashItem, TreeQuery, }, }; use common::model::CommonResult; diff --git a/mono/src/api/issue/issue_router.rs b/mono/src/api/issue/issue_router.rs index c3696b231..d1da7e550 100644 --- a/mono/src/api/issue/issue_router.rs +++ b/mono/src/api/issue/issue_router.rs @@ -8,9 +8,12 @@ use utoipa_axum::{router::OpenApiRouter, routes}; use common::model::{CommonPage, CommonResult, PageParams}; -use crate::api::issue::{IssueDetail, IssueItem, NewIssue}; use crate::api::mr::SaveCommentRequest; use crate::api::MonoApiServiceState; +use crate::api::{ + issue::{IssueDetail, IssueItem, NewIssue}, + oauth::model::LoginUser, +}; use crate::{api::error::ApiError, server::https_server::ISSUE_TAG}; pub fn routers() -> OpenApiRouter { @@ -104,14 +107,14 @@ async fn issue_detail( tag = ISSUE_TAG )] async fn new_issue( - // user: LoginUser, + user: LoginUser, state: State, Json(json): Json, ) -> Result>, ApiError> { let stg = state.issue_stg().clone(); let res = stg.save_issue(0, &json.title).await.unwrap(); let res = stg - .add_issue_conversation(&res.link, 0, Some(json.description)) + .add_issue_conversation(&res.link, user.campsite_user_id, Some(json.description)) .await; let res = match res { Ok(_) => CommonResult::success(None), @@ -182,14 +185,14 @@ async fn reopen_issue( tag = ISSUE_TAG )] async fn save_comment( - // user: LoginUser, + user: LoginUser, Path(link): Path, state: State, Json(payload): Json, ) -> Result>, ApiError> { let res = match state .issue_stg() - .add_issue_conversation(&link, 0, Some(payload.content)) + .add_issue_conversation(&link, user.campsite_user_id, Some(payload.content)) .await { Ok(_) => CommonResult::success(None), @@ -211,6 +214,7 @@ async fn save_comment( tag = ISSUE_TAG )] async fn delete_comment( + _: LoginUser, Path(id): Path, state: State, ) -> Result>, ApiError> { diff --git a/mono/src/api/mod.rs b/mono/src/api/mod.rs index 81b2a863c..faef204a7 100644 --- a/mono/src/api/mod.rs +++ b/mono/src/api/mod.rs @@ -21,6 +21,8 @@ use jupiter::{ storage::{issue_storage::IssueStorage, mr_storage::MrStorage, user_storage::UserStorage}, }; +use crate::api::oauth::campsite_store::CampsiteApiStore; + pub mod api_router; pub mod error; pub mod issue; @@ -52,12 +54,17 @@ pub type GithubClient< pub struct MonoApiServiceState { pub context: Context, pub oauth_client: Option, - // TODO: Replace MemoryStore - pub store: Option, + pub store: Option, pub listen_addr: String, } impl FromRef for MemoryStore { + fn from_ref(_: &MonoApiServiceState) -> Self { + MemoryStore::new() + } +} + +impl FromRef for CampsiteApiStore { fn from_ref(state: &MonoApiServiceState) -> Self { state.store.clone().unwrap() } diff --git a/mono/src/api/mr/mod.rs b/mono/src/api/mr/mod.rs index af7d1b884..8ef8a472d 100644 --- a/mono/src/api/mr/mod.rs +++ b/mono/src/api/mr/mod.rs @@ -64,7 +64,7 @@ impl From for MRDetail { #[derive(Serialize, Deserialize, ToSchema)] pub struct MegaConversation { pub id: i64, - pub user_id: i64, + pub user_id: String, pub conv_type: ConvTypeEnum, pub comment: Option, pub created_at: i64, diff --git a/mono/src/api/mr/mr_router.rs b/mono/src/api/mr/mr_router.rs index f2d5c9a8d..b81041be5 100644 --- a/mono/src/api/mr/mr_router.rs +++ b/mono/src/api/mr/mr_router.rs @@ -4,16 +4,21 @@ use axum::{ extract::{Path, State}, Json, }; +use saturn::ActionEnum; use utoipa_axum::{router::OpenApiRouter, routes}; use callisto::sea_orm_active_enums::{ConvTypeEnum, MergeStatusEnum}; use ceres::protocol::mr::MergeRequest; use common::model::{CommonPage, CommonResult, PageParams}; -use crate::api::mr::{ - FilesChangedItem, FilesChangedList, MRDetail, MRStatusParams, MrInfoItem, SaveCommentRequest, +use crate::api::{ + mr::{ + FilesChangedItem, FilesChangedList, MRDetail, MRStatusParams, MrInfoItem, + SaveCommentRequest, + }, + oauth::model::LoginUser, }; -use crate::api::MonoApiServiceState; +use crate::api::{util, MonoApiServiceState}; use crate::{api::error::ApiError, server::https_server::MR_TAG}; pub fn routers() -> OpenApiRouter { @@ -44,6 +49,7 @@ pub fn routers() -> OpenApiRouter { tag = MR_TAG )] async fn reopen_mr( + user: LoginUser, Path(link): Path, state: State, ) -> Result>, ApiError> { @@ -59,7 +65,11 @@ async fn reopen_mr( // .unwrap(); let mut mr: MergeRequest = model.into(); mr.status = MergeStatusEnum::Open; - let res = match state.mr_stg().reopen_mr(mr.into(), 0, "admin").await { + let res = match state + .mr_stg() + .reopen_mr(mr.into(), user.campsite_user_id, &user.name) + .await + { Ok(_) => CommonResult::success(None), Err(err) => CommonResult::failed(&err.to_string()), }; @@ -82,7 +92,7 @@ async fn reopen_mr( tag = MR_TAG )] async fn close_mr( - // user: LoginUser, + user: LoginUser, Path(link): Path, state: State, ) -> Result>, ApiError> { @@ -98,7 +108,11 @@ async fn close_mr( // .unwrap(); let mut mr: MergeRequest = model.into(); mr.status = MergeStatusEnum::Closed; - let res = match state.mr_stg().close_mr(mr.into(), 0, "admin").await { + let res = match state + .mr_stg() + .close_mr(mr.into(), user.campsite_user_id, &user.name) + .await + { Ok(_) => CommonResult::success(None), Err(err) => CommonResult::failed(&err.to_string()), }; @@ -121,22 +135,25 @@ async fn close_mr( tag = MR_TAG )] async fn merge( - // user: LoginUser, + user: LoginUser, Path(link): Path, state: State, ) -> Result>, ApiError> { if let Some(model) = state.mr_stg().get_mr(&link).await.unwrap() { if model.status == MergeStatusEnum::Open { - // let path = model.path.clone(); - // util::check_permissions( - // &user.name, - // &path, - // ActionEnum::ApproveMergeRequest, - // state.clone(), - // ) - // .await - // .unwrap(); - let res = state.monorepo().merge_mr(&mut model.into()).await; + let path = model.path.clone(); + util::check_permissions( + &user.name, + &path, + ActionEnum::ApproveMergeRequest, + state.clone(), + ) + .await + .unwrap(); + let res = state + .monorepo() + .merge_mr(user.campsite_user_id, &mut model.into()) + .await; let res = match res { Ok(_) => CommonResult::success(None), Err(err) => CommonResult::failed(&err.to_string()), @@ -270,7 +287,7 @@ async fn get_mr_files_changed( tag = MR_TAG )] async fn save_comment( - // user: LoginUser, + user: LoginUser, Path(link): Path, state: State, Json(payload): Json, @@ -278,7 +295,12 @@ async fn save_comment( let res = if let Some(model) = state.mr_stg().get_mr(&link).await.unwrap() { state .mr_stg() - .add_mr_conversation(&model.link, 0, ConvTypeEnum::Comment, Some(payload.content)) + .add_mr_conversation( + &model.link, + user.campsite_user_id, + ConvTypeEnum::Comment, + Some(payload.content), + ) .await .unwrap(); CommonResult::success(None) diff --git a/mono/src/api/oauth/campsite_store.rs b/mono/src/api/oauth/campsite_store.rs new file mode 100644 index 000000000..c16fc4d83 --- /dev/null +++ b/mono/src/api/oauth/campsite_store.rs @@ -0,0 +1,77 @@ +use std::sync::Arc; + +use anyhow::Context; +use async_session::{async_trait, Result, Session, SessionStore}; +use http::header::COOKIE; +use reqwest::Client; +use reqwest::Url; + +use crate::api::oauth::model::CampsiteUserJson; +use crate::api::oauth::model::LoginUser; +use crate::api::oauth::CAMPSITE_API_COOKIE; + +#[derive(Debug, Clone)] +pub struct CampsiteApiStore { + client: Arc, + // cookie_store: Arc, + api_base_url: String, +} + +#[async_trait] +impl SessionStore for CampsiteApiStore { + async fn load_session(&self, cookie_value: String) -> Result> { + let mut session = Session::new(); + let url = format!("{}/v1/users/me", self.api_base_url) + .parse::() + .context("failed to parse API base URL")?; + // self.cookie_store.add_cookie_str(cookie, &url); + let resp = self + .client + .get(url) + .header(COOKIE, format!("{}={}", CAMPSITE_API_COOKIE, cookie_value)) + .send() + .await?; + + if resp.status().is_success() { + // let text = resp.text().await?; + // println!("Raw response: {}", text); + let campsite_user = resp + .json::() + .await + .context("failed to deserialize response as JSON")?; + let login_user: LoginUser = campsite_user.into(); + session + .insert("user", &login_user) + .context("failed in inserting serialized value into session")?; + Ok(Some(session)) + } else { + tracing::error!("load session Status: {}", resp.status()); + Ok(None) + } + } + + async fn store_session(&self, _: Session) -> Result> { + Err(anyhow::anyhow!("store_session is not supported")) + } + + async fn destroy_session(&self, _: Session) -> Result { + Err(anyhow::anyhow!("destroy_session is not supported")) + } + + async fn clear_store(&self) -> Result { + Err(anyhow::anyhow!("clear_store is not supported")) + } +} + +impl CampsiteApiStore { + pub fn new(api_base_url: String) -> Self { + let client = Client::builder() + .no_proxy() + .build() + .expect("Failed to build client"); + Self { + client: Arc::new(client), + api_base_url, + } + } +} diff --git a/mono/src/api/oauth/mod.rs b/mono/src/api/oauth/mod.rs index 4e7c5ecad..b3881f01b 100644 --- a/mono/src/api/oauth/mod.rs +++ b/mono/src/api/oauth/mod.rs @@ -17,19 +17,21 @@ use oauth2::{ }; use common::config::OauthConfig; -use jupiter::storage::user_storage::UserStorage; use model::{GitHubUserJson, LoginUser, OauthCallbackParams}; use utoipa_axum::router::OpenApiRouter; -use crate::api::error::ApiError; use crate::api::MonoApiServiceState; +use crate::api::{error::ApiError, oauth::campsite_store::CampsiteApiStore}; use super::GithubClient; +pub mod campsite_store; pub mod model; static COOKIE_NAME: &str = "SESSION"; +static CAMPSITE_API_COOKIE: &str = "_campsite_api_session"; + pub fn routers() -> OpenApiRouter { OpenApiRouter::new() .route("/github", get(github_auth)) @@ -200,15 +202,14 @@ impl IntoResponse for AuthRedirect { impl FromRequestParts for LoginUser where - MemoryStore: FromRef, - UserStorage: FromRef, + CampsiteApiStore: FromRef, S: Send + Sync, { // If anything goes wrong or no session is found, redirect to the auth page type Rejection = AuthRedirect; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - let store = MemoryStore::from_ref(state); + let store = CampsiteApiStore::from_ref(state); let cookies = parts .extract::>() @@ -220,7 +221,7 @@ where }, _ => panic!("unexpected error getting cookies: {e}"), })?; - let session_cookie = cookies.get(COOKIE_NAME).ok_or(AuthRedirect)?; + let session_cookie = cookies.get(CAMPSITE_API_COOKIE).ok_or(AuthRedirect)?; let session = store .load_session(session_cookie.to_string()) diff --git a/mono/src/api/oauth/model.rs b/mono/src/api/oauth/model.rs index 1c58993c4..ce9db36c1 100644 --- a/mono/src/api/oauth/model.rs +++ b/mono/src/api/oauth/model.rs @@ -1,4 +1,4 @@ -use chrono::NaiveDateTime; +use chrono::{DateTime, NaiveDateTime, Utc}; use common::utils::generate_id; use serde::{Deserialize, Serialize}; @@ -40,9 +40,30 @@ impl From for user::Model { } } +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct CampsiteUserJson { + pub username: String, + pub id: String, + pub avatar_url: String, + pub email: Option, + pub created_at: DateTime, +} + +impl From for LoginUser { + fn from(value: CampsiteUserJson) -> Self { + Self { + name: value.username, + email: value.email.unwrap_or_default(), + avatar_url: value.avatar_url, + campsite_user_id: value.id, + created_at: value.created_at.naive_utc(), + } + } +} + #[derive(Serialize, Deserialize, Clone, Debug)] pub struct LoginUser { - pub user_id: i64, + pub campsite_user_id: String, pub name: String, pub avatar_url: String, pub email: String, @@ -52,11 +73,11 @@ pub struct LoginUser { impl From for LoginUser { fn from(value: user::Model) -> Self { Self { - user_id: value.id, name: value.name, avatar_url: value.avatar_url, email: value.email, created_at: value.created_at, + campsite_user_id: String::new(), } } } diff --git a/mono/src/api/user/user_router.rs b/mono/src/api/user/user_router.rs index 16540fd3f..eeb41d5ba 100644 --- a/mono/src/api/user/user_router.rs +++ b/mono/src/api/user/user_router.rs @@ -59,7 +59,7 @@ async fn add_key( let res = state .user_stg() .save_ssh_key( - user.user_id, + user.campsite_user_id, &title, &json.ssh_key, &key.fingerprint(HashAlg::Sha256).to_string(), @@ -77,7 +77,10 @@ async fn remove_key( state: State, Path(key_id): Path, ) -> Result>, ApiError> { - let res = state.user_stg().delete_ssh_key(user.user_id, key_id).await; + let res = state + .user_stg() + .delete_ssh_key(user.campsite_user_id, key_id) + .await; let res = match res { Ok(_) => CommonResult::success(None), Err(err) => CommonResult::failed(&err.to_string()), @@ -89,7 +92,7 @@ async fn list_key( user: LoginUser, state: State, ) -> Result>>, ApiError> { - let res = state.user_stg().list_user_ssh(user.user_id).await; + let res = state.user_stg().list_user_ssh(user.campsite_user_id).await; let res = match res { Ok(data) => CommonResult::success(Some(data.into_iter().map(|x| x.into()).collect())), Err(err) => CommonResult::failed(&err.to_string()), @@ -101,7 +104,7 @@ async fn generate_token( user: LoginUser, state: State, ) -> Result>, ApiError> { - let res = state.user_stg().generate_token(user.user_id).await; + let res = state.user_stg().generate_token(user.campsite_user_id).await; let res = match res { Ok(data) => CommonResult::success(Some(data)), Err(err) => CommonResult::failed(&err.to_string()), @@ -114,7 +117,10 @@ async fn remove_token( state: State, Path(key_id): Path, ) -> Result>, ApiError> { - let res = state.user_stg().delete_token(user.user_id, key_id).await; + let res = state + .user_stg() + .delete_token(user.campsite_user_id, key_id) + .await; let res = match res { Ok(_) => CommonResult::success(None), Err(err) => CommonResult::failed(&err.to_string()), @@ -126,7 +132,7 @@ async fn list_token( user: LoginUser, state: State, ) -> Result>>, ApiError> { - let res = state.user_stg().list_token(user.user_id).await; + let res = state.user_stg().list_token(user.campsite_user_id).await; let res = match res { Ok(data) => { let res = data.into_iter().map(|x| x.into()).collect(); diff --git a/mono/src/git_protocol/http.rs b/mono/src/git_protocol/http.rs index fafdd675d..ce889827d 100644 --- a/mono/src/git_protocol/http.rs +++ b/mono/src/git_protocol/http.rs @@ -71,10 +71,11 @@ async fn http_auth(header: &HeaderMap, context: &Context) -> bool { .await .unwrap() { - Some(user) => { + // TODO refactor later + Some(_) => { return context .user_stg() - .check_token(user.id, token) + .check_token(String::new(), token) .await .unwrap(); } diff --git a/mono/src/server/https_server.rs b/mono/src/server/https_server.rs index 3b16737c7..9c3edfe65 100644 --- a/mono/src/server/https_server.rs +++ b/mono/src/server/https_server.rs @@ -3,17 +3,17 @@ use std::path::PathBuf; use std::str::FromStr; use anyhow::Result; -use async_session::MemoryStore; use axum::body::Body; use axum::extract::{Query, State}; use axum::http::{self, Request, Uri}; use axum::response::Response; use axum::routing::get; use axum::Router; +use http::{HeaderValue, Method}; use lazy_static::lazy_static; use regex::Regex; use tower::ServiceBuilder; -use tower_http::cors::{Any, CorsLayer}; +use tower_http::cors::CorsLayer; use tower_http::decompression::RequestDecompressionLayer; use tower_http::trace::TraceLayer; @@ -27,7 +27,8 @@ use utoipa_swagger_ui::SwaggerUi; use crate::api::api_router::{self}; use crate::api::lfs::lfs_router; -use crate::api::oauth::{self, oauth_client}; +use crate::api::oauth::campsite_store::CampsiteApiStore; +use crate::api::oauth::oauth_client; use crate::api::MonoApiServiceState; #[derive(Clone)] @@ -92,13 +93,20 @@ pub async fn app(context: Context, host: String, port: u16) -> Router { }; let config = context.config.clone(); + let oauth_config = config.oauth.clone().unwrap(); let api_state = MonoApiServiceState { context: context.clone(), - oauth_client: Some(oauth_client(config.oauth.clone().unwrap()).unwrap()), - store: Some(MemoryStore::new()), + oauth_client: Some(oauth_client(oauth_config.clone()).unwrap()), + store: Some(CampsiteApiStore::new(oauth_config.campsite_api_domain)), listen_addr: format!("http://{}:{}", host, port), }; + let origins: Vec = oauth_config + .allowed_cors_origins + .into_iter() + .map(|x| x.parse::().unwrap()) + .collect(); + // add RequestDecompressionLayer for handle gzip encode // add TraceLayer for log record // add CorsLayer to add cors header @@ -108,18 +116,19 @@ pub async fn app(context: Context, host: String, port: u16) -> Router { "/api/v1", api_router::routers().with_state(api_state.clone()), ) - .nest("/auth", oauth::routers().with_state(api_state.clone())) + // .nest("/auth", oauth::routers().with_state(api_state.clone())) // Using Regular Expressions for Path Matching in Protocol .route("/{*path}", get(get_method_router).post(post_method_router)) .layer( ServiceBuilder::new().layer( CorsLayer::new() - .allow_origin(Any) + .allow_origin(origins) .allow_headers(vec![ http::header::AUTHORIZATION, http::header::CONTENT_TYPE, ]) - .allow_methods(Any), + .allow_methods([Method::GET, Method::POST, Method::OPTIONS, Method::DELETE]) + .allow_credentials(true), ), ) .layer(TraceLayer::new_for_http()) diff --git a/moon/apps/web/next.config.js b/moon/apps/web/next.config.js index 31dcaffeb..678eef0f7 100644 --- a/moon/apps/web/next.config.js +++ b/moon/apps/web/next.config.js @@ -37,6 +37,8 @@ const cspResourcesByDirective = { 'wss://*.gitmono.com', 'ws://*.gitmega.com', 'http://*.gitmega.com', + 'http://*.gitmono.test:3001', + 'http://*.gitmono.test:8000', process.env.NODE_ENV !== 'production' && 'http://api.gitmega.com', process.env.NODE_ENV !== 'production' && 'http://git.gitmega.com', process.env.NODE_ENV !== 'production' && 'localhost:8000', diff --git a/moon/apps/web/utils/queryClient.ts b/moon/apps/web/utils/queryClient.ts index 879e28141..6e9ef126e 100644 --- a/moon/apps/web/utils/queryClient.ts +++ b/moon/apps/web/utils/queryClient.ts @@ -148,6 +148,7 @@ export const apiClient = new Api({ export const legacyApiClient = new Api({ baseUrl: LEGACY_API_URL, baseApiParams: { + credentials: 'include', headers: { 'Content-Type': 'application/json' }, format: 'json' }