From 8d3728573a44a5c332179228b7b6f8660b0cfa4e Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Fri, 17 Jan 2025 00:51:32 +0800 Subject: [PATCH 1/4] :construction:Some messy work --- Cargo.lock | 164 ++++- Cargo.toml | 1 + gitdata-apis/Cargo.toml | 1 + gitdata-apis/src/apis/app_router.rs | 43 +- gitdata-apis/src/apis/v1/auth/passwd.rs | 3 +- gitdata-apis/src/apis/v1/user/ssh_keys.rs | 2 +- gitdata-apis/src/jobs/email.rs | 55 +- gitdata-apis/src/main.rs | 33 +- gitdata-apis/src/public/captcha.html | 51 ++ gitdata-apis/src/public/mod.rs | 12 + .../src/public/users_forgetpasswd.html | 61 ++ gitdata-apis/src/service/auth/passwd.rs | 2 +- gitdata-apis/src/service/emails/captcha.rs | 5 +- gitdata-apis/src/service/mod.rs | 14 +- .../src/service/repository/new_repo.rs | 3 +- gitdata-apis/src/service/users/apply.rs | 4 +- gitdata-health/Cargo.toml | 6 + gitdata-health/src/main.rs | 3 + gitdata-proxy/Cargo.toml | 11 + gitdata-proxy/src/main.rs | 5 + libs/config/email.rs | 77 ++ libs/config/mod.rs | 2 + libs/model/migrate/mod.rs | 15 + libs/model/migrate/users.rs | 311 ++++++++ libs/model/mod.rs | 1 + libs/model/repository/repository.rs | 39 + libs/model/users/emails.rs | 2 +- libs/model/users/follow.rs | 2 +- libs/model/users/token_key.rs | 2 +- libs/model/users/users.rs | 2 +- libs/model/users/watch.rs | 2 +- libs/rpc/core_git.rs | 341 +++++++++ libs/rpc/git_core.rs | 670 ++++++++++++++++++ 33 files changed, 1909 insertions(+), 36 deletions(-) create mode 100644 gitdata-apis/src/public/captcha.html create mode 100644 gitdata-apis/src/public/mod.rs create mode 100644 gitdata-apis/src/public/users_forgetpasswd.html create mode 100644 gitdata-health/Cargo.toml create mode 100644 gitdata-health/src/main.rs create mode 100644 gitdata-proxy/Cargo.toml create mode 100644 gitdata-proxy/src/main.rs create mode 100644 libs/config/email.rs create mode 100644 libs/model/migrate/mod.rs create mode 100644 libs/model/migrate/users.rs create mode 100644 libs/rpc/core_git.rs create mode 100644 libs/rpc/git_core.rs diff --git a/Cargo.lock b/Cargo.lock index 7212863..108c1b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1173,12 +1173,52 @@ dependencies = [ "ansi_term", "atty", "bitflags 1.3.2", - "strsim", + "strsim 0.8.0", "textwrap", "unicode-width", "vec_map", ] +[[package]] +name = "clap" +version = "4.5.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.5.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + [[package]] name = "colorchoice" version = "1.0.3" @@ -1474,6 +1514,40 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.96", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1705,7 +1779,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" dependencies = [ - "clap", + "clap 2.34.0", ] [[package]] @@ -2214,6 +2288,7 @@ dependencies = [ "pin-project", "prost", "sea-orm", + "sea-orm-migration", "serde", "serde_json", "serde_yaml", @@ -2256,6 +2331,7 @@ dependencies = [ "lettre", "rand", "sea-orm", + "sea-orm-migration", "serde", "serde_json", "sha256", @@ -2798,6 +2874,12 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -4695,6 +4777,23 @@ dependencies = [ "uuid", ] +[[package]] +name = "sea-orm-cli" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e6e0e741bfdf434e6f6aadab156ba4d439e78c9449048698d98fa377871224a" +dependencies = [ + "chrono", + "clap 4.5.26", + "dotenvy", + "glob", + "regex", + "sea-schema", + "tracing", + "tracing-subscriber", + "url", +] + [[package]] name = "sea-orm-macros" version = "1.1.4" @@ -4709,6 +4808,23 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sea-orm-migration" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0bb76ba314552ce15e3a24778cf9c116fc1225fa406e48b0a36e5a3cdbc1e21" +dependencies = [ + "async-trait", + "clap 4.5.26", + "dotenvy", + "futures", + "sea-orm", + "sea-orm-cli", + "sea-schema", + "tracing", + "tracing-subscriber", +] + [[package]] name = "sea-query" version = "0.32.1" @@ -4720,6 +4836,7 @@ dependencies = [ "inherent", "ordered-float 3.9.2", "rust_decimal", + "sea-query-derive", "serde_json", "time", "uuid", @@ -4741,6 +4858,43 @@ dependencies = [ "uuid", ] +[[package]] +name = "sea-query-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9834af2c4bd8c5162f00c89f1701fb6886119a88062cf76fe842ea9e232b9839" +dependencies = [ + "darling", + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.96", + "thiserror 1.0.69", +] + +[[package]] +name = "sea-schema" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef5dd7848c993f3789d09a2616484c72c9330cae2b048df59d8c9b8c0343e95" +dependencies = [ + "futures", + "sea-query", + "sea-schema-derive", +] + +[[package]] +name = "sea-schema-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "seahash" version = "4.1.0" @@ -5378,6 +5532,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.3" diff --git a/Cargo.toml b/Cargo.toml index 406e236..29ebca2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ tokio = "1" pin-project = "1" http-body = "1.0.1" sea-orm = { version = "1", features = ["sqlx-all", "runtime-tokio"] } +sea-orm-migration = { version = "1", features = ["runtime-tokio","sqlx-postgres", "cli"] } uuid = { version = "1.11.0", features = ["serde","v4"] } chrono = { version = "0.4.39", features = ["clock","std","serde"] } serde_json = "1" diff --git a/gitdata-apis/Cargo.toml b/gitdata-apis/Cargo.toml index 358715a..ac8401d 100644 --- a/gitdata-apis/Cargo.toml +++ b/gitdata-apis/Cargo.toml @@ -26,6 +26,7 @@ serde = { version = "1.0.217", features = ["derive"] } serde_json = { version = "1.0.134", features = ["alloc"] } tonic = { version = "0.12.3", features = ["default"] } sea-orm = { version = "1", features = ["sqlx-all","runtime-tokio"] } +sea-orm-migration = { version = "1", features = ["runtime-tokio","sqlx-postgres","cli","default"] } sha256 = { version = "1.5.0", features = ["tokio"] } lettre = {version = "0.11.10",features = [ "smtp-transport", diff --git a/gitdata-apis/src/apis/app_router.rs b/gitdata-apis/src/apis/app_router.rs index 72cfcf3..1fa54cd 100644 --- a/gitdata-apis/src/apis/app_router.rs +++ b/gitdata-apis/src/apis/app_router.rs @@ -1,25 +1,20 @@ -use actix_web::web::post; +use actix_web::web::{delete, get, post}; use actix_web::web::scope; -use super::v1::auth::logout::api_v1_auth_logout; -use super::v1::auth::passwd::api_v1_auth_passwd; -use super::v1::email::captcha::api_v1_email_captcha_check; -use super::v1::email::captcha::api_v1_email_captcha_post; - #[allow(non_snake_case)] -pub async fn AppRouter(cfg : &mut actix_web::web::ServiceConfig) { +pub fn AppRouter(cfg : &mut actix_web::web::ServiceConfig) { cfg.service( scope("/api").service( scope("/v1") .service( scope("/auth") - .route("/passwd", post().to(api_v1_auth_passwd)) - .route("/logout", post().to(api_v1_auth_logout)), + .route("/passwd", post().to(crate::apis::v1::auth::passwd::api_v1_auth_passwd)) + .route("/logout", post().to(crate::apis::v1::auth::logout::api_v1_auth_logout)), ) .service( scope("/email") - .route("/captcha", post().to(api_v1_email_captcha_post)) - .route("/captcha/check", post().to(api_v1_email_captcha_check)), + .route("/captcha", post().to(crate::apis::v1::email::captcha::api_v1_email_captcha_post)) + .route("/captcha/check", post().to(crate::apis::v1::email::captcha::api_v1_email_captcha_check)), ) .service( scope("/user").service( @@ -39,8 +34,30 @@ pub async fn AppRouter(cfg : &mut actix_web::web::ServiceConfig) { .route( "/pined", post().to(super::v1::user::setting::api_v1_user_setting_pinned), - ), - ), + ) + , + ).route("/apply", post().to(super::v1::user::apply::api_v1_user_apply)) + .service( + scope("/info") + .route("", get().to(super::v1::user::info::api_v1_users_info)) + .route( + "/{username}", + get().to(super::v1::user::info::api_v1_users_info_by_username), + ) + ) + .service( + scope("/ssh") + .route("", get().to(super::v1::user::ssh_keys::api_v1_user_ssh_key_list)) + .route("/{ssh_key_uid}", delete().to(super::v1::user::ssh_keys::api_v1_user_ssh_key_delete)) + .route("", post().to(super::v1::user::ssh_keys::api_v1_user_ssh_key_create)) + ) + .service( + scope("token") + .route("", post().to(super::v1::user::token_key::api_v1_user_token_create)) + .route("/{token_uid}", delete().to(super::v1::user::token_key::api_v1_user_token_delete)) + .route("", get().to(super::v1::user::token_key::api_v1_user_token_list)) + ) + , ), ), ); diff --git a/gitdata-apis/src/apis/v1/auth/passwd.rs b/gitdata-apis/src/apis/v1/auth/passwd.rs index 4392c83..cc7bb94 100644 --- a/gitdata-apis/src/apis/v1/auth/passwd.rs +++ b/gitdata-apis/src/apis/v1/auth/passwd.rs @@ -4,7 +4,7 @@ use actix_web::HttpMessage; use actix_web::HttpRequest; use actix_web::Responder; use actix_web::web; - +use sha256::Sha256Digest; use crate::apis::app_writer::AppWrite; use crate::service::AppState; use crate::service::auth::AuthInner; @@ -23,6 +23,7 @@ pub async fn api_v1_auth_passwd( return AppWrite::fail(err.to_string()); } }; + dbg!(inner.clone().password.digest()); match app_state.auth_by_passwd(inner).await { Ok(token) => { session.insert("token", token.clone()).unwrap(); diff --git a/gitdata-apis/src/apis/v1/user/ssh_keys.rs b/gitdata-apis/src/apis/v1/user/ssh_keys.rs index c4a42cc..1c14f79 100644 --- a/gitdata-apis/src/apis/v1/user/ssh_keys.rs +++ b/gitdata-apis/src/apis/v1/user/ssh_keys.rs @@ -33,7 +33,7 @@ pub async fn api_v1_user_ssh_key_create( /// /api/v1/user/ssh DELETE pub async fn api_v1_user_ssh_key_delete( session : Session, - param : web::Json, + param : web::Query, app_state : web::Data, ) -> impl Responder { let ident = match UsersInfoReplay::from_session(session) { diff --git a/gitdata-apis/src/jobs/email.rs b/gitdata-apis/src/jobs/email.rs index 57266be..5cad803 100644 --- a/gitdata-apis/src/jobs/email.rs +++ b/gitdata-apis/src/jobs/email.rs @@ -1,9 +1,13 @@ use std::io; - +use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor}; +use lettre::message::header::ContentType; use lettre::message::Mailbox; +use lettre::transport::smtp::authentication::Credentials; use serde::Deserialize; use serde::Serialize; - +use tokio::sync::OnceCell; +use tracing::{error, info}; +use gitdata::config::email::EmailConfig; use crate::public::CAPTCHA; use crate::public::USER_FOR_GET_PASSWD; @@ -14,6 +18,7 @@ pub struct EmailJobs { pub to : Mailbox, pub subject : String, pub body : EmailType, + pub code: String, } impl EmailJobs { @@ -23,6 +28,7 @@ impl EmailJobs { to : Mailbox, subject : String, body : EmailType, + code: String, ) -> Self { EmailJobs { from, @@ -30,12 +36,25 @@ impl EmailJobs { to, subject, body, + code, } } } -pub async fn send_email(job : EmailJobs) -> Result<(), io::Error> { - dbg!(job); +pub async fn send_email(tx : EmailJobs) -> Result<(), io::Error> { + let email_server = EmailServer::get().await; + let email = Message::builder() + .from(tx.from) + .reply_to(tx.reply) + .to(tx.to.clone()) + .subject(tx.subject) + .header(ContentType::TEXT_HTML) + .body(tx.body.to_email().replace("123456", &tx.code)) + .unwrap(); + match email_server.cred.send(email).await { + Ok(_) => info!("Email sent {} successfully!", tx.to.to_string()), + Err(e) => error!("Could not send email: {e:?}"), + } Ok(()) } @@ -53,3 +72,31 @@ impl EmailType { } } } + +static EMAIL: OnceCell = OnceCell::const_new(); + +#[derive(Clone)] +struct EmailServer{ + cred: AsyncSmtpTransport +} + +impl EmailServer { + pub fn init() -> EmailServer { + let config = EmailConfig::load().expect("Failed to load email config"); + let creds = + Credentials::new(config.username.to_owned(), config.password.to_owned()); + let mailer: AsyncSmtpTransport = + AsyncSmtpTransport::::relay(&config.smtp) + .unwrap() + .credentials(creds) + .build(); + EmailServer{ + cred: mailer + } + } + pub async fn get() -> Self{ + EMAIL.get_or_init(|| async { + EmailServer::init() + }).await.clone() + } +} \ No newline at end of file diff --git a/gitdata-apis/src/main.rs b/gitdata-apis/src/main.rs index 5d48ac5..19aa829 100644 --- a/gitdata-apis/src/main.rs +++ b/gitdata-apis/src/main.rs @@ -1,9 +1,15 @@ +use actix_session::config::{CookieContentSecurity, PersistentSession, TtlExtensionPolicy}; +use actix_session::SessionMiddleware; +use actix_session::storage::RedisSessionStore; use actix_settings::ApplySettings; use actix_web::App; +use actix_web::cookie::Key; +use actix_web::cookie::time::Duration; use actix_web::HttpServer; use actix_web::web; use gitdata_apis::service::AppState; use tracing::info; +use gitdata_apis::apis::app_router::AppRouter; const CONFIG_FILE : &str = "./config/api.toml"; #[tokio::main] @@ -18,7 +24,32 @@ async fn main() -> anyhow::Result<()> { return Err(anyhow::anyhow!("Please configure the configuration file")); }; let state = AppState::init().await?; - HttpServer::new(move || App::new().app_data(web::Data::new(state.clone()))) + let session = RedisSessionStore::builder_pooled(state.redis_pool.clone()) + .build() + .await?; + info!("Redis session store initialized."); + info!("API server started."); + HttpServer::new(move || + App::new() + .wrap(actix_identity::IdentityMiddleware::default()) + .wrap( + SessionMiddleware::builder(session.clone(), Key::from(&[0; 64])) + .cookie_name("SessionID".to_string()) + .cookie_path("/".to_string()) + .cookie_http_only(false) + .cookie_content_security(CookieContentSecurity::Private) + .session_lifecycle( + PersistentSession::default() + .session_ttl(Duration::days(30)) + .session_ttl_extension_policy(TtlExtensionPolicy::OnEveryRequest), + ) + .cookie_secure(false) + .build(), + ) + .wrap(actix_web::middleware::Logger::default()) + .app_data(web::Data::new(state.clone())) + .configure(AppRouter) + ) .try_apply_settings(&api_config) .expect("Failed to apply settings") .run() diff --git a/gitdata-apis/src/public/captcha.html b/gitdata-apis/src/public/captcha.html new file mode 100644 index 0000000..40059be --- /dev/null +++ b/gitdata-apis/src/public/captcha.html @@ -0,0 +1,51 @@ + + + + + + email-template + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ GitData +
+

您好,

+

Hello,


非常感谢您的加入!您的验证码为:
Thanks for your signing up!Your verification code is: +
+ + + + +
123456
+
GitData.AI是一个用于数据产品(例如AI模型)的开发、管理、交易的一站式协作平台,
帮助您高效地开发和探索数据产品。
GitData.AI is a one-stop collaborative platform for the development,management,and trading of data products(such as AI + models)
to help you efficiently develop and explore data products.
Copyright©2024|GitData.AI
+
+ + + \ No newline at end of file diff --git a/gitdata-apis/src/public/mod.rs b/gitdata-apis/src/public/mod.rs new file mode 100644 index 0000000..2438698 --- /dev/null +++ b/gitdata-apis/src/public/mod.rs @@ -0,0 +1,12 @@ +/* + * + * * Copyright (c) 2024-2025, GitDataAI Ltd. + * * All rights reserved. + * * + * * Licensed under your choice of the GitDataAI Source Available License 1.0 + * * (Licensed_GSALv1) or the Server Side Public License v1 (Licensed_SSPLv1). + * + */ + +pub const CAPTCHA : &str = include_str!("captcha.html"); +pub const USER_FOR_GET_PASSWD : &str = include_str!("users_forgetpasswd.html"); diff --git a/gitdata-apis/src/public/users_forgetpasswd.html b/gitdata-apis/src/public/users_forgetpasswd.html new file mode 100644 index 0000000..9bb8e35 --- /dev/null +++ b/gitdata-apis/src/public/users_forgetpasswd.html @@ -0,0 +1,61 @@ + + + + + <!DOCTYPE html> + <html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width,initial-scale=1.0"> + <title>email-template + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ GitData + +
+

您好,

+

Hello,


+ + 重置密码请点击按钮:
+ To reset your password please click the button: +
+ + + + +
+ 重置密码 +
+
GitData.AI 是一个用于数据产品(例如AI模型)的开发、管理、交易的一站式协作平台,
帮助您高效地开发和探索数据产品。
GitData.AI is a one-stop collaborative platform for the development, management, and trading of data products (such as + AI models)
to help you efficiently develop and explore data products.
Copyright © 2024 | GitData.AI
+
+ + \ No newline at end of file diff --git a/gitdata-apis/src/service/auth/passwd.rs b/gitdata-apis/src/service/auth/passwd.rs index 1c83af2..8b02010 100644 --- a/gitdata-apis/src/service/auth/passwd.rs +++ b/gitdata-apis/src/service/auth/passwd.rs @@ -5,7 +5,7 @@ use sha256::Sha256Digest; use crate::service::AppState; use crate::service::users::info::UsersInfoReplay; -#[derive(Deserialize, Clone)] +#[derive(Deserialize, Clone,Debug)] pub struct UsersAuthPasswdParam { pub username : String, pub password : String, diff --git a/gitdata-apis/src/service/emails/captcha.rs b/gitdata-apis/src/service/emails/captcha.rs index 219d1f1..0968809 100644 --- a/gitdata-apis/src/service/emails/captcha.rs +++ b/gitdata-apis/src/service/emails/captcha.rs @@ -40,10 +40,9 @@ impl AppState { "gitdata-bot@gitdata.ai".parse()?, "gitdata-bot@gitdata.ai".parse()?, email, - EmailType::RegistrationVerificationCode - .to_email() - .replace("123456", &captcha.to_string()), + "GitData 验证码".parse()?, EmailType::RegistrationVerificationCode, + captcha.to_string() ); let mut email_jobs = self .email_jobs diff --git a/gitdata-apis/src/service/mod.rs b/gitdata-apis/src/service/mod.rs index 881bc54..e7b47a7 100644 --- a/gitdata-apis/src/service/mod.rs +++ b/gitdata-apis/src/service/mod.rs @@ -10,12 +10,13 @@ use apalis_sql::postgres::PostgresStorage; use deadpool_redis::Runtime; use gitdata::config::database::DatabaseConfig; use gitdata::config::database::PgConfig; -use sea_orm::ConnectOptions; +use sea_orm::{ConnectOptions}; use sea_orm::Database; use sea_orm::DatabaseConnection; +use sea_orm_migration::MigratorTrait; use tracing::debug; use tracing::info; - +use gitdata::model::migrate::DatabaseMigrate; use crate::jobs::email::EmailJobs; use crate::jobs::email::send_email; @@ -44,12 +45,16 @@ impl AppState { let active_write = database_conn(config.pg.get("ActiveWrite").unwrap().clone()).await?; info!("Connect Deprecated Database Pool"); let deprecated = database_conn(config.pg.get("Deprecated").unwrap().clone()).await?; + let jobs_margin = database_conn(config.pg.get("Jobs").unwrap().clone()).await?; info!("Connect Jobs Database Pool"); let jobs_pool = apalis_sql::postgres::PgPool::connect(&*config.pg.get("Jobs").unwrap().format()) .await?; let mut pg = PostgresStorage::new_with_config(jobs_pool.clone(), Config::new("apalis::Email")); + let migrator = PostgresStorage::migrations(); + let mut job_txn = jobs_margin.get_postgres_connection_pool().acquire().await?; + migrator.run(&mut job_txn).await?; let mut listener = PgListen::new(jobs_pool).await?; listener.subscribe_with(&mut pg); tokio::spawn(async move { @@ -98,5 +103,10 @@ async fn database_conn(url : PgConfig) -> anyhow::Result { .sqlx_logging(true) .sqlx_logging_level(url.level()); let db = Database::connect(opt).await?; + if url.dbname.to_lowercase() != "jobs".to_string() { + DatabaseMigrate::install(&db).await?; + DatabaseMigrate::up(&db, None).await?; + } Ok(db) } + diff --git a/gitdata-apis/src/service/repository/new_repo.rs b/gitdata-apis/src/service/repository/new_repo.rs index c8f9592..8ffff6f 100644 --- a/gitdata-apis/src/service/repository/new_repo.rs +++ b/gitdata-apis/src/service/repository/new_repo.rs @@ -15,7 +15,8 @@ pub struct RepoCreateParam { } impl AppState { - pub async fn repository_new(&self) { + pub async fn repository_new(&self, param : RepoCreateParam) { + todo!() } } diff --git a/gitdata-apis/src/service/users/apply.rs b/gitdata-apis/src/service/users/apply.rs index 28c5d1c..029cb5b 100644 --- a/gitdata-apis/src/service/users/apply.rs +++ b/gitdata-apis/src/service/users/apply.rs @@ -4,7 +4,7 @@ use sea_orm::Set; use sea_orm::prelude::Uuid; use serde::Deserialize; use serde::Serialize; - +use sha256::Sha256Digest; use crate::service::AppState; #[derive(Deserialize, Serialize, Clone)] @@ -61,7 +61,7 @@ impl AppState { } _ => {} }; - let mut model = users::ActiveModel::new_users(param.username, param.email, param.passwd); + let mut model = users::ActiveModel::new_users(param.username, param.email, param.passwd.digest()); model.state = Set("Active".to_string()); loop { match self diff --git a/gitdata-health/Cargo.toml b/gitdata-health/Cargo.toml new file mode 100644 index 0000000..fab92df --- /dev/null +++ b/gitdata-health/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "gitdata-health" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/gitdata-health/src/main.rs b/gitdata-health/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/gitdata-health/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/gitdata-proxy/Cargo.toml b/gitdata-proxy/Cargo.toml new file mode 100644 index 0000000..dedb7cf --- /dev/null +++ b/gitdata-proxy/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "gitdata-proxy" +version = "0.1.0" +edition = "2024" + +[dependencies] +hyper-reverse-proxy = "0.5" +hyper = { version = "~0.14", features = ["full"] } +tokio = { version = "1", features = ["full"] } + +gitdata = { path = ".."} \ No newline at end of file diff --git a/gitdata-proxy/src/main.rs b/gitdata-proxy/src/main.rs new file mode 100644 index 0000000..b2638c0 --- /dev/null +++ b/gitdata-proxy/src/main.rs @@ -0,0 +1,5 @@ +use gitdata::config::database::DatabaseConfig; + +fn main() { + DatabaseConfig::default().write().unwrap(); +} diff --git a/libs/config/email.rs b/libs/config/email.rs new file mode 100644 index 0000000..1125f94 --- /dev/null +++ b/libs/config/email.rs @@ -0,0 +1,77 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize, Default, Clone)] +pub struct EmailConfig { + #[serde(default)] + pub smtp: String, + #[serde(default)] + pub port: u32, + #[serde(default)] + pub username: String, + #[serde(default)] + pub password: String, + #[serde(default)] + pub from: String, +} + +impl EmailConfig { + pub fn new( + smtp: String, + port: u32, + username: String, + password: String, + from: String, + ) -> Self { + EmailConfig { + smtp, + port, + username, + password, + from, + } + } + pub fn save(&self) -> anyhow::Result<()> { + let config_file = std::env::var("CONFIG_FILE").unwrap_or("./config/email.toml".to_string()); + std::fs::write(config_file, toml::to_string_pretty(self)?)?; + Ok(()) + } + pub fn load() -> anyhow::Result { + let config_file = std::env::var("CONFIG_FILE").unwrap_or("./config/email.toml".to_string()); + let config = std::fs::read_to_string(config_file)?; + Ok(toml::from_str(&config)?) + } + pub fn check(&self) -> anyhow::Result<()> { + if self.smtp.is_empty() { + Self::default().save()?; + return Err(anyhow::anyhow!("smtp is empty")); + } + if self.port == 0 { + Self::default().save()?; + return Err(anyhow::anyhow!("port is empty")); + } + if self.username.is_empty() { + Self::default().save()?; + return Err(anyhow::anyhow!("username is empty")); + } + if self.password.is_empty() { + Self::default().save()?; + return Err(anyhow::anyhow!("password is empty")); + } + if self.from.is_empty() { + Self::default().save()?; + return Err(anyhow::anyhow!("from is empty")); + } + Ok(()) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_email_config() { + EmailConfig::default().save().ok(); + } +} \ No newline at end of file diff --git a/libs/config/mod.rs b/libs/config/mod.rs index c9322e4..f20a7d3 100644 --- a/libs/config/mod.rs +++ b/libs/config/mod.rs @@ -7,6 +7,8 @@ pub mod api; pub mod database; pub mod git; pub mod rpc; +pub mod email; + pub struct GitDataConfig {} impl GitDataConfig { diff --git a/libs/model/migrate/mod.rs b/libs/model/migrate/mod.rs new file mode 100644 index 0000000..2925fb4 --- /dev/null +++ b/libs/model/migrate/mod.rs @@ -0,0 +1,15 @@ +use async_trait::async_trait; +use sea_orm_migration::{MigrationTrait, MigratorTrait}; + +mod users; + + +pub struct DatabaseMigrate; +#[async_trait] +impl MigratorTrait for DatabaseMigrate { + fn migrations() -> Vec> { + vec![ + Box::new(users::UsersMigration) + ] + } +} \ No newline at end of file diff --git a/libs/model/migrate/users.rs b/libs/model/migrate/users.rs new file mode 100644 index 0000000..502eed2 --- /dev/null +++ b/libs/model/migrate/users.rs @@ -0,0 +1,311 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct UsersMigration; + +#[async_trait::async_trait] +impl MigrationTrait for UsersMigration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Email::Table) + .if_not_exists() + .col( + ColumnDef::new(Email::Uid) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Email::UserUid).uuid().not_null()) + .col(ColumnDef::new(Email::Name).string().not_null()) + .col(ColumnDef::new(Email::Email).string().not_null()) + .col( + ColumnDef::new(Email::CreatedAt) + .date_time() + .not_null() + .extra("DEFAULT CURRENT_TIMESTAMP".to_string()), + ) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(Follow::Table) + .if_not_exists() + .col( + ColumnDef::new(Follow::Uid) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Follow::UserUid).uuid().not_null()) + .col(ColumnDef::new(Follow::TargetUid).uuid().not_null()) + .col( + ColumnDef::new(Follow::CreatedAt) + .date_time() + .not_null() + .extra("DEFAULT CURRENT_TIMESTAMP".to_string()), + ) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(SshKeys::Table) + .if_not_exists() + .col( + ColumnDef::new(SshKeys::Uid) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(SshKeys::UserUid).uuid().not_null()) + .col(ColumnDef::new(SshKeys::Name).string().not_null()) + .col(ColumnDef::new(SshKeys::Description).string().null()) + .col(ColumnDef::new(SshKeys::SshKey).string().not_null()) + .col(ColumnDef::new(SshKeys::CreatedAt).big_integer().not_null()) + .col(ColumnDef::new(SshKeys::UpdatedAt).big_integer().not_null()) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(Stars::Table) + .if_not_exists() + .col( + ColumnDef::new(Stars::Uid) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Stars::RepositoryUid).uuid().not_null()) + .col(ColumnDef::new(Stars::UserUid).uuid().not_null()) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(Token::Table) + .if_not_exists() + .col( + ColumnDef::new(Token::Uid) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Token::Name).string().not_null()) + .col(ColumnDef::new(Token::Description).string().null()) + .col(ColumnDef::new(Token::UserUid).uuid().not_null()) + .col(ColumnDef::new(Token::Token).string().not_null()) + .col(ColumnDef::new(Token::Access).integer().not_null()) + .col(ColumnDef::new(Token::CreatedAt).big_integer().not_null()) + .col(ColumnDef::new(Token::UpdatedAt).big_integer().not_null()) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(Users::Table) + .if_not_exists() + .col( + ColumnDef::new(Users::Uid) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Users::Username).string().not_null()) + .col(ColumnDef::new(Users::Name).string().not_null()) + .col(ColumnDef::new(Users::MainEmail).string().not_null()) + .col(ColumnDef::new(Users::EmailVisible).boolean().not_null()) + .col(ColumnDef::new(Users::HashPass).string().not_null()) + .col(ColumnDef::new(Users::Mindset).string().null()) + .col(ColumnDef::new(Users::State).string().not_null()) + .col(ColumnDef::new(Users::AvatarUrl).string().null()) + .col(ColumnDef::new(Users::Company).string().null()) + .col(ColumnDef::new(Users::JobTitle).string().null()) + .col(ColumnDef::new(Users::Website).string().null()) + .col(ColumnDef::new(Users::Social).array(ColumnType::Text).null()) + .col(ColumnDef::new(Users::Bio).string().null()) + .col(ColumnDef::new(Users::Location).string().null()) + .col(ColumnDef::new(Users::Appellative).string().null()) + .col(ColumnDef::new(Users::Topic).array(ColumnType::Text).null()) + .col(ColumnDef::new(Users::Pinned).array(ColumnType::Uuid).null()) + .col(ColumnDef::new(Users::RepositoryLimit).integer().not_null()) + .col(ColumnDef::new(Users::CreatedAt).big_integer().not_null()) + .col(ColumnDef::new(Users::UpdatedAt).big_integer().not_null()) + .col(ColumnDef::new(Users::LastUsed).big_integer().null()) + .col(ColumnDef::new(Users::Professional).boolean().not_null()) + .col(ColumnDef::new(Users::ProfessionalEndTime).big_integer().null()) + .col(ColumnDef::new(Users::ProfessionalStartTime).big_integer().null()) + .col(ColumnDef::new(Users::Organize).boolean().not_null()) + .col(ColumnDef::new(Users::Member).array(ColumnType::Uuid).null()) + .col(ColumnDef::new(Users::Team).array(ColumnType::Uuid).null()) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(Watch::Table) + .if_not_exists() + .col( + ColumnDef::new(Watch::Uid) + .uuid() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Watch::UserUid).uuid().not_null()) + .col(ColumnDef::new(Watch::RepositoryUid).uuid().not_null()) + .col(ColumnDef::new(Watch::CreatedAt).big_integer().not_null()) + .to_owned(), + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Email::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Follow::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(SshKeys::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Stars::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Token::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Users::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Watch::Table).to_owned()) + .await?; + + Ok(()) + } +} + +#[derive(DeriveIden)] +enum Email { + #[sea_orm(iden = "emails")] + Table, + Uid, + UserUid, + Name, + Email, + CreatedAt, +} + +#[derive(DeriveIden)] +enum Follow { + #[sea_orm(iden = "follows")] + Table, + Uid, + UserUid, + TargetUid, + CreatedAt, +} + +#[derive(DeriveIden)] +enum SshKeys { + #[sea_orm(iden = "ssh_keys")] + Table, + Uid, + UserUid, + Name, + Description, + SshKey, + CreatedAt, + UpdatedAt, +} + +#[derive(DeriveIden)] +enum Stars { + #[sea_orm(iden = "stars")] + Table, + Uid, + RepositoryUid, + UserUid, +} + +#[derive(DeriveIden)] +enum Token { + #[sea_orm(iden = "token_keys")] + Table, + Uid, + Name, + Description, + UserUid, + Token, + Access, + CreatedAt, + UpdatedAt, +} + +#[derive(DeriveIden)] +enum Users { + #[sea_orm(iden = "users")] + Table, + Uid, + Username, + Name, + MainEmail, + EmailVisible, + HashPass, + Mindset, + State, + AvatarUrl, + Company, + JobTitle, + Website, + Social, + Bio, + Location, + Appellative, + Topic, + Pinned, + RepositoryLimit, + CreatedAt, + UpdatedAt, + LastUsed, + Professional, + ProfessionalEndTime, + ProfessionalStartTime, + Organize, + Member, + Team, +} + +#[derive(DeriveIden)] +enum Watch { + #[sea_orm(iden = "watch")] + Table, + Uid, + UserUid, + RepositoryUid, + CreatedAt, +} \ No newline at end of file diff --git a/libs/model/mod.rs b/libs/model/mod.rs index 329e235..83a521c 100644 --- a/libs/model/mod.rs +++ b/libs/model/mod.rs @@ -17,3 +17,4 @@ pub mod pr; pub mod reactions; pub mod repository; pub mod users; +pub mod migrate; diff --git a/libs/model/repository/repository.rs b/libs/model/repository/repository.rs index dd74510..c366edd 100644 --- a/libs/model/repository/repository.rs +++ b/libs/model/repository/repository.rs @@ -56,3 +56,42 @@ impl Entity { Entity::find().filter(Column::Uid.eq(uid)) } } + + +impl ActiveModel { + pub fn new( + name: String, + owner_uid: Uuid, + description: Option, + visible: bool, + default_branch: Option, + ) -> Self { + Self { + uid: Set(Uuid::new_v4()), + name: Set(name.clone()), + owner_uid: Set(owner_uid), + description: Set(description), + visible: Set(visible), + default_branch: Set(default_branch.unwrap_or("".to_string())), + template: Set(false), + mirrors: Set(false), + archive: Set(false), + archive_time: Set(None), + ssh_path: Set(format!("git@gitdata.ai:{}/{}", owner_uid, name)), + http_path: Set(format!("https://gitdata.ai/{}/{}", owner_uid, name)), + storage_node: Set("".to_string()), + fork: Set(false), + fork_uid: Set(None), + nums_star: Set(0), + nums_fork: Set(0), + nums_watch: Set(0), + nums_issue: Set(0), + nums_pull: Set(0), + nums_commit: Set(0), + head: Set("".parse().unwrap()), + license: Set(vec![]), + created_at: Set(chrono::Utc::now().timestamp()), + updated_at: Set(chrono::Utc::now().timestamp()), + } + } +} \ No newline at end of file diff --git a/libs/model/users/emails.rs b/libs/model/users/emails.rs index f0719c5..2079023 100644 --- a/libs/model/users/emails.rs +++ b/libs/model/users/emails.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, DeriveEntityModel)] -#[sea_orm(table_name = "email")] +#[sea_orm(table_name = "emails")] pub struct Model { #[sea_orm(primary_key)] pub uid: Uuid, diff --git a/libs/model/users/follow.rs b/libs/model/users/follow.rs index 77c12af..7f4859f 100644 --- a/libs/model/users/follow.rs +++ b/libs/model/users/follow.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, DeriveEntityModel)] -#[sea_orm(table_name = "follow")] +#[sea_orm(table_name = "follows")] pub struct Model { #[sea_orm(primary_key)] pub uid: Uuid, diff --git a/libs/model/users/token_key.rs b/libs/model/users/token_key.rs index e46b881..439f02a 100644 --- a/libs/model/users/token_key.rs +++ b/libs/model/users/token_key.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, DeriveEntityModel)] -#[sea_orm(table_name = "token")] +#[sea_orm(table_name = "token_keys")] pub struct Model { #[sea_orm(primary_key)] pub uid: Uuid, diff --git a/libs/model/users/users.rs b/libs/model/users/users.rs index cfdaa02..445f3e9 100644 --- a/libs/model/users/users.rs +++ b/libs/model/users/users.rs @@ -36,7 +36,7 @@ pub struct Model { pub topic: Vec, pub pinned: Vec, - pub repository_limit: u64, // default 20 + pub repository_limit: i32, // default 20 pub created_at: i64, pub updated_at: i64, diff --git a/libs/model/users/watch.rs b/libs/model/users/watch.rs index 4049018..74b8623 100644 --- a/libs/model/users/watch.rs +++ b/libs/model/users/watch.rs @@ -18,7 +18,7 @@ pub struct Model { pub uid: Uuid, pub user_uid: Uuid, pub repository_uid: Uuid, - pub created_at: chrono::DateTime, + pub created_at: i64, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} diff --git a/libs/rpc/core_git.rs b/libs/rpc/core_git.rs new file mode 100644 index 0000000..fb1d1ba --- /dev/null +++ b/libs/rpc/core_git.rs @@ -0,0 +1,341 @@ +// This file is @generated by prost-build. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryStoragePosition { + #[prost(enumeration = "RepositoryStoragePositionType", tag = "1")] + pub r#type: i32, + #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub node: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryCreate { + #[prost(message, optional, tag = "1")] + pub storage_position: ::core::option::Option, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RepositoryStoragePositionType { + Local = 0, + S3 = 1, + Nfs = 2, +} +impl RepositoryStoragePositionType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Local => "LOCAL", + Self::S3 => "S3", + Self::Nfs => "NFS", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "LOCAL" => Some(Self::Local), + "S3" => Some(Self::S3), + "NFS" => Some(Self::Nfs), + _ => None, + } + } +} +/// Generated client implementations. +pub mod rep_repository_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + #[derive(Debug, Clone)] + pub struct RepRepositoryClient { + inner: tonic::client::Grpc, + } + impl RepRepositoryClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl RepRepositoryClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> RepRepositoryClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + RepRepositoryClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + pub async fn create( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/core_git.RepRepository/Create", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("core_git.RepRepository", "Create")); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +pub mod rep_repository_server { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with RepRepositoryServer. + #[async_trait] + pub trait RepRepository: std::marker::Send + std::marker::Sync + 'static { + async fn create( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + #[derive(Debug)] + pub struct RepRepositoryServer { + inner: Arc, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + impl RepRepositoryServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for RepRepositoryServer + where + T: RepRepository, + B: Body + std::marker::Send + 'static, + B::Error: Into + std::marker::Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + match req.uri().path() { + "/core_git.RepRepository/Create" => { + #[allow(non_camel_case_types)] + struct CreateSvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService + for CreateSvc { + type Response = super::RepositoryStoragePosition; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = CreateSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + let mut response = http::Response::new(empty_body()); + let headers = response.headers_mut(); + headers + .insert( + tonic::Status::GRPC_STATUS, + (tonic::Code::Unimplemented as i32).into(), + ); + headers + .insert( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ); + Ok(response) + }) + } + } + } + } + impl Clone for RepRepositoryServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + /// Generated gRPC service name + pub const SERVICE_NAME: &str = "core_git.RepRepository"; + impl tonic::server::NamedService for RepRepositoryServer { + const NAME: &'static str = SERVICE_NAME; + } +} diff --git a/libs/rpc/git_core.rs b/libs/rpc/git_core.rs new file mode 100644 index 0000000..026f993 --- /dev/null +++ b/libs/rpc/git_core.rs @@ -0,0 +1,670 @@ +// This file is @generated by prost-build. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryStoragePosition { + #[prost(enumeration = "RepositoryStoragePositionType", tag = "1")] + pub r#type: i32, + #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub node: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryCreate { + #[prost(message, optional, tag = "1")] + pub storage_position: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PathRequest { + #[prost(string, tag = "1")] + pub owner: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub repo: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TokenRequest { + #[prost(string, tag = "1")] + pub owner: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub repo: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub token: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublickeyRequest { + #[prost(string, tag = "1")] + pub owner: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub repo: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub publickey: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryList { + #[prost(message, repeated, tag = "1")] + pub repositories: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Repository { + #[prost(string, tag = "1")] + pub uid: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub description: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub owner_uid: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub default_branch: ::prost::alloc::string::String, + #[prost(bool, tag = "6")] + pub visible: bool, + #[prost(bool, tag = "7")] + pub template: bool, + #[prost(bool, tag = "8")] + pub mirrors: bool, + #[prost(bool, tag = "9")] + pub archive: bool, + #[prost(int64, tag = "10")] + pub archive_time: i64, + #[prost(string, tag = "11")] + pub ssh_path: ::prost::alloc::string::String, + #[prost(string, tag = "12")] + pub http_path: ::prost::alloc::string::String, + #[prost(bool, tag = "13")] + pub fork: bool, + #[prost(string, tag = "25")] + pub storage_node: ::prost::alloc::string::String, + #[prost(string, tag = "14")] + pub fork_uid: ::prost::alloc::string::String, + #[prost(int64, tag = "15")] + pub nums_star: i64, + #[prost(int64, tag = "16")] + pub nums_fork: i64, + #[prost(int64, tag = "17")] + pub nums_watch: i64, + #[prost(int64, tag = "18")] + pub nums_issue: i64, + #[prost(int64, tag = "19")] + pub nums_pull: i64, + #[prost(int64, tag = "20")] + pub nums_commit: i64, + #[prost(string, tag = "21")] + pub head: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "22")] + pub license: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(int64, tag = "23")] + pub created_at: i64, + #[prost(int64, tag = "24")] + pub updated_at: i64, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RepositoryAccess { + None = 0, + Read = 1, + Write = 2, + Admin = 3, +} +impl RepositoryAccess { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::None => "NONE", + Self::Read => "READ", + Self::Write => "WRITE", + Self::Admin => "ADMIN", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "NONE" => Some(Self::None), + "READ" => Some(Self::Read), + "WRITE" => Some(Self::Write), + "ADMIN" => Some(Self::Admin), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RepositoryStoragePositionType { + Local = 0, + S3 = 1, + Nfs = 2, +} +impl RepositoryStoragePositionType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Local => "LOCAL", + Self::S3 => "S3", + Self::Nfs => "NFS", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "LOCAL" => Some(Self::Local), + "S3" => Some(Self::S3), + "NFS" => Some(Self::Nfs), + _ => None, + } + } +} +/// Generated client implementations. +pub mod rep_repository_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + #[derive(Debug, Clone)] + pub struct RepRepositoryClient { + inner: tonic::client::Grpc, + } + impl RepRepositoryClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl RepRepositoryClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> RepRepositoryClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + RepRepositoryClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + pub async fn path( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/git_core.RepRepository/Path", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("git_core.RepRepository", "Path")); + self.inner.unary(req, path, codec).await + } + pub async fn token( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/git_core.RepRepository/Token", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("git_core.RepRepository", "Token")); + self.inner.unary(req, path, codec).await + } + pub async fn publickey( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/git_core.RepRepository/Publickey", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("git_core.RepRepository", "Publickey")); + self.inner.unary(req, path, codec).await + } + pub async fn create( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/git_core.RepRepository/Create", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("git_core.RepRepository", "Create")); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +pub mod rep_repository_server { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with RepRepositoryServer. + #[async_trait] + pub trait RepRepository: std::marker::Send + std::marker::Sync + 'static { + async fn path( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn token( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + async fn publickey( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + async fn create( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + #[derive(Debug)] + pub struct RepRepositoryServer { + inner: Arc, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + impl RepRepositoryServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for RepRepositoryServer + where + T: RepRepository, + B: Body + std::marker::Send + 'static, + B::Error: Into + std::marker::Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + match req.uri().path() { + "/git_core.RepRepository/Path" => { + #[allow(non_camel_case_types)] + struct PathSvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService for PathSvc { + type Response = super::RepositoryStoragePosition; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::path(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = PathSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/git_core.RepRepository/Token" => { + #[allow(non_camel_case_types)] + struct TokenSvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService for TokenSvc { + type Response = super::RepositoryList; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::token(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = TokenSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/git_core.RepRepository/Publickey" => { + #[allow(non_camel_case_types)] + struct PublickeySvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService + for PublickeySvc { + type Response = super::RepositoryList; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::publickey(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = PublickeySvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/git_core.RepRepository/Create" => { + #[allow(non_camel_case_types)] + struct CreateSvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService + for CreateSvc { + type Response = super::RepositoryStoragePosition; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = CreateSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + let mut response = http::Response::new(empty_body()); + let headers = response.headers_mut(); + headers + .insert( + tonic::Status::GRPC_STATUS, + (tonic::Code::Unimplemented as i32).into(), + ); + headers + .insert( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ); + Ok(response) + }) + } + } + } + } + impl Clone for RepRepositoryServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + /// Generated gRPC service name + pub const SERVICE_NAME: &str = "git_core.RepRepository"; + impl tonic::server::NamedService for RepRepositoryServer { + const NAME: &'static str = SERVICE_NAME; + } +} From 4ffebbb5e91246176117bcd4b8660da911742476 Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Fri, 17 Jan 2025 02:56:11 +0800 Subject: [PATCH 2/4] :construction:Some messy work --- .gitignore | 3 +- gitdata-apis/src/apis/app_router.rs | 106 +++++++++---- gitdata-apis/src/apis/v1/auth/passwd.rs | 1 + gitdata-apis/src/jobs/email.rs | 44 +++--- gitdata-apis/src/main.rs | 22 +-- gitdata-apis/src/service/auth/passwd.rs | 2 +- gitdata-apis/src/service/core_git_rpc.rs | 14 ++ gitdata-apis/src/service/emails/captcha.rs | 2 +- gitdata-apis/src/service/mod.rs | 6 +- .../src/service/repository/new_repo.rs | 78 +++++++++- gitdata-apis/src/service/users/apply.rs | 4 +- gitdata-gits/src/main.rs | 19 ++- gitdata-gits/src/mount/local.rs | 115 +++++++++++++- gitdata-gits/src/mount/mod.rs | 12 +- gitdata-gits/src/mount/nfs.rs | 113 ++++++++++++++ gitdata-gits/src/mount/s3.rs | 113 ++++++++++++++ gitdata-gits/src/rpc/core_git.rs | 147 +++++++++++++++++- gitdata-gits/src/rpc/mod.rs | 4 - libs/config/git.rs | 58 +++++++ libs/model/repository/repository.rs | 6 +- libs/rpc/core_git.rs | 100 ++++++++++++ proto/core-git.proto | 15 ++ 22 files changed, 882 insertions(+), 102 deletions(-) diff --git a/.gitignore b/.gitignore index a44616b..b2147d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target .idea logs/** -config/** \ No newline at end of file +config/** +data/** \ No newline at end of file diff --git a/gitdata-apis/src/apis/app_router.rs b/gitdata-apis/src/apis/app_router.rs index 1fa54cd..2494671 100644 --- a/gitdata-apis/src/apis/app_router.rs +++ b/gitdata-apis/src/apis/app_router.rs @@ -1,4 +1,6 @@ -use actix_web::web::{delete, get, post}; +use actix_web::web::delete; +use actix_web::web::get; +use actix_web::web::post; use actix_web::web::scope; #[allow(non_snake_case)] @@ -8,56 +10,92 @@ pub fn AppRouter(cfg : &mut actix_web::web::ServiceConfig) { scope("/v1") .service( scope("/auth") - .route("/passwd", post().to(crate::apis::v1::auth::passwd::api_v1_auth_passwd)) - .route("/logout", post().to(crate::apis::v1::auth::logout::api_v1_auth_logout)), + .route( + "/passwd", + post().to(crate::apis::v1::auth::passwd::api_v1_auth_passwd), + ) + .route( + "/logout", + post().to(crate::apis::v1::auth::logout::api_v1_auth_logout), + ), ) .service( scope("/email") - .route("/captcha", post().to(crate::apis::v1::email::captcha::api_v1_email_captcha_post)) - .route("/captcha/check", post().to(crate::apis::v1::email::captcha::api_v1_email_captcha_check)), + .route( + "/captcha", + post().to(crate::apis::v1::email::captcha::api_v1_email_captcha_post), + ) + .route( + "/captcha/check", + post().to(crate::apis::v1::email::captcha::api_v1_email_captcha_check), + ), ) .service( - scope("/user").service( - scope("/setting") - .route( - "/basic", - post().to(super::v1::user::setting::api_v1_user_setting_basic), - ) - .route( - "/topic", - post().to(super::v1::user::setting::api_v1_user_setting_topic), - ) - .route( - "/avatar", - post().to(super::v1::user::setting::api_v1_user_setting_avatar), - ) - .route( - "/pined", - post().to(super::v1::user::setting::api_v1_user_setting_pinned), - ) - , - ).route("/apply", post().to(super::v1::user::apply::api_v1_user_apply)) + scope("/user") + .service( + scope("/setting") + .route( + "/basic", + post().to(super::v1::user::setting::api_v1_user_setting_basic), + ) + .route( + "/topic", + post().to(super::v1::user::setting::api_v1_user_setting_topic), + ) + .route( + "/avatar", + post().to(super::v1::user::setting::api_v1_user_setting_avatar), + ) + .route( + "/pined", + post().to(super::v1::user::setting::api_v1_user_setting_pinned), + ), + ) + .route( + "/apply", + post().to(super::v1::user::apply::api_v1_user_apply), + ) .service( scope("/info") .route("", get().to(super::v1::user::info::api_v1_users_info)) .route( "/{username}", get().to(super::v1::user::info::api_v1_users_info_by_username), - ) + ), ) .service( scope("/ssh") - .route("", get().to(super::v1::user::ssh_keys::api_v1_user_ssh_key_list)) - .route("/{ssh_key_uid}", delete().to(super::v1::user::ssh_keys::api_v1_user_ssh_key_delete)) - .route("", post().to(super::v1::user::ssh_keys::api_v1_user_ssh_key_create)) + .route( + "", + get().to(super::v1::user::ssh_keys::api_v1_user_ssh_key_list), + ) + .route( + "/{ssh_key_uid}", + delete() + .to(super::v1::user::ssh_keys::api_v1_user_ssh_key_delete), + ) + .route( + "", + post() + .to(super::v1::user::ssh_keys::api_v1_user_ssh_key_create), + ), ) .service( scope("token") - .route("", post().to(super::v1::user::token_key::api_v1_user_token_create)) - .route("/{token_uid}", delete().to(super::v1::user::token_key::api_v1_user_token_delete)) - .route("", get().to(super::v1::user::token_key::api_v1_user_token_list)) - ) - , + .route( + "", + post().to(super::v1::user::token_key::api_v1_user_token_create), + ) + .route( + "/{token_uid}", + delete() + .to(super::v1::user::token_key::api_v1_user_token_delete), + ) + .route( + "", + get().to(super::v1::user::token_key::api_v1_user_token_list), + ), + ), ), ), ); diff --git a/gitdata-apis/src/apis/v1/auth/passwd.rs b/gitdata-apis/src/apis/v1/auth/passwd.rs index cc7bb94..5b825be 100644 --- a/gitdata-apis/src/apis/v1/auth/passwd.rs +++ b/gitdata-apis/src/apis/v1/auth/passwd.rs @@ -5,6 +5,7 @@ use actix_web::HttpRequest; use actix_web::Responder; use actix_web::web; use sha256::Sha256Digest; + use crate::apis::app_writer::AppWrite; use crate::service::AppState; use crate::service::auth::AuthInner; diff --git a/gitdata-apis/src/jobs/email.rs b/gitdata-apis/src/jobs/email.rs index 5cad803..4207b88 100644 --- a/gitdata-apis/src/jobs/email.rs +++ b/gitdata-apis/src/jobs/email.rs @@ -1,13 +1,19 @@ use std::io; -use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor}; -use lettre::message::header::ContentType; + +use gitdata::config::email::EmailConfig; +use lettre::AsyncSmtpTransport; +use lettre::AsyncTransport; +use lettre::Message; +use lettre::Tokio1Executor; use lettre::message::Mailbox; +use lettre::message::header::ContentType; use lettre::transport::smtp::authentication::Credentials; use serde::Deserialize; use serde::Serialize; use tokio::sync::OnceCell; -use tracing::{error, info}; -use gitdata::config::email::EmailConfig; +use tracing::error; +use tracing::info; + use crate::public::CAPTCHA; use crate::public::USER_FOR_GET_PASSWD; @@ -18,7 +24,7 @@ pub struct EmailJobs { pub to : Mailbox, pub subject : String, pub body : EmailType, - pub code: String, + pub code : String, } impl EmailJobs { @@ -28,7 +34,7 @@ impl EmailJobs { to : Mailbox, subject : String, body : EmailType, - code: String, + code : String, ) -> Self { EmailJobs { from, @@ -73,30 +79,28 @@ impl EmailType { } } -static EMAIL: OnceCell = OnceCell::const_new(); +static EMAIL : OnceCell = OnceCell::const_new(); #[derive(Clone)] -struct EmailServer{ - cred: AsyncSmtpTransport +struct EmailServer { + cred : AsyncSmtpTransport, } impl EmailServer { pub fn init() -> EmailServer { let config = EmailConfig::load().expect("Failed to load email config"); - let creds = - Credentials::new(config.username.to_owned(), config.password.to_owned()); - let mailer: AsyncSmtpTransport = + let creds = Credentials::new(config.username.to_owned(), config.password.to_owned()); + let mailer : AsyncSmtpTransport = AsyncSmtpTransport::::relay(&config.smtp) .unwrap() .credentials(creds) .build(); - EmailServer{ - cred: mailer - } + EmailServer { cred : mailer } } - pub async fn get() -> Self{ - EMAIL.get_or_init(|| async { - EmailServer::init() - }).await.clone() + pub async fn get() -> Self { + EMAIL + .get_or_init(|| async { EmailServer::init() }) + .await + .clone() } -} \ No newline at end of file +} diff --git a/gitdata-apis/src/main.rs b/gitdata-apis/src/main.rs index 19aa829..44fb062 100644 --- a/gitdata-apis/src/main.rs +++ b/gitdata-apis/src/main.rs @@ -1,15 +1,17 @@ -use actix_session::config::{CookieContentSecurity, PersistentSession, TtlExtensionPolicy}; use actix_session::SessionMiddleware; +use actix_session::config::CookieContentSecurity; +use actix_session::config::PersistentSession; +use actix_session::config::TtlExtensionPolicy; use actix_session::storage::RedisSessionStore; use actix_settings::ApplySettings; use actix_web::App; +use actix_web::HttpServer; use actix_web::cookie::Key; use actix_web::cookie::time::Duration; -use actix_web::HttpServer; use actix_web::web; +use gitdata_apis::apis::app_router::AppRouter; use gitdata_apis::service::AppState; use tracing::info; -use gitdata_apis::apis::app_router::AppRouter; const CONFIG_FILE : &str = "./config/api.toml"; #[tokio::main] @@ -29,7 +31,7 @@ async fn main() -> anyhow::Result<()> { .await?; info!("Redis session store initialized."); info!("API server started."); - HttpServer::new(move || + HttpServer::new(move || { App::new() .wrap(actix_identity::IdentityMiddleware::default()) .wrap( @@ -49,11 +51,11 @@ async fn main() -> anyhow::Result<()> { .wrap(actix_web::middleware::Logger::default()) .app_data(web::Data::new(state.clone())) .configure(AppRouter) - ) - .try_apply_settings(&api_config) - .expect("Failed to apply settings") - .run() - .await - .expect("Failed to start server"); + }) + .try_apply_settings(&api_config) + .expect("Failed to apply settings") + .run() + .await + .expect("Failed to start server"); Ok(()) } diff --git a/gitdata-apis/src/service/auth/passwd.rs b/gitdata-apis/src/service/auth/passwd.rs index 8b02010..12b153b 100644 --- a/gitdata-apis/src/service/auth/passwd.rs +++ b/gitdata-apis/src/service/auth/passwd.rs @@ -5,7 +5,7 @@ use sha256::Sha256Digest; use crate::service::AppState; use crate::service::users::info::UsersInfoReplay; -#[derive(Deserialize, Clone,Debug)] +#[derive(Deserialize, Clone, Debug)] pub struct UsersAuthPasswdParam { pub username : String, pub password : String, diff --git a/gitdata-apis/src/service/core_git_rpc.rs b/gitdata-apis/src/service/core_git_rpc.rs index 0fe5a56..86cf481 100644 --- a/gitdata-apis/src/service/core_git_rpc.rs +++ b/gitdata-apis/src/service/core_git_rpc.rs @@ -4,6 +4,8 @@ use tokio::sync::OnceCell; pub static RPC_CLIENT : OnceCell = OnceCell::const_new(); + +#[derive(Clone,Debug)] pub struct CoreGitRpc { pub client : RepRepositoryClient, } @@ -24,3 +26,15 @@ impl CoreGitRpc { }) } } + + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_rpc() { + let rpc = CoreGitRpc::get().await; + dbg!(rpc); + } +} \ No newline at end of file diff --git a/gitdata-apis/src/service/emails/captcha.rs b/gitdata-apis/src/service/emails/captcha.rs index 0968809..e79768f 100644 --- a/gitdata-apis/src/service/emails/captcha.rs +++ b/gitdata-apis/src/service/emails/captcha.rs @@ -42,7 +42,7 @@ impl AppState { email, "GitData 验证码".parse()?, EmailType::RegistrationVerificationCode, - captcha.to_string() + captcha.to_string(), ); let mut email_jobs = self .email_jobs diff --git a/gitdata-apis/src/service/mod.rs b/gitdata-apis/src/service/mod.rs index e7b47a7..a7b80a0 100644 --- a/gitdata-apis/src/service/mod.rs +++ b/gitdata-apis/src/service/mod.rs @@ -10,13 +10,14 @@ use apalis_sql::postgres::PostgresStorage; use deadpool_redis::Runtime; use gitdata::config::database::DatabaseConfig; use gitdata::config::database::PgConfig; -use sea_orm::{ConnectOptions}; +use gitdata::model::migrate::DatabaseMigrate; +use sea_orm::ConnectOptions; use sea_orm::Database; use sea_orm::DatabaseConnection; use sea_orm_migration::MigratorTrait; use tracing::debug; use tracing::info; -use gitdata::model::migrate::DatabaseMigrate; + use crate::jobs::email::EmailJobs; use crate::jobs::email::send_email; @@ -109,4 +110,3 @@ async fn database_conn(url : PgConfig) -> anyhow::Result { } Ok(db) } - diff --git a/gitdata-apis/src/service/repository/new_repo.rs b/gitdata-apis/src/service/repository/new_repo.rs index 8ffff6f..f4ee95f 100644 --- a/gitdata-apis/src/service/repository/new_repo.rs +++ b/gitdata-apis/src/service/repository/new_repo.rs @@ -1,9 +1,13 @@ +use crate::service::core_git_rpc::CoreGitRpc; +use crate::service::AppState; +use gitdata::model::repository::repository; +use gitdata::model::users::users; +use gitdata::rpc::core_git::{RepositoryAddFileRequest, RepositoryCreate, RepositoryStoragePosition, RepositoryStoragePositionType}; use sea_orm::prelude::Uuid; +use sea_orm::{ActiveModelTrait, TransactionTrait}; use serde::Deserialize; use serde::Serialize; -use crate::service::AppState; - #[derive(Deserialize, Serialize)] pub struct RepoCreateParam { pub name : String, @@ -12,11 +16,75 @@ pub struct RepoCreateParam { pub visible : bool, pub default_branch : Option, pub readme : bool, + pub node: String, + pub storage_position : i32, + pub message: Option, } impl AppState { - pub async fn repository_new(&self, param : RepoCreateParam) { - - todo!() + pub async fn repository_new(&self, user_model: users::Model, param : RepoCreateParam) -> anyhow::Result<()> { + let active_model = repository::ActiveModel::new( + param.name.clone(), + param.owner_uid, + param.description, + param.visible, + param.default_branch.clone(), + ); + let txn = self.active_write.begin().await?; + match active_model.clone().insert(&txn).await{ + Ok(_) => {}, + Err(e) => { + txn.rollback().await?; + return Err(e.into()) + } + }; + let mut client = match CoreGitRpc::get().await{ + Ok(client) => client.clone(), + Err(e) => { + txn.rollback().await?; + return Err(e); + } + }; + let mut node = RepositoryStoragePosition::default(); + node.node = param.node; + node.path = active_model.uid.unwrap().to_string(); + node.r#type = match RepositoryStoragePositionType::try_from(param.storage_position){ + Ok(t) => i32::from(t), + Err(e) => { + txn.rollback().await?; + return Err(e.into()); + } + }; + match client.client.create(RepositoryCreate { + storage_position: Some(node.clone()), + }).await { + Ok(_) => { + }, + Err(e) => { + txn.rollback().await?; + return Err(e.into()) + } + }; + if param.readme { + let bytes = format!("### {}", param.name); + match client.client.add_file(RepositoryAddFileRequest { + repository_storage_position: Some(node), + path: "/".to_string(), + content: bytes.into_bytes(), + email: user_model.main_email.clone(), + user: user_model.name.clone(), + message: param.message.unwrap_or("Create README.md".to_string()), + file_name: "README.md".to_string(), + branch: param.default_branch.unwrap_or("main".to_string()), + }).await { + Ok(_) => {}, + Err(e) => { + txn.rollback().await?; + return Err(e.into()) + } + } + } + txn.commit().await?; + Ok(()) } } diff --git a/gitdata-apis/src/service/users/apply.rs b/gitdata-apis/src/service/users/apply.rs index 029cb5b..9693fb0 100644 --- a/gitdata-apis/src/service/users/apply.rs +++ b/gitdata-apis/src/service/users/apply.rs @@ -5,6 +5,7 @@ use sea_orm::prelude::Uuid; use serde::Deserialize; use serde::Serialize; use sha256::Sha256Digest; + use crate::service::AppState; #[derive(Deserialize, Serialize, Clone)] @@ -61,7 +62,8 @@ impl AppState { } _ => {} }; - let mut model = users::ActiveModel::new_users(param.username, param.email, param.passwd.digest()); + let mut model = + users::ActiveModel::new_users(param.username, param.email, param.passwd.digest()); model.state = Set("Active".to_string()); loop { match self diff --git a/gitdata-gits/src/main.rs b/gitdata-gits/src/main.rs index c1d84e7..f1cd738 100644 --- a/gitdata-gits/src/main.rs +++ b/gitdata-gits/src/main.rs @@ -1,8 +1,8 @@ use std::io; -use gitdata::config; use log::info; use tonic::transport::Server; +use crate::mount::local::LocalStorage; pub mod health; pub mod http; @@ -14,17 +14,24 @@ pub mod ssh; async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt().init(); info!("starting gitdata"); - let pool = mount::StoragePool::new(); + let mut pool = mount::StoragePool::new(); + pool + .add_local( + "default".to_string(), + LocalStorage { + root : std::env::current_dir()?.join("./data"), + } + ); let http = tokio::spawn(http::http(pool.clone())); let health = tokio::spawn(async move { let health = gitdata::health::service::HealthService::default(); let core_git = rpc::core_git::CoreGit::new(pool.clone()); info!("starting health service"); Server::builder() - // .trace_fn(|x|{ - // info!("Url: {:?} Method: {}", x.uri(),x.method()); - // tracing::Span::current() - // }) + .trace_fn(|x|{ + info!("Url: {:?} Method: {}", x.uri(),x.method()); + tracing::Span::current() + }) .add_service(gitdata::rpc::health::health_server::HealthServer::new( health, )) diff --git a/gitdata-gits/src/mount/local.rs b/gitdata-gits/src/mount/local.rs index 4c706da..3556bdc 100644 --- a/gitdata-gits/src/mount/local.rs +++ b/gitdata-gits/src/mount/local.rs @@ -12,6 +12,7 @@ use async_fn_stream::fn_stream; use bytes::Bytes; use flate2::read::GzDecoder; use futures_core::Stream; +use git2::Signature; use crate::service::GitServiceType; @@ -98,7 +99,7 @@ impl LocalStorage { let mut decoder = GzDecoder::new(Cursor::new(payload)); let mut decoded_data = Vec::new(); if let Err(e) = io::copy(&mut decoder, &mut decoded_data) { - return Err(io::Error::new( + return Err(Error::new( io::ErrorKind::Other, format!("Error running command {}", e), )); @@ -168,4 +169,116 @@ impl LocalStorage { ), )) } + pub(crate) async fn create_repository(&self, path : String) -> anyhow::Result<()> { + if std::fs::read_dir(&self.root.join(path.clone())).is_ok() { + return Err(anyhow::anyhow!("Repository Path already exists")); + } + let git = git2::Repository::init_bare(&self.root.join(path.clone())); + if git.is_ok() { + Ok(()) + } else if let Err(r) = git { + Err(anyhow::anyhow!("{}", r)) + } else { + Err(anyhow::anyhow!("Unknown Error")) + } + } + pub(crate) async fn add_file( + &self, + path : String, + file_path : String, + bytes : Vec, + commit_email : String, + commit_users : String, + commit_message : String, + file_name : String, + branch_name : String, + ) -> anyhow::Result<()> { + use anyhow::Context; + + let path = self.root.join(path); + let tmp = tempfile::tempdir().context("Failed to create temporary directory")?; + let clone_repository = git2::Repository::clone(path.to_str().unwrap(), tmp.path()) + .context("Failed to clone repository")?; + + let branch = match clone_repository.find_branch(&branch_name, git2::BranchType::Local) { + Ok(branch) => branch, + Err(_) => { + let head_commit = clone_repository + .head() + .context("Failed to get HEAD")? + .peel_to_commit() + .context("Failed to peel HEAD to commit")?; + clone_repository + .branch(&branch_name, &head_commit, false) + .context("Failed to create branch")?; + clone_repository + .find_branch(&branch_name, git2::BranchType::Local) + .context("Failed to find branch after creation")? + } + }; + + let branch_name = branch + .name() + .transpose() + .context("Failed to get branch name")? + .map_err(|_| anyhow::anyhow!("Branch name is empty"))?; + + if !branch.is_head() { + clone_repository + .set_head(&branch_name) + .context("Failed to set HEAD to branch")?; + clone_repository + .checkout_head(Some(git2::build::CheckoutBuilder::default().force())) + .context("Failed to check out HEAD")?; + } + + let full_file_path = tmp.path().join(&file_path).join(&file_name); + std::fs::create_dir_all( + full_file_path + .parent() + .context("Failed to get parent directory")?, + )?; + std::fs::write(&full_file_path, bytes).context("Failed to write file")?; + + let relative_path = full_file_path + .strip_prefix(tmp.path()) + .context("Failed to strip prefix from file path")?; + let mut index = clone_repository + .index() + .context("Failed to get repository index")?; + index + .add_path(relative_path) + .context("Failed to add file to index")?; + index.write().context("Failed to write index")?; + + let time = chrono::Utc::now().timestamp(); + let time = git2::Time::new(time, 0); + let user = Signature::new(&commit_users, &commit_email, &time) + .context("Failed to create commit signature")?; + let tree = clone_repository + .find_tree(index.write_tree().context("Failed to write tree")?) + .context("Failed to find tree")?; + let parent_commit = clone_repository + .find_commit( + branch + .get() + .target() + .context("Failed to get branch target")?, + ) + .context("Failed to find parent commit")?; + clone_repository + .commit(Some("HEAD"), &user, &user, &commit_message, &tree, &[ + &parent_commit, + ]) + .context("Failed to create commit")?; + + let refspec = format!("refs/heads/{}:refs/heads/{}", branch_name, branch_name); + clone_repository + .find_remote("origin") + .context("Failed to find remote 'origin'")? + .push(&[refspec.as_str()], Some(&mut git2::PushOptions::new())) + .context("Failed to push changes to remote")?; + + Ok(()) + } } diff --git a/gitdata-gits/src/mount/mod.rs b/gitdata-gits/src/mount/mod.rs index 7fa1f09..f85b9d4 100644 --- a/gitdata-gits/src/mount/mod.rs +++ b/gitdata-gits/src/mount/mod.rs @@ -1,11 +1,7 @@ use std::collections::HashMap; use std::io; -use std::io::Error; use actix_files::NamedFile; -use async_trait::async_trait; -use bytes::Bytes; -use futures_core::Stream; use crate::mount::local::LocalStorage; use crate::mount::nfs::NfsStorage; @@ -19,9 +15,9 @@ pub mod s3; #[derive(Clone)] pub struct StoragePool { - s3 : HashMap, - local : HashMap, - nfs : HashMap, + pub(crate) s3 : HashMap, + pub(crate) local : HashMap, + pub(crate) nfs : HashMap, } impl Default for StoragePool { @@ -67,7 +63,7 @@ impl StoragePool { } } } - return None; + None } } diff --git a/gitdata-gits/src/mount/nfs.rs b/gitdata-gits/src/mount/nfs.rs index 9d3b22e..af2bb72 100644 --- a/gitdata-gits/src/mount/nfs.rs +++ b/gitdata-gits/src/mount/nfs.rs @@ -12,6 +12,7 @@ use async_fn_stream::fn_stream; use bytes::Bytes; use flate2::read::GzDecoder; use futures_core::Stream; +use git2::Signature; use crate::service::GitServiceType; @@ -168,4 +169,116 @@ impl NfsStorage { ), )) } + pub(crate) async fn create_repository(&self, path : String) -> anyhow::Result<()> { + if std::fs::read_dir(&self.root.join(path.clone())).is_ok() { + return Err(anyhow::anyhow!("Repository Path already exists")); + } + let git = git2::Repository::init_bare(&self.root.join(path.clone())); + if git.is_ok() { + Ok(()) + } else if let Err(r) = git { + Err(anyhow::anyhow!("{}", r)) + } else { + Err(anyhow::anyhow!("Unknown Error")) + } + } + pub(crate) async fn add_file( + &self, + path : String, + file_path : String, + bytes : Vec, + commit_email : String, + commit_users : String, + commit_message : String, + file_name : String, + branch_name : String, + ) -> anyhow::Result<()> { + use anyhow::Context; + + let path = self.root.join(path); + let tmp = tempfile::tempdir().context("Failed to create temporary directory")?; + let clone_repository = git2::Repository::clone(path.to_str().unwrap(), tmp.path()) + .context("Failed to clone repository")?; + + let branch = match clone_repository.find_branch(&branch_name, git2::BranchType::Local) { + Ok(branch) => branch, + Err(_) => { + let head_commit = clone_repository + .head() + .context("Failed to get HEAD")? + .peel_to_commit() + .context("Failed to peel HEAD to commit")?; + clone_repository + .branch(&branch_name, &head_commit, false) + .context("Failed to create branch")?; + clone_repository + .find_branch(&branch_name, git2::BranchType::Local) + .context("Failed to find branch after creation")? + } + }; + + let branch_name = branch + .name() + .transpose() + .context("Failed to get branch name")? + .map_err(|_| anyhow::anyhow!("Branch name is empty"))?; + + if !branch.is_head() { + clone_repository + .set_head(&branch_name) + .context("Failed to set HEAD to branch")?; + clone_repository + .checkout_head(Some(git2::build::CheckoutBuilder::default().force())) + .context("Failed to check out HEAD")?; + } + + let full_file_path = tmp.path().join(&file_path).join(&file_name); + std::fs::create_dir_all( + full_file_path + .parent() + .context("Failed to get parent directory")?, + )?; + std::fs::write(&full_file_path, bytes).context("Failed to write file")?; + + let relative_path = full_file_path + .strip_prefix(tmp.path()) + .context("Failed to strip prefix from file path")?; + let mut index = clone_repository + .index() + .context("Failed to get repository index")?; + index + .add_path(relative_path) + .context("Failed to add file to index")?; + index.write().context("Failed to write index")?; + + let time = chrono::Utc::now().timestamp(); + let time = git2::Time::new(time, 0); + let user = Signature::new(&commit_users, &commit_email, &time) + .context("Failed to create commit signature")?; + let tree = clone_repository + .find_tree(index.write_tree().context("Failed to write tree")?) + .context("Failed to find tree")?; + let parent_commit = clone_repository + .find_commit( + branch + .get() + .target() + .context("Failed to get branch target")?, + ) + .context("Failed to find parent commit")?; + clone_repository + .commit(Some("HEAD"), &user, &user, &commit_message, &tree, &[ + &parent_commit, + ]) + .context("Failed to create commit")?; + + let refspec = format!("refs/heads/{}:refs/heads/{}", branch_name, branch_name); + clone_repository + .find_remote("origin") + .context("Failed to find remote 'origin'")? + .push(&[refspec.as_str()], Some(&mut git2::PushOptions::new())) + .context("Failed to push changes to remote")?; + + Ok(()) + } } diff --git a/gitdata-gits/src/mount/s3.rs b/gitdata-gits/src/mount/s3.rs index 1623c3e..4756ae6 100644 --- a/gitdata-gits/src/mount/s3.rs +++ b/gitdata-gits/src/mount/s3.rs @@ -12,6 +12,7 @@ use async_fn_stream::fn_stream; use bytes::Bytes; use flate2::read::GzDecoder; use futures_core::Stream; +use git2::Signature; use crate::service::GitServiceType; @@ -168,4 +169,116 @@ impl S3Storage { ), )) } + pub(crate) async fn create_repository(&self, path : String) -> anyhow::Result<()> { + if std::fs::read_dir(&self.root.join(path.clone())).is_ok() { + return Err(anyhow::anyhow!("Repository Path already exists")); + } + let git = git2::Repository::init_bare(&self.root.join(path.clone())); + if git.is_ok() { + Ok(()) + } else if let Err(r) = git { + Err(anyhow::anyhow!("{}", r)) + } else { + Err(anyhow::anyhow!("Unknown Error")) + } + } + pub(crate) async fn add_file( + &self, + path : String, + file_path : String, + bytes : Vec, + commit_email : String, + commit_users : String, + commit_message : String, + file_name : String, + branch_name : String, + ) -> anyhow::Result<()> { + use anyhow::Context; + + let path = self.root.join(path); + let tmp = tempfile::tempdir().context("Failed to create temporary directory")?; + let clone_repository = git2::Repository::clone(path.to_str().unwrap(), tmp.path()) + .context("Failed to clone repository")?; + + let branch = match clone_repository.find_branch(&branch_name, git2::BranchType::Local) { + Ok(branch) => branch, + Err(_) => { + let head_commit = clone_repository + .head() + .context("Failed to get HEAD")? + .peel_to_commit() + .context("Failed to peel HEAD to commit")?; + clone_repository + .branch(&branch_name, &head_commit, false) + .context("Failed to create branch")?; + clone_repository + .find_branch(&branch_name, git2::BranchType::Local) + .context("Failed to find branch after creation")? + } + }; + + let branch_name = branch + .name() + .transpose() + .context("Failed to get branch name")? + .map_err(|_| anyhow::anyhow!("Branch name is empty"))?; + + if !branch.is_head() { + clone_repository + .set_head(&branch_name) + .context("Failed to set HEAD to branch")?; + clone_repository + .checkout_head(Some(git2::build::CheckoutBuilder::default().force())) + .context("Failed to check out HEAD")?; + } + + let full_file_path = tmp.path().join(&file_path).join(&file_name); + std::fs::create_dir_all( + full_file_path + .parent() + .context("Failed to get parent directory")?, + )?; + std::fs::write(&full_file_path, bytes).context("Failed to write file")?; + + let relative_path = full_file_path + .strip_prefix(tmp.path()) + .context("Failed to strip prefix from file path")?; + let mut index = clone_repository + .index() + .context("Failed to get repository index")?; + index + .add_path(relative_path) + .context("Failed to add file to index")?; + index.write().context("Failed to write index")?; + + let time = chrono::Utc::now().timestamp(); + let time = git2::Time::new(time, 0); + let user = Signature::new(&commit_users, &commit_email, &time) + .context("Failed to create commit signature")?; + let tree = clone_repository + .find_tree(index.write_tree().context("Failed to write tree")?) + .context("Failed to find tree")?; + let parent_commit = clone_repository + .find_commit( + branch + .get() + .target() + .context("Failed to get branch target")?, + ) + .context("Failed to find parent commit")?; + clone_repository + .commit(Some("HEAD"), &user, &user, &commit_message, &tree, &[ + &parent_commit, + ]) + .context("Failed to create commit")?; + + let refspec = format!("refs/heads/{}:refs/heads/{}", branch_name, branch_name); + clone_repository + .find_remote("origin") + .context("Failed to find remote 'origin'")? + .push(&[refspec.as_str()], Some(&mut git2::PushOptions::new())) + .context("Failed to push changes to remote")?; + + Ok(()) + } } diff --git a/gitdata-gits/src/rpc/core_git.rs b/gitdata-gits/src/rpc/core_git.rs index fbbfc4a..02623d8 100644 --- a/gitdata-gits/src/rpc/core_git.rs +++ b/gitdata-gits/src/rpc/core_git.rs @@ -1,12 +1,16 @@ use async_trait::async_trait; use gitdata::rpc; +use gitdata::rpc::core_git::RepositoryAddFileRequest; +use gitdata::rpc::core_git::RepositoryAddFilesResponse; use gitdata::rpc::core_git::RepositoryCreate; -use gitdata::rpc::core_git::RepositoryStoragePosition; +use gitdata::rpc::core_git::RepositoryStoragePosition as Pos; use tonic::Request; use tonic::Response; use tonic::Status; use crate::mount::StoragePool; +use crate::mount::StorageSingleton; +use crate::rpc::NodePath; pub struct CoreGit { pub storage : StoragePool, @@ -20,10 +24,143 @@ impl CoreGit { #[async_trait] impl rpc::core_git::rep_repository_server::RepRepository for CoreGit { - async fn create( + async fn create(&self, request : Request) -> Result, Status> { + let request = request.into_inner(); + let storage_position = match request.storage_position { + Some(storage_position) => storage_position, + None => { + return Err(Status::invalid_argument("storage_position is required")); + } + }; + let node = NodePath { + path : storage_position.path.clone(), + node : storage_position.node.clone(), + }; + let storge = match storage_position.r#type { + 0 => StorageSingleton::S3(self.storage.s3.get(&node.node).unwrap().clone()), + 1 => StorageSingleton::Local(self.storage.local.get(&node.node).unwrap().clone()), + 2 => StorageSingleton::Nfs(self.storage.nfs.get(&node.node).unwrap().clone()), + _ => { + return Err(Status::invalid_argument( + "storage_position.r#type is invalid", + )); + } + }; + match storge { + StorageSingleton::S3(x) => { + return match x.create_repository(storage_position.path.clone()).await { + Ok(_) => Ok(Response::new(Pos { + r#type : storage_position.r#type, + path : storage_position.path.clone(), + node : storage_position.node.clone(), + })), + Err(e) => Err(Status::internal(e.to_string())), + }; + } + StorageSingleton::Local(x) => { + return match x.create_repository(storage_position.path.clone()).await { + Ok(_) => Ok(Response::new(Pos { + r#type : storage_position.r#type, + path : storage_position.path.clone(), + node : storage_position.node.clone(), + })), + Err(e) => Err(Status::internal(e.to_string())), + }; + } + StorageSingleton::Nfs(x) => { + return match x.create_repository(storage_position.path.clone()).await { + Ok(_) => Ok(Response::new(Pos { + r#type : storage_position.r#type, + path : storage_position.path.clone(), + node : storage_position.node.clone(), + })), + Err(e) => Err(Status::internal(e.to_string())), + }; + } + } + } + async fn add_file( &self, - request : Request, - ) -> Result, Status> { - todo!() + request : Request, + ) -> Result, Status> { + let request = request.into_inner(); + let storage_position = match request.repository_storage_position { + Some(storage_position) => storage_position, + None => { + return Err(Status::invalid_argument("storage_position is required")); + } + }; + let node = NodePath { + path : storage_position.path.clone(), + node : storage_position.node.clone(), + }; + let storge = match storage_position.r#type { + 0 => StorageSingleton::S3(self.storage.s3.get(&node.node).unwrap().clone()), + 1 => StorageSingleton::Local(self.storage.local.get(&node.node).unwrap().clone()), + 2 => StorageSingleton::Nfs(self.storage.nfs.get(&node.node).unwrap().clone()), + _ => { + return Err(Status::invalid_argument( + "storage_position.r#type is invalid", + )); + } + }; + match storge { + StorageSingleton::S3(x) => { + match x + .add_file( + storage_position.path.clone(), + request.path.clone(), + request.content.clone(), + request.email.clone(), + request.user.clone(), + request.message.clone(), + request.file_name.clone(), + request.branch.clone(), + ) + .await + { + Ok(_) => Ok(Response::new(RepositoryAddFilesResponse::default())), + Err(x) => { + return Err(Status::internal(x.to_string())); + } + } + } + StorageSingleton::Local(x) => { + match x + .add_file( + storage_position.path.clone(), + request.path.clone(), + request.content.clone(), + request.email.clone(), + request.user.clone(), + request.message.clone(), + request.file_name.clone(), + request.branch.clone(), + ) + .await + { + Ok(_) => Ok(Response::new(RepositoryAddFilesResponse::default())), + Err(x) => return Err(Status::internal(x.to_string())), + } + } + StorageSingleton::Nfs(x) => { + match x + .add_file( + storage_position.path.clone(), + request.path.clone(), + request.content.clone(), + request.email.clone(), + request.user.clone(), + request.message.clone(), + request.file_name.clone(), + request.branch.clone(), + ) + .await + { + Ok(_) => Ok(Response::new(RepositoryAddFilesResponse::default())), + Err(x) => return Err(Status::internal(x.to_string())), + } + } + } } } diff --git a/gitdata-gits/src/rpc/mod.rs b/gitdata-gits/src/rpc/mod.rs index dd41ef1..5406ba8 100644 --- a/gitdata-gits/src/rpc/mod.rs +++ b/gitdata-gits/src/rpc/mod.rs @@ -1,10 +1,6 @@ pub mod core_git; pub mod git_core; -use std::io; - -use async_trait::async_trait; - pub enum RepositoryAccess { None, Read, diff --git a/libs/config/git.rs b/libs/config/git.rs index 8b13789..0d9ccee 100644 --- a/libs/config/git.rs +++ b/libs/config/git.rs @@ -1 +1,59 @@ +use log::warn; +use serde::{Deserialize, Serialize}; +use tokio::sync::OnceCell; +#[derive(Deserialize,Serialize,Clone,Debug, Default)] +pub struct GitConfig { + pub http: String, + pub ssh: String, +} + +static GIT_CONFIG: OnceCell = OnceCell::const_new(); + + +impl GitConfig { + pub fn get() -> Self { + if let Some(config) = GIT_CONFIG.get() { + return config.clone(); + } + let config = GitConfig::load().unwrap_or_default(); + GIT_CONFIG.set(config.clone()).unwrap(); + config + } + pub fn new(http: String, ssh: String) -> Self { + GitConfig { http, ssh } + } + pub fn save(&self) -> anyhow::Result<()> { + if std::fs::read_dir("./config").is_err(){ + std::fs::create_dir("./config")?; + } + std::fs::write("./config/git.toml", toml::to_string(self)?)?; + Ok(()) + } + pub fn load() -> anyhow::Result { + if std::fs::read_dir("./config").is_err(){ + std::fs::create_dir("./config")?; + Self::default().save()?; + warn!("git config not found, use default config."); + return Ok(Self::default()); + } + let config = match std::fs::read_to_string("./config/git.toml"){ + Ok(config) => config, + Err(_) => { + Self::default().save()?; + warn!("git config not found, use default config."); + return Ok(Self::default()); + } + }; + let config = match toml::from_str::(&config){ + Ok(config) => config, + Err(_) => { + Self::default().save()?; + warn!("git config not found, use default config."); + return Ok(Self::default()); + } + }; + Ok(config) + } + +} \ No newline at end of file diff --git a/libs/model/repository/repository.rs b/libs/model/repository/repository.rs index c366edd..cc715d1 100644 --- a/libs/model/repository/repository.rs +++ b/libs/model/repository/repository.rs @@ -11,6 +11,7 @@ use sea_orm::*; use serde::{Deserialize, Serialize}; use uuid::Uuid; +use crate::config::git::GitConfig; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveEntityModel)] #[sea_orm(table_name = "repository")] @@ -66,6 +67,7 @@ impl ActiveModel { visible: bool, default_branch: Option, ) -> Self { + let git_config = GitConfig::get(); Self { uid: Set(Uuid::new_v4()), name: Set(name.clone()), @@ -77,8 +79,8 @@ impl ActiveModel { mirrors: Set(false), archive: Set(false), archive_time: Set(None), - ssh_path: Set(format!("git@gitdata.ai:{}/{}", owner_uid, name)), - http_path: Set(format!("https://gitdata.ai/{}/{}", owner_uid, name)), + ssh_path: Set(format!("{}:{}/{}", git_config.ssh, owner_uid, name)), + http_path: Set(format!("{}/{}/{}", git_config.http, owner_uid, name)), storage_node: Set("".to_string()), fork: Set(false), fork_uid: Set(None), diff --git a/libs/rpc/core_git.rs b/libs/rpc/core_git.rs index fb1d1ba..69976ed 100644 --- a/libs/rpc/core_git.rs +++ b/libs/rpc/core_git.rs @@ -13,6 +13,30 @@ pub struct RepositoryCreate { #[prost(message, optional, tag = "1")] pub storage_position: ::core::option::Option, } +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryAddFileRequest { + #[prost(message, optional, tag = "1")] + pub repository_storage_position: ::core::option::Option, + #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "3")] + pub content: ::prost::alloc::vec::Vec, + #[prost(string, tag = "4")] + pub email: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub user: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub message: ::prost::alloc::string::String, + #[prost(string, tag = "7")] + pub file_name: ::prost::alloc::string::String, + #[prost(string, tag = "8")] + pub branch: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryAddFilesResponse { + #[prost(string, tag = "1")] + pub code: ::prost::alloc::string::String, +} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum RepositoryStoragePositionType { @@ -157,6 +181,30 @@ pub mod rep_repository_client { .insert(GrpcMethod::new("core_git.RepRepository", "Create")); self.inner.unary(req, path, codec).await } + pub async fn add_file( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/core_git.RepRepository/AddFile", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("core_git.RepRepository", "AddFile")); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -179,6 +227,13 @@ pub mod rep_repository_server { tonic::Response, tonic::Status, >; + async fn add_file( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } #[derive(Debug)] pub struct RepRepositoryServer { @@ -301,6 +356,51 @@ pub mod rep_repository_server { }; Box::pin(fut) } + "/core_git.RepRepository/AddFile" => { + #[allow(non_camel_case_types)] + struct AddFileSvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService + for AddFileSvc { + type Response = super::RepositoryAddFilesResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::add_file(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = AddFileSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { let mut response = http::Response::new(empty_body()); diff --git a/proto/core-git.proto b/proto/core-git.proto index 499be49..e6d6194 100644 --- a/proto/core-git.proto +++ b/proto/core-git.proto @@ -21,6 +21,21 @@ message RepositoryCreate { RepositoryStoragePosition storage_position = 1; } +message RepositoryAddFileRequest { + RepositoryStoragePosition RepositoryStoragePosition = 1; + string path = 2; + bytes content = 3; + string email = 4; + string user = 5; + string message = 6; + string file_name = 7; + string branch = 8; +} + +message RepositoryAddFilesResponse { + string code = 1; +} service RepRepository { rpc Create(RepositoryCreate) returns (RepositoryStoragePosition) {} + rpc AddFile(RepositoryAddFileRequest) returns (RepositoryAddFilesResponse) {} } From e7990c35dc960617de03f28dd6b7b2e4b0a3c8dc Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Fri, 17 Jan 2025 04:23:44 +0800 Subject: [PATCH 3/4] :construction:Some messy work --- gitdata-apis/src/service/core_git_rpc.rs | 6 +- .../src/service/repository/new_repo.rs | 71 +-- gitdata-gits/src/http/pack.rs | 69 +-- gitdata-gits/src/http/refs.rs | 30 +- gitdata-gits/src/http/text.rs | 17 +- gitdata-gits/src/main.rs | 17 +- gitdata-gits/src/mount/{local.rs => git.rs} | 234 ++++++++- gitdata-gits/src/mount/mod.rs | 82 +--- gitdata-gits/src/mount/nfs.rs | 284 ----------- gitdata-gits/src/mount/s3.rs | 284 ----------- gitdata-gits/src/rpc/core_git.rs | 274 ++++++----- gitdata-gits/src/rpc/git_core.rs | 34 +- gitdata-gits/src/rpc/mod.rs | 16 - libs/rpc/core_git.rs | 450 ++++++++++++++++-- libs/rpc/git_core.rs | 31 -- proto/core-git.proto | 83 +++- proto/git-core.proto | 6 - 17 files changed, 958 insertions(+), 1030 deletions(-) rename gitdata-gits/src/mount/{local.rs => git.rs} (54%) delete mode 100644 gitdata-gits/src/mount/nfs.rs delete mode 100644 gitdata-gits/src/mount/s3.rs diff --git a/gitdata-apis/src/service/core_git_rpc.rs b/gitdata-apis/src/service/core_git_rpc.rs index 86cf481..0857c78 100644 --- a/gitdata-apis/src/service/core_git_rpc.rs +++ b/gitdata-apis/src/service/core_git_rpc.rs @@ -4,8 +4,7 @@ use tokio::sync::OnceCell; pub static RPC_CLIENT : OnceCell = OnceCell::const_new(); - -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub struct CoreGitRpc { pub client : RepRepositoryClient, } @@ -27,7 +26,6 @@ impl CoreGitRpc { } } - #[cfg(test)] mod tests { use super::*; @@ -37,4 +35,4 @@ mod tests { let rpc = CoreGitRpc::get().await; dbg!(rpc); } -} \ No newline at end of file +} diff --git a/gitdata-apis/src/service/repository/new_repo.rs b/gitdata-apis/src/service/repository/new_repo.rs index f4ee95f..d219d38 100644 --- a/gitdata-apis/src/service/repository/new_repo.rs +++ b/gitdata-apis/src/service/repository/new_repo.rs @@ -1,13 +1,16 @@ -use crate::service::core_git_rpc::CoreGitRpc; -use crate::service::AppState; use gitdata::model::repository::repository; use gitdata::model::users::users; -use gitdata::rpc::core_git::{RepositoryAddFileRequest, RepositoryCreate, RepositoryStoragePosition, RepositoryStoragePositionType}; +use gitdata::rpc::core_git::RepositoryAddFileRequest; +use gitdata::rpc::core_git::RepositoryStoragePosition; +use sea_orm::ActiveModelTrait; +use sea_orm::TransactionTrait; use sea_orm::prelude::Uuid; -use sea_orm::{ActiveModelTrait, TransactionTrait}; use serde::Deserialize; use serde::Serialize; +use crate::service::AppState; +use crate::service::core_git_rpc::CoreGitRpc; + #[derive(Deserialize, Serialize)] pub struct RepoCreateParam { pub name : String, @@ -16,13 +19,17 @@ pub struct RepoCreateParam { pub visible : bool, pub default_branch : Option, pub readme : bool, - pub node: String, + pub node : String, pub storage_position : i32, - pub message: Option, + pub message : Option, } impl AppState { - pub async fn repository_new(&self, user_model: users::Model, param : RepoCreateParam) -> anyhow::Result<()> { + pub async fn repository_new( + &self, + user_model : users::Model, + param : RepoCreateParam, + ) -> anyhow::Result<()> { let active_model = repository::ActiveModel::new( param.name.clone(), param.owner_uid, @@ -31,14 +38,14 @@ impl AppState { param.default_branch.clone(), ); let txn = self.active_write.begin().await?; - match active_model.clone().insert(&txn).await{ - Ok(_) => {}, + match active_model.clone().insert(&txn).await { + Ok(_) => {} Err(e) => { txn.rollback().await?; - return Err(e.into()) + return Err(e.into()); } }; - let mut client = match CoreGitRpc::get().await{ + let mut client = match CoreGitRpc::get().await { Ok(client) => client.clone(), Err(e) => { txn.rollback().await?; @@ -48,39 +55,33 @@ impl AppState { let mut node = RepositoryStoragePosition::default(); node.node = param.node; node.path = active_model.uid.unwrap().to_string(); - node.r#type = match RepositoryStoragePositionType::try_from(param.storage_position){ - Ok(t) => i32::from(t), + match client.client.create(node.clone()).await { + Ok(_) => {} Err(e) => { txn.rollback().await?; return Err(e.into()); } }; - match client.client.create(RepositoryCreate { - storage_position: Some(node.clone()), - }).await { - Ok(_) => { - }, - Err(e) => { - txn.rollback().await?; - return Err(e.into()) - } - }; if param.readme { let bytes = format!("### {}", param.name); - match client.client.add_file(RepositoryAddFileRequest { - repository_storage_position: Some(node), - path: "/".to_string(), - content: bytes.into_bytes(), - email: user_model.main_email.clone(), - user: user_model.name.clone(), - message: param.message.unwrap_or("Create README.md".to_string()), - file_name: "README.md".to_string(), - branch: param.default_branch.unwrap_or("main".to_string()), - }).await { - Ok(_) => {}, + match client + .client + .add_file(RepositoryAddFileRequest { + repository_storage_position : Some(node), + path : "/".to_string(), + content : bytes.into_bytes(), + email : user_model.main_email.clone(), + user : user_model.name.clone(), + message : param.message.unwrap_or("Create README.md".to_string()), + file_name : "README.md".to_string(), + branch : param.default_branch.unwrap_or("main".to_string()), + }) + .await + { + Ok(_) => {} Err(e) => { txn.rollback().await?; - return Err(e.into()) + return Err(e.into()); } } } diff --git a/gitdata-gits/src/http/pack.rs b/gitdata-gits/src/http/pack.rs index 8ca6e7e..7745f7f 100644 --- a/gitdata-gits/src/http/pack.rs +++ b/gitdata-gits/src/http/pack.rs @@ -11,7 +11,6 @@ use bytes::Bytes; use futures::StreamExt; use crate::mount::StoragePool; -use crate::mount::StorageSingleton; use crate::rpc::git_core::RepositoryRpc; use crate::service::GitServiceType; @@ -66,56 +65,22 @@ pub async fn pack( response.append_header(("Transfer-Encoding", "chunked")); response.append_header(("X-Content-Type-Options", "nosniff")); let storage = storage.read().unwrap(); - match match storage.get(path.clone()) { + let storage = match storage.node.get(&path.node) { Some(storage) => storage, - None => { - return actix_web::HttpResponse::NotFound().finish(); - } - } { - StorageSingleton::S3(s) => { - match s - .pack( - path.path().to_string(), - service, - Some(version.to_string()), - gzip, - bytes, - ) - .await - { - Ok(bytes) => response.streaming(bytes), - Err(_) => actix_web::HttpResponse::NotFound().finish(), - } - } - StorageSingleton::Local(s) => { - match s - .pack( - path.path().to_string(), - service, - Some(version.to_string()), - gzip, - bytes, - ) - .await - { - Ok(bytes) => response.streaming(bytes), - Err(_) => actix_web::HttpResponse::NotFound().finish(), - } - } - StorageSingleton::Nfs(s) => { - match s - .pack( - path.path().to_string(), - service, - Some(version.to_string()), - gzip, - bytes, - ) - .await - { - Ok(bytes) => response.streaming(bytes), - Err(_) => actix_web::HttpResponse::NotFound().finish(), - } - } - } + None => return actix_web::HttpResponse::NotFound().finish(), + }; + let result = match storage + .pack( + path.path.clone(), + service, + Some(version.to_string()), + gzip, + bytes, + ) + .await + { + Ok(response) => response, + Err(_) => return actix_web::HttpResponse::NotFound().finish(), + }; + response.streaming(result) } diff --git a/gitdata-gits/src/http/refs.rs b/gitdata-gits/src/http/refs.rs index 281a12a..76b6873 100644 --- a/gitdata-gits/src/http/refs.rs +++ b/gitdata-gits/src/http/refs.rs @@ -44,28 +44,22 @@ pub async fn info_refs( .to_str() .map(|s| s.to_string()) .unwrap_or("".to_string()); - let data = match storage.get(path.clone()) { - Some(data) => match data - .refs( - path.path(), - service, - if version.is_empty() { - None - } else { - Some(&version) - }, - ) - .await - { - Ok(data) => data, - Err(_) => { - return HttpResponse::NotFound().body("Not found"); - } - }, + + let storage = match storage.node.get(&path.clone().node) { + Some(storage) => storage, None => { return HttpResponse::NotFound().body("Not found"); } }; + let data = match storage + .refs(&*path.path.clone(), service, Some(&version)) + .await + { + Ok(data) => data, + Err(_) => { + return HttpResponse::NotFound().body("Not found"); + } + }; let mut response = HttpResponseBuilder::new(StatusCode::OK); response.append_header(( "Content-Type", diff --git a/gitdata-gits/src/http/text.rs b/gitdata-gits/src/http/text.rs index 500be79..6f688bf 100644 --- a/gitdata-gits/src/http/text.rs +++ b/gitdata-gits/src/http/text.rs @@ -7,7 +7,6 @@ use actix_web::http::header::HeaderValue; use actix_web::web; use crate::mount::StoragePool; -use crate::mount::StorageSingleton; use crate::rpc::git_core::RepositoryRpc; pub(crate) async fn text( @@ -26,19 +25,15 @@ pub(crate) async fn text( .uri() .to_string() .replace(&format!("{}/{}", owner, path), ""); - let storage = match match storage.get(nodepath.clone()) { + let storage = match storage.node.get(&nodepath.node) { Some(storage) => storage, None => return HttpResponse::NotFound().finish(), - } { - StorageSingleton::S3(x) => x.text(&nodepath.path(), &file_path).await, - StorageSingleton::Local(x) => x.text(&nodepath.path(), &file_path).await, - StorageSingleton::Nfs(x) => x.text(&nodepath.path(), &file_path).await, }; - if storage.is_err() { - return HttpResponse::NotFound().finish(); - } - let storage = storage.unwrap(); - let namedfile = storage.use_last_modified(true); + let namedfile = match storage.text(&*nodepath.path, &*file_path).await { + Ok(namedfile) => namedfile, + Err(_) => return HttpResponse::NotFound().finish(), + }; + let namedfile = namedfile.use_last_modified(true); let mut response = namedfile.into_response(&request); response.headers_mut().insert( HeaderName::try_from("Pragma".to_string()).unwrap(), diff --git a/gitdata-gits/src/main.rs b/gitdata-gits/src/main.rs index f1cd738..54ba186 100644 --- a/gitdata-gits/src/main.rs +++ b/gitdata-gits/src/main.rs @@ -2,7 +2,8 @@ use std::io; use log::info; use tonic::transport::Server; -use crate::mount::local::LocalStorage; + +use crate::mount::git::NodeStorage; pub mod health; pub mod http; @@ -15,21 +16,17 @@ async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt().init(); info!("starting gitdata"); let mut pool = mount::StoragePool::new(); - pool - .add_local( - "default".to_string(), - LocalStorage { - root : std::env::current_dir()?.join("./data"), - } - ); + pool.add_node("default".to_string(), NodeStorage { + root : std::env::current_dir()?.join("./data"), + }); let http = tokio::spawn(http::http(pool.clone())); let health = tokio::spawn(async move { let health = gitdata::health::service::HealthService::default(); let core_git = rpc::core_git::CoreGit::new(pool.clone()); info!("starting health service"); Server::builder() - .trace_fn(|x|{ - info!("Url: {:?} Method: {}", x.uri(),x.method()); + .trace_fn(|x| { + info!("Url: {:?} Method: {}", x.uri(), x.method()); tracing::Span::current() }) .add_service(gitdata::rpc::health::health_server::HealthServer::new( diff --git a/gitdata-gits/src/mount/local.rs b/gitdata-gits/src/mount/git.rs similarity index 54% rename from gitdata-gits/src/mount/local.rs rename to gitdata-gits/src/mount/git.rs index 3556bdc..160f665 100644 --- a/gitdata-gits/src/mount/local.rs +++ b/gitdata-gits/src/mount/git.rs @@ -3,6 +3,7 @@ use std::io::Cursor; use std::io::Error; use std::io::Read; use std::io::Write; +use std::path::Path; use std::path::PathBuf; use std::process::Command; use std::process::Stdio; @@ -13,15 +14,19 @@ use bytes::Bytes; use flate2::read::GzDecoder; use futures_core::Stream; use git2::Signature; +use gitdata::rpc::core_git::RepositoryBranch; +use gitdata::rpc::core_git::RepositoryCommit; +use gitdata::rpc::core_git::RepositoryTags; +use gitdata::rpc::core_git::RepositoryTree; use crate::service::GitServiceType; #[derive(Clone)] -pub struct LocalStorage { +pub struct NodeStorage { pub root : PathBuf, } -impl LocalStorage { +impl NodeStorage { pub(crate) async fn refs( &self, path : &str, @@ -57,7 +62,7 @@ impl LocalStorage { pub(crate) async fn text(&self, path : &str, file_path : &str) -> io::Result { let file_path = self.root.join(path).join(file_path); if !file_path.exists() { - return Err(io::Error::new(io::ErrorKind::NotFound, "File not found")); + return Err(Error::new(io::ErrorKind::NotFound, "File not found")); } if file_path.is_dir() { return Err(io::Error::new(io::ErrorKind::Other, "File is a directory")); @@ -197,8 +202,14 @@ impl LocalStorage { let path = self.root.join(path); let tmp = tempfile::tempdir().context("Failed to create temporary directory")?; - let clone_repository = git2::Repository::clone(path.to_str().unwrap(), tmp.path()) - .context("Failed to clone repository")?; + let clone_repository = git2::Repository::clone( + match path.to_str() { + Some(x) => x, + None => return Err(anyhow::anyhow!("Path is not valid")), + }, + tmp.path(), + ) + .context("Failed to clone repository")?; let branch = match clone_repository.find_branch(&branch_name, git2::BranchType::Local) { Ok(branch) => branch, @@ -281,4 +292,217 @@ impl LocalStorage { Ok(()) } + pub(crate) async fn branch(&self, path : String) -> anyhow::Result> { + use anyhow::Context; + let repository = match git2::Repository::open(self.root.join(path)) { + Ok(repo) => repo, + Err(e) => { + return Err(anyhow::anyhow!("{}", e)); + } + }; + let branches = repository + .branches(Some(git2::BranchType::Local)) + .context("Failed to get branches")?; + let mut result = Vec::new(); + for branch in branches { + if let Ok((branch, _)) = branch { + let commit = match branch.get().peel_to_commit() { + Ok(commit) => commit, + Err(_) => { + continue; + } + }; + let name = match branch.name() { + Ok(name) => match name { + Some(name) => name.to_string(), + None => { + continue; + } + }, + Err(_) => { + continue; + } + }; + let commit_id = commit.id().to_string(); + let count = commit.parent_count(); + result.push(RepositoryBranch { + name, + head : commit_id, + commit_nums : count as i32, + from : None, + to : None, + start : false, + }); + } + } + Ok(result) + } + pub(crate) async fn commit( + &self, + path : String, + branch_name : String, + ) -> anyhow::Result> { + let repository = match git2::Repository::open(self.root.join(path)) { + Ok(repo) => repo, + Err(e) => { + return Err(anyhow::anyhow!("{}", e)); + } + }; + let branch = match repository.find_branch(&branch_name.clone(), git2::BranchType::Local) { + Ok(branch) => branch, + Err(e) => { + return Err(anyhow::anyhow!("{}", e)); + } + }; + let mut commit = match branch.get().peel_to_commit() { + Ok(commit) => commit, + Err(e) => { + return Err(anyhow::anyhow!("{}", e)); + } + }; + let mut result = Vec::new(); + loop { + let commit_id = commit.id().to_string(); + let commit_message = match commit.message() { + Some(message) => message.to_string(), + None => { + continue; + } + }; + let commit_time = commit.time().seconds(); + let commit_author = match commit.author().name() { + Some(name) => name.to_string(), + None => { + continue; + } + }; + let commit_email = match commit.author().email() { + Some(email) => email.to_string(), + None => { + continue; + } + }; + let mut file_tree = Vec::new(); + let commit_tree = match commit.tree() { + Ok(tree) => tree, + Err(_) => { + continue; + } + }; + match commit_tree.walk(git2::TreeWalkMode::PreOrder, |path, entry| { + let file_path = path.to_string(); + let file_name = entry.name().unwrap_or("").to_string(); + let hash = entry.id().to_string(); + let mode = entry.filemode(); + let size = match repository.find_blob(entry.id()) { + Ok(file) => file.size(), + Err(_) => 0, + }; + file_tree.push(RepositoryTree { + hash, + path : file_path, + size : size.to_string(), + name : file_name, + mode : mode.to_string(), + }); + 0 + }) { + Ok(_) => {} + Err(_) => { + continue; + } + } + result.push(RepositoryCommit { + hash : commit_id, + message : commit_message, + author : commit_author, + email : commit_email, + date : commit_time.to_string(), + branch : branch_name.clone(), + file_nums : file_tree.len() as i32, + tree : file_tree, + }); + commit = match commit.parent(0) { + Ok(commit) => commit, + Err(_) => { + break Ok(result); + } + }; + } + } + pub(crate) async fn get_file( + &self, + path : String, + file_path : String, + hash : String, + ) -> anyhow::Result> { + let repository = match git2::Repository::open(self.root.join(path)) { + Ok(repo) => repo, + Err(e) => { + return Err(anyhow::anyhow!("{}", e)); + } + }; + let commit = match repository.find_commit(git2::Oid::from_str(&hash)?) { + Ok(commit) => commit, + Err(e) => { + return Err(anyhow::anyhow!("{}", e)); + } + }; + if let Ok(blob) = commit + .tree() + .and_then(|tree| tree.get_path(Path::new(&file_path))) + { + if let Ok(blob) = repository.find_blob(blob.id()) { + return Ok(blob.content().to_vec()); + } + } + Err(anyhow::anyhow!("File not found")) + } + pub(crate) async fn tage(&self, path : String) -> anyhow::Result> { + let repository = match git2::Repository::open(self.root.join(path)) { + Ok(repo) => repo, + Err(e) => { + return Err(anyhow::anyhow!("{}", e)); + } + }; + let mut result = Vec::new(); + for tag in &repository.tag_names(None)? { + if tag.is_none() { + continue; + } + let tag = tag.unwrap(); + let tag = match repository.find_tag(git2::Oid::from_str(&tag)?) { + Ok(tag) => tag, + Err(e) => { + return Err(anyhow::anyhow!("{}", e)); + } + }; + + result.push(RepositoryTags { + name : match tag.name() { + Some(name) => name.to_string(), + None => { + continue; + } + }, + hash : tag.id().to_string(), + date : match tag.target() { + Ok(target) => match target.as_commit() { + Some(commit) => commit.time().seconds().to_string(), + None => "0".to_string(), + }, + Err(_) => "0".to_string(), + }, + author : match tag.tagger() { + Some(tagger) => tagger.name().unwrap_or("N/A").to_string(), + None => "N/A".to_string(), + }, + email : match tag.tagger() { + Some(tagger) => tagger.email().unwrap_or("N/A").to_string(), + None => "N/A".to_string(), + }, + }); + } + Ok(result) + } } diff --git a/gitdata-gits/src/mount/mod.rs b/gitdata-gits/src/mount/mod.rs index f85b9d4..8785ec2 100644 --- a/gitdata-gits/src/mount/mod.rs +++ b/gitdata-gits/src/mount/mod.rs @@ -1,23 +1,12 @@ use std::collections::HashMap; -use std::io; -use actix_files::NamedFile; +use crate::mount::git::NodeStorage; -use crate::mount::local::LocalStorage; -use crate::mount::nfs::NfsStorage; -use crate::mount::s3::S3Storage; -use crate::rpc::RepositoryStoragePosition; -use crate::service::GitServiceType; - -pub mod local; -pub mod nfs; -pub mod s3; +pub mod git; #[derive(Clone)] pub struct StoragePool { - pub(crate) s3 : HashMap, - pub(crate) local : HashMap, - pub(crate) nfs : HashMap, + pub(crate) node : HashMap, } impl Default for StoragePool { @@ -29,70 +18,11 @@ impl Default for StoragePool { impl StoragePool { pub fn new() -> Self { StoragePool { - s3 : HashMap::new(), - local : HashMap::new(), - nfs : HashMap::new(), - } - } - - pub fn add_s3(&mut self, name : String, storage : S3Storage) { - self.s3.insert(name, storage); - } - - pub fn add_local(&mut self, name : String, storage : LocalStorage) { - self.local.insert(name, storage); - } - pub fn add_nfs(&mut self, name : String, storage : NfsStorage) { - self.nfs.insert(name, storage); - } - pub fn get(&self, node : RepositoryStoragePosition) -> Option { - match node { - RepositoryStoragePosition::S3(x) => { - if let Some(storage) = self.s3.get(&x.node) { - return Some(StorageSingleton::S3(storage.clone())); - } - } - RepositoryStoragePosition::Local(x) => { - if let Some(storage) = self.local.get(&x.node) { - return Some(StorageSingleton::Local(storage.clone())); - } - } - RepositoryStoragePosition::Nfs(x) => { - if let Some(storage) = self.nfs.get(&x.node) { - return Some(StorageSingleton::Nfs(storage.clone())); - } - } + node : HashMap::new(), } - None } -} - -#[derive(Clone)] -pub enum StorageSingleton { - S3(S3Storage), - Local(LocalStorage), - Nfs(NfsStorage), -} -impl StorageSingleton { - pub async fn refs( - &self, - path : &str, - service : GitServiceType, - version : Option<&str>, - ) -> io::Result { - match self { - StorageSingleton::S3(x) => x.refs(path, service, version).await, - StorageSingleton::Local(x) => x.refs(path, service, version).await, - StorageSingleton::Nfs(x) => x.refs(path, service, version).await, - } - } - - pub async fn text(&self, path : &str, file_path : &str) -> io::Result { - match self { - StorageSingleton::S3(x) => x.text(path, file_path).await, - StorageSingleton::Local(x) => x.text(path, file_path).await, - StorageSingleton::Nfs(x) => x.text(path, file_path).await, - } + pub fn add_node(&mut self, name : String, storage : NodeStorage) { + self.node.insert(name, storage); } } diff --git a/gitdata-gits/src/mount/nfs.rs b/gitdata-gits/src/mount/nfs.rs deleted file mode 100644 index af2bb72..0000000 --- a/gitdata-gits/src/mount/nfs.rs +++ /dev/null @@ -1,284 +0,0 @@ -use std::io; -use std::io::Cursor; -use std::io::Error; -use std::io::Read; -use std::io::Write; -use std::path::PathBuf; -use std::process::Command; -use std::process::Stdio; - -use actix_files::NamedFile; -use async_fn_stream::fn_stream; -use bytes::Bytes; -use flate2::read::GzDecoder; -use futures_core::Stream; -use git2::Signature; - -use crate::service::GitServiceType; - -#[derive(Clone)] -pub struct NfsStorage { - pub root : PathBuf, -} - -impl NfsStorage { - pub(crate) async fn refs( - &self, - path : &str, - service : GitServiceType, - version : Option<&str>, - ) -> io::Result { - let mut cmd = Command::new("git"); - cmd.arg(service.to_string()); - cmd.arg("--stateless-rpc"); - cmd.arg("--advertise-refs"); - cmd.arg("."); - cmd.current_dir(self.root.join(path)); - if !version.is_some() { - cmd.env("GIT_PROTOCOL", version.unwrap_or("")); - } - let output = match cmd.output() { - Ok(output) => output, - Err(e) => { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - }; - if !output.status.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - "Error running command", - )); - } - Ok(String::from_utf8(output.stdout).unwrap_or("".to_string())) - } - pub(crate) async fn text(&self, path : &str, file_path : &str) -> io::Result { - let file_path = self.root.join(path).join(file_path); - if !file_path.exists() { - return Err(io::Error::new(io::ErrorKind::NotFound, "File not found")); - } - if file_path.is_dir() { - return Err(io::Error::new(io::ErrorKind::Other, "File is a directory")); - } - Ok(NamedFile::open(file_path)?) - } - pub(crate) async fn pack( - &self, - path : String, - service : GitServiceType, - version : Option, - gzip : bool, - payload : Bytes, - ) -> io::Result> + use<>> { - let mut cmd = Command::new("git"); - cmd.arg(service.to_string()); - cmd.arg("receive-pack"); - cmd.arg("--stateless-rpc"); - cmd.arg("."); - if !version.is_some() { - cmd.env("GIT_PROTOCOL", version.unwrap_or("".to_string())); - } - cmd.stderr(Stdio::piped()); - cmd.stdin(Stdio::piped()); - cmd.stdout(Stdio::piped()); - cmd.current_dir(self.root.join(path)); - let mut git_process = match cmd.spawn() { - Ok(process) => process, - Err(e) => { - return Err(Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - }; - let mut stdin = git_process.stdin.take().unwrap(); - let mut stdout = git_process.stdout.take().unwrap(); - let bytes = if gzip { - let mut decoder = GzDecoder::new(Cursor::new(payload)); - let mut decoded_data = Vec::new(); - if let Err(e) = io::copy(&mut decoder, &mut decoded_data) { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - decoded_data - } else { - payload.to_vec() - }; - if let Err(e) = stdin.write_all(&bytes) { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - drop(stdin); - Ok(fn_stream(move |emitter| async move { - let mut buffer = [0; 8192]; - loop { - match stdout.read(&mut buffer) { - Ok(0) => break, // EOF - Ok(n) => { - emitter - .emit(Ok(Bytes::copy_from_slice(&buffer[0..n]))) - .await; - } - Err(e) => { - emitter.emit(Err(Error::new(io::ErrorKind::Other, e))).await; - break; - } - } - } - })) - } - pub async fn pack_ssh( - &self, - path : String, - service : GitServiceType, - version : Option, - ) -> io::Result<( - tokio::process::ChildStdin, - (tokio::process::ChildStdout, tokio::process::ChildStderr), - )> { - let mut cmd = tokio::process::Command::new("git"); - cmd.arg(service.to_string()); - cmd.arg("."); - if !version.is_some() { - cmd.env("GIT_PROTOCOL", version.unwrap_or("".to_string())); - } - cmd.stderr(Stdio::piped()); - cmd.stdin(Stdio::piped()); - cmd.stdout(Stdio::piped()); - cmd.current_dir(self.root.join(path)); - let mut git_process = match cmd.spawn() { - Ok(process) => process, - Err(e) => { - return Err(Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - }; - Ok(( - git_process.stdin.take().unwrap(), - ( - git_process.stdout.take().unwrap(), - git_process.stderr.take().unwrap(), - ), - )) - } - pub(crate) async fn create_repository(&self, path : String) -> anyhow::Result<()> { - if std::fs::read_dir(&self.root.join(path.clone())).is_ok() { - return Err(anyhow::anyhow!("Repository Path already exists")); - } - let git = git2::Repository::init_bare(&self.root.join(path.clone())); - if git.is_ok() { - Ok(()) - } else if let Err(r) = git { - Err(anyhow::anyhow!("{}", r)) - } else { - Err(anyhow::anyhow!("Unknown Error")) - } - } - pub(crate) async fn add_file( - &self, - path : String, - file_path : String, - bytes : Vec, - commit_email : String, - commit_users : String, - commit_message : String, - file_name : String, - branch_name : String, - ) -> anyhow::Result<()> { - use anyhow::Context; - - let path = self.root.join(path); - let tmp = tempfile::tempdir().context("Failed to create temporary directory")?; - let clone_repository = git2::Repository::clone(path.to_str().unwrap(), tmp.path()) - .context("Failed to clone repository")?; - - let branch = match clone_repository.find_branch(&branch_name, git2::BranchType::Local) { - Ok(branch) => branch, - Err(_) => { - let head_commit = clone_repository - .head() - .context("Failed to get HEAD")? - .peel_to_commit() - .context("Failed to peel HEAD to commit")?; - clone_repository - .branch(&branch_name, &head_commit, false) - .context("Failed to create branch")?; - clone_repository - .find_branch(&branch_name, git2::BranchType::Local) - .context("Failed to find branch after creation")? - } - }; - - let branch_name = branch - .name() - .transpose() - .context("Failed to get branch name")? - .map_err(|_| anyhow::anyhow!("Branch name is empty"))?; - - if !branch.is_head() { - clone_repository - .set_head(&branch_name) - .context("Failed to set HEAD to branch")?; - clone_repository - .checkout_head(Some(git2::build::CheckoutBuilder::default().force())) - .context("Failed to check out HEAD")?; - } - - let full_file_path = tmp.path().join(&file_path).join(&file_name); - std::fs::create_dir_all( - full_file_path - .parent() - .context("Failed to get parent directory")?, - )?; - std::fs::write(&full_file_path, bytes).context("Failed to write file")?; - - let relative_path = full_file_path - .strip_prefix(tmp.path()) - .context("Failed to strip prefix from file path")?; - let mut index = clone_repository - .index() - .context("Failed to get repository index")?; - index - .add_path(relative_path) - .context("Failed to add file to index")?; - index.write().context("Failed to write index")?; - - let time = chrono::Utc::now().timestamp(); - let time = git2::Time::new(time, 0); - let user = Signature::new(&commit_users, &commit_email, &time) - .context("Failed to create commit signature")?; - let tree = clone_repository - .find_tree(index.write_tree().context("Failed to write tree")?) - .context("Failed to find tree")?; - let parent_commit = clone_repository - .find_commit( - branch - .get() - .target() - .context("Failed to get branch target")?, - ) - .context("Failed to find parent commit")?; - clone_repository - .commit(Some("HEAD"), &user, &user, &commit_message, &tree, &[ - &parent_commit, - ]) - .context("Failed to create commit")?; - - let refspec = format!("refs/heads/{}:refs/heads/{}", branch_name, branch_name); - clone_repository - .find_remote("origin") - .context("Failed to find remote 'origin'")? - .push(&[refspec.as_str()], Some(&mut git2::PushOptions::new())) - .context("Failed to push changes to remote")?; - - Ok(()) - } -} diff --git a/gitdata-gits/src/mount/s3.rs b/gitdata-gits/src/mount/s3.rs deleted file mode 100644 index 4756ae6..0000000 --- a/gitdata-gits/src/mount/s3.rs +++ /dev/null @@ -1,284 +0,0 @@ -use std::io; -use std::io::Cursor; -use std::io::Error; -use std::io::Read; -use std::io::Write; -use std::path::PathBuf; -use std::process::Command; -use std::process::Stdio; - -use actix_files::NamedFile; -use async_fn_stream::fn_stream; -use bytes::Bytes; -use flate2::read::GzDecoder; -use futures_core::Stream; -use git2::Signature; - -use crate::service::GitServiceType; - -#[derive(Clone)] -pub struct S3Storage { - pub root : PathBuf, -} - -impl S3Storage { - pub(crate) async fn refs( - &self, - path : &str, - service : GitServiceType, - version : Option<&str>, - ) -> io::Result { - let mut cmd = Command::new("git"); - cmd.arg(service.to_string()); - cmd.arg("--stateless-rpc"); - cmd.arg("--advertise-refs"); - cmd.arg("."); - cmd.current_dir(self.root.join(path)); - if !version.is_some() { - cmd.env("GIT_PROTOCOL", version.unwrap_or("")); - } - let output = match cmd.output() { - Ok(output) => output, - Err(e) => { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - }; - if !output.status.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - "Error running command", - )); - } - Ok(String::from_utf8(output.stdout).unwrap_or("".to_string())) - } - pub(crate) async fn text(&self, path : &str, file_path : &str) -> io::Result { - let file_path = self.root.join(path).join(file_path); - if !file_path.exists() { - return Err(io::Error::new(io::ErrorKind::NotFound, "File not found")); - } - if file_path.is_dir() { - return Err(io::Error::new(io::ErrorKind::Other, "File is a directory")); - } - Ok(NamedFile::open(file_path)?) - } - pub(crate) async fn pack( - &self, - path : String, - service : GitServiceType, - version : Option, - gzip : bool, - payload : Bytes, - ) -> io::Result> + use<>> { - let mut cmd = Command::new("git"); - cmd.arg(service.to_string()); - cmd.arg("receive-pack"); - cmd.arg("--stateless-rpc"); - cmd.arg("."); - if !version.is_some() { - cmd.env("GIT_PROTOCOL", version.unwrap_or("".to_string())); - } - cmd.stderr(Stdio::piped()); - cmd.stdin(Stdio::piped()); - cmd.stdout(Stdio::piped()); - cmd.current_dir(self.root.join(path)); - let mut git_process = match cmd.spawn() { - Ok(process) => process, - Err(e) => { - return Err(Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - }; - let mut stdin = git_process.stdin.take().unwrap(); - let mut stdout = git_process.stdout.take().unwrap(); - let bytes = if gzip { - let mut decoder = GzDecoder::new(Cursor::new(payload)); - let mut decoded_data = Vec::new(); - if let Err(e) = io::copy(&mut decoder, &mut decoded_data) { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - decoded_data - } else { - payload.to_vec() - }; - if let Err(e) = stdin.write_all(&bytes) { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - drop(stdin); - Ok(fn_stream(move |emitter| async move { - let mut buffer = [0; 8192]; - loop { - match stdout.read(&mut buffer) { - Ok(0) => break, - Ok(n) => { - emitter - .emit(Ok(Bytes::copy_from_slice(&buffer[0..n]))) - .await; - } - Err(e) => { - emitter.emit(Err(Error::new(io::ErrorKind::Other, e))).await; - break; - } - } - } - })) - } - pub async fn pack_ssh( - &self, - path : String, - service : GitServiceType, - version : Option, - ) -> io::Result<( - tokio::process::ChildStdin, - (tokio::process::ChildStdout, tokio::process::ChildStderr), - )> { - let mut cmd = tokio::process::Command::new("git"); - cmd.arg(service.to_string()); - cmd.arg("."); - if !version.is_some() { - cmd.env("GIT_PROTOCOL", version.unwrap_or("".to_string())); - } - cmd.stderr(Stdio::piped()); - cmd.stdin(Stdio::piped()); - cmd.stdout(Stdio::piped()); - cmd.current_dir(self.root.join(path)); - let mut git_process = match cmd.spawn() { - Ok(process) => process, - Err(e) => { - return Err(Error::new( - io::ErrorKind::Other, - format!("Error running command {}", e), - )); - } - }; - Ok(( - git_process.stdin.take().unwrap(), - ( - git_process.stdout.take().unwrap(), - git_process.stderr.take().unwrap(), - ), - )) - } - pub(crate) async fn create_repository(&self, path : String) -> anyhow::Result<()> { - if std::fs::read_dir(&self.root.join(path.clone())).is_ok() { - return Err(anyhow::anyhow!("Repository Path already exists")); - } - let git = git2::Repository::init_bare(&self.root.join(path.clone())); - if git.is_ok() { - Ok(()) - } else if let Err(r) = git { - Err(anyhow::anyhow!("{}", r)) - } else { - Err(anyhow::anyhow!("Unknown Error")) - } - } - pub(crate) async fn add_file( - &self, - path : String, - file_path : String, - bytes : Vec, - commit_email : String, - commit_users : String, - commit_message : String, - file_name : String, - branch_name : String, - ) -> anyhow::Result<()> { - use anyhow::Context; - - let path = self.root.join(path); - let tmp = tempfile::tempdir().context("Failed to create temporary directory")?; - let clone_repository = git2::Repository::clone(path.to_str().unwrap(), tmp.path()) - .context("Failed to clone repository")?; - - let branch = match clone_repository.find_branch(&branch_name, git2::BranchType::Local) { - Ok(branch) => branch, - Err(_) => { - let head_commit = clone_repository - .head() - .context("Failed to get HEAD")? - .peel_to_commit() - .context("Failed to peel HEAD to commit")?; - clone_repository - .branch(&branch_name, &head_commit, false) - .context("Failed to create branch")?; - clone_repository - .find_branch(&branch_name, git2::BranchType::Local) - .context("Failed to find branch after creation")? - } - }; - - let branch_name = branch - .name() - .transpose() - .context("Failed to get branch name")? - .map_err(|_| anyhow::anyhow!("Branch name is empty"))?; - - if !branch.is_head() { - clone_repository - .set_head(&branch_name) - .context("Failed to set HEAD to branch")?; - clone_repository - .checkout_head(Some(git2::build::CheckoutBuilder::default().force())) - .context("Failed to check out HEAD")?; - } - - let full_file_path = tmp.path().join(&file_path).join(&file_name); - std::fs::create_dir_all( - full_file_path - .parent() - .context("Failed to get parent directory")?, - )?; - std::fs::write(&full_file_path, bytes).context("Failed to write file")?; - - let relative_path = full_file_path - .strip_prefix(tmp.path()) - .context("Failed to strip prefix from file path")?; - let mut index = clone_repository - .index() - .context("Failed to get repository index")?; - index - .add_path(relative_path) - .context("Failed to add file to index")?; - index.write().context("Failed to write index")?; - - let time = chrono::Utc::now().timestamp(); - let time = git2::Time::new(time, 0); - let user = Signature::new(&commit_users, &commit_email, &time) - .context("Failed to create commit signature")?; - let tree = clone_repository - .find_tree(index.write_tree().context("Failed to write tree")?) - .context("Failed to find tree")?; - let parent_commit = clone_repository - .find_commit( - branch - .get() - .target() - .context("Failed to get branch target")?, - ) - .context("Failed to find parent commit")?; - clone_repository - .commit(Some("HEAD"), &user, &user, &commit_message, &tree, &[ - &parent_commit, - ]) - .context("Failed to create commit")?; - - let refspec = format!("refs/heads/{}:refs/heads/{}", branch_name, branch_name); - clone_repository - .find_remote("origin") - .context("Failed to find remote 'origin'")? - .push(&[refspec.as_str()], Some(&mut git2::PushOptions::new())) - .context("Failed to push changes to remote")?; - - Ok(()) - } -} diff --git a/gitdata-gits/src/rpc/core_git.rs b/gitdata-gits/src/rpc/core_git.rs index 02623d8..dddd166 100644 --- a/gitdata-gits/src/rpc/core_git.rs +++ b/gitdata-gits/src/rpc/core_git.rs @@ -1,16 +1,12 @@ +use std::path::PathBuf; + use async_trait::async_trait; -use gitdata::rpc; -use gitdata::rpc::core_git::RepositoryAddFileRequest; -use gitdata::rpc::core_git::RepositoryAddFilesResponse; -use gitdata::rpc::core_git::RepositoryCreate; -use gitdata::rpc::core_git::RepositoryStoragePosition as Pos; +use gitdata::rpc::core_git::*; use tonic::Request; use tonic::Response; use tonic::Status; use crate::mount::StoragePool; -use crate::mount::StorageSingleton; -use crate::rpc::NodePath; pub struct CoreGit { pub storage : StoragePool, @@ -23,59 +19,27 @@ impl CoreGit { } #[async_trait] -impl rpc::core_git::rep_repository_server::RepRepository for CoreGit { - async fn create(&self, request : Request) -> Result, Status> { +impl rep_repository_server::RepRepository for CoreGit { + async fn create( + &self, + request : Request, + ) -> Result, Status> { let request = request.into_inner(); - let storage_position = match request.storage_position { - Some(storage_position) => storage_position, + let storage = match self.storage.node.get(&request.node) { + Some(storage) => storage, None => { - return Err(Status::invalid_argument("storage_position is required")); + return Err(Status::invalid_argument("node not found")); } }; - let node = NodePath { - path : storage_position.path.clone(), - node : storage_position.node.clone(), - }; - let storge = match storage_position.r#type { - 0 => StorageSingleton::S3(self.storage.s3.get(&node.node).unwrap().clone()), - 1 => StorageSingleton::Local(self.storage.local.get(&node.node).unwrap().clone()), - 2 => StorageSingleton::Nfs(self.storage.nfs.get(&node.node).unwrap().clone()), - _ => { - return Err(Status::invalid_argument( - "storage_position.r#type is invalid", - )); + match storage.create_repository(request.path.clone()).await { + Ok(_) => { + return Ok(Response::new(RepositoryStoragePosition { + node : request.node, + path : request.path, + })); } - }; - match storge { - StorageSingleton::S3(x) => { - return match x.create_repository(storage_position.path.clone()).await { - Ok(_) => Ok(Response::new(Pos { - r#type : storage_position.r#type, - path : storage_position.path.clone(), - node : storage_position.node.clone(), - })), - Err(e) => Err(Status::internal(e.to_string())), - }; - } - StorageSingleton::Local(x) => { - return match x.create_repository(storage_position.path.clone()).await { - Ok(_) => Ok(Response::new(Pos { - r#type : storage_position.r#type, - path : storage_position.path.clone(), - node : storage_position.node.clone(), - })), - Err(e) => Err(Status::internal(e.to_string())), - }; - } - StorageSingleton::Nfs(x) => { - return match x.create_repository(storage_position.path.clone()).await { - Ok(_) => Ok(Response::new(Pos { - r#type : storage_position.r#type, - path : storage_position.path.clone(), - node : storage_position.node.clone(), - })), - Err(e) => Err(Status::internal(e.to_string())), - }; + Err(e) => { + return Err(Status::internal(e.to_string())); } } } @@ -84,83 +48,143 @@ impl rpc::core_git::rep_repository_server::RepRepository for CoreGit { request : Request, ) -> Result, Status> { let request = request.into_inner(); - let storage_position = match request.repository_storage_position { - Some(storage_position) => storage_position, + let node = match request.repository_storage_position { + Some(node) => node, + None => { + return Err(Status::invalid_argument("node not found")); + } + }; + let storage = match self.storage.node.get(&node.node) { + Some(storage) => storage, + None => { + return Err(Status::invalid_argument("node not found")); + } + }; + match storage + .add_file( + node.path.clone(), + request.path.clone(), + request.content.clone(), + request.email, + request.user, + request.message, + request.file_name, + request.branch, + ) + .await + { + Ok(_) => { + return Ok(Response::new(RepositoryAddFilesResponse { + code : "200".to_string(), + })); + } + Err(e) => { + return Err(Status::internal(e.to_string())); + } + } + } + async fn sync_branch( + &self, + request : tonic::Request, + ) -> std::result::Result, tonic::Status> { + let request = request.into_inner(); + let storage = match self.storage.node.get(&request.node) { + Some(storage) => storage, + None => { + return Err(Status::invalid_argument("node not found")); + } + }; + match storage.branch(request.path.clone()).await { + Ok(x) => { + return Ok(Response::new(RepositorySyncBranchResponse { branches : x })); + } + Err(e) => { + return Err(Status::internal(e.to_string())); + } + } + } + async fn sync_commit( + &self, + request : tonic::Request, + ) -> std::result::Result, tonic::Status> { + let request = request.into_inner(); + let node = match request.position { + Some(node) => node, + None => { + return Err(Status::invalid_argument("node not found")); + } + }; + let storage = match self.storage.node.get(&node.node) { + Some(storage) => storage, None => { - return Err(Status::invalid_argument("storage_position is required")); + return Err(Status::invalid_argument("node not found")); } }; - let node = NodePath { - path : storage_position.path.clone(), - node : storage_position.node.clone(), + match storage.commit(node.path.clone(), request.branch).await { + Ok(x) => { + return Ok(Response::new(RepositorySyncCommitResponse { commits : x })); + } + Err(e) => { + return Err(Status::internal(e.to_string())); + } + } + } + async fn sync_blob( + &self, + request : tonic::Request, + ) -> std::result::Result, tonic::Status> { + let request = request.into_inner(); + let node = match request.position { + Some(node) => node, + None => { + return Err(Status::invalid_argument("node not found")); + } }; - let storge = match storage_position.r#type { - 0 => StorageSingleton::S3(self.storage.s3.get(&node.node).unwrap().clone()), - 1 => StorageSingleton::Local(self.storage.local.get(&node.node).unwrap().clone()), - 2 => StorageSingleton::Nfs(self.storage.nfs.get(&node.node).unwrap().clone()), - _ => { - return Err(Status::invalid_argument( - "storage_position.r#type is invalid", - )); + let storage = match self.storage.node.get(&node.node) { + Some(storage) => storage, + None => { + return Err(Status::invalid_argument("node not found")); } }; - match storge { - StorageSingleton::S3(x) => { - match x - .add_file( - storage_position.path.clone(), - request.path.clone(), - request.content.clone(), - request.email.clone(), - request.user.clone(), - request.message.clone(), - request.file_name.clone(), - request.branch.clone(), - ) - .await - { - Ok(_) => Ok(Response::new(RepositoryAddFilesResponse::default())), - Err(x) => { - return Err(Status::internal(x.to_string())); - } - } - } - StorageSingleton::Local(x) => { - match x - .add_file( - storage_position.path.clone(), - request.path.clone(), - request.content.clone(), - request.email.clone(), - request.user.clone(), - request.message.clone(), - request.file_name.clone(), - request.branch.clone(), - ) - .await - { - Ok(_) => Ok(Response::new(RepositoryAddFilesResponse::default())), - Err(x) => return Err(Status::internal(x.to_string())), - } - } - StorageSingleton::Nfs(x) => { - match x - .add_file( - storage_position.path.clone(), - request.path.clone(), - request.content.clone(), - request.email.clone(), - request.user.clone(), - request.message.clone(), - request.file_name.clone(), - request.branch.clone(), - ) - .await - { - Ok(_) => Ok(Response::new(RepositoryAddFilesResponse::default())), - Err(x) => return Err(Status::internal(x.to_string())), - } + match storage + .get_file( + node.path.clone(), + request.path.clone(), + request.hash.clone(), + ) + .await + { + Ok(x) => Ok(Response::new(RepositoryFileResponse { + hash : request.hash, + path : request.path.clone(), + size : x.len().to_string(), + name : PathBuf::from(request.path) + .file_name() + .unwrap_or("".as_ref()) + .to_str() + .unwrap_or("") + .to_string(), + mode : "0".to_string(), + content : x, + })), + Err(e) => Err(Status::internal(e.to_string())), + } + } + async fn sync_tags( + &self, + request : tonic::Request, + ) -> std::result::Result, tonic::Status> { + let request = request.into_inner(); + + let storage = match self.storage.node.get(&request.node) { + Some(storage) => storage, + None => { + return Err(Status::invalid_argument("node not found")); } + }; + match storage.tage(request.path.clone()).await { + Ok(x) => Ok(Response::new(RepositoryTagsResponse { tags : x })), + Err(e) => Err(Status::internal(e.to_string())), } } } diff --git a/gitdata-gits/src/rpc/git_core.rs b/gitdata-gits/src/rpc/git_core.rs index 0da1d18..f234ea2 100644 --- a/gitdata-gits/src/rpc/git_core.rs +++ b/gitdata-gits/src/rpc/git_core.rs @@ -6,7 +6,6 @@ use gitdata::rpc::git_core::Repository; use tokio::sync::Mutex; use crate::rpc::NodePath; -use crate::rpc::RepositoryStoragePosition; #[derive(Clone)] pub struct RepositoryRpc { @@ -15,32 +14,17 @@ pub struct RepositoryRpc { } impl RepositoryRpc { - pub async fn path( - &self, - owner : String, - repo : String, - ) -> io::Result { + pub async fn path(&self, owner : String, repo : String) -> io::Result { let mut client = self.client.lock().await; - let result = match client.path(git_core::PathRequest { owner, repo }).await { - Ok(x) => x.into_inner(), - Err(e) => { - return Err(io::Error::new(io::ErrorKind::Other, e)); + match client.path(git_core::PathRequest { owner, repo }).await { + Ok(x) => { + let x = x.into_inner(); + Ok(NodePath { + node : x.node, + path : x.path, + }) } - }; - match result.r#type { - 0 => Ok(RepositoryStoragePosition::Local(NodePath { - path : result.path, - node : result.node, - })), - 1 => Ok(RepositoryStoragePosition::S3(NodePath { - path : result.path, - node : result.node, - })), - 2 => Ok(RepositoryStoragePosition::Nfs(NodePath { - path : result.path, - node : result.node, - })), - _ => Err(io::Error::new(io::ErrorKind::Other, "unknown type")), + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), } } diff --git a/gitdata-gits/src/rpc/mod.rs b/gitdata-gits/src/rpc/mod.rs index 5406ba8..2f54795 100644 --- a/gitdata-gits/src/rpc/mod.rs +++ b/gitdata-gits/src/rpc/mod.rs @@ -8,22 +8,6 @@ pub enum RepositoryAccess { Admin, } -#[derive(Clone)] -pub enum RepositoryStoragePosition { - Local(NodePath), - S3(NodePath), - Nfs(NodePath), -} -impl RepositoryStoragePosition { - pub fn path(&self) -> &str { - match self { - RepositoryStoragePosition::Local(p) => p.path.as_str(), - RepositoryStoragePosition::S3(p) => p.path.as_str(), - RepositoryStoragePosition::Nfs(p) => p.path.as_str(), - } - } -} - #[derive(Clone, Debug)] pub struct NodePath { pub path : String, diff --git a/libs/rpc/core_git.rs b/libs/rpc/core_git.rs index 69976ed..5d6bb3a 100644 --- a/libs/rpc/core_git.rs +++ b/libs/rpc/core_git.rs @@ -1,19 +1,12 @@ // This file is @generated by prost-build. #[derive(Clone, PartialEq, ::prost::Message)] pub struct RepositoryStoragePosition { - #[prost(enumeration = "RepositoryStoragePositionType", tag = "1")] - pub r#type: i32, #[prost(string, tag = "2")] pub path: ::prost::alloc::string::String, #[prost(string, tag = "3")] pub node: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] -pub struct RepositoryCreate { - #[prost(message, optional, tag = "1")] - pub storage_position: ::core::option::Option, -} -#[derive(Clone, PartialEq, ::prost::Message)] pub struct RepositoryAddFileRequest { #[prost(message, optional, tag = "1")] pub repository_storage_position: ::core::option::Option, @@ -37,34 +30,111 @@ pub struct RepositoryAddFilesResponse { #[prost(string, tag = "1")] pub code: ::prost::alloc::string::String, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum RepositoryStoragePositionType { - Local = 0, - S3 = 1, - Nfs = 2, +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositorySyncBranchResponse { + #[prost(message, repeated, tag = "1")] + pub branches: ::prost::alloc::vec::Vec, } -impl RepositoryStoragePositionType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - Self::Local => "LOCAL", - Self::S3 => "S3", - Self::Nfs => "NFS", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "LOCAL" => Some(Self::Local), - "S3" => Some(Self::S3), - "NFS" => Some(Self::Nfs), - _ => None, - } - } +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositorySyncCommitResponse { + #[prost(message, repeated, tag = "1")] + pub commits: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryBranch { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub head: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub commit_nums: i32, + #[prost(string, optional, tag = "4")] + pub from: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "5")] + pub to: ::core::option::Option<::prost::alloc::string::String>, + #[prost(bool, tag = "6")] + pub start: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryCommit { + #[prost(string, tag = "1")] + pub hash: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub message: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub author: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub email: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub date: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub branch: ::prost::alloc::string::String, + #[prost(int32, tag = "7")] + pub file_nums: i32, + #[prost(message, repeated, tag = "8")] + pub tree: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryTree { + #[prost(string, tag = "1")] + pub hash: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub size: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub mode: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryFileResponse { + #[prost(string, tag = "1")] + pub hash: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub size: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub mode: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "7")] + pub content: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryFileRequest { + #[prost(message, optional, tag = "1")] + pub position: ::core::option::Option, + #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub hash: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryTagsResponse { + #[prost(message, repeated, tag = "1")] + pub tags: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryTags { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub hash: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub date: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub author: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub email: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RepositoryCommitRequest { + #[prost(message, optional, tag = "1")] + pub position: ::core::option::Option, + #[prost(string, tag = "2")] + pub branch: ::prost::alloc::string::String, } /// Generated client implementations. pub mod rep_repository_client { @@ -159,7 +229,7 @@ pub mod rep_repository_client { } pub async fn create( &mut self, - request: impl tonic::IntoRequest, + request: impl tonic::IntoRequest, ) -> std::result::Result< tonic::Response, tonic::Status, @@ -205,6 +275,102 @@ pub mod rep_repository_client { .insert(GrpcMethod::new("core_git.RepRepository", "AddFile")); self.inner.unary(req, path, codec).await } + pub async fn sync_branch( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/core_git.RepRepository/SyncBranch", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("core_git.RepRepository", "SyncBranch")); + self.inner.unary(req, path, codec).await + } + pub async fn sync_commit( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/core_git.RepRepository/SyncCommit", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("core_git.RepRepository", "SyncCommit")); + self.inner.unary(req, path, codec).await + } + pub async fn sync_blob( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/core_git.RepRepository/SyncBlob", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("core_git.RepRepository", "SyncBlob")); + self.inner.unary(req, path, codec).await + } + pub async fn sync_tags( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/core_git.RepRepository/SyncTags", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("core_git.RepRepository", "SyncTags")); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -222,7 +388,7 @@ pub mod rep_repository_server { pub trait RepRepository: std::marker::Send + std::marker::Sync + 'static { async fn create( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< tonic::Response, tonic::Status, @@ -234,6 +400,34 @@ pub mod rep_repository_server { tonic::Response, tonic::Status, >; + async fn sync_branch( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn sync_commit( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn sync_blob( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn sync_tags( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } #[derive(Debug)] pub struct RepRepositoryServer { @@ -316,7 +510,7 @@ pub mod rep_repository_server { struct CreateSvc(pub Arc); impl< T: RepRepository, - > tonic::server::UnaryService + > tonic::server::UnaryService for CreateSvc { type Response = super::RepositoryStoragePosition; type Future = BoxFuture< @@ -325,7 +519,7 @@ pub mod rep_repository_server { >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { @@ -401,6 +595,186 @@ pub mod rep_repository_server { }; Box::pin(fut) } + "/core_git.RepRepository/SyncBranch" => { + #[allow(non_camel_case_types)] + struct SyncBranchSvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService + for SyncBranchSvc { + type Response = super::RepositorySyncBranchResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::sync_branch(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SyncBranchSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/core_git.RepRepository/SyncCommit" => { + #[allow(non_camel_case_types)] + struct SyncCommitSvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService + for SyncCommitSvc { + type Response = super::RepositorySyncCommitResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::sync_commit(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SyncCommitSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/core_git.RepRepository/SyncBlob" => { + #[allow(non_camel_case_types)] + struct SyncBlobSvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService + for SyncBlobSvc { + type Response = super::RepositoryFileResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::sync_blob(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SyncBlobSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/core_git.RepRepository/SyncTags" => { + #[allow(non_camel_case_types)] + struct SyncTagsSvc(pub Arc); + impl< + T: RepRepository, + > tonic::server::UnaryService + for SyncTagsSvc { + type Response = super::RepositoryTagsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::sync_tags(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SyncTagsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { let mut response = http::Response::new(empty_body()); diff --git a/libs/rpc/git_core.rs b/libs/rpc/git_core.rs index 026f993..de5ac37 100644 --- a/libs/rpc/git_core.rs +++ b/libs/rpc/git_core.rs @@ -1,8 +1,6 @@ // This file is @generated by prost-build. #[derive(Clone, PartialEq, ::prost::Message)] pub struct RepositoryStoragePosition { - #[prost(enumeration = "RepositoryStoragePositionType", tag = "1")] - pub r#type: i32, #[prost(string, tag = "2")] pub path: ::prost::alloc::string::String, #[prost(string, tag = "3")] @@ -128,35 +126,6 @@ impl RepositoryAccess { } } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum RepositoryStoragePositionType { - Local = 0, - S3 = 1, - Nfs = 2, -} -impl RepositoryStoragePositionType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - Self::Local => "LOCAL", - Self::S3 => "S3", - Self::Nfs => "NFS", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "LOCAL" => Some(Self::Local), - "S3" => Some(Self::S3), - "NFS" => Some(Self::Nfs), - _ => None, - } - } -} /// Generated client implementations. pub mod rep_repository_client { #![allow( diff --git a/proto/core-git.proto b/proto/core-git.proto index e6d6194..88f5e1c 100644 --- a/proto/core-git.proto +++ b/proto/core-git.proto @@ -6,20 +6,12 @@ package core_git; -enum RepositoryStoragePositionType { - LOCAL = 0; - S3 = 1; - NFS = 2; -} + message RepositoryStoragePosition { - RepositoryStoragePositionType type = 1; string path = 2; string node = 3; } -message RepositoryCreate { - RepositoryStoragePosition storage_position = 1; -} message RepositoryAddFileRequest { RepositoryStoragePosition RepositoryStoragePosition = 1; @@ -36,6 +28,77 @@ message RepositoryAddFilesResponse { string code = 1; } service RepRepository { - rpc Create(RepositoryCreate) returns (RepositoryStoragePosition) {} + rpc Create(RepositoryStoragePosition) returns (RepositoryStoragePosition) {} rpc AddFile(RepositoryAddFileRequest) returns (RepositoryAddFilesResponse) {} + rpc SyncBranch(RepositoryStoragePosition) returns (RepositorySyncBranchResponse) {} + rpc SyncCommit(RepositoryCommitRequest) returns (RepositorySyncCommitResponse) {} + rpc SyncBlob(RepositoryFileRequest) returns (RepositoryFileResponse) {} + rpc SyncTags(RepositoryStoragePosition) returns (RepositoryTagsResponse) {} +} + +message RepositorySyncBranchResponse { + repeated RepositoryBranch branches = 1; +} +message RepositorySyncCommitResponse { + repeated RepositoryCommit commits = 1; +} + +message RepositoryBranch { + string name = 1; + string head = 2; + int32 commit_nums = 3; + optional string from = 4; + optional string to = 5; + bool start = 6; +} + +message RepositoryCommit { + string hash = 1; + string message = 2; + string author = 3; + string email = 4; + string date = 5; + string branch = 6; + int32 file_nums = 7; + repeated RepositoryTree tree = 8; +} + +message RepositoryTree { + string hash = 1; + string path = 2; + string size = 4; + string name = 5; + string mode = 6; +} + +message RepositoryFileResponse { + string hash = 1; + string path = 2; + string size = 4; + string name = 5; + string mode = 6; + bytes content = 7; +} + +message RepositoryFileRequest { + RepositoryStoragePosition position = 1; + string path = 2; + string hash = 3; +} + +message RepositoryTagsResponse { + repeated RepositoryTags tags = 1; } + +message RepositoryTags { + string name = 1; + string hash = 2; + string date = 3; + string author = 4; + string email = 5; +} + +message RepositoryCommitRequest { + RepositoryStoragePosition position = 1; + string branch = 2; +} \ No newline at end of file diff --git a/proto/git-core.proto b/proto/git-core.proto index 48a228e..8e628fe 100644 --- a/proto/git-core.proto +++ b/proto/git-core.proto @@ -11,14 +11,8 @@ enum RepositoryAccess { ADMIN = 3; } -enum RepositoryStoragePositionType { - LOCAL = 0; - S3 = 1; - NFS = 2; -} message RepositoryStoragePosition { - RepositoryStoragePositionType type = 1; string path = 2; string node = 3; } From 55b16fd8c4007004af11e77afecf2db2e1c12e4f Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Fri, 17 Jan 2025 05:51:22 +0800 Subject: [PATCH 4/4] :construction: API module Track --- Cargo.lock | 641 +----------------- gitdata-apis/Cargo.toml | 2 - gitdata-apis/config/rpc.toml | 17 + gitdata-apis/src/jobs/mod.rs | 1 + gitdata-apis/src/jobs/sync_repo.rs | 41 ++ gitdata-apis/src/lib.rs | 1 - gitdata-apis/src/rpc_server.rs | 1 - gitdata-apis/src/service/core_git_rpc.rs | 38 -- gitdata-apis/src/service/mod.rs | 71 +- gitdata-apis/src/service/repository/access.rs | 71 ++ gitdata-apis/src/service/repository/info.rs | 19 + gitdata-apis/src/service/repository/mod.rs | 2 + .../src/service/repository/new_repo.rs | 4 +- gitdata-apis/src/service/rpc.rs | 101 +++ gitdata-gits/src/main.rs | 7 +- gitdata-gits/src/rpc/core_git.rs | 4 +- gitdata-gits/src/rpc/git_core.rs | 13 +- libs/model/repository/repository.rs | 6 +- libs/rpc/git_core.rs | 158 +---- proto/git-core.proto | 47 +- 20 files changed, 358 insertions(+), 887 deletions(-) create mode 100644 gitdata-apis/config/rpc.toml create mode 100644 gitdata-apis/src/jobs/sync_repo.rs delete mode 100644 gitdata-apis/src/rpc_server.rs delete mode 100644 gitdata-apis/src/service/core_git_rpc.rs create mode 100644 gitdata-apis/src/service/repository/access.rs create mode 100644 gitdata-apis/src/service/repository/info.rs create mode 100644 gitdata-apis/src/service/rpc.rs diff --git a/Cargo.lock b/Cargo.lock index 108c1b6..9d1647a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -724,132 +724,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "background-jobs" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a10cb6081f857fdad834d8b7fa588bc4637e28afb3f19b79eee3b24c194c6b0" -dependencies = [ - "background-jobs-actix", - "background-jobs-core", - "background-jobs-metrics", - "background-jobs-postgres", - "background-jobs-sled", - "background-jobs-tokio", -] - -[[package]] -name = "background-jobs-actix" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a65756883718c1c7c09882ae4b02dfbe7fe0bf2c4ea048e96a5c5e7a14ba237" -dependencies = [ - "actix-rt", - "async-trait", - "background-jobs-core", - "metrics", - "serde", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tracing", - "uuid", -] - -[[package]] -name = "background-jobs-core" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8e50ca661b42d735dc16745f1cda817d7b6f175a2ff98a1308d7de2aca326f" -dependencies = [ - "async-trait", - "event-listener", - "metrics", - "serde", - "serde_json", - "thiserror 1.0.69", - "time", - "tracing", - "uuid", -] - -[[package]] -name = "background-jobs-metrics" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865f8df02e12efea214f90b8a4cb24da832cd92ade113ecaebeac0d7e8135125" -dependencies = [ - "async-trait", - "background-jobs-core", - "metrics", - "metrics-util", - "tracing", - "uuid", -] - -[[package]] -name = "background-jobs-postgres" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3389d127a99cec569f475dfeaa6bfdda2a100265473f0bc8326c17c714f9d08b" -dependencies = [ - "async-trait", - "background-jobs-core", - "barrel", - "dashmap", - "deadpool 0.9.5", - "diesel", - "diesel-async", - "diesel-derive-enum", - "flume", - "futures-core", - "metrics", - "pin-project-lite", - "refinery", - "serde_json", - "time", - "tokio", - "tokio-postgres", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "background-jobs-sled" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac884579e71bf086fbd1c5f31549697955209f3fd45777a998dd26a56cda495" -dependencies = [ - "async-trait", - "background-jobs-core", - "bincode", - "serde", - "serde_cbor", - "sled", - "thiserror 1.0.69", - "time", - "tokio", - "tracing", - "uuid", -] - -[[package]] -name = "background-jobs-tokio" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f6d5cee6c6fecc4c4b66af9f7734529a9990386cff5bdc20289ca6131a376b3" -dependencies = [ - "async-trait", - "background-jobs-core", - "metrics", - "serde", - "serde_json", - "tokio", - "tracing", - "uuid", -] - [[package]] name = "backtrace" version = "0.3.74" @@ -865,12 +739,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "barrel" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9e605929a6964efbec5ac0884bd0fe93f12a3b1eb271f52c251316640c68d9" - [[package]] name = "base16ct" version = "0.2.0" @@ -920,15 +788,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bit-vec" version = "0.6.3" @@ -1548,38 +1407,12 @@ dependencies = [ "syn 2.0.96", ] -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core 0.9.10", -] - [[package]] name = "data-encoding" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" -[[package]] -name = "deadpool" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" -dependencies = [ - "async-trait", - "deadpool-runtime", - "num_cpus", - "retain_mut", - "tokio", -] - [[package]] name = "deadpool" version = "0.12.1" @@ -1598,7 +1431,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ed4f481f6a4770b95e09b91e183ee5ed6e1d4a34c0b09814012b3ee5e585f70" dependencies = [ - "deadpool 0.12.1", + "deadpool", "redis", "serde", ] @@ -1687,69 +1520,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "diesel" -version = "2.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" -dependencies = [ - "bitflags 2.7.0", - "byteorder", - "diesel_derives", - "itoa", - "serde_json", - "time", - "uuid", -] - -[[package]] -name = "diesel-async" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be" -dependencies = [ - "async-trait", - "deadpool 0.9.5", - "diesel", - "futures-util", - "scoped-futures", - "tokio", - "tokio-postgres", -] - -[[package]] -name = "diesel-derive-enum" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81c5131a2895ef64741dad1d483f358c2a229a3a2d1b256778cdc5e146db64d4" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.96", -] - -[[package]] -name = "diesel_derives" -version = "2.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" -dependencies = [ - "diesel_table_macro_syntax", - "proc-macro2", - "quote", - "syn 2.0.96", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" -dependencies = [ - "syn 2.0.96", -] - [[package]] name = "digest" version = "0.10.7" @@ -1882,12 +1652,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - [[package]] name = "env_filter" version = "0.1.3" @@ -1955,12 +1719,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fastrand" version = "2.3.0" @@ -2007,7 +1765,6 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", - "nanorand", "spin", ] @@ -2019,7 +1776,7 @@ checksum = "cf5efcf77a4da27927d3ab0509dec5b0954bb3bc59da5a1de9e52642ebd4cdf9" dependencies = [ "ahash 0.8.11", "num_cpus", - "parking_lot 0.12.3", + "parking_lot", "seize", ] @@ -2059,16 +1816,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "funty" version = "2.0.0" @@ -2125,7 +1872,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.12.3", + "parking_lot", ] [[package]] @@ -2194,15 +1941,6 @@ dependencies = [ "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -2322,8 +2060,6 @@ dependencies = [ "apalis", "apalis-cron", "apalis-sql", - "background-jobs", - "background-jobs-actix", "base64 0.22.1", "chrono", "deadpool-redis", @@ -2948,15 +2684,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "io-uring" version = "0.6.4" @@ -3211,35 +2938,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "metrics" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261" -dependencies = [ - "ahash 0.8.11", - "portable-atomic", -] - -[[package]] -name = "metrics-util" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4259040465c955f9f2f1a4a8a16dc46726169bca0f88e8fb2dbeced487c3e828" -dependencies = [ - "aho-corasick", - "crossbeam-epoch", - "crossbeam-utils", - "hashbrown 0.14.5", - "indexmap 2.7.0", - "metrics", - "num_cpus", - "ordered-float 4.6.0", - "quanta", - "radix_trie", - "sketches-ddsketch", -] - [[package]] name = "mime" version = "0.3.17" @@ -3289,15 +2987,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" -dependencies = [ - "getrandom", -] - [[package]] name = "native-tls" version = "0.2.12" @@ -3315,15 +3004,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - [[package]] name = "nom" version = "7.1.3" @@ -3567,15 +3247,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ordered-float" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" -dependencies = [ - "num-traits", -] - [[package]] name = "ouroboros" version = "0.18.5" @@ -3665,17 +3336,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.3" @@ -3683,21 +3343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -3708,7 +3354,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -3754,24 +3400,6 @@ dependencies = [ "indexmap 2.7.0", ] -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project" version = "1.1.8" @@ -3871,59 +3499,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "portable-atomic" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" - -[[package]] -name = "postgres" -version = "0.19.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c918733159f4d55d2ceb262950f00b0aebd6af4aa97b5a47bb0655120475ed" -dependencies = [ - "bytes", - "fallible-iterator", - "futures-util", - "log", - "tokio", - "tokio-postgres", -] - -[[package]] -name = "postgres-protocol" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" -dependencies = [ - "base64 0.22.1", - "byteorder", - "bytes", - "fallible-iterator", - "hmac", - "md-5", - "memchr", - "rand", - "sha2", - "stringprep", -] - -[[package]] -name = "postgres-types" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" -dependencies = [ - "bytes", - "fallible-iterator", - "postgres-protocol", - "serde", - "serde_json", - "time", - "uuid", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -4092,21 +3667,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "quanta" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" -dependencies = [ - "crossbeam-utils", - "libc", - "once_cell", - "raw-cpuid", - "wasi", - "web-sys", - "winapi", -] - [[package]] name = "quote" version = "1.0.38" @@ -4128,16 +3688,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "rand" version = "0.8.5" @@ -4168,15 +3718,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "raw-cpuid" -version = "11.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" -dependencies = [ - "bitflags 2.7.0", -] - [[package]] name = "rayon" version = "1.10.0" @@ -4223,15 +3764,6 @@ dependencies = [ "url", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.8" @@ -4241,52 +3773,6 @@ dependencies = [ "bitflags 2.7.0", ] -[[package]] -name = "refinery" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0904191f0566c3d3e0091d5cc8dec22e663d77def2d247b16e7a438b188bf75d" -dependencies = [ - "refinery-core", - "refinery-macros", -] - -[[package]] -name = "refinery-core" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf253999e1899ae476c910b994959e341d84c4389ba9533d3dacbe06df04825" -dependencies = [ - "async-trait", - "cfg-if", - "log", - "postgres", - "regex", - "serde", - "siphasher", - "thiserror 1.0.69", - "time", - "tokio", - "tokio-postgres", - "toml", - "url", - "walkdir", -] - -[[package]] -name = "refinery-macros" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd81f69687fe8a1fa10995108b3ffc7cdbd63e682a4f8fbfd1020130780d7e17" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "refinery-core", - "regex", - "syn 2.0.96", -] - [[package]] name = "regex" version = "1.11.1" @@ -4346,12 +3832,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "retain_mut" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" - [[package]] name = "rfc6979" version = "0.4.0" @@ -4692,15 +4172,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schannel" version = "0.1.27" @@ -4710,15 +4181,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "scoped-futures" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b24aae2d0636530f359e9d5ef0c04669d11c5e756699b27a6a6d845d8329091" -dependencies = [ - "pin-project-lite", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -4834,7 +4296,7 @@ dependencies = [ "bigdecimal", "chrono", "inherent", - "ordered-float 3.9.2", + "ordered-float", "rust_decimal", "sea-query-derive", "serde_json", @@ -4972,16 +4434,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_cbor_2" version = "0.12.0-dev" @@ -5147,18 +4599,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "sketches-ddsketch" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85636c14b73d81f541e525f585c0a2109e6744e1565b5c1668e31c70c10ed65c" - [[package]] name = "slab" version = "0.4.9" @@ -5168,22 +4608,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "sled" -version = "0.34.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" -dependencies = [ - "crc32fast", - "crossbeam-epoch", - "crossbeam-utils", - "fs2", - "fxhash", - "libc", - "log", - "parking_lot 0.11.2", -] - [[package]] name = "smallvec" version = "1.13.2" @@ -5757,12 +5181,11 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot 0.12.3", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2 0.5.8", "tokio-macros", - "tracing", "windows-sys 0.52.0", ] @@ -5787,32 +5210,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-postgres" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" -dependencies = [ - "async-trait", - "byteorder", - "bytes", - "fallible-iterator", - "futures-channel", - "futures-util", - "log", - "parking_lot 0.12.3", - "percent-encoding", - "phf", - "pin-project-lite", - "postgres-protocol", - "postgres-types", - "rand", - "socket2 0.5.8", - "tokio", - "tokio-util", - "whoami", -] - [[package]] name = "tokio-retry" version = "0.3.0" @@ -6033,7 +5430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "parking_lot 0.12.3", + "parking_lot", "thiserror 1.0.69", "time", "tracing-subscriber", @@ -6303,16 +5700,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -6436,9 +5823,8 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall 0.5.8", + "redox_syscall", "wasite", - "web-sys", ] [[package]] @@ -6457,15 +5843,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/gitdata-apis/Cargo.toml b/gitdata-apis/Cargo.toml index ac8401d..77d285c 100644 --- a/gitdata-apis/Cargo.toml +++ b/gitdata-apis/Cargo.toml @@ -18,8 +18,6 @@ actix-session = { version = "0.10.1", features = ["redis-pool","redis-session"," actix-session-ext = { version = "0.1.0", features = [] } actix-identity = { version = "0.8.0", features = [] } actix-settings = { version = "0.8.0", features = [] } -background-jobs = { version = "0.19.0", features = ["tokio","sled","postgres"] } -background-jobs-actix = { version = "0.19.0", features = [] } deadpool-redis = { version = "~0.16.0", features = ["rt_tokio_1","cluster","serde"] } anyhow = { version = "1", features = ["backtrace", "std"] } serde = { version = "1.0.217", features = ["derive"] } diff --git a/gitdata-apis/config/rpc.toml b/gitdata-apis/config/rpc.toml new file mode 100644 index 0000000..51b9146 --- /dev/null +++ b/gitdata-apis/config/rpc.toml @@ -0,0 +1,17 @@ +[[nodes]] +[nodes.Health] +node_id = "Health-0" +host ="http://127.0.0.1" +port = 50051 + +[[nodes]] +[nodes.CoreGit] +node_id = "CoreGit-0" +host = "http://127.0.0.1" +port = 50051 + +[[nodes]] +[nodes.GitCore] +node_id = "GitCore-0" +host = "http://127.0.0.1" +port = 50051 diff --git a/gitdata-apis/src/jobs/mod.rs b/gitdata-apis/src/jobs/mod.rs index aa5f45d..3815311 100644 --- a/gitdata-apis/src/jobs/mod.rs +++ b/gitdata-apis/src/jobs/mod.rs @@ -1 +1,2 @@ pub mod email; +pub mod sync_repo; \ No newline at end of file diff --git a/gitdata-apis/src/jobs/sync_repo.rs b/gitdata-apis/src/jobs/sync_repo.rs new file mode 100644 index 0000000..a4ff508 --- /dev/null +++ b/gitdata-apis/src/jobs/sync_repo.rs @@ -0,0 +1,41 @@ +use crate::service::AppState; +use apalis::prelude::Data; +use serde::{Deserialize, Serialize}; +use std::io; + +#[derive(Clone,Deserialize,Serialize,Debug)] +pub struct SyncRepoMessage { + pub node: String, + pub path: String, +} + +pub(crate) async fn sync_repo( + message: SyncRepoMessage, + data: Data, +) -> io::Result<()> { + SyncRepo{ + node: message.node, + path: message.path, + state: data, + }.run().await + .map_err(|x| { + io::Error::new(io::ErrorKind::Other, x) + })?; + Ok(()) +} + +pub struct SyncRepo { + pub node: String, + pub path: String, + pub state: Data, +} + + +impl SyncRepo { + + pub async fn run(self) -> anyhow::Result<()> { + + + Ok(()) + } +} \ No newline at end of file diff --git a/gitdata-apis/src/lib.rs b/gitdata-apis/src/lib.rs index f2ab957..6d060b4 100644 --- a/gitdata-apis/src/lib.rs +++ b/gitdata-apis/src/lib.rs @@ -3,5 +3,4 @@ pub mod fields; pub mod jobs; pub mod middleware; pub mod public; -pub mod rpc_server; pub mod service; diff --git a/gitdata-apis/src/rpc_server.rs b/gitdata-apis/src/rpc_server.rs deleted file mode 100644 index 8b13789..0000000 --- a/gitdata-apis/src/rpc_server.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/gitdata-apis/src/service/core_git_rpc.rs b/gitdata-apis/src/service/core_git_rpc.rs deleted file mode 100644 index 0857c78..0000000 --- a/gitdata-apis/src/service/core_git_rpc.rs +++ /dev/null @@ -1,38 +0,0 @@ -use gitdata::config::rpc::RpcConfig; -use gitdata::rpc::core_git::rep_repository_client::RepRepositoryClient; -use tokio::sync::OnceCell; - -pub static RPC_CLIENT : OnceCell = OnceCell::const_new(); - -#[derive(Clone, Debug)] -pub struct CoreGitRpc { - pub client : RepRepositoryClient, -} - -impl CoreGitRpc { - pub async fn get() -> anyhow::Result<&'static CoreGitRpc> { - RPC_CLIENT.get_or_try_init(Self::connect).await - } - async fn connect() -> anyhow::Result { - let rpc = RpcConfig::load(); - if rpc.is_err() { - return Err(anyhow::anyhow!("Failed to load rpc config")); - } - let rpc = rpc?; - let core_git_rpc = RepRepositoryClient::connect(rpc.coregit_node()?.url()).await?; - Ok(CoreGitRpc { - client : core_git_rpc, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_rpc() { - let rpc = CoreGitRpc::get().await; - dbg!(rpc); - } -} diff --git a/gitdata-apis/src/service/mod.rs b/gitdata-apis/src/service/mod.rs index a7b80a0..38e6de5 100644 --- a/gitdata-apis/src/service/mod.rs +++ b/gitdata-apis/src/service/mod.rs @@ -1,3 +1,4 @@ +use std::io; use std::sync::Arc; use std::sync::Mutex; use std::time::Duration; @@ -15,14 +16,16 @@ use sea_orm::ConnectOptions; use sea_orm::Database; use sea_orm::DatabaseConnection; use sea_orm_migration::MigratorTrait; +use tonic::transport::Server; use tracing::debug; use tracing::info; - use crate::jobs::email::EmailJobs; use crate::jobs::email::send_email; +use crate::jobs::sync_repo::{sync_repo, SyncRepoMessage}; +use crate::service::rpc::GitCoreRpc; pub mod auth; -pub mod core_git_rpc; +pub mod rpc; pub mod emails; pub mod repository; pub mod users; @@ -51,6 +54,11 @@ impl AppState { let jobs_pool = apalis_sql::postgres::PgPool::connect(&*config.pg.get("Jobs").unwrap().format()) .await?; + let mut sync_jobs = PostgresStorage::new_with_config( + jobs_pool.clone(), + Config::new("apalis::Repository::Sync"), + ); + let mut pg = PostgresStorage::new_with_config(jobs_pool.clone(), Config::new("apalis::Email")); let migrator = PostgresStorage::migrations(); @@ -58,10 +66,26 @@ impl AppState { migrator.run(&mut job_txn).await?; let mut listener = PgListen::new(jobs_pool).await?; listener.subscribe_with(&mut pg); + listener.subscribe_with(&mut sync_jobs); tokio::spawn(async move { listener.listen().await.ok(); }); let pc = pg.clone(); + let sync_pg:PostgresStorage = sync_jobs.clone(); + info!("Connect Redis Pool"); + let redis_cfg = + deadpool_redis::Config::from_url(config.redis.get("Session").unwrap().format()); + let redis_pool = redis_cfg.create_pool(Some(Runtime::Tokio1))?; + + let state = AppState { + active_read, + active_write, + deprecated, + email_jobs : Arc::new(Mutex::new(pg)), + redis_pool, + }; + let state_replace = state.clone(); + let rpc_state = state_replace.clone(); tokio::spawn(async move { Monitor::new() .register({ @@ -71,6 +95,14 @@ impl AppState { .backend(pc.clone()) .build_fn(send_email) }) + .register({ + WorkerBuilder::new("sync-repo") + .data(state_replace.clone()) + .enable_tracing() + .retry(RetryPolicy::retries(5)) + .backend(sync_pg.clone()) + .build_fn(sync_repo) + }) .on_event(|e| debug!("{e}")) .run_with_signal(async { tokio::signal::ctrl_c().await?; @@ -80,17 +112,30 @@ impl AppState { .await .ok(); }); - info!("Connect Redis Pool"); - let redis_cfg = - deadpool_redis::Config::from_url(config.redis.get("Session").unwrap().format()); - let redis_pool = redis_cfg.create_pool(Some(Runtime::Tokio1))?; - Ok(AppState { - active_read, - active_write, - deprecated, - email_jobs : Arc::new(Mutex::new(pg)), - redis_pool, - }) + + tokio::spawn(async move { + let config = gitdata::config::rpc::RpcConfig::load().expect("failed to load rpc config"); + let git_core = GitCoreRpc::new(rpc_state.clone()); + let health = gitdata::health::service::HealthService::default(); + let addr = config.gitcore_node().expect("failed to load gitcore node").url().parse().expect("failed to parse url"); + info!("Starting GitCore RPC Server at {:?}", addr); + Server::builder() + .trace_fn(|x| { + tracing::log::info!("Url: {:?} Method: {}", x.uri(), x.method()); + tracing::Span::current() + }) + .add_service(gitdata::rpc::health::health_server::HealthServer::new( + health, + )) + .add_service( + gitdata::rpc::git_core::rep_repository_server::RepRepositoryServer::new(git_core), + ) + .serve(addr) + .await + .map_err(|x| io::Error::new(io::ErrorKind::Other, x)) + .unwrap(); + }); + Ok(state.clone()) } } diff --git a/gitdata-apis/src/service/repository/access.rs b/gitdata-apis/src/service/repository/access.rs new file mode 100644 index 0000000..7f8dd03 --- /dev/null +++ b/gitdata-apis/src/service/repository/access.rs @@ -0,0 +1,71 @@ +use sea_orm::*; +use gitdata::model::repository::repository; +use gitdata::model::users::{ssh_keys, token_key, users}; +use crate::service::AppState; + +impl AppState { + pub async fn repo_access_token(&self, repo: repository::Model, tk: String) -> anyhow::Result{ + let owner_model = users::Entity::find_by_uid(repo.owner_uid) + .one(&self.active_read) + .await? + .ok_or_else(|| anyhow::anyhow!("user not found"))?; + let tokens = if owner_model.organize { + let user_uids = users::Entity::find() + .filter(users::Column::Uid.is_in(owner_model.member)) + .all(&self.active_read) + .await + .map(|users| users.into_iter().map(|user| user.uid).collect::>())?; + token_key::Entity::find() + .filter(token_key::Column::UserUid.is_in(user_uids)) + .all(&self.active_read) + .await + .map_err(|_| anyhow::anyhow!("token not found"))? + } else { + match token_key::Entity::find() + .filter(token_key::Column::UserUid.eq(owner_model.uid)) + .all(&self.active_read) + .await{ + Ok(token) => token, + Err(_) => return Err(anyhow::anyhow!("token not found")) + } + }; + for token in tokens { + if token.token == tk { + return Ok(token); + } + } + Err(anyhow::anyhow!("token not found")) + } + pub async fn repo_access_ssh(&self, repo: repository::Model, pbk: String) -> anyhow::Result{ + let owner_model = users::Entity::find_by_uid(repo.owner_uid) + .one(&self.active_read) + .await? + .ok_or_else(|| anyhow::anyhow!("user not found"))?; + let keys = if owner_model.organize { + let user_uids = users::Entity::find() + .filter(users::Column::Uid.is_in(owner_model.member)) + .all(&self.active_read) + .await + .map(|users| users.into_iter().map(|user| user.uid).collect::>())?; + ssh_keys::Entity::find() + .filter(ssh_keys::Column::UserUid.is_in(user_uids)) + .all(&self.active_read) + .await + .map_err(|_| anyhow::anyhow!("ssh key not found"))? + }else { + match ssh_keys::Entity::find() + .filter(ssh_keys::Column::UserUid.eq(owner_model.uid)) + .all(&self.active_read) + .await{ + Ok(key) => key, + Err(_) => return Err(anyhow::anyhow!("ssh key not found")) + } + }; + for key in keys { + if key.ssh_key == pbk { + return Ok(key); + } + } + Err(anyhow::anyhow!("ssh key not found")) + } +} \ No newline at end of file diff --git a/gitdata-apis/src/service/repository/info.rs b/gitdata-apis/src/service/repository/info.rs new file mode 100644 index 0000000..7afdfea --- /dev/null +++ b/gitdata-apis/src/service/repository/info.rs @@ -0,0 +1,19 @@ +use sea_orm::*; +use gitdata::model::repository::repository; +use gitdata::model::users::users; +use crate::service::AppState; + +impl AppState { + pub async fn repo_owner_name(&self, owner: String, repo: String) -> anyhow::Result { + let user_model = users::Entity::find_by_username(&owner) + .one(&self.active_read) + .await? + .ok_or_else(|| anyhow::anyhow!("user not found"))?; + let repo_model = repository::Entity::find_by_owner(user_model.uid) + .filter(repository::Column::Name.eq(repo)) + .one(&self.active_read) + .await? + .ok_or_else(|| anyhow::anyhow!("repository not found"))?; + Ok(repo_model) + } +} \ No newline at end of file diff --git a/gitdata-apis/src/service/repository/mod.rs b/gitdata-apis/src/service/repository/mod.rs index 7d8ddcb..855a520 100644 --- a/gitdata-apis/src/service/repository/mod.rs +++ b/gitdata-apis/src/service/repository/mod.rs @@ -1 +1,3 @@ pub mod new_repo; +pub mod info; +pub mod access; \ No newline at end of file diff --git a/gitdata-apis/src/service/repository/new_repo.rs b/gitdata-apis/src/service/repository/new_repo.rs index d219d38..340ae55 100644 --- a/gitdata-apis/src/service/repository/new_repo.rs +++ b/gitdata-apis/src/service/repository/new_repo.rs @@ -9,7 +9,7 @@ use serde::Deserialize; use serde::Serialize; use crate::service::AppState; -use crate::service::core_git_rpc::CoreGitRpc; +use crate::service::rpc::CoreGitRpc; #[derive(Deserialize, Serialize)] pub struct RepoCreateParam { @@ -20,7 +20,6 @@ pub struct RepoCreateParam { pub default_branch : Option, pub readme : bool, pub node : String, - pub storage_position : i32, pub message : Option, } @@ -36,6 +35,7 @@ impl AppState { param.description, param.visible, param.default_branch.clone(), + param.node.clone(), ); let txn = self.active_write.begin().await?; match active_model.clone().insert(&txn).await { diff --git a/gitdata-apis/src/service/rpc.rs b/gitdata-apis/src/service/rpc.rs new file mode 100644 index 0000000..5065c60 --- /dev/null +++ b/gitdata-apis/src/service/rpc.rs @@ -0,0 +1,101 @@ +use crate::service::AppState; +use gitdata::config::rpc::RpcConfig; +use gitdata::rpc; +use gitdata::rpc::core_git::rep_repository_client::RepRepositoryClient; +use gitdata::rpc::git_core::{AccessResponse, PathRequest, PublickeyRequest, RepositoryAccess, RepositoryStoragePosition, TokenRequest}; +use tokio::sync::OnceCell; +use tonic::{async_trait, Request, Response, Status}; + +pub static RPC_CLIENT : OnceCell = OnceCell::const_new(); + +#[derive(Clone, Debug)] +pub struct CoreGitRpc { + pub client : RepRepositoryClient, +} + +impl CoreGitRpc { + pub async fn get() -> anyhow::Result<&'static CoreGitRpc> { + RPC_CLIENT.get_or_try_init(Self::connect).await + } + async fn connect() -> anyhow::Result { + let rpc = RpcConfig::load(); + if rpc.is_err() { + return Err(anyhow::anyhow!("Failed to load rpc config")); + } + let rpc = rpc?; + let core_git_rpc = RepRepositoryClient::connect(rpc.coregit_node()?.url()).await?; + Ok(CoreGitRpc { + client : core_git_rpc, + }) + } +} + + + +pub struct GitCoreRpc { + state: AppState +} + +impl GitCoreRpc { + pub fn new(app_state: AppState) -> Self { + Self { + state: app_state + } + } +} + +#[async_trait] +impl rpc::git_core::rep_repository_server::RepRepository for GitCoreRpc { + async fn path(&self, request: Request) -> Result, Status> { + let request = request.into_inner(); + let path = match self.state.repo_owner_name(request.owner,request.repo).await{ + Ok(path) => path, + Err(_) => return Err(Status::not_found("Repo not found")) + }; + return Ok(Response::new(RepositoryStoragePosition{ + path: path.uid.to_string(), + node: path.storage_node, + })) + } + + async fn token(&self, request: Request) -> Result, Status> { + let request = request.into_inner(); + let model = match self.state.repo_owner_name(request.owner,request.repo).await{ + Ok(path) => path, + Err(_) => return Err(Status::not_found("Repo not found")) + }; + let tokens = match self.state.repo_access_token(model,request.token).await{ + Ok(tokens) => tokens, + Err(_) => return Err(Status::not_found("Token not found")) + }; + match tokens.access { + 1 => Ok(Response::new(AccessResponse{ + access: i32::from(RepositoryAccess::Read), + })), + 4 => Ok(Response::new(AccessResponse{ + access: i32::from(RepositoryAccess::Write), + })), + 7 => Ok(Response::new(AccessResponse{ + access: i32::from(RepositoryAccess::Admin), + })), + _ => Ok(Response::new(AccessResponse{ + access: i32::from(RepositoryAccess::None), + })) + } + } + + async fn publickey(&self, request: Request) -> Result, Status> { + let request = request.into_inner(); + let model = match self.state.repo_owner_name(request.owner,request.repo).await{ + Ok(path) => path, + Err(_) => return Err(Status::not_found("Repo not found")) + }; + match self.state.repo_access_ssh(model,request.publickey).await{ + Ok(_) => Ok(Response::new(AccessResponse{ + access: i32::from(RepositoryAccess::Admin), + })), + Err(_) => Err(Status::not_found("Token not found")) + } + } +} + diff --git a/gitdata-gits/src/main.rs b/gitdata-gits/src/main.rs index 54ba186..fb6a2b4 100644 --- a/gitdata-gits/src/main.rs +++ b/gitdata-gits/src/main.rs @@ -2,7 +2,7 @@ use std::io; use log::info; use tonic::transport::Server; - +use gitdata::config; use crate::mount::git::NodeStorage; pub mod health; @@ -24,6 +24,9 @@ async fn main() -> anyhow::Result<()> { let health = gitdata::health::service::HealthService::default(); let core_git = rpc::core_git::CoreGit::new(pool.clone()); info!("starting health service"); + let config = config::rpc::RpcConfig::load().expect("failed to load rpc config"); + let addr = config.coregit_node().expect("failed to load gitcore node").url().parse().expect("failed to parse url"); + info!("connecting to gitcore node at {:?}", addr); Server::builder() .trace_fn(|x| { info!("Url: {:?} Method: {}", x.uri(), x.method()); @@ -35,7 +38,7 @@ async fn main() -> anyhow::Result<()> { .add_service( gitdata::rpc::core_git::rep_repository_server::RepRepositoryServer::new(core_git), ) - .serve("0.0.0.0:50051".parse().unwrap()) + .serve(addr) .await .map_err(|x| io::Error::new(io::ErrorKind::Other, x)) .unwrap(); diff --git a/gitdata-gits/src/rpc/core_git.rs b/gitdata-gits/src/rpc/core_git.rs index dddd166..756062d 100644 --- a/gitdata-gits/src/rpc/core_git.rs +++ b/gitdata-gits/src/rpc/core_git.rs @@ -122,10 +122,10 @@ impl rep_repository_server::RepRepository for CoreGit { }; match storage.commit(node.path.clone(), request.branch).await { Ok(x) => { - return Ok(Response::new(RepositorySyncCommitResponse { commits : x })); + Ok(Response::new(RepositorySyncCommitResponse { commits: x })) } Err(e) => { - return Err(Status::internal(e.to_string())); + Err(Status::internal(e.to_string())) } } } diff --git a/gitdata-gits/src/rpc/git_core.rs b/gitdata-gits/src/rpc/git_core.rs index f234ea2..9e90e3c 100644 --- a/gitdata-gits/src/rpc/git_core.rs +++ b/gitdata-gits/src/rpc/git_core.rs @@ -1,12 +1,11 @@ use std::sync::Arc; +use crate::rpc::NodePath; use futures::io; use gitdata::rpc::git_core; -use gitdata::rpc::git_core::Repository; +use gitdata::rpc::git_core::AccessResponse; use tokio::sync::Mutex; -use crate::rpc::NodePath; - #[derive(Clone)] pub struct RepositoryRpc { client : @@ -33,13 +32,13 @@ impl RepositoryRpc { owner : String, repo : String, token : String, - ) -> io::Result> { + ) -> io::Result { let mut client = self.client.lock().await; match client .token(git_core::TokenRequest { owner, repo, token }) .await { - Ok(x) => Ok(x.into_inner().repositories), + Ok(x) => Ok(x.into_inner()), Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), } } @@ -49,7 +48,7 @@ impl RepositoryRpc { owner : String, repo : String, publickey : String, - ) -> io::Result> { + ) -> io::Result { let mut client = self.client.lock().await; match client .publickey(git_core::PublickeyRequest { @@ -59,7 +58,7 @@ impl RepositoryRpc { }) .await { - Ok(x) => Ok(x.into_inner().repositories), + Ok(x) => Ok(x.into_inner()), Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), } } diff --git a/libs/model/repository/repository.rs b/libs/model/repository/repository.rs index cc715d1..71d257f 100644 --- a/libs/model/repository/repository.rs +++ b/libs/model/repository/repository.rs @@ -56,6 +56,9 @@ impl Entity { pub fn find_by_uid(uid: Uuid) -> Select { Entity::find().filter(Column::Uid.eq(uid)) } + pub fn find_by_name(name: String) -> Select { + Entity::find().filter(Column::Name.eq(name)) + } } @@ -66,6 +69,7 @@ impl ActiveModel { description: Option, visible: bool, default_branch: Option, + node: String, ) -> Self { let git_config = GitConfig::get(); Self { @@ -81,7 +85,7 @@ impl ActiveModel { archive_time: Set(None), ssh_path: Set(format!("{}:{}/{}", git_config.ssh, owner_uid, name)), http_path: Set(format!("{}/{}/{}", git_config.http, owner_uid, name)), - storage_node: Set("".to_string()), + storage_node: Set(node), fork: Set(false), fork_uid: Set(None), nums_star: Set(0), diff --git a/libs/rpc/git_core.rs b/libs/rpc/git_core.rs index de5ac37..8c949a2 100644 --- a/libs/rpc/git_core.rs +++ b/libs/rpc/git_core.rs @@ -6,10 +6,10 @@ pub struct RepositoryStoragePosition { #[prost(string, tag = "3")] pub node: ::prost::alloc::string::String, } -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RepositoryCreate { - #[prost(message, optional, tag = "1")] - pub storage_position: ::core::option::Option, +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct AccessResponse { + #[prost(enumeration = "RepositoryAccess", tag = "1")] + pub access: i32, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct PathRequest { @@ -36,71 +36,13 @@ pub struct PublickeyRequest { #[prost(string, tag = "3")] pub publickey: ::prost::alloc::string::String, } -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RepositoryList { - #[prost(message, repeated, tag = "1")] - pub repositories: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Repository { - #[prost(string, tag = "1")] - pub uid: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub name: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub description: ::prost::alloc::string::String, - #[prost(string, tag = "4")] - pub owner_uid: ::prost::alloc::string::String, - #[prost(string, tag = "5")] - pub default_branch: ::prost::alloc::string::String, - #[prost(bool, tag = "6")] - pub visible: bool, - #[prost(bool, tag = "7")] - pub template: bool, - #[prost(bool, tag = "8")] - pub mirrors: bool, - #[prost(bool, tag = "9")] - pub archive: bool, - #[prost(int64, tag = "10")] - pub archive_time: i64, - #[prost(string, tag = "11")] - pub ssh_path: ::prost::alloc::string::String, - #[prost(string, tag = "12")] - pub http_path: ::prost::alloc::string::String, - #[prost(bool, tag = "13")] - pub fork: bool, - #[prost(string, tag = "25")] - pub storage_node: ::prost::alloc::string::String, - #[prost(string, tag = "14")] - pub fork_uid: ::prost::alloc::string::String, - #[prost(int64, tag = "15")] - pub nums_star: i64, - #[prost(int64, tag = "16")] - pub nums_fork: i64, - #[prost(int64, tag = "17")] - pub nums_watch: i64, - #[prost(int64, tag = "18")] - pub nums_issue: i64, - #[prost(int64, tag = "19")] - pub nums_pull: i64, - #[prost(int64, tag = "20")] - pub nums_commit: i64, - #[prost(string, tag = "21")] - pub head: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "22")] - pub license: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - #[prost(int64, tag = "23")] - pub created_at: i64, - #[prost(int64, tag = "24")] - pub updated_at: i64, -} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum RepositoryAccess { None = 0, Read = 1, - Write = 2, - Admin = 3, + Write = 4, + Admin = 7, } impl RepositoryAccess { /// String value of the enum field names used in the ProtoBuf definition. @@ -244,7 +186,7 @@ pub mod rep_repository_client { pub async fn token( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result, tonic::Status> { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await @@ -265,7 +207,7 @@ pub mod rep_repository_client { pub async fn publickey( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result, tonic::Status> { + ) -> std::result::Result, tonic::Status> { self.inner .ready() .await @@ -283,30 +225,6 @@ pub mod rep_repository_client { .insert(GrpcMethod::new("git_core.RepRepository", "Publickey")); self.inner.unary(req, path, codec).await } - pub async fn create( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/git_core.RepRepository/Create", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("git_core.RepRepository", "Create")); - self.inner.unary(req, path, codec).await - } } } /// Generated server implementations. @@ -332,18 +250,11 @@ pub mod rep_repository_server { async fn token( &self, request: tonic::Request, - ) -> std::result::Result, tonic::Status>; + ) -> std::result::Result, tonic::Status>; async fn publickey( &self, request: tonic::Request, - ) -> std::result::Result, tonic::Status>; - async fn create( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; } #[derive(Debug)] pub struct RepRepositoryServer { @@ -471,7 +382,7 @@ pub mod rep_repository_server { impl< T: RepRepository, > tonic::server::UnaryService for TokenSvc { - type Response = super::RepositoryList; + type Response = super::AccessResponse; type Future = BoxFuture< tonic::Response, tonic::Status, @@ -516,7 +427,7 @@ pub mod rep_repository_server { T: RepRepository, > tonic::server::UnaryService for PublickeySvc { - type Response = super::RepositoryList; + type Response = super::AccessResponse; type Future = BoxFuture< tonic::Response, tonic::Status, @@ -554,51 +465,6 @@ pub mod rep_repository_server { }; Box::pin(fut) } - "/git_core.RepRepository/Create" => { - #[allow(non_camel_case_types)] - struct CreateSvc(pub Arc); - impl< - T: RepRepository, - > tonic::server::UnaryService - for CreateSvc { - type Response = super::RepositoryStoragePosition; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::create(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = CreateSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } _ => { Box::pin(async move { let mut response = http::Response::new(empty_body()); diff --git a/proto/git-core.proto b/proto/git-core.proto index 8e628fe..5034da5 100644 --- a/proto/git-core.proto +++ b/proto/git-core.proto @@ -7,8 +7,8 @@ package git_core; enum RepositoryAccess { NONE = 0; READ = 1; - WRITE = 2; - ADMIN = 3; + WRITE = 4; + ADMIN = 7; } @@ -16,17 +16,15 @@ message RepositoryStoragePosition { string path = 2; string node = 3; } -message RepositoryCreate { - RepositoryStoragePosition storage_position = 1; -} service RepRepository { rpc Path(PathRequest) returns (RepositoryStoragePosition) {} - rpc Token(TokenRequest) returns (RepositoryList) {} - rpc Publickey(PublickeyRequest) returns (RepositoryList) {} - rpc Create(RepositoryCreate) returns (RepositoryStoragePosition) {} + rpc Token(TokenRequest) returns (AccessResponse) {} + rpc Publickey(PublickeyRequest) returns (AccessResponse) {} +} +message AccessResponse { + RepositoryAccess access = 1; } - message PathRequest { string owner = 1; string repo = 2; @@ -43,34 +41,3 @@ message PublickeyRequest { string repo = 2; string publickey = 3; } -message RepositoryList { - repeated Repository repositories = 1; -} -message Repository { - string uid = 1; - string name = 2; - string description = 3; - string owner_uid = 4; - string default_branch = 5; - bool visible = 6; - bool template = 7; - bool mirrors = 8; - bool archive = 9; - int64 archive_time = 10; - string ssh_path = 11; - string http_path = 12; - bool fork = 13; - string storage_node = 25; - string fork_uid = 14; - int64 nums_star = 15; - int64 nums_fork = 16; - int64 nums_watch = 17; - int64 nums_issue = 18; - int64 nums_pull = 19; - int64 nums_commit = 20; - string head = 21; - repeated string license = 22; - int64 created_at = 23; - int64 updated_at = 24; -} -