diff --git a/.github/release-template.md b/.github/release-template.md index e49390198..31873dcf0 100644 --- a/.github/release-template.md +++ b/.github/release-template.md @@ -8,15 +8,15 @@ Version: ${VERSION} ### mono-engine -docker pull ghcr.io/web3infra-foundation/mono-engine:${VERSION} +docker pull ghcr.io/gitmono-dev/mono-engine:${VERSION} ### ui -docker pull ghcr.io/web3infra-foundation/ui:${VERSION} +docker pull ghcr.io/gitmono-dev/ui:${VERSION} ### orion-server -docker pull ghcr.io/web3infra-foundation/orion-server:${VERSION} +docker pull ghcr.io/gitmono-dev/orion-server:${VERSION} --- diff --git a/.github/workflows/base.yml b/.github/workflows/base.yml index 3c7a0a474..5232399e5 100644 --- a/.github/workflows/base.yml +++ b/.github/workflows/base.yml @@ -26,7 +26,7 @@ concurrency: jobs: format: - if: ${{ !(github.repository == 'web3infra-foundation/mega' && github.event_name == 'push') }} + if: ${{ !(github.repository == 'gitmono-dev/mega' && github.event_name == 'push') }} name: Rustfmt Check runs-on: ubuntu-latest steps: @@ -45,7 +45,7 @@ jobs: # clippy: - if: ${{ !(github.repository == 'web3infra-foundation/mega' && github.event_name == 'push') }} + if: ${{ !(github.repository == 'gitmono-dev/mega' && github.event_name == 'push') }} name: Clippy Check runs-on: ubuntu-latest env: @@ -114,11 +114,77 @@ jobs: rg 'use crate::(pack|transport)' ceres/src/application/ exit 1 fi - if rg --glob '!lfs_router.rs' -q '\.storage\.|_stg\(\)' mono/src/api/router/; then + if rg -q 'application::' ceres/src/transport/; then + echo "ceres transport must not import application modules" + rg 'application::' ceres/src/transport/ + exit 1 + fi + if rg -q '\.storage\.|_stg\(\)' mono/src/api/router/; then echo "mono routers must use MonoApiService facades, not storage" - rg --glob '!lfs_router.rs' '\.storage\.|_stg\(\)' mono/src/api/router/ + rg '\.storage\.|_stg\(\)' mono/src/api/router/ + exit 1 + fi + MONO_MONOREPO_WHITELIST=( + mono/src/api/api_router.rs + mono/src/api/router/commit_router.rs + mono/src/api/router/preview_router.rs + mono/src/api/router/repo_router.rs + mono/src/api/router/tag_router.rs + mono/src/api/router/buck_router.rs + ) + for f in mono/src/api/router/*.rs; do + skip=false + for w in "${MONO_MONOREPO_WHITELIST[@]}"; do + if [ "$f" = "$w" ]; then + skip=true + break + fi + done + if $skip; then + continue + fi + if rg -q 'monorepo\(\)' "$f"; then + echo "mono router $f must use state.services().() or state.git() instead of monorepo()" + rg 'monorepo\(\)' "$f" + exit 1 + fi + done + if rg -q 'artifact_app_service|build_trigger_service' mono/; then + echo "mono must use state.services().artifact() / build_trigger() instead of state factories" + rg 'artifact_app_service|build_trigger_service' mono/ + exit 1 + fi + if rg -q 'ProtocolApiState' ceres/ mono/; then + echo "ProtocolApiState alias removed; use TransportRuntime" + rg 'ProtocolApiState' ceres/ mono/ exit 1 fi + MONO_STORAGE_ALLOWLIST=( + ceres/src/application/api_service/mono/admin/group.rs + ceres/src/application/api_service/mono/admin/bot.rs + ceres/src/application/api_service/mono/commit.rs + ceres/src/application/api_service/mono/edit/entry.rs + ceres/src/application/api_service/mono/sync.rs + ceres/src/application/api_service/mono/tag.rs + ceres/src/application/api_service/mono/buck/upload.rs + ) + for f in $(find ceres/src/application/api_service/mono ceres/src/application/webhook -name '*.rs'); do + skip=false + for w in "${MONO_STORAGE_ALLOWLIST[@]}"; do + if [ "$f" = "$w" ]; then + skip=true + break + fi + done + if $skip; then + continue + fi + if rg -q '\.(issue_storage|cl_storage|conversation_storage)(\(\)|\b)' "$f"; then + echo "ceres mono modules must use jupiter services, not *_storage in $f" + rg '\.(issue_storage|cl_storage|conversation_storage)(\(\)|\b)' "$f" + exit 1 + fi + done if rg -q 'ceres::(api_service|pack|protocol|build_trigger|code_edit)' mono/; then echo "mono must use ceres::application::* and ceres::transport::* instead of legacy re-exports" rg 'ceres::(api_service|pack|protocol|build_trigger|code_edit)' mono/ @@ -131,13 +197,13 @@ jobs: sccache --show-stats || true test: - if: ${{ !(github.repository == 'web3infra-foundation/mega' && github.event_name == 'push') }} + if: ${{ !(github.repository == 'gitmono-dev/mega' && github.event_name == 'push') }} name: Full Test - runs-on: ${{ fromJson(github.repository == 'web3infra-foundation/mega' && '["self-hosted","Linux","X64"]' || '"ubuntu-latest"') }} + runs-on: ${{ fromJson(github.repository == 'gitmono-dev/mega' && '["self-hosted","Linux","X64"]' || '"ubuntu-latest"') }} env: CARGO_TERM_COLOR: always RUSTUP_TOOLCHAIN: stable - HOME: ${{ github.repository == 'web3infra-foundation/mega' && '/home/github' || '/home/runner' }} + HOME: ${{ github.repository == 'gitmono-dev/mega' && '/home/github' || '/home/runner' }} RUSTC_WRAPPER: sccache SCCACHE_DIR: ${{ github.workspace }}/.sccache steps: @@ -156,7 +222,7 @@ jobs: with: cache-key: sysdeps platform: ubuntu - self-hosted: ${{ github.repository == 'web3infra-foundation/mega' }} + self-hosted: ${{ github.repository == 'gitmono-dev/mega' }} use-gtk: false - name: Install sccache run: | @@ -199,4 +265,4 @@ jobs: sccache --show-stats || true # Note: The fuse/scorpio job has been removed as scorpio has been moved - # to its own repository: https://github.com/web3infra-foundation/scorpiofs + # to its own repository: https://github.com/gitmono-dev/scorpiofs diff --git a/.github/workflows/mono-engine-deploy.yml b/.github/workflows/mono-engine-deploy.yml index c7d137e3c..6824d1d0a 100644 --- a/.github/workflows/mono-engine-deploy.yml +++ b/.github/workflows/mono-engine-deploy.yml @@ -128,7 +128,7 @@ jobs: deploy-aws: needs: manifest - if: ${{ github.repository == 'web3infra-foundation/mega' }} + if: ${{ github.repository == 'gitmono-dev/mega' }} runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/orion-server-deploy.yml b/.github/workflows/orion-server-deploy.yml index 316fddd8b..d1932920c 100644 --- a/.github/workflows/orion-server-deploy.yml +++ b/.github/workflows/orion-server-deploy.yml @@ -118,7 +118,7 @@ jobs: deploy-aws: needs: manifest - if: ${{ github.repository == 'web3infra-foundation/mega' }} + if: ${{ github.repository == 'gitmono-dev/mega' }} runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/web-deploy.yml b/.github/workflows/web-deploy.yml index df94e78f4..b9f695d57 100644 --- a/.github/workflows/web-deploy.yml +++ b/.github/workflows/web-deploy.yml @@ -128,7 +128,7 @@ jobs: deploy-aws: needs: manifest - if: ${{ github.repository == 'web3infra-foundation/mega' }} + if: ${{ github.repository == 'gitmono-dev/mega' }} runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/web-sync-server-deploy.yml b/.github/workflows/web-sync-server-deploy.yml index 7442b0294..73fcca023 100644 --- a/.github/workflows/web-sync-server-deploy.yml +++ b/.github/workflows/web-sync-server-deploy.yml @@ -68,7 +68,7 @@ jobs: deploy-aws: needs: build-and-push - if: ${{ github.repository == 'web3infra-foundation/mega' }} + if: ${{ github.repository == 'gitmono-dev/mega' }} runs-on: ubuntu-latest strategy: matrix: diff --git a/ceres/Cargo.toml b/ceres/Cargo.toml index 2b82a7c62..2fb5caaa4 100644 --- a/ceres/Cargo.toml +++ b/ceres/Cargo.toml @@ -16,7 +16,6 @@ common = { workspace = true } jupiter = { workspace = true } callisto = { workspace = true } git-internal = { workspace = true } -orion-client = { workspace = true } saturn = { workspace = true } anyhow = { workspace = true } @@ -49,4 +48,5 @@ migrate = ["jupiter/migrate"] [dev-dependencies] axum = { workspace = true } jupiter-migrate = { workspace = true } +orion-client = { workspace = true } tempfile = { workspace = true } diff --git a/ceres/README.md b/ceres/README.md index 86ca78322..4f35090d9 100644 --- a/ceres/README.md +++ b/ceres/README.md @@ -2,21 +2,42 @@ Monorepo domain library for Mega: Git transport, REST application logic, and shared models. +## Design goals + +Ceres is the **domain layer** of Mega. `mono` is the composition root (HTTP/SSH servers, routers, auth, OpenAPI); `jupiter` is persistence. Ceres owns everything in between: what Git pushes mean, how CLs merge, how REST APIs behave, and which DTOs cross crate boundaries. + +| Goal | What it means in practice | +|------|---------------------------| +| **Transport / application split** | `transport/` handles Git Smart HTTP/SSH and pack I/O only. `application/` owns business reactions (CL edits, merge queue, builds, webhooks). The two layers talk through `bus` events (`MonoReceivePackFinalized`, `ImportReceivePackFinalized`), not direct imports. | +| **Thin `mono` routers** | HTTP handlers in `mono` validate input, call `MonoAppServices`, and map errors to HTTP. Routers must not touch `Storage`, `callisto`, or `jupiter::service` directly. | +| **Domain-scoped facades** | `MonoAppServices` exposes per-domain services (`cl()`, `issue()`, `user()`, `artifact()`, …). `MonoApiService` remains the Git/protocol-oriented core and narrows further over time (`git_ops`, `stack`, merge paths). | +| **DTO hub** | `ceres/model` is the only HTTP/OpenAPI schema surface for mono REST. Map `jupiter::model` / `callisto` entities here; never leak storage types into `mono`. | +| **Typed errors** | Application code returns `MegaError`. Git transport returns `ProtocolError`. HTTP status mapping lives in `common::errors`; `mono` adapts to `ApiError` / `protocol_error`. | +| **Framework isolation** | Ceres application code does not depend on `axum`. Web framework concerns stay in `mono`. (`axum-core` is confined to `infra/pack_decode.rs` for a `git-internal` stream error shim.) | +| **Enforced boundaries** | Dependency rules below are checked in CI (`.github/workflows/base.yml`): no `jupiter::model` in `mono`, no `MonoApiService` in `transport`, no legacy `ceres::api_service` paths in `mono`, no `.storage` / `_stg()` in routers. | +| **Incremental modularization** | Split the historical god-object (`mono_api_service.rs`) into `application/api_service/mono/*` modules without breaking callers. Prefer new logic in domain modules + `*ApplicationService` types over growing `MonoApiService` blindly. | +| **Substitutable ports** | Cross-cutting integration uses explicit ports (`GitOpsPort`, `BuildDispatchPort`, `changes_port`) so transport and triggers can be tested or swapped without reaching into storage details. | + +**Non-goals:** Ceres does not run servers, own database migrations (`jupiter-migrate`), or define the mono ↔ Orion wire protocol (`api-model`). It does not embed frontend or deployment config. + ## Module layout ``` ceres/src/ ├── lib.rs ├── bus/ # Transport ↔ application event bus -├── infra/ # Shared infrastructure (GitObjectCache, pack streams, decode errors) +├── infra/ # TransportContext, GitObjectCache, pack streams, decode errors ├── transport/ │ ├── protocol/ # Smart HTTP/SSH Git protocol │ └── pack/ # receive-pack / upload-pack handlers ├── application/ -│ ├── api_service/ # MonoApiService, REST-facing ops +│ ├── api_service/ # MonoApiService, MonoAppServices, domain *ApplicationService │ ├── code_edit/ # CL create/update pipelines + post-receive handlers -│ └── build_trigger/ # Orion build dispatch -├── model/ # HTTP/API DTOs +│ ├── build_trigger/ # Orion build dispatch +│ ├── artifact/ # Artifact upload orchestration +│ ├── notification/ # Email dispatch + notification triggers +│ └── webhook/ # Webhook admin + delivery +├── model/ # HTTP/API DTOs (utoipa schemas) ├── diff/, merge_checker/, lfs/ ``` @@ -45,7 +66,7 @@ Three DTO layers; keep imports aligned with this table: Rules: -- `mono` routers must **not** `use jupiter::model` — map via `ceres::model` and `MonoApiService` facades. +- `mono` routers must **not** `use jupiter::model` — map via `ceres::model` and `MonoAppServices` facades. - `mono/src` must **not** `use callisto::` or `jupiter::service::` — storage entities and service calls stay in `ceres` application layer. - `ceres/src/transport` must **not** reference `MonoApiService` (transport ↔ application boundary). - `api-model` is **not** mono HTTP schema (except shared wrappers like `CommonPage` / `Pagination`). @@ -63,9 +84,10 @@ Rules: Rules: -- REST routers and `MonoApiService` use `MegaError` only; `api_handler` resolves import vs mono handlers and returns `MegaError`. +- REST routers and application services use `MegaError` only; `api_handler` resolves import vs mono handlers and returns `MegaError`. - `ProtocolError` is confined to Git client protocol paths; use `mega_to_protocol_error` at transport boundaries when mapping domain failures. - Do not use `ProtocolError` in REST handlers. +- Prefer typed `MegaError` variants (`BadRequest`, `NotFound`, `Conflict`, …) over `[code:xxx]` string encoding in new code. Long term: extract `ceres/model` → `mega-api-types` only if a non-mono consumer needs HTTP DTOs without ceres domain code. @@ -91,9 +113,14 @@ Import-repo pushes follow the same pattern via `ImportReceivePackFinalized` → ## Assembly (`mono`) -`mono` constructs a `TransportRuntime` (alias: `ProtocolApiState`) with storage, `GitObjectCache`, and `RuntimeApplicationHandler`, then passes it to HTTP/SSH Git routers and REST handlers. +`mono` constructs `MonoAppServices` (which owns `TransportRuntime`, domain `*ApplicationService`s, and `MonoApiService`), then passes it to REST and Git protocol handlers via `MonoApiServiceState`. ```rust -let runtime = TransportRuntime::new(storage, git_object_cache); -// runtime.application handles MonoReceivePackFinalized / ImportReceivePackFinalized +let services = MonoAppServices::new(storage, git_object_cache, build_dispatch); +// services.transport_runtime() — Git pack handlers + application event bus +// services.cl() / services.issue() / … — REST domain facades +// services.git() — Git/protocol-oriented MonoApiService (GitApplicationService alias) +// services.changes_port() — ChangesPort for build trigger (Arc) ``` + +`build_mono_stack()` in `stack.rs` is the sole construction path for the shared `git` + `cl` pair. diff --git a/ceres/src/application/api_service/import_api_service.rs b/ceres/src/application/api_service/import_api_service.rs index 22ee7dbb5..3a3adee37 100644 --- a/ceres/src/application/api_service/import_api_service.rs +++ b/ceres/src/application/api_service/import_api_service.rs @@ -43,9 +43,19 @@ use crate::{ #[derive(Clone)] pub struct ImportApiService { - pub storage: Storage, - pub repo: Repo, - pub git_object_cache: Arc, + storage: Storage, + repo: Repo, + git_object_cache: Arc, +} + +impl ImportApiService { + pub fn new(storage: Storage, repo: Repo, git_object_cache: Arc) -> Self { + Self { + storage, + repo, + git_object_cache, + } + } } #[async_trait] diff --git a/ceres/src/application/api_service/mono/admin/bot.rs b/ceres/src/application/api_service/mono/admin/bot.rs index 284d57fd3..03a2a46a5 100644 --- a/ceres/src/application/api_service/mono/admin/bot.rs +++ b/ceres/src/application/api_service/mono/admin/bot.rs @@ -6,24 +6,29 @@ use common::errors::MegaError; use jupiter::sea_orm::prelude::DateTimeWithTimeZone; use crate::{ - application::api_service::mono::MonoApiService, + application::api_service::mono::context::AdminApplicationService, model::bots::{ BotRes, ChangeInstallationStatus, CreateBotTokenResponse, InstallBotReq, InstallationTargetType, ListBotTokenItem, }, }; -impl MonoApiService { +impl AdminApplicationService { pub async fn get_bot_by_id( &self, bot_id: i64, ) -> Result, MegaError> { - self.storage.bots_storage().get_bot_by_id(bot_id).await + self.ctx + .storage() + .bots_storage() + .get_bot_by_id(bot_id) + .await } pub async fn install_bot(&self, bot_id: i64, req: InstallBotReq) -> Result { let bot = self - .storage + .ctx + .storage() .bots_storage() .install_bot( bot_id, @@ -37,7 +42,8 @@ impl MonoApiService { pub async fn list_installed_bots(&self, bot_id: i64) -> Result, MegaError> { Ok(self - .storage + .ctx + .storage() .bots_storage() .get_installed_bot_by_id(bot_id) .await? @@ -53,7 +59,8 @@ impl MonoApiService { payload: ChangeInstallationStatus, ) -> Result { let model = self - .storage + .ctx + .storage() .bots_storage() .change_installed_bot_status( bot_id, @@ -71,7 +78,8 @@ impl MonoApiService { target_type: InstallationTargetType, installation_id: i64, ) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .bots_storage() .uninstall_bot(bot_id, target_type.into(), installation_id) .await @@ -84,7 +92,8 @@ impl MonoApiService { expires_at: Option, ) -> Result { let (model, token_plain) = self - .storage + .ctx + .storage() .bots_storage() .generate_bot_token(bot_id, token_name, expires_at) .await?; @@ -98,7 +107,8 @@ impl MonoApiService { pub async fn list_bot_tokens(&self, bot_id: i64) -> Result, MegaError> { Ok(self - .storage + .ctx + .storage() .bots_storage() .list_bot_tokens(bot_id) .await? @@ -114,14 +124,16 @@ impl MonoApiService { } pub async fn revoke_bot_token(&self, bot_id: i64, token_id: i64) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .bots_storage() .revoke_bot_token(bot_id, token_id) .await } pub async fn revoke_all_bot_tokens(&self, bot_id: i64) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .bots_storage() .revoke_bot_tokens_by_bot(bot_id) .await @@ -135,7 +147,7 @@ impl MonoApiService { _resource_id: &str, required_permission: PermissionEnum, ) -> Result { - let bots_storage = self.storage.bots_storage(); + let bots_storage = self.ctx.storage().bots_storage(); let bot = match bots_storage.get_bot_by_id(bot_id).await? { Some(b) => b, diff --git a/ceres/src/application/api_service/mono/admin/group.rs b/ceres/src/application/api_service/mono/admin/group.rs index 0d66bfd91..905f303c5 100644 --- a/ceres/src/application/api_service/mono/admin/group.rs +++ b/ceres/src/application/api_service/mono/admin/group.rs @@ -7,7 +7,7 @@ use common::errors::MegaError; use jupiter::model::group_dto::{DeleteGroupStats, ResourcePermissionBinding}; use crate::{ - application::api_service::mono::MonoApiService, + application::api_service::mono::context::AdminApplicationService, model::group::{ CreateGroupRequest, PermissionBindingRequest, PermissionValue, ResourceTypeValue, UpdateGroupRequest, @@ -75,7 +75,7 @@ pub struct EffectiveResourcePermission { pub permission: Option, } -impl MonoApiService { +impl AdminApplicationService { pub async fn create_group( &self, payload: CreateGroupRequest, @@ -83,7 +83,8 @@ impl MonoApiService { let name = validate_group_name(&payload.name)?; let description = normalize_optional_description(payload.description); - self.storage + self.ctx + .storage() .group_storage() .create_group(create_group_payload(CreateGroupRequest { name, @@ -97,14 +98,18 @@ impl MonoApiService { page: Pagination, ) -> Result<(Vec, u64), MegaError> { validate_pagination(&page)?; - self.storage.group_storage().list_groups(page).await + self.ctx.storage().group_storage().list_groups(page).await } pub async fn get_group_by_id( &self, group_id: i64, ) -> Result, MegaError> { - self.storage.group_storage().get_group_by_id(group_id).await + self.ctx + .storage() + .group_storage() + .get_group_by_id(group_id) + .await } pub async fn update_group( @@ -114,7 +119,8 @@ impl MonoApiService { ) -> Result { let name = validate_group_name(&payload.name)?; let description = normalize_optional_description(payload.description); - self.storage + self.ctx + .storage() .group_storage() .update_group( group_id, @@ -125,7 +131,8 @@ impl MonoApiService { pub async fn delete_group(&self, group_id: i64) -> Result { let stats = self - .storage + .ctx + .storage() .group_storage() .delete_group_with_relations(group_id) .await?; @@ -148,7 +155,7 @@ impl MonoApiService { if usernames.is_empty() { return Err(MegaError::bad_request("usernames must not be empty")); } - let group_storage = self.storage.group_storage(); + let group_storage = self.ctx.storage().group_storage(); group_storage.add_group_members(group_id, &usernames).await } @@ -157,7 +164,7 @@ impl MonoApiService { group_id: i64, username: &str, ) -> Result { - let group_storage = self.storage.group_storage(); + let group_storage = self.ctx.storage().group_storage(); if group_storage.get_group_by_id(group_id).await?.is_none() { return Err(MegaError::NotFound(format!( "Group not found: {}", @@ -173,7 +180,7 @@ impl MonoApiService { page: Pagination, ) -> Result<(Vec, u64), MegaError> { validate_pagination(&page)?; - let group_storage = self.storage.group_storage(); + let group_storage = self.ctx.storage().group_storage(); if group_storage.get_group_by_id(group_id).await?.is_none() { return Err(MegaError::NotFound(format!( "Group not found: {}", @@ -190,7 +197,8 @@ impl MonoApiService { permissions: Vec, ) -> Result, MegaError> { let bindings = permission_bindings(permissions); - self.storage + self.ctx + .storage() .group_storage() .replace_resource_permissions(resource_type, resource_id, &bindings) .await @@ -201,7 +209,8 @@ impl MonoApiService { resource_type: ResourceTypeEnum, resource_id: &str, ) -> Result, MegaError> { - self.storage + self.ctx + .storage() .group_storage() .list_resource_permissions(resource_type, resource_id) .await @@ -214,7 +223,8 @@ impl MonoApiService { permissions: Vec, ) -> Result, MegaError> { let bindings = permission_bindings(permissions); - self.storage + self.ctx + .storage() .group_storage() .upsert_resource_permissions(resource_type, resource_id, &bindings) .await @@ -225,7 +235,8 @@ impl MonoApiService { resource_type: ResourceTypeEnum, resource_id: &str, ) -> Result { - self.storage + self.ctx + .storage() .group_storage() .delete_resource_permissions(resource_type, resource_id) .await @@ -235,7 +246,8 @@ impl MonoApiService { &self, username: &str, ) -> Result, MegaError> { - self.storage + self.ctx + .storage() .group_storage() .find_groups_by_username(username) .await @@ -254,7 +266,7 @@ impl MonoApiService { }); } - let group_storage = self.storage.group_storage(); + let group_storage = self.ctx.storage().group_storage(); let group_ids = group_storage .find_group_ids_by_username(username) .await @@ -297,7 +309,8 @@ impl MonoApiService { match resource_type { ResourceTypeValue::Note => { let note = self - .storage + .ctx + .storage() .note_storage() .get_note_by_public_id(normalized_resource_id) .await?; diff --git a/ceres/src/application/api_service/mono/admin/permissions.rs b/ceres/src/application/api_service/mono/admin/permissions.rs index aa84c7d3f..c4e207586 100644 --- a/ceres/src/application/api_service/mono/admin/permissions.rs +++ b/ceres/src/application/api_service/mono/admin/permissions.rs @@ -13,7 +13,7 @@ use common::errors::MegaError; use git_internal::internal::object::tree::Tree; use jupiter::{redis::AsyncCommands, utils::converter::FromMegaModel}; -use crate::application::api_service::mono::MonoApiService; +use crate::application::api_service::mono::context::AdminApplicationService; /// Cache TTL for admin list (10 minutes). pub const ADMIN_CACHE_TTL: u64 = 600; @@ -24,7 +24,7 @@ pub const ADMIN_FILE: &str = ".mega_cedar.json"; /// Redis cache key suffix for admin list. const ADMIN_CACHE_KEY_SUFFIX: &str = "admin:list"; -impl MonoApiService { +impl AdminApplicationService { /// Check if a user is an admin. pub async fn check_is_admin(&self, username: &str) -> Result { let admins = self.get_effective_admins().await?; @@ -59,10 +59,11 @@ impl MonoApiService { /// Invalidate the admin list cache. /// This should be called when the `.mega_cedar.json` file is modified. pub async fn invalidate_admin_cache(&self) { - let mut conn = self.git_object_cache.connection.clone(); + let mut conn = self.ctx.git_object_cache().connection.clone(); let key = format!( "{}:{}", - self.git_object_cache.prefix, ADMIN_CACHE_KEY_SUFFIX + self.ctx.git_object_cache().prefix, + ADMIN_CACHE_KEY_SUFFIX ); if let Err(e) = conn.del::<_, ()>(&key).await { tracing::warn!("Failed to invalidate admin cache: {}", e); @@ -71,7 +72,7 @@ impl MonoApiService { /// Load EntityStore from `/.mega_cedar.json`. async fn load_admin_entity_store(&self) -> Result { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.ctx.storage().mono_storage(); let root_ref = mono_storage .get_main_ref("/") @@ -95,7 +96,8 @@ impl MonoApiService { let blob_hash = blob_item.id.to_string(); let content_bytes = match self - .storage + .ctx + .storage() .git_service .get_object_as_bytes(&blob_hash) .await @@ -104,7 +106,8 @@ impl MonoApiService { Err(e) => { // Best-effort classification/logging for ObjStorageNotFound cases. let e = self - .storage + .ctx + .storage() .classify_blob_objstorage_not_found(&blob_hash, e) .await; return Err(e); @@ -119,10 +122,11 @@ impl MonoApiService { } async fn get_admins_from_cache(&self) -> Result, MegaError> { - let mut conn = self.git_object_cache.connection.clone(); + let mut conn = self.ctx.git_object_cache().connection.clone(); let key = format!( "{}:{}", - self.git_object_cache.prefix, ADMIN_CACHE_KEY_SUFFIX + self.ctx.git_object_cache().prefix, + ADMIN_CACHE_KEY_SUFFIX ); let data: Option = conn.get(&key).await?; @@ -134,13 +138,14 @@ impl MonoApiService { } async fn cache_admins(&self, admins: &[String]) -> Result<(), MegaError> { - let mut conn = self.git_object_cache.connection.clone(); + let mut conn = self.ctx.git_object_cache().connection.clone(); let json = serde_json::to_string(admins) .map_err(|e| MegaError::Other(format!("Serialize failed: {}", e)))?; let key = format!( "{}:{}", - self.git_object_cache.prefix, ADMIN_CACHE_KEY_SUFFIX + self.ctx.git_object_cache().prefix, + ADMIN_CACHE_KEY_SUFFIX ); conn.set_ex::<_, _, ()>(&key, json, ADMIN_CACHE_TTL).await?; Ok(()) diff --git a/ceres/src/application/api_service/mono/app_services.rs b/ceres/src/application/api_service/mono/app_services.rs index 60b5e88fe..5d05db373 100644 --- a/ceres/src/application/api_service/mono/app_services.rs +++ b/ceres/src/application/api_service/mono/app_services.rs @@ -1,61 +1,172 @@ -//! Domain-scoped accessors for [`MonoApiService`] (gradual split entry point). +//! Domain-scoped accessors for monorepo application services. use std::sync::Arc; use jupiter::storage::Storage; -use super::service::MonoApiService; -use crate::infra::TransportContext; +use super::{ + context::{ + AdminApplicationService, ClApplicationService, CodeReviewApplicationService, + ConversationApplicationService, IssueApplicationService, LfsApplicationService, + NoteApplicationService, ReviewerApplicationService, ServiceContext, + SidebarApplicationService, UserApplicationService, WebhookApplicationService, + }, + service::MonoApiService, + stack::build_mono_stack, +}; +use crate::{ + TransportRuntime, + application::{ + api_service::cache::GitObjectCache, + artifact::ArtifactApplicationService, + build_trigger::{BuildTriggerService, ChangesPort, SharedBuildDispatch}, + }, +}; /// Bundles monorepo application services for injection into HTTP handlers. #[derive(Clone)] pub struct MonoAppServices { - inner: MonoApiService, + ctx: ServiceContext, + cl: ClApplicationService, + issue: IssueApplicationService, + conversation: ConversationApplicationService, + admin: AdminApplicationService, + user: UserApplicationService, + lfs: LfsApplicationService, + artifact: ArtifactApplicationService, + build_trigger: Option>, + transport: TransportRuntime, + webhook: WebhookApplicationService, + sidebar: SidebarApplicationService, + code_review: CodeReviewApplicationService, + reviewer: ReviewerApplicationService, + note: NoteApplicationService, + git: MonoApiService, + changes_port: Arc, } impl MonoAppServices { pub fn new( storage: Storage, - git_object_cache: Arc, + git_object_cache: Arc, + build_dispatch: Option, ) -> Self { + let ctx = ServiceContext::new(storage, git_object_cache, build_dispatch); + let (git, cl, admin) = build_mono_stack( + ctx.storage().clone(), + ctx.git_object_cache().clone(), + ctx.build_dispatch(), + ); + let changes_port: Arc = Arc::new(cl.clone()); + let transport = TransportRuntime::new( + ctx.storage().clone(), + ctx.git_object_cache().clone(), + git.clone(), + cl.clone(), + ); + let build_trigger = ctx.build_dispatch().map(|build_dispatch| { + Arc::new(BuildTriggerService::new( + ctx.storage().clone(), + build_dispatch, + changes_port.clone(), + )) + }); Self { - inner: MonoApiService::new(TransportContext::new(storage, git_object_cache)), + cl: cl.clone(), + issue: IssueApplicationService::new(ctx.clone()), + conversation: ConversationApplicationService::new(ctx.clone()), + admin, + user: UserApplicationService::new(ctx.clone()), + lfs: LfsApplicationService::new(ctx.clone()), + artifact: ArtifactApplicationService::from_storage(ctx.storage()), + build_trigger, + transport, + webhook: WebhookApplicationService::new(ctx.clone()), + sidebar: SidebarApplicationService::new(ctx.clone()), + code_review: CodeReviewApplicationService::new(ctx.clone()), + reviewer: ReviewerApplicationService::new(ctx.clone()), + note: NoteApplicationService::new(ctx.clone()), + git, + changes_port, + ctx, } } - pub fn monorepo(&self) -> &MonoApiService { - &self.inner + pub fn storage(&self) -> &Storage { + self.ctx.storage() } - pub fn cl(&self) -> &MonoApiService { - &self.inner + pub fn git_object_cache(&self) -> Arc { + self.ctx.git_object_cache().clone() } - pub fn issue(&self) -> &MonoApiService { - &self.inner + pub fn build_dispatch(&self) -> Option { + self.ctx.build_dispatch() } - pub fn conversation(&self) -> &MonoApiService { - &self.inner + pub fn transport_runtime(&self) -> &TransportRuntime { + &self.transport } - pub fn admin(&self) -> &MonoApiService { - &self.inner + pub fn changes_port(&self) -> Arc { + self.changes_port.clone() } - pub fn user(&self) -> &MonoApiService { - &self.inner + pub fn git(&self) -> &MonoApiService { + &self.git } -} -impl From for MonoApiService { - fn from(services: MonoAppServices) -> Self { - services.inner + pub fn webhook(&self) -> &WebhookApplicationService { + &self.webhook + } + + pub fn sidebar(&self) -> &SidebarApplicationService { + &self.sidebar + } + + pub fn code_review(&self) -> &CodeReviewApplicationService { + &self.code_review + } + + pub fn reviewer(&self) -> &ReviewerApplicationService { + &self.reviewer + } + + pub fn note(&self) -> &NoteApplicationService { + &self.note + } + + pub fn cl(&self) -> &ClApplicationService { + &self.cl + } + + pub fn issue(&self) -> &IssueApplicationService { + &self.issue + } + + pub fn conversation(&self) -> &ConversationApplicationService { + &self.conversation + } + + pub fn admin(&self) -> &AdminApplicationService { + &self.admin + } + + pub fn user(&self) -> &UserApplicationService { + &self.user + } + + pub fn lfs(&self) -> &LfsApplicationService { + &self.lfs + } + + pub fn artifact(&self) -> &ArtifactApplicationService { + &self.artifact } -} -impl From<&MonoAppServices> for MonoApiService { - fn from(services: &MonoAppServices) -> Self { - services.inner.clone() + pub fn build_trigger(&self) -> &BuildTriggerService { + self.build_trigger + .as_deref() + .expect("build trigger requires build_dispatch") } } diff --git a/ceres/src/application/api_service/mono/buck/upload.rs b/ceres/src/application/api_service/mono/buck/upload.rs index d55811f51..80580e33d 100644 --- a/ceres/src/application/api_service/mono/buck/upload.rs +++ b/ceres/src/application/api_service/mono/buck/upload.rs @@ -1,6 +1,6 @@ //! Buck upload operations for [`MonoApiService`](super::service::MonoApiService). -use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use std::{collections::HashMap, path::PathBuf}; use callisto; use common::errors::{BuckError, MegaError}; @@ -12,7 +12,6 @@ use jupiter::{ storage::buck_storage::{session_status, upload_status}, utils::converter::IntoMegaModel, }; -use orion_client::OrionBuildClient; use crate::{ application::{ @@ -31,13 +30,14 @@ use crate::{ impl MonoApiService { /// Triggers a build for Buck upload completion fn trigger_build_for_buck_upload(&self, response: &CompleteResponse, username: &str) { - let config = self.storage.config(); - let orion_client = Arc::new(OrionBuildClient::new(config.build.clone())); - if !orion_client.enable_build() { + let Some(build_dispatch) = self.build_dispatch() else { + return; + }; + if !build_dispatch.enable_build() { return; } - let storage = self.storage.clone(); - let git_cache = self.git_object_cache.clone(); + let storage = self.storage().clone(); + let git_cache = self.git_object_cache().clone(); let mut context = TriggerContext::from_buck_upload( response.repo_path.clone(), response.from_hash.clone(), @@ -50,7 +50,7 @@ impl MonoApiService { context.ref_type = Some("branch".to_string()); tokio::spawn(async move { if let Err(e) = - BuildTriggerService::build_by_context(storage, git_cache, orion_client, context) + BuildTriggerService::build_by_context(storage, git_cache, build_dispatch, context) .await { tracing::error!("Failed to create build trigger for buck upload: {}", e); @@ -68,7 +68,7 @@ impl MonoApiService { } let normalized_path = MonoServiceLogic::normalize_repo_path(path)?; let refs = self - .storage + .storage() .mono_storage() .get_main_ref(&normalized_path) .await? @@ -81,7 +81,7 @@ impl MonoApiService { // Use canonical path from mega_refs as the single source of truth for repository path let canonical_path = refs.path.clone(); let response = self - .storage + .storage() .buck_service .create_session( username, @@ -110,7 +110,7 @@ impl MonoApiService { payload: ManifestPayload, ) -> Result { let session = self - .storage + .storage() .buck_storage() .get_session(cl_link) .await? @@ -159,7 +159,7 @@ impl MonoApiService { }; let svc_resp = self - .storage + .storage() .buck_service .process_manifest( username, @@ -190,7 +190,7 @@ impl MonoApiService { } pub fn buck_max_file_size(&self) -> u64 { - self.storage.buck_service.max_file_size() + self.storage().buck_service.max_file_size() } pub fn buck_try_acquire_upload_permits( @@ -203,7 +203,7 @@ impl MonoApiService { ), MegaError, > { - self.storage + self.storage() .buck_service .try_acquire_upload_permits(file_size) } @@ -217,7 +217,7 @@ impl MonoApiService { file_hash: Option<&str>, file_content: bytes::Bytes, ) -> Result { - self.storage + self.storage() .buck_service .upload_file( username, @@ -250,7 +250,7 @@ impl MonoApiService { _payload: CompletePayload, ) -> Result { let session = self - .storage + .storage() .buck_storage() .get_session(cl_link) .await? @@ -276,7 +276,7 @@ impl MonoApiService { } let pending = self - .storage + .storage() .buck_storage() .count_pending_files(cl_link) .await?; @@ -286,7 +286,7 @@ impl MonoApiService { })); } - let all_files = self.storage.buck_storage().get_all_files(cl_link).await?; + let all_files = self.storage().buck_storage().get_all_files(cl_link).await?; for file in &all_files { if file.blob_id.is_none() { return Err(MegaError::Buck(BuckError::ValidationError(format!( @@ -323,7 +323,7 @@ impl MonoApiService { let commit_result = if file_changes.is_empty() { None } else { - let builder = BuckCommitBuilder::new(self.storage.mono_storage()); + let builder = BuckCommitBuilder::new(self.storage().mono_storage()); let result = builder .build_commit( session.from_hash.as_deref().unwrap_or_default(), @@ -352,7 +352,7 @@ impl MonoApiService { }); let svc_resp: SvcCompleteResponse = self - .storage + .storage() .buck_service .complete_upload(username, cl_link, SvcCompletePayload {}, artifacts) .await?; diff --git a/ceres/src/application/api_service/mono/cl/branch.rs b/ceres/src/application/api_service/mono/cl/branch.rs index 70fa1fffa..2799443f5 100644 --- a/ceres/src/application/api_service/mono/cl/branch.rs +++ b/ceres/src/application/api_service/mono/cl/branch.rs @@ -1,4 +1,4 @@ -//! Branch update / rebase operations for [`MonoApiService`](super::service::MonoApiService). +//! Branch update / rebase operations for [`ClApplicationService`](super::service::ClApplicationService). use std::{ collections::HashMap, @@ -22,7 +22,7 @@ use tracing::debug; use crate::{ application::{ api_service::mono::{ - MonoApiService, + ClApplicationService, types::{ApplyChangeContext, RefUpdate, TreeUpdateResult}, }, code_edit::utils as edit_utils, @@ -30,14 +30,14 @@ use crate::{ model::change_list::{ClDiffFile, UpdateBranchStatusRes}, }; -impl MonoApiService { +impl ClApplicationService { pub(crate) async fn apply_changes_as_single_commit( &self, cl: &mega_cl::Model, changes: &[ClDiffFile], target_head: &str, ) -> Result { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); // Load base commit and its root tree let base_commit = mono_storage @@ -457,16 +457,17 @@ impl MonoApiService { &self, cl_link: &str, ) -> Result { - let stg = self.storage.cl_storage(); + let stg = self.storage().cl_service.cl_store(); let cl = stg .get_cl(cl_link) .await? .ok_or_else(|| MegaError::Other("CL Not Found".to_string()))?; - let main_ref = match self.storage.mono_storage().get_main_ref(&cl.path).await? { + let main_ref = match self.storage().mono_storage().get_main_ref(&cl.path).await? { Some(r) => r, None if crate::application::api_service::mono::cl_merge::path_lacks_main_ref( - self, &cl.path, + self.git_ops(), + &cl.path, ) .await? => { @@ -490,8 +491,8 @@ impl MonoApiService { /// Update Branch (rebase-like) for Open CL: applies CL file changes onto latest target head /// and updates CL's base/head commits. Returns new head commit id on success. pub async fn update_branch(&self, username: &str, cl_link: &str) -> Result { - let stg = self.storage.cl_storage(); - let conv_stg = self.storage.conversation_storage(); + let stg = self.storage().cl_service.cl_store(); + let conv_stg = self.storage().cl_service.conversation_store(); let cl = stg .get_cl(cl_link) @@ -506,7 +507,7 @@ impl MonoApiService { } let main_ref = self - .storage + .storage() .mono_storage() .get_main_ref(&cl.path) .await diff --git a/ceres/src/application/api_service/mono/cl/diff.rs b/ceres/src/application/api_service/mono/cl/diff.rs index fbd000f65..d4954c5ef 100644 --- a/ceres/src/application/api_service/mono/cl/diff.rs +++ b/ceres/src/application/api_service/mono/cl/diff.rs @@ -1,4 +1,4 @@ -//! Diff and patch operations for [`MonoApiService`](super::service::MonoApiService). +//! Diff and patch operations for [`ClApplicationService`](super::service::ClApplicationService). use std::{ collections::{HashMap, HashSet}, @@ -14,7 +14,7 @@ use git_internal::{ use jupiter::utils::converter::FromMegaModel; use crate::{ - application::api_service::{ApiHandler, mono::MonoApiService}, + application::api_service::{ApiHandler, mono::ClApplicationService}, diff::tree_diff, model::change_list::{ClDiffFile, ClFilesChangedItemSchema}, }; @@ -33,7 +33,7 @@ struct PagedClDiffItem { old_path: Option, } -impl MonoApiService { +impl ClApplicationService { /// Fetches the content difference for a merge request, paginated by page_id and page_size. /// # Arguments /// * `cl_link` - The link to the merge request. @@ -50,7 +50,7 @@ impl MonoApiService { let per_page = page.per_page as usize; let page_id = page.page as usize; - let stg = self.storage.cl_storage(); + let stg = self.storage().cl_service.cl_store(); let cl = stg.get_cl(cl_link).await.unwrap().ok_or_else(|| { GitError::CustomError(format!("Merge request not found: {cl_link}")) @@ -196,7 +196,7 @@ impl MonoApiService { // Fetch all blobs with better error handling and logging let mut failed_hashes = Vec::new(); for hash in all_hashes { - match self.get_raw_blob_by_hash(&hash.to_string()).await { + match self.git().get_raw_blob_by_hash(&hash.to_string()).await { Ok(data) => { blob_cache.insert(hash, data); } @@ -409,8 +409,9 @@ impl MonoApiService { ) -> Result, MegaError> { let normalized_prefix = path.map(|prefix| prefix.replace('\\', "/")); let cl = self - .storage - .cl_storage() + .storage() + .cl_service + .cl_store() .get_cl(cl_link) .await .unwrap() @@ -464,7 +465,7 @@ impl MonoApiService { } for hash in candidate_hashes { - match self.get_raw_blob_by_hash(&hash.to_string()).await { + match self.git().get_raw_blob_by_hash(&hash.to_string()).await { Ok(data) => { blob_cache.insert(hash, data); } @@ -486,7 +487,7 @@ impl MonoApiService { ); } - let rename_config = self.storage.config().monorepo.rename.clone(); + let rename_config = self.storage().config().monorepo.rename.clone(); tree_diff::calculate_tree_diff_with_blobs(old_files, new_files, &rename_config, &blob_cache) } @@ -495,13 +496,13 @@ impl MonoApiService { commit_hash: &str, ) -> Result, MegaError> { let mut res = vec![]; - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); let commit = mono_storage.get_commit_by_hash(commit_hash).await?; if let Some(commit) = commit { let tree = mono_storage.get_tree_by_hash(&commit.tree).await?; if let Some(tree) = tree { let tree: Tree = Tree::from_mega_model(tree); - res = self.traverse_tree(tree).await?; + res = self.git_ops().traverse_tree(tree).await?; } } Ok(res) @@ -546,7 +547,9 @@ mod tests { use git_internal::{DiffItem, hash::ObjectHash}; use super::collect_page_blobs; - use crate::{application::api_service::mono::MonoApiService, model::change_list::ClDiffFile}; + use crate::{ + application::api_service::mono::ClApplicationService, model::change_list::ClDiffFile, + }; #[test] fn test_paging_calculation_basic() { @@ -914,7 +917,7 @@ index 1111111..2222222 100644\n\ -old line\n\ +new line\n"; - let relocated = MonoApiService::relocate_patch_body( + let relocated = ClApplicationService::relocate_patch_body( raw_patch, Path::new("old/name.txt"), Path::new("new/name.txt"), @@ -940,7 +943,7 @@ index 1111111..2222222 100644\n\ -let path = \"C:\\\\temp\\\\old\";\n\ +let path = \"C:\\\\temp\\\\new\";\n"; - let relocated = MonoApiService::relocate_patch_body( + let relocated = ClApplicationService::relocate_patch_body( raw_patch, Path::new("old/name.txt"), Path::new("new/name.txt"), @@ -959,7 +962,7 @@ index 1111111..2222222 100644\n\ data: "diff --git a/dir\\nested\\file.txt b/dir\\nested\\file.txt\n".to_string(), }; - let normalized = MonoApiService::normalize_diff_item(item); + let normalized = ClApplicationService::normalize_diff_item(item); assert_eq!(normalized.path, "dir/nested/file.txt"); assert!( normalized @@ -978,7 +981,7 @@ diff --git a/dir\\nested\\file.txt b/dir\\nested\\file.txt\n\ -let path = \"C:\\\\temp\\\\old\";\n\ +let path = \"C:\\\\temp\\\\new\";\n"; - let normalized = MonoApiService::normalize_patch_header_paths(raw_patch); + let normalized = ClApplicationService::normalize_patch_header_paths(raw_patch); assert!(normalized.contains("diff --git a/dir/nested/file.txt b/dir/nested/file.txt")); assert!(normalized.contains("--- a/dir/nested/file.txt")); diff --git a/ceres/src/application/api_service/mono/cl/lifecycle.rs b/ceres/src/application/api_service/mono/cl/lifecycle.rs index 0a1ec6cdc..e1a7c0c4f 100644 --- a/ceres/src/application/api_service/mono/cl/lifecycle.rs +++ b/ceres/src/application/api_service/mono/cl/lifecycle.rs @@ -8,20 +8,20 @@ use jupiter::model::cl_dto::CLDetails; use crate::{ application::{ - api_service::mono::MonoApiService, + api_service::mono::ClApplicationService, webhook::{WebhookEvent, dispatch_cl_webhook}, }, model::change_list::{CLDetailRes, Condition, MergeBoxRes, UpdateClStatusPayload}, }; -impl MonoApiService { +impl ClApplicationService { pub async fn get_cl_details( &self, link: &str, username: String, ) -> Result { - let cl_storage = self.storage.cl_storage(); - let conversation_storage = self.storage.conversation_storage(); + let cl_storage = self.storage().cl_service.cl_store(); + let conversation_storage = self.storage().cl_service.conversation_store(); let (cl, labels) = cl_storage .get_cl_labels(link) @@ -48,7 +48,7 @@ impl MonoApiService { } pub async fn reopen_cl(&self, link: &str, username: &str) -> Result<(), MegaError> { - let cl_storage = self.storage.cl_storage(); + let cl_storage = self.storage().cl_service.cl_store(); let model = cl_storage .get_cl(link) .await? @@ -60,8 +60,9 @@ impl MonoApiService { let link = model.link.clone(); cl_storage.reopen_cl(model.clone()).await?; - self.storage - .conversation_storage() + self.storage() + .cl_service + .conversation_store() .add_conversation( &link, username, @@ -71,13 +72,13 @@ impl MonoApiService { .await?; if let Some(updated_model) = cl_storage.get_cl(&link).await? { - dispatch_cl_webhook(&self.storage, WebhookEvent::ClReopened, &updated_model); + dispatch_cl_webhook(self.storage(), WebhookEvent::ClReopened, &updated_model); } Ok(()) } pub async fn close_cl(&self, link: &str, username: &str) -> Result<(), MegaError> { - let cl_storage = self.storage.cl_storage(); + let cl_storage = self.storage().cl_service.cl_store(); let model = cl_storage .get_cl(link) .await? @@ -89,8 +90,9 @@ impl MonoApiService { let link = model.link.clone(); cl_storage.close_cl(model.clone()).await?; - self.storage - .conversation_storage() + self.storage() + .cl_service + .conversation_store() .add_conversation( &link, username, @@ -100,13 +102,13 @@ impl MonoApiService { .await?; if let Some(updated_model) = cl_storage.get_cl(&link).await? { - dispatch_cl_webhook(&self.storage, WebhookEvent::ClClosed, &updated_model); + dispatch_cl_webhook(self.storage(), WebhookEvent::ClClosed, &updated_model); } Ok(()) } pub async fn merge_open_cl(&self, username: &str, link: &str) -> Result<(), MegaError> { - let cl_storage = self.storage.cl_storage(); + let cl_storage = self.storage().cl_service.cl_store(); let model = cl_storage .get_cl(link) .await? @@ -119,14 +121,14 @@ impl MonoApiService { if model.status == MergeStatusEnum::Open { self.merge_cl(username, model.clone()).await?; if let Some(updated_model) = cl_storage.get_cl(link).await? { - dispatch_cl_webhook(&self.storage, WebhookEvent::ClMerged, &updated_model); + dispatch_cl_webhook(self.storage(), WebhookEvent::ClMerged, &updated_model); } } Ok(()) } pub async fn merge_open_cl_no_auth(&self, link: &str) -> Result<(), MegaError> { - let cl_storage = self.storage.cl_storage(); + let cl_storage = self.storage().cl_service.cl_store(); let model = cl_storage .get_cl(link) .await? @@ -141,13 +143,13 @@ impl MonoApiService { self.merge_cl("system", model.clone()).await?; if let Some(updated_model) = cl_storage.get_cl(link).await? { - dispatch_cl_webhook(&self.storage, WebhookEvent::ClMerged, &updated_model); + dispatch_cl_webhook(self.storage(), WebhookEvent::ClMerged, &updated_model); } Ok(()) } pub async fn get_merge_box(&self, link: &str) -> Result { - let cl_storage = self.storage.cl_storage(); + let cl_storage = self.storage().cl_service.cl_store(); let cl = cl_storage .get_cl(link) .await? @@ -179,7 +181,7 @@ impl MonoApiService { content: &str, ) -> Result<(), MegaError> { let conv_type = if self - .storage + .storage() .reviewer_storage() .is_reviewer(link, username) .await? @@ -189,8 +191,9 @@ impl MonoApiService { ConvTypeEnum::Comment }; - self.storage - .conversation_storage() + self.storage() + .cl_service + .conversation_store() .add_conversation(link, username, Some(content.to_string()), conv_type) .await?; @@ -198,16 +201,20 @@ impl MonoApiService { tracing::warn!("failed to enqueue cl comment notifications: {e}"); } - if let Some(cl_model) = self.storage.cl_storage().get_cl(link).await? { - dispatch_cl_webhook(&self.storage, WebhookEvent::ClCommentCreated, &cl_model); + if let Some(cl_model) = self.storage().cl_service.cl_store().get_cl(link).await? { + dispatch_cl_webhook(self.storage(), WebhookEvent::ClCommentCreated, &cl_model); } Ok(()) } pub async fn edit_cl_title(&self, link: &str, content: &str) -> Result<(), MegaError> { - self.storage.cl_storage().edit_title(link, content).await?; - if let Some(cl_model) = self.storage.cl_storage().get_cl(link).await? { - dispatch_cl_webhook(&self.storage, WebhookEvent::ClUpdated, &cl_model); + self.storage() + .cl_service + .cl_store() + .edit_title(link, content) + .await?; + if let Some(cl_model) = self.storage().cl_service.cl_store().get_cl(link).await? { + dispatch_cl_webhook(self.storage(), WebhookEvent::ClUpdated, &cl_model); } Ok(()) } @@ -218,7 +225,7 @@ impl MonoApiService { username: &str, payload: &UpdateClStatusPayload, ) -> Result<(), MegaError> { - let cl_storage = self.storage.cl_storage(); + let cl_storage = self.storage().cl_service.cl_store(); let model = cl_storage .get_cl(link) .await? @@ -239,8 +246,9 @@ impl MonoApiService { cl_storage .update_cl_status(model.clone(), new_status.clone()) .await?; - self.storage - .conversation_storage() + self.storage() + .cl_service + .conversation_store() .add_conversation( link, username, @@ -249,15 +257,16 @@ impl MonoApiService { ) .await?; if let Some(updated_model) = cl_storage.get_cl(link).await? { - dispatch_cl_webhook(&self.storage, WebhookEvent::ClCreated, &updated_model); + dispatch_cl_webhook(self.storage(), WebhookEvent::ClCreated, &updated_model); } } (MergeStatusEnum::Open, MergeStatusEnum::Draft) => { cl_storage .update_cl_status(model.clone(), new_status.clone()) .await?; - self.storage - .conversation_storage() + self.storage() + .cl_service + .conversation_store() .add_conversation( link, username, @@ -266,7 +275,7 @@ impl MonoApiService { ) .await?; if let Some(updated_model) = cl_storage.get_cl(link).await? { - dispatch_cl_webhook(&self.storage, WebhookEvent::ClUpdated, &updated_model); + dispatch_cl_webhook(self.storage(), WebhookEvent::ClUpdated, &updated_model); } } _ => { @@ -284,8 +293,8 @@ impl MonoApiService { link: &str, ) -> Result { let new_head = self.update_branch(username, link).await?; - if let Some(cl_model) = self.storage.cl_storage().get_cl(link).await? { - dispatch_cl_webhook(&self.storage, WebhookEvent::ClUpdated, &cl_model); + if let Some(cl_model) = self.storage().cl_service.cl_store().get_cl(link).await? { + dispatch_cl_webhook(self.storage(), WebhookEvent::ClUpdated, &cl_model); } Ok(new_head) } @@ -294,22 +303,22 @@ impl MonoApiService { const EVENT_CL_COMMENT_CREATED: &str = "cl.comment.created"; async fn enqueue_cl_comment_notifications( - service: &MonoApiService, + service: &ClApplicationService, actor_username: &str, cl_link: &str, comment_text: &str, ) -> Result<(), MegaError> { - let notif_stg = service.storage.notification_storage(); + let notif_stg = service.storage().notification_storage(); ensure_cl_comment_event_type(¬if_stg).await?; - let cl_stg = service.storage.cl_storage(); + let cl_stg = service.storage().cl_service.cl_store(); let cl = cl_stg .get_cl(cl_link) .await? .ok_or_else(|| MegaError::NotFound(format!("CL {cl_link} not found")))?; let reviewers = service - .storage + .storage() .reviewer_storage() .list_reviewers(cl_link) .await?; diff --git a/ceres/src/application/api_service/mono/cl/merge.rs b/ceres/src/application/api_service/mono/cl/merge.rs index 8c9d54f44..a23bcd6e6 100644 --- a/ceres/src/application/api_service/mono/cl/merge.rs +++ b/ceres/src/application/api_service/mono/cl/merge.rs @@ -1,6 +1,6 @@ -//! CL merge operations for [`MonoApiService`](super::service::MonoApiService). +//! CL merge operations for [`ClApplicationService`](super::service::ClApplicationService). -use std::{path::PathBuf, sync::Arc}; +use std::path::PathBuf; use callisto::{mega_cl, mega_tree, sea_orm_active_enums::ConvTypeEnum}; use common::{errors::MegaError, utils::MEGA_BRANCH_NAME}; @@ -13,29 +13,26 @@ use jupiter::{ storage::{base_storage::StorageConnector, mono_storage::RefUpdateData}, utils::converter::IntoMegaModel, }; -use orion_client::OrionBuildClient; use tracing::debug; use crate::{ - application::{ - api_service::{ - ApiHandler, - mono::{MonoApiService, logic::MonoServiceLogic, types::TreeUpdateResult}, - }, - code_edit::on_edit::OneditCodeEdit, + application::api_service::{ + ApiHandler, + mono::{ClApplicationService, logic::MonoServiceLogic, types::TreeUpdateResult}, }, merge_checker::CheckerRegistry, }; -impl MonoApiService { +impl ClApplicationService { // This function is intended to be called before merging a CL to ensure it meets all required checks. pub(crate) async fn ensure_cl_mergeable(&self, cl: &mega_cl::Model) -> Result<(), MegaError> { - let check_reg = CheckerRegistry::new(self.storage.clone().into(), cl.username.clone()); + let check_reg = CheckerRegistry::new(self.storage().clone().into(), cl.username.clone()); check_reg.run_checks(cl.clone().into()).await?; let required_check_types = self - .storage - .cl_storage() + .storage() + .cl_service + .cl_store() .get_checks_config_by_path(&cl.path) .await? .into_iter() @@ -44,8 +41,9 @@ impl MonoApiService { .collect::>(); let failed_checks = self - .storage - .cl_storage() + .storage() + .cl_service + .cl_store() .get_check_result(&cl.link) .await? .into_iter() @@ -73,7 +71,7 @@ impl MonoApiService { cl: &mega_cl::Model, ) -> Result<(), GitError> { let main_ref = self - .storage + .storage() .mono_storage() .get_main_ref(&cl.path) .await @@ -93,37 +91,19 @@ impl MonoApiService { } } - pub(crate) async fn trigger_build_for_cl( - &self, - editor: &OneditCodeEdit, - cl: &mega_cl::Model, - username: &str, - ) -> Result<(), GitError> { - let config = self.storage.config(); - let orion_client = OrionBuildClient::new(config.build.clone()); - let git_cache = self.git_object_cache.clone(); - editor - .trigger_build_and_check( - self.storage.clone(), - git_cache, - Arc::new(orion_client), - cl, - username, - ) - .await?; - - Ok(()) - } pub async fn merge_cl(&self, username: &str, mut cl: mega_cl::Model) -> Result<(), GitError> { - crate::application::api_service::mono::cl_merge::prepare_cl_path_for_merge(self, &mut cl) - .await - .map_err(|e| GitError::CustomError(e.to_string()))?; + crate::application::api_service::mono::cl_merge::prepare_cl_path_for_merge( + self.git_ops(), + &mut cl, + ) + .await + .map_err(|e| GitError::CustomError(e.to_string()))?; self.ensure_cl_mergeable(&cl) .await .map_err(|e| GitError::CustomError(e.to_string()))?; - let storage = self.storage.mono_storage(); + let storage = self.storage().mono_storage(); let refs = storage .get_main_ref(&cl.path) .await @@ -147,7 +127,7 @@ impl MonoApiService { username: &str, cl: mega_cl::Model, ) -> Result<(), GitError> { - let storage = self.storage.mono_storage(); + let storage = self.storage().mono_storage(); let strategy = crate::application::api_service::mono::cl_merge::resolve_merge_strategy(self, &cl) @@ -168,21 +148,25 @@ impl MonoApiService { let parent = path.parent().ok_or_else(|| { GitError::CustomError(format!("Invalid CL path: {}", normalized_path)) })?; - if crate::application::api_service::mono::cl_merge::needs_path_tree_attach(self, &path) - .await - .map_err(|e| GitError::CustomError(e.to_string()))? + if crate::application::api_service::mono::cl_merge::needs_path_tree_attach( + self.git_ops(), + &path, + ) + .await + .map_err(|e| GitError::CustomError(e.to_string()))? { - self.attach_project_path_to_monorepo_root(&normalized_path) + self.git() + .attach_project_path_to_monorepo_root(&normalized_path) .await .map_err(|e| GitError::CustomError(e.to_string()))?; crate::application::api_service::mono::cl_merge::sync_path_prefix_main_refs( - self, + self.git_ops(), &normalized_path, ) .await .map_err(|e| GitError::CustomError(e.to_string()))?; } - let update_chain = self.search_tree_for_update(parent).await?; + let update_chain = self.git().search_tree_for_update(parent).await?; (path, update_chain) }; @@ -203,14 +187,16 @@ impl MonoApiService { // TODO: self.clean_dangling_commits().await; } // add conversation - self.storage - .conversation_storage() + self.storage() + .cl_service + .conversation_store() .add_conversation(&cl.link, username, None, ConvTypeEnum::Merged) .await .map_err(|e| GitError::CustomError(format!("Failed to add conversation: {}", e)))?; // update cl status last - self.storage - .cl_storage() + self.storage() + .cl_service + .cl_store() .merge_cl(cl.clone()) .await .map_err(|e| GitError::CustomError(format!("Failed to update CL status: {}", e)))?; @@ -222,7 +208,7 @@ impl MonoApiService { normalized.ends_with(crate::application::api_service::mono::ADMIN_FILE) }); if admin_file_modified { - self.invalidate_admin_cache().await; + self.admin().invalidate_admin_cache().await; } } @@ -235,7 +221,7 @@ impl MonoApiService { commit_msg: &str, cl_link: Option<&str>, ) -> Result { - let storage = self.storage.mono_storage(); + let storage = self.storage().mono_storage(); let mut new_commit_id = String::new(); let mut commits: Vec = Vec::new(); @@ -268,7 +254,7 @@ impl MonoApiService { } let txn = self - .storage + .storage() .begin_db_transaction() .await .map_err(|e| GitError::CustomError(e.to_string()))?; @@ -315,7 +301,7 @@ impl MonoApiService { cl_link: &str, parent_override: Option, ) -> Result { - let storage = self.storage.mono_storage(); + let storage = self.storage().mono_storage(); let mut new_commit_id = String::new(); let mut commits: Vec = Vec::new(); diff --git a/ceres/src/application/api_service/mono/cl/merge_strategy.rs b/ceres/src/application/api_service/mono/cl/merge_strategy.rs index fd94b4f32..88160e267 100644 --- a/ceres/src/application/api_service/mono/cl/merge_strategy.rs +++ b/ceres/src/application/api_service/mono/cl/merge_strategy.rs @@ -7,7 +7,10 @@ use common::{errors::MegaError, utils::ZERO_ID}; use git_internal::{errors::GitError, internal::object::commit::Commit}; use jupiter::utils::converter::FromMegaModel; -use crate::application::api_service::{mono::MonoApiService, tree_ops}; +use crate::application::api_service::{ + mono::{ClApplicationService, GitOpsPort}, + tree_ops, +}; /// How a CL should be applied onto monorepo main. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -28,9 +31,9 @@ impl ClMergeStrategy { } /// Returns true when the path has no `refs/heads/main` row yet. -pub async fn path_lacks_main_ref(service: &MonoApiService, path: &str) -> Result { - Ok(service - .storage +pub async fn path_lacks_main_ref(git: &dyn GitOpsPort, path: &str) -> Result { + Ok(git + .storage() .mono_storage() .get_main_ref(path) .await? @@ -68,14 +71,10 @@ pub fn path_prefixes(path: &str) -> Vec { } /// Sync path-level main refs for each strict prefix after the root tree changed. -pub async fn sync_path_prefix_main_refs( - service: &MonoApiService, - path: &str, -) -> Result<(), MegaError> { +pub async fn sync_path_prefix_main_refs(git: &dyn GitOpsPort, path: &str) -> Result<(), MegaError> { for prefix in path_prefixes(path) { - let hash = - crate::application::code_edit::utils::create_repo_commit(&service.storage, &prefix) - .await?; + let hash = crate::application::code_edit::utils::create_repo_commit(git.storage(), &prefix) + .await?; if hash == ZERO_ID { return Err(MegaError::Other(format!( "Failed to sync main ref for prefix {prefix}" @@ -87,29 +86,29 @@ pub async fn sync_path_prefix_main_refs( /// Bootstrap a new monorepo path: attach under `/`, sync prefix refs, create path main baseline. pub async fn bootstrap_monorepo_path( - service: &MonoApiService, + git: &dyn GitOpsPort, path: &str, cl: Option<&mega_cl::Model>, ) -> Result { - let mono_storage = service.storage.mono_storage(); + let mono_storage = git.storage().mono_storage(); if let Some(existing) = mono_storage.get_main_ref(path).await? { if let Some(cl) = cl && cl.from_hash == ZERO_ID { - service - .storage - .cl_storage() + git.storage() + .cl_service + .cl_store() .update_cl_hash(cl.clone(), &existing.ref_commit_hash, &cl.to_hash) .await?; } return Ok(existing.ref_commit_hash); } - service.attach_project_path_to_monorepo_root(path).await?; - sync_path_prefix_main_refs(service, path).await?; + git.attach_project_path_to_monorepo_root(path).await?; + sync_path_prefix_main_refs(git, path).await?; let baseline_hash = - crate::application::code_edit::utils::create_repo_commit(&service.storage, path).await?; + crate::application::code_edit::utils::create_repo_commit(git.storage(), path).await?; if baseline_hash == ZERO_ID { return Err(MegaError::Other(format!( "Failed to create main ref baseline for {path}" @@ -119,9 +118,9 @@ pub async fn bootstrap_monorepo_path( if let Some(cl) = cl && cl.from_hash == ZERO_ID { - service - .storage - .cl_storage() + git.storage() + .cl_service + .cl_store() .update_cl_hash(cl.clone(), &baseline_hash, &cl.to_hash) .await?; } @@ -131,34 +130,35 @@ pub async fn bootstrap_monorepo_path( /// Prepare a CL path for merge (bootstrap new `/project/*` directories). pub async fn prepare_cl_path_for_merge( - service: &MonoApiService, + git: &dyn GitOpsPort, cl: &mut mega_cl::Model, ) -> Result<(), MegaError> { if !cl.path.starts_with("/project/") { return Ok(()); } - bootstrap_monorepo_path(service, &cl.path, Some(cl)).await?; + bootstrap_monorepo_path(git, &cl.path, Some(cl)).await?; - if let Some(fresh) = service.storage.cl_storage().get_cl(&cl.link).await? { + if let Some(fresh) = git.storage().cl_service.cl_store().get_cl(&cl.link).await? { *cl = fresh; } Ok(()) } pub async fn resolve_merge_strategy( - service: &MonoApiService, + cl_svc: &ClApplicationService, cl: &mega_cl::Model, ) -> Result { + let git = cl_svc.git_ops(); if cl.from_hash == ZERO_ID { return Ok(ClMergeStrategy::SubtreeReplace); } - if path_lacks_main_ref(service, &cl.path).await? { + if path_lacks_main_ref(git, &cl.path).await? { return Ok(ClMergeStrategy::SubtreeReplace); } - if is_gitkeep_baseline(service, &cl.from_hash).await? { + if is_gitkeep_baseline(cl_svc, &cl.from_hash).await? { return Ok(ClMergeStrategy::SubtreeReplace); } @@ -166,10 +166,10 @@ pub async fn resolve_merge_strategy( } async fn is_gitkeep_baseline( - service: &MonoApiService, + cl_svc: &ClApplicationService, commit_hash: &str, ) -> Result { - let blobs = service.get_commit_blobs(commit_hash).await?; + let blobs = cl_svc.get_commit_blobs(commit_hash).await?; if blobs.is_empty() { return Ok(true); } @@ -181,10 +181,7 @@ async fn is_gitkeep_baseline( } /// Returns true when the final path segment is not yet present in the monorepo tree. -pub async fn needs_path_tree_attach( - service: &MonoApiService, - path: &Path, -) -> Result { +pub async fn needs_path_tree_attach(git: &dyn GitOpsPort, path: &Path) -> Result { let parent = match path.parent() { Some(p) if p.as_os_str().is_empty() || p == Path::new("/") => Path::new("/"), Some(p) => p, @@ -193,7 +190,7 @@ pub async fn needs_path_tree_attach( let Some(name) = path.file_name().and_then(|n| n.to_str()) else { return Ok(false); }; - let parent_tree = match tree_ops::search_tree_by_path(service, parent, None).await? { + let parent_tree = match tree_ops::search_tree_by_path(git, parent, None).await? { Some(tree) => tree, None => return Ok(true), }; @@ -205,11 +202,11 @@ pub async fn needs_path_tree_attach( /// Leaf tree hash to mount at `cl.path` for merge. pub async fn resolve_merge_leaf_tree_id( - service: &MonoApiService, + cl_svc: &ClApplicationService, cl: &mega_cl::Model, strategy: ClMergeStrategy, ) -> Result { - let storage = service.storage.mono_storage(); + let storage = cl_svc.storage().mono_storage(); match strategy { ClMergeStrategy::SubtreeReplace => { @@ -230,20 +227,20 @@ pub async fn resolve_merge_leaf_tree_id( .map_err(|e| GitError::CustomError(e.to_string()))? .ok_or_else(|| GitError::CustomError("Main ref not found".to_string()))?; - let old_blobs = service + let old_blobs = cl_svc .get_commit_blobs(&cl.from_hash) .await .map_err(|e| GitError::CustomError(e.to_string()))?; - let new_blobs = service + let new_blobs = cl_svc .get_commit_blobs(&cl.to_hash) .await .map_err(|e| GitError::CustomError(e.to_string()))?; - let cl_changed = service + let cl_changed = cl_svc .cl_files_list(old_blobs, new_blobs) .await .map_err(|e| GitError::CustomError(e.to_string()))?; - let merged_commit_hash = service + let merged_commit_hash = cl_svc .apply_changes_as_single_commit(cl, &cl_changed, &main_ref.ref_commit_hash) .await?; diff --git a/ceres/src/application/api_service/mono/cl/queue.rs b/ceres/src/application/api_service/mono/cl/queue.rs index 33a2253aa..ded7f3eda 100644 --- a/ceres/src/application/api_service/mono/cl/queue.rs +++ b/ceres/src/application/api_service/mono/cl/queue.rs @@ -1,4 +1,4 @@ -//! Merge queue background processor for [`MonoApiService`](super::service::MonoApiService). +//! Merge queue background processor for [`ClApplicationService`](super::service::ClApplicationService). use std::time::Duration; @@ -7,14 +7,14 @@ use common::errors::MegaError; use tracing; use crate::{ - application::api_service::mono::MonoApiService, + application::api_service::mono::ClApplicationService, model::merge_queue::{ AddToQueueResponse, QueueItem, QueueListResponse, QueueStatsResponse, QueueStatus, QueueStatusResponse, }, }; -impl MonoApiService { +impl ClApplicationService { // ========== Merge Queue Methods ========== /// Queue polling interval in seconds when no items are processed @@ -36,7 +36,12 @@ impl MonoApiService { /// * `Err(MegaError)` - If validation fails or database error occurs pub async fn add_to_merge_queue(&self, cl_link: String) -> Result { // Validate CL exists and is in Open status - let cl = self.storage.cl_storage().get_cl(&cl_link).await?; + let cl = self + .storage() + .cl_service + .cl_store() + .get_cl(&cl_link) + .await?; let model = cl.ok_or(MegaError::Other("CL not found".to_string()))?; if model.status != MergeStatusEnum::Open { @@ -48,7 +53,7 @@ impl MonoApiService { // Add to queue via jupiter layer service let position = self - .storage + .storage() .merge_queue_service .add_to_queue(cl_link) .await?; @@ -70,7 +75,7 @@ impl MonoApiService { /// * `Err(MegaError)` - If database error occurs pub async fn retry_merge_queue_item(&self, cl_link: &str) -> Result { let result = self - .storage + .storage() .merge_queue_service .retry_queue_item(cl_link) .await?; @@ -90,7 +95,7 @@ impl MonoApiService { ) -> Result { let position = self.add_to_merge_queue(cl_link.clone()).await?; let display_position = self - .storage + .storage() .merge_queue_service .get_display_position_by_position(position) .await @@ -113,14 +118,14 @@ impl MonoApiService { } pub async fn remove_from_merge_queue(&self, cl_link: &str) -> Result { - self.storage + self.storage() .merge_queue_service .remove_from_queue(cl_link) .await } pub async fn get_merge_queue_list(&self) -> Result { - let items = self.storage.merge_queue_service.get_queue_list().await?; + let items = self.storage().merge_queue_service.get_queue_list().await?; Ok(QueueListResponse::from(items)) } @@ -129,7 +134,7 @@ impl MonoApiService { cl_link: &str, ) -> Result { let item_model = self - .storage + .storage() .merge_queue_service .get_cl_queue_status(cl_link) .await?; @@ -140,7 +145,7 @@ impl MonoApiService { match item.status { QueueStatus::Waiting | QueueStatus::Testing | QueueStatus::Merging => { match self - .storage + .storage() .merge_queue_service .get_display_position(&item.cl_link) .await @@ -167,12 +172,12 @@ impl MonoApiService { } pub async fn get_merge_queue_stats(&self) -> Result { - let stats = self.storage.merge_queue_service.get_queue_stats().await?; + let stats = self.storage().merge_queue_service.get_queue_stats().await?; Ok(QueueStatsResponse::from(stats)) } pub async fn cancel_all_pending_merge_queue(&self) -> Result<(), MegaError> { - self.storage + self.storage() .merge_queue_service .cancel_all_pending() .await?; @@ -185,10 +190,10 @@ impl MonoApiService { /// The processor automatically stops when no active items remain in queue. fn ensure_merge_processor_running(&self) { // Get the processor running flag from merge queue service - if self.storage.merge_queue_service.try_start_processor() { + if self.storage().merge_queue_service.try_start_processor() { let service = self.clone(); tokio::spawn(async move { - tracing::info!("Merge queue processor started (from MonoApiService)"); + tracing::info!("Merge queue processor started (from ClApplicationService)"); service.run_merge_processor_loop().await; }); } @@ -203,7 +208,8 @@ impl MonoApiService { Ok(processed) => { if !processed { // Check if there are active items - if let Ok(stats) = self.storage.merge_queue_service.get_queue_stats().await + if let Ok(stats) = + self.storage().merge_queue_service.get_queue_stats().await { let has_active = stats.waiting_count > 0 || stats.testing_count > 0 @@ -211,7 +217,7 @@ impl MonoApiService { if !has_active { // No active items, stop processor - self.storage.merge_queue_service.stop_processor(); + self.storage().merge_queue_service.stop_processor(); tracing::info!("Merge queue processor stopped (no active items)"); break; } @@ -235,7 +241,7 @@ impl MonoApiService { /// * `Ok(false)` - No items to process /// * `Err(MegaError)` - System error occurred async fn process_next_queue_item(&self) -> Result { - let queue_service = &self.storage.merge_queue_service; + let queue_service = &self.storage().merge_queue_service; // Get next waiting item from queue let next_item = queue_service.get_next_waiting_item().await?; @@ -303,12 +309,13 @@ impl MonoApiService { &self, cl_link: &str, ) -> Result<(), (QueueFailureTypeEnum, String)> { - let queue_service = &self.storage.merge_queue_service; + let queue_service = &self.storage().merge_queue_service; // Step 1: Validate CL still exists and is not closed let cl = self - .storage - .cl_storage() + .storage() + .cl_service + .cl_store() .get_cl(cl_link) .await .map_err(|e| { diff --git a/ceres/src/application/api_service/mono/cl_list.rs b/ceres/src/application/api_service/mono/cl_list.rs index 1acbf442a..5e0aab50c 100644 --- a/ceres/src/application/api_service/mono/cl_list.rs +++ b/ceres/src/application/api_service/mono/cl_list.rs @@ -1,18 +1,19 @@ use api_model::common::{CommonPage, Pagination}; use common::errors::MegaError; -use super::service::MonoApiService; +use super::context::ClApplicationService; use crate::model::{change_list::ListPayload, issue::ItemRes}; -impl MonoApiService { +impl ClApplicationService { pub async fn get_cl_list( &self, filter: ListPayload, pagination: Pagination, ) -> Result, MegaError> { let (items, total) = self - .storage - .cl_storage() + .storage() + .cl_service + .cl_store() .get_cl_list(filter.into(), pagination) .await?; Ok(CommonPage { @@ -22,8 +23,9 @@ impl MonoApiService { } pub async fn get_cl_model(&self, link: &str) -> Result { - self.storage - .cl_storage() + self.storage() + .cl_service + .cl_store() .get_cl(link) .await? .ok_or_else(|| MegaError::NotFound(format!("CL {link} not found"))) diff --git a/ceres/src/application/api_service/mono/cla.rs b/ceres/src/application/api_service/mono/cla.rs index d6bf8b67f..3defe2921 100644 --- a/ceres/src/application/api_service/mono/cla.rs +++ b/ceres/src/application/api_service/mono/cla.rs @@ -1,21 +1,23 @@ -//! CLA (Contributor License Agreement) operations for [`MonoApiService`](super::service::MonoApiService). +//! CLA (Contributor License Agreement) operations for [`UserApplicationService`]. use bytes::Bytes; use common::errors::MegaError; use futures::{StreamExt, stream}; use io_orbit::object_storage::{ObjectKey, ObjectMeta, ObjectNamespace}; -use crate::{application::api_service::mono::MonoApiService, merge_checker::CheckerRegistry}; +use super::context::UserApplicationService; +use crate::merge_checker::CheckerRegistry; const CLA_CONTENT_OBJECT_KEY: &str = "cla/content/current.txt"; -impl MonoApiService { +impl UserApplicationService { pub async fn get_or_init_cla_sign_status( &self, username: &str, ) -> Result<(bool, Option), MegaError> { let model = self - .storage + .ctx + .storage() .cla_storage() .get_or_create_status(username) .await?; @@ -29,7 +31,8 @@ impl MonoApiService { }; let stream = self - .storage + .ctx + .storage() .git_service .obj_storage .inner @@ -68,7 +71,8 @@ impl MonoApiService { ..Default::default() }; - self.storage + self.ctx + .storage() .git_service .obj_storage .inner @@ -80,15 +84,17 @@ impl MonoApiService { &self, username: &str, ) -> Result<(bool, Option), MegaError> { - let model = self.storage.cla_storage().sign(username).await?; + let model = self.ctx.storage().cla_storage().sign(username).await?; self.refresh_checks_for_open_cls_by_author(username).await?; Ok((model.cla_signed, model.cla_signed_at)) } async fn refresh_checks_for_open_cls_by_author(&self, username: &str) -> Result<(), MegaError> { let open_cls = self - .storage - .cl_storage() + .ctx + .storage() + .cl_service + .cl_store() .get_open_cls() .await? .into_iter() @@ -98,7 +104,8 @@ impl MonoApiService { return Ok(()); } - let check_reg = CheckerRegistry::new(self.storage.clone().into(), username.to_string()); + let check_reg = + CheckerRegistry::new(self.ctx.storage().clone().into(), username.to_string()); for cl in open_cls { check_reg.run_checks(cl.into()).await?; } diff --git a/ceres/src/application/api_service/mono/code_review.rs b/ceres/src/application/api_service/mono/code_review.rs index 910e8df4e..c89048048 100644 --- a/ceres/src/application/api_service/mono/code_review.rs +++ b/ceres/src/application/api_service/mono/code_review.rs @@ -1,18 +1,19 @@ use common::errors::MegaError; -use super::service::MonoApiService; +use super::context::CodeReviewApplicationService; use crate::model::code_review::{ CodeReviewResponse, CommentReplyRequest, CommentReviewResponse, InitializeCommentRequest, ThreadReviewResponse, ThreadStatusResponse, UpdateCommentRequest, }; -impl MonoApiService { +impl CodeReviewApplicationService { pub async fn get_code_review_comments( &self, link: &str, ) -> Result { let comments = self - .storage + .ctx + .storage() .code_review_service .get_all_comments_by_link(link) .await?; @@ -26,7 +27,8 @@ impl MonoApiService { payload: InitializeCommentRequest, ) -> Result { let thread = self - .storage + .ctx + .storage() .code_review_service .create_inline_comment( link, @@ -51,7 +53,8 @@ impl MonoApiService { payload: CommentReplyRequest, ) -> Result { let comment = self - .storage + .ctx + .storage() .code_review_service .reply_to_comment( thread_id, @@ -70,7 +73,8 @@ impl MonoApiService { payload: UpdateCommentRequest, ) -> Result { let comment = self - .storage + .ctx + .storage() .code_review_comment_storage() .find_comment_by_id(comment_id) .await? @@ -83,7 +87,8 @@ impl MonoApiService { } let updated = self - .storage + .ctx + .storage() .code_review_service .update_comment(comment_id, payload.content) .await?; @@ -95,7 +100,8 @@ impl MonoApiService { thread_id: i64, ) -> Result { let thread = self - .storage + .ctx + .storage() .code_review_service .resolve_thread(thread_id) .await?; @@ -107,7 +113,8 @@ impl MonoApiService { thread_id: i64, ) -> Result { let thread = self - .storage + .ctx + .storage() .code_review_service .reopen_thread(thread_id) .await?; @@ -115,7 +122,8 @@ impl MonoApiService { } pub async fn delete_code_review_thread(&self, thread_id: i64) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .code_review_service .delete_thread(thread_id) .await?; @@ -128,7 +136,8 @@ impl MonoApiService { username: &str, ) -> Result<(), MegaError> { let comment = self - .storage + .ctx + .storage() .code_review_comment_storage() .find_comment_by_id(comment_id) .await? @@ -140,7 +149,8 @@ impl MonoApiService { )); } - self.storage + self.ctx + .storage() .code_review_service .delete_comment(comment_id) .await?; diff --git a/ceres/src/application/api_service/mono/commit.rs b/ceres/src/application/api_service/mono/commit.rs index ea334db7e..d37580337 100644 --- a/ceres/src/application/api_service/mono/commit.rs +++ b/ceres/src/application/api_service/mono/commit.rs @@ -25,7 +25,7 @@ impl MonoApiService { }) }; - self.storage + self.storage() .commit_binding_storage() .upsert_binding(sha, final_username.clone(), final_username.is_none()) .await?; @@ -36,14 +36,14 @@ impl MonoApiService { } pub fn import_dir(&self) -> PathBuf { - self.storage.config().monorepo.import_dir.clone() + self.storage().config().monorepo.import_dir.clone() } pub async fn find_git_repo_like_path( &self, path: &str, ) -> Result, MegaError> { - self.storage + self.storage() .git_db_storage() .find_git_repo_like_path(path) .await @@ -66,7 +66,7 @@ impl MonoApiService { let std_path = StdPath::new(path); if std_path.starts_with(&import_dir) && std_path != StdPath::new(&import_dir) { if let Some(repo_model) = self.find_git_repo_like_path(path).await? { - let git = self.storage.git_db_storage(); + let git = self.storage().git_db_storage(); if let Ok(Some(r)) = git.get_default_ref(repo_model.id).await { return Ok(r.ref_git_id); } @@ -78,7 +78,7 @@ impl MonoApiService { return Ok("HEAD".to_string()); } } else { - let mono = self.storage.mono_storage(); + let mono = self.storage().mono_storage(); let resolved_path = path_context.unwrap_or("/"); if let Ok(Some(r)) = mono.get_main_ref(resolved_path).await { return Ok(r.ref_commit_hash); @@ -90,7 +90,7 @@ impl MonoApiService { } } - let mono = self.storage.mono_storage(); + let mono = self.storage().mono_storage(); if let Ok(Some(root_ref)) = mono.get_main_ref("/").await { return Ok(root_ref.ref_commit_hash); } diff --git a/ceres/src/application/api_service/mono/context.rs b/ceres/src/application/api_service/mono/context.rs new file mode 100644 index 000000000..128547c94 --- /dev/null +++ b/ceres/src/application/api_service/mono/context.rs @@ -0,0 +1,148 @@ +//! Shared context and domain-scoped application services. + +use std::sync::Arc; + +use jupiter::storage::Storage; + +use crate::{ + application::{api_service::cache::GitObjectCache, build_trigger::SharedBuildDispatch}, + infra::TransportContext, +}; +#[derive(Clone)] +pub(crate) struct ServiceContext { + storage: Storage, + git_object_cache: Arc, + build_dispatch: Option, +} + +impl ServiceContext { + pub(crate) fn from_transport(ctx: TransportContext) -> Self { + Self { + storage: ctx.storage, + git_object_cache: ctx.git_object_cache, + build_dispatch: ctx.build_dispatch, + } + } + + pub(crate) fn new( + storage: Storage, + git_object_cache: Arc, + build_dispatch: Option, + ) -> Self { + Self { + storage, + git_object_cache, + build_dispatch, + } + } + + pub(crate) fn storage(&self) -> &Storage { + &self.storage + } + + pub(crate) fn git_object_cache(&self) -> &Arc { + &self.git_object_cache + } + + pub(crate) fn build_dispatch(&self) -> Option { + self.build_dispatch.clone() + } +} + +macro_rules! app_service { + ($(#[$meta:meta])* $vis:vis struct $name:ident) => { + $(#[$meta])* + #[derive(Clone)] + $vis struct $name { + pub(super) ctx: ServiceContext, + } + + impl $name { + pub(crate) fn new(ctx: ServiceContext) -> Self { + Self { ctx } + } + } + }; +} + +app_service! { + /// Issue and label operations. + pub struct IssueApplicationService +} +app_service! { + /// Conversation and comment operations. + pub struct ConversationApplicationService +} +app_service! { + /// Admin operations (groups, bots, permissions). + pub struct AdminApplicationService +} +app_service! { + /// User profile, tokens, and notification preferences. + pub struct UserApplicationService +} +app_service! { + /// Git LFS operations. + pub struct LfsApplicationService +} + +/// Change-list (CL) operations. +#[derive(Clone)] +pub struct ClApplicationService { + pub(super) ctx: ServiceContext, + git: super::service::MonoApiService, + admin: AdminApplicationService, +} + +app_service! { + /// Webhook CRUD operations. + pub struct WebhookApplicationService +} + +impl WebhookApplicationService { + pub(crate) fn storage(&self) -> &Storage { + self.ctx.storage() + } +} +app_service! { + /// Dynamic sidebar menu operations. + pub struct SidebarApplicationService +} +app_service! { + /// Code review thread/comment operations. + pub struct CodeReviewApplicationService +} +app_service! { + /// CL reviewer operations. + pub struct ReviewerApplicationService +} +app_service! { + /// Note sync operations. + pub struct NoteApplicationService +} + +impl ClApplicationService { + pub(crate) fn new( + ctx: ServiceContext, + git: super::service::MonoApiService, + admin: AdminApplicationService, + ) -> Self { + Self { ctx, git, admin } + } + + pub(crate) fn git(&self) -> &super::service::MonoApiService { + &self.git + } + + pub(crate) fn git_ops(&self) -> &dyn super::git_ops::GitOpsPort { + &self.git + } + + pub(crate) fn storage(&self) -> &Storage { + self.ctx.storage() + } + + pub(crate) fn admin(&self) -> &AdminApplicationService { + &self.admin + } +} diff --git a/ceres/src/application/api_service/mono/conversation.rs b/ceres/src/application/api_service/mono/conversation.rs index b1629d8b3..7949853f4 100644 --- a/ceres/src/application/api_service/mono/conversation.rs +++ b/ceres/src/application/api_service/mono/conversation.rs @@ -1,12 +1,12 @@ use common::errors::MegaError; -use super::service::MonoApiService; +use super::context::ConversationApplicationService; use crate::model::{ change_list::MergeStatus, conversation::{ConvType, ReferenceType}, }; -impl MonoApiService { +impl ConversationApplicationService { pub async fn add_conversation( &self, link: &str, @@ -14,8 +14,10 @@ impl MonoApiService { comment: Option, conv_type: ConvType, ) -> Result { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .add_conversation(link, username, comment, conv_type.into()) .await } @@ -26,8 +28,10 @@ impl MonoApiService { ref_link: &str, username: &str, ) -> Result<(), MegaError> { - self.storage - .issue_storage() + self.ctx + .storage() + .issue_service + .issue_store() .add_reference(source_link, ref_link, ReferenceType::Mention.into()) .await?; self.add_conversation( @@ -42,8 +46,10 @@ impl MonoApiService { pub async fn cl_merge_status(&self, link: &str) -> Result { let model = self - .storage - .cl_storage() + .ctx + .storage() + .cl_service + .cl_store() .get_cl(link) .await? .ok_or_else(|| MegaError::NotFound(format!("CL {link} not found")))?; @@ -57,8 +63,10 @@ impl MonoApiService { comment_type: &str, username: &str, ) -> Result<(), MegaError> { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .add_reactions(content, comment_id, comment_type, username) .await?; Ok(()) @@ -69,15 +77,19 @@ impl MonoApiService { reaction_id: &str, username: &str, ) -> Result<(), MegaError> { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .delete_reaction(reaction_id, username) .await } pub async fn remove_conversation(&self, comment_id: i64) -> Result<(), MegaError> { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .remove_conversation(comment_id) .await } @@ -87,8 +99,10 @@ impl MonoApiService { comment_id: i64, content: Option, ) -> Result<(), MegaError> { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .update_comment(comment_id, content) .await } @@ -99,8 +113,10 @@ impl MonoApiService { conversation_id: &i64, resolved: bool, ) -> Result<(), MegaError> { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .change_review_state(link, conversation_id, resolved) .await } diff --git a/ceres/src/application/api_service/mono/dynamic_sidebar.rs b/ceres/src/application/api_service/mono/dynamic_sidebar.rs index 338d4b0a3..55c66ee69 100644 --- a/ceres/src/application/api_service/mono/dynamic_sidebar.rs +++ b/ceres/src/application/api_service/mono/dynamic_sidebar.rs @@ -1,12 +1,13 @@ use common::errors::MegaError; -use super::service::MonoApiService; +use super::context::SidebarApplicationService; use crate::model::dynamic_sidebar::{SidebarMenuListRes, SidebarRes, SidebarSyncPayload}; -impl MonoApiService { +impl SidebarApplicationService { pub async fn list_sidebars(&self) -> Result { Ok(self - .storage + .ctx + .storage() .dynamic_sidebar_storage() .get_sidebars() .await? @@ -24,7 +25,8 @@ impl MonoApiService { order_index: i32, ) -> Result { let res = self - .storage + .ctx + .storage() .dynamic_sidebar_storage() .new_sidebar(public_id, label, href, visible, order_index) .await?; @@ -41,7 +43,8 @@ impl MonoApiService { order_index: Option, ) -> Result { let res = self - .storage + .ctx + .storage() .dynamic_sidebar_storage() .update_sidebar(id, public_id, label, href, visible, order_index) .await?; @@ -53,7 +56,8 @@ impl MonoApiService { payloads: Vec, ) -> Result, MegaError> { let res = self - .storage + .ctx + .storage() .dynamic_sidebar_storage() .sync_sidebar(payloads.into_iter().map(|item| item.into()).collect()) .await?; @@ -62,7 +66,8 @@ impl MonoApiService { pub async fn delete_sidebar(&self, id: i32) -> Result { let res = self - .storage + .ctx + .storage() .dynamic_sidebar_storage() .delete_sidebar(id) .await?; diff --git a/ceres/src/application/api_service/mono/edit/entry.rs b/ceres/src/application/api_service/mono/edit/entry.rs index a7ba6ca78..556ef0a05 100644 --- a/ceres/src/application/api_service/mono/edit/entry.rs +++ b/ceres/src/application/api_service/mono/edit/entry.rs @@ -344,7 +344,7 @@ impl MonoApiService { let cl_root_path = MonoServiceLogic::subtree_ref_path(parent_path) .map_err(|e| GitError::CustomError(e.to_string()))?; let build_repo_path = match edit_utils::resolve_build_repo_root( - &self.storage, + self.storage(), &cl_root_path, ) .await @@ -406,7 +406,7 @@ impl MonoApiService { })?; let src_commit = - edit_utils::get_repo_main_latest_commit(&self.storage, &build_repo_path).await?; + edit_utils::get_repo_main_latest_commit(self.storage(), &build_repo_path).await?; let dst_commit = Commit::from_tree_id( target_tree_id, vec![ @@ -423,7 +423,7 @@ impl MonoApiService { .clone() .unwrap_or("Anonymous".to_string()); - self.storage + self.storage() .mono_service .mono_storage .save_mega_commits(vec![dst_commit], None) @@ -440,7 +440,7 @@ impl MonoApiService { }) .collect(); - self.storage + self.storage() .mono_service .mono_storage .batch_save_model(save_trees) @@ -454,11 +454,11 @@ impl MonoApiService { .unwrap_or(MEGA_BRANCH_NAME), &src_commit.id.to_string(), self, - self.storage.mono_storage(), + self.storage().mono_storage(), ); let cl = editor .find_or_create_cl_for_edit( - &self.storage, + self.storage(), &editor, payload.mode, &new_commit_id, @@ -466,7 +466,7 @@ impl MonoApiService { ) .await?; - self.storage + self.storage() .mono_service .save_blobs(&new_commit_id, vec![new_blob.clone()]) .await?; @@ -496,7 +496,7 @@ impl MonoApiService { &self, entry_info: CreateEntryInfo, ) -> Result { - let storage = self.storage.mono_storage(); + let storage = self.storage().mono_storage(); let CreateEntryUpdate { update_result, blob, @@ -508,7 +508,7 @@ impl MonoApiService { let repo_path_str = MonoServiceLogic::subtree_ref_path(&repo_path) .map_err(|e| GitError::CustomError(e.to_string()))?; let build_repo_path = match edit_utils::resolve_build_repo_root( - &self.storage, + self.storage(), &repo_path_str, ) .await @@ -525,7 +525,7 @@ impl MonoApiService { }; let src_commit = - edit_utils::get_repo_main_latest_commit(&self.storage, &build_repo_path).await?; + edit_utils::get_repo_main_latest_commit(self.storage(), &build_repo_path).await?; let base_commit = ObjectHash::from_str(&src_commit.id.to_string()).map_err(|e| { GitError::CustomError(format!("Invalid commit hash {}: {e}", src_commit.id)) })?; @@ -556,7 +556,7 @@ impl MonoApiService { tree_model.into() }) .collect(); - self.storage + self.storage() .mono_service .save_blobs(&new_commit_id, vec![blob]) .await?; @@ -566,7 +566,7 @@ impl MonoApiService { .await .map_err(|e| GitError::CustomError(e.to_string()))?; - self.storage + self.storage() .mono_service .mono_storage .save_mega_commits(vec![dst_commit], None) @@ -579,11 +579,11 @@ impl MonoApiService { .unwrap_or(MEGA_BRANCH_NAME), &src_commit.id.to_string(), self, - self.storage.mono_storage(), + self.storage().mono_storage(), ); let cl = editor .find_or_create_cl_for_edit( - &self.storage, + self.storage(), &editor, entry_info.mode.clone(), &new_commit_id, diff --git a/ceres/src/application/api_service/mono/git_ops.rs b/ceres/src/application/api_service/mono/git_ops.rs new file mode 100644 index 000000000..f841e79f5 --- /dev/null +++ b/ceres/src/application/api_service/mono/git_ops.rs @@ -0,0 +1,51 @@ +//! Git operations port used by CL application logic. + +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; + +use async_trait::async_trait; +use common::errors::MegaError; +use git_internal::{hash::ObjectHash, internal::object::tree::Tree}; +use jupiter::storage::Storage; + +use super::service::MonoApiService; +use crate::application::api_service::ApiHandler; + +/// Git tree/blob operations required by CL diff and merge flows. +#[async_trait] +pub trait GitOpsPort: ApiHandler + Send + Sync { + fn storage(&self) -> &Storage; + + async fn traverse_tree(&self, root_tree: Tree) + -> Result, MegaError>; + + async fn search_tree_for_update(&self, parent: &Path) -> Result>, MegaError>; + + async fn attach_project_path_to_monorepo_root(&self, path: &str) -> Result<(), MegaError>; +} + +#[async_trait] +impl GitOpsPort for MonoApiService { + fn storage(&self) -> &Storage { + MonoApiService::storage(self) + } + + async fn traverse_tree( + &self, + root_tree: Tree, + ) -> Result, MegaError> { + MonoApiService::traverse_tree(self, root_tree).await + } + + async fn search_tree_for_update(&self, parent: &Path) -> Result>, MegaError> { + ApiHandler::search_tree_for_update(self, parent) + .await + .map_err(|e| MegaError::Other(e.to_string())) + } + + async fn attach_project_path_to_monorepo_root(&self, path: &str) -> Result<(), MegaError> { + MonoApiService::attach_project_path_to_monorepo_root(self, path).await + } +} diff --git a/ceres/src/application/api_service/mono/gpg.rs b/ceres/src/application/api_service/mono/gpg.rs index 585b4cc51..53e6d0782 100644 --- a/ceres/src/application/api_service/mono/gpg.rs +++ b/ceres/src/application/api_service/mono/gpg.rs @@ -1,18 +1,20 @@ use common::errors::MegaError; -use super::service::MonoApiService; +use super::context::UserApplicationService; use crate::model::gpg::GpgKey; -impl MonoApiService { +impl UserApplicationService { pub async fn add_gpg_key(&self, user_id: String, gpg_content: String) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .gpg_storage() .add_gpg_key(user_id, gpg_content) .await } pub async fn remove_gpg_key(&self, user_id: String, key_id: String) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .gpg_storage() .remove_gpg_key(user_id, key_id) .await @@ -20,7 +22,8 @@ impl MonoApiService { pub async fn list_user_gpg_keys(&self, user_id: String) -> Result, MegaError> { let raw_keys = self - .storage + .ctx + .storage() .gpg_storage() .list_user_gpg(user_id.clone()) .await; diff --git a/ceres/src/application/api_service/mono/issue.rs b/ceres/src/application/api_service/mono/issue.rs index 84be0e070..79a7ffa35 100644 --- a/ceres/src/application/api_service/mono/issue.rs +++ b/ceres/src/application/api_service/mono/issue.rs @@ -1,21 +1,22 @@ use api_model::common::Pagination; use common::errors::MegaError; -use super::service::MonoApiService; +use super::context::IssueApplicationService; use crate::model::{ change_list::ListPayload, issue::{IssueDetailRes, IssueSuggestions, ItemRes}, label::LabelItem, }; -impl MonoApiService { +impl IssueApplicationService { pub async fn get_issue_details( &self, link: &str, username: String, ) -> Result { let details = self - .storage + .ctx + .storage() .issue_service .get_issue_details(link, username) .await?; @@ -26,7 +27,12 @@ impl MonoApiService { &self, query: &str, ) -> Result, MegaError> { - let (issues, cls) = self.storage.issue_service.get_suggestions(query).await?; + let (issues, cls) = self + .ctx + .storage() + .issue_service + .get_suggestions(query) + .await?; let mut res: Vec = issues.into_iter().map(|m| m.into()).collect(); let mut mr_list: Vec = cls.into_iter().map(|m| m.into()).collect(); res.append(&mut mr_list); @@ -40,8 +46,10 @@ impl MonoApiService { pagination: Pagination, ) -> Result<(Vec, u64), MegaError> { let (items, total) = self - .storage - .issue_storage() + .ctx + .storage() + .issue_service + .issue_store() .get_issue_list(filter.into(), pagination) .await?; Ok((items.into_iter().map(|m| m.into()).collect(), total)) @@ -52,22 +60,39 @@ impl MonoApiService { username: &str, title: &str, ) -> Result { - self.storage - .issue_storage() + self.ctx + .storage() + .issue_service + .issue_store() .save_issue(username, title) .await } pub async fn close_issue(&self, link: &str) -> Result<(), MegaError> { - self.storage.issue_storage().close_issue(link).await + self.ctx + .storage() + .issue_service + .issue_store() + .close_issue(link) + .await } pub async fn reopen_issue(&self, link: &str) -> Result<(), MegaError> { - self.storage.issue_storage().reopen_issue(link).await + self.ctx + .storage() + .issue_service + .issue_store() + .reopen_issue(link) + .await } pub async fn edit_issue_title(&self, link: &str, title: &str) -> Result<(), MegaError> { - self.storage.issue_storage().edit_title(link, title).await + self.ctx + .storage() + .issue_service + .issue_store() + .edit_title(link, title) + .await } pub async fn list_labels_by_page( @@ -76,8 +101,10 @@ impl MonoApiService { query: &str, ) -> Result<(Vec, u64), MegaError> { let (items, total) = self - .storage - .issue_storage() + .ctx + .storage() + .issue_service + .issue_store() .list_labels_by_page(pagination, query) .await?; Ok((items.into_iter().map(|m| m.into()).collect(), total)) @@ -90,15 +117,23 @@ impl MonoApiService { description: &str, ) -> Result { let model = self - .storage - .issue_storage() + .ctx + .storage() + .issue_service + .issue_store() .new_label(name, color, description) .await?; Ok(model.into()) } pub async fn get_label_by_id(&self, id: i64) -> Result, MegaError> { - let label = self.storage.issue_storage().get_label_by_id(id).await?; + let label = self + .ctx + .storage() + .issue_service + .issue_store() + .get_label_by_id(id) + .await?; Ok(label.map(|m| m.into())) } } diff --git a/ceres/src/application/api_service/mono/label_assignee.rs b/ceres/src/application/api_service/mono/label_assignee.rs index 19e62dae4..87ed54195 100644 --- a/ceres/src/application/api_service/mono/label_assignee.rs +++ b/ceres/src/application/api_service/mono/label_assignee.rs @@ -4,9 +4,9 @@ use callisto::sea_orm_active_enums::ConvTypeEnum; use common::errors::MegaError; use jupiter::model::common::LabelAssigneeParams; -use crate::application::api_service::mono::MonoApiService; +use super::context::IssueApplicationService; -impl MonoApiService { +impl IssueApplicationService { pub async fn update_item_labels( &self, username: &str, @@ -15,7 +15,7 @@ impl MonoApiService { label_ids: Vec, link: &str, ) -> Result<(), MegaError> { - let issue_storage = self.storage.issue_storage(); + let issue_storage = self.ctx.storage().issue_service.issue_store(); let old_labels = issue_storage.find_item_exist_labels(item_id).await?; let old_ids: HashSet = old_labels.iter().map(|l| l.label_id).collect(); @@ -34,8 +34,10 @@ impl MonoApiService { .await?; if !to_remove.is_empty() { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .add_conversation( link, username, @@ -46,8 +48,10 @@ impl MonoApiService { } if !to_add.is_empty() { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .add_conversation( link, username, @@ -68,7 +72,7 @@ impl MonoApiService { assignees: Vec, link: &str, ) -> Result<(), MegaError> { - let issue_storage = self.storage.issue_storage(); + let issue_storage = self.ctx.storage().issue_service.issue_store(); let old_models = issue_storage.find_item_exist_assignees(item_id).await?; let old_ids: HashSet = old_models.iter().map(|m| m.assignnee_id.clone()).collect(); @@ -87,8 +91,10 @@ impl MonoApiService { .await?; if !to_remove.is_empty() { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .add_conversation( link, username, @@ -99,8 +105,10 @@ impl MonoApiService { } if !to_add.is_empty() { - self.storage - .conversation_storage() + self.ctx + .storage() + .issue_service + .conversation_store() .add_conversation( link, username, diff --git a/ceres/src/application/api_service/mono/lfs.rs b/ceres/src/application/api_service/mono/lfs.rs new file mode 100644 index 000000000..523492528 --- /dev/null +++ b/ceres/src/application/api_service/mono/lfs.rs @@ -0,0 +1,60 @@ +use std::pin::Pin; + +use bytes::Bytes; +use common::errors::{GitLFSError, MegaError}; +use futures::Stream; + +use super::context::LfsApplicationService; +use crate::lfs::{ + handler, + lfs_structs::{ + BatchRequest, BatchResponse, Lock, LockList, LockListQuery, LockRequest, RequestObject, + UnlockRequest, VerifiableLockList, VerifiableLockRequest, + }, +}; + +impl LfsApplicationService { + pub async fn lfs_retrieve_lock(&self, query: LockListQuery) -> Result { + handler::lfs_retrieve_lock(self.ctx.storage().lfs_db_storage(), query).await + } + + pub async fn lfs_verify_lock( + &self, + req: VerifiableLockRequest, + ) -> Result { + handler::lfs_verify_lock(self.ctx.storage().lfs_db_storage(), req).await + } + + pub async fn lfs_create_lock(&self, req: LockRequest) -> Result { + handler::lfs_create_lock(self.ctx.storage().lfs_db_storage(), req).await + } + + pub async fn lfs_delete_lock(&self, id: &str, req: UnlockRequest) -> Result { + handler::lfs_delete_lock(self.ctx.storage().lfs_db_storage(), id, req).await + } + + pub async fn lfs_process_batch( + &self, + request: BatchRequest, + listen_addr: &str, + ) -> Result { + handler::lfs_process_batch(&self.ctx.storage().lfs_service, request, listen_addr).await + } + + pub async fn lfs_download_object( + &self, + oid: String, + ) -> Result> + Send>>, GitLFSError> { + let service = self.ctx.storage().lfs_service.clone(); + let stream = handler::lfs_download_object(service, oid).await?; + Ok(Box::pin(stream)) + } + + pub async fn lfs_upload_object( + &self, + req_obj: &RequestObject, + body_bytes: Vec, + ) -> Result<(), GitLFSError> { + handler::lfs_upload_object(&self.ctx.storage().lfs_service, req_obj, body_bytes).await + } +} diff --git a/ceres/src/application/api_service/mono/mod.rs b/ceres/src/application/api_service/mono/mod.rs index aceaf0e73..f830894d5 100644 --- a/ceres/src/application/api_service/mono/mod.rs +++ b/ceres/src/application/api_service/mono/mod.rs @@ -2,6 +2,7 @@ pub mod admin; pub mod app_services; +pub mod context; pub mod logic; pub mod service; pub mod types; @@ -15,11 +16,14 @@ pub mod commit; pub mod conversation; pub mod dynamic_sidebar; pub mod edit; +pub mod git_ops; pub mod gpg; pub mod issue; pub mod label_assignee; +pub mod lfs; pub mod note; pub mod reviewer; +pub mod stack; pub mod sync; pub mod tag; pub mod user; @@ -27,6 +31,14 @@ pub mod user; pub use admin::{ADMIN_FILE, EffectiveResourcePermission}; pub use app_services::MonoAppServices; pub use cl::merge_strategy as cl_merge; +pub use context::{ + AdminApplicationService, ClApplicationService, CodeReviewApplicationService, + ConversationApplicationService, IssueApplicationService, LfsApplicationService, + NoteApplicationService, ReviewerApplicationService, SidebarApplicationService, + UserApplicationService, WebhookApplicationService, +}; +pub use git_ops::GitOpsPort; pub use logic::MonoServiceLogic; -pub use service::MonoApiService; +pub use service::{GitApplicationService, MonoApiService}; +pub use stack::build_mono_stack; pub use types::{RefUpdate, TreeUpdateResult}; diff --git a/ceres/src/application/api_service/mono/note.rs b/ceres/src/application/api_service/mono/note.rs index f8cd5607e..ca42fe1d1 100644 --- a/ceres/src/application/api_service/mono/note.rs +++ b/ceres/src/application/api_service/mono/note.rs @@ -1,12 +1,13 @@ use common::errors::MegaError; -use super::service::MonoApiService; +use super::context::NoteApplicationService; use crate::model::note::NoteShowResponse; -impl MonoApiService { +impl NoteApplicationService { pub async fn get_note_sync_state(&self, id: i32) -> Result { let note = self - .storage + .ctx + .storage() .note_storage() .get_note_by_id(id.into()) .await? @@ -34,7 +35,8 @@ impl MonoApiService { description_schema_version: i32, ) -> Result<(), MegaError> { let note = self - .storage + .ctx + .storage() .note_storage() .get_note_by_id(id.into()) .await? @@ -47,7 +49,8 @@ impl MonoApiService { ))); } - self.storage + self.ctx + .storage() .note_storage() .update_note( id, diff --git a/ceres/src/application/api_service/mono/reviewer.rs b/ceres/src/application/api_service/mono/reviewer.rs index f27be7850..cca58eaa8 100644 --- a/ceres/src/application/api_service/mono/reviewer.rs +++ b/ceres/src/application/api_service/mono/reviewer.rs @@ -1,11 +1,12 @@ use common::errors::MegaError; -use super::service::MonoApiService; +use super::context::ReviewerApplicationService; use crate::model::change_list::{ReviewerInfo, ReviewersResponse}; -impl MonoApiService { +impl ReviewerApplicationService { pub async fn add_reviewers(&self, link: &str, reviewers: Vec) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .reviewer_storage() .add_reviewers(link, reviewers) .await @@ -16,7 +17,8 @@ impl MonoApiService { link: &str, reviewers: &[String], ) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .reviewer_storage() .remove_reviewers(link, reviewers) .await @@ -24,7 +26,8 @@ impl MonoApiService { pub async fn list_reviewers(&self, link: &str) -> Result { let reviewers = self - .storage + .ctx + .storage() .reviewer_storage() .list_reviewers(link) .await? @@ -44,14 +47,16 @@ impl MonoApiService { username: &str, approved: bool, ) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .reviewer_storage() .reviewer_change_state(link, username, approved) .await } pub async fn is_reviewer(&self, link: &str, username: &str) -> Result { - self.storage + self.ctx + .storage() .reviewer_storage() .is_reviewer(link, username) .await diff --git a/ceres/src/application/api_service/mono/service.rs b/ceres/src/application/api_service/mono/service.rs index bfef04dd9..28e715063 100644 --- a/ceres/src/application/api_service/mono/service.rs +++ b/ceres/src/application/api_service/mono/service.rs @@ -21,26 +21,63 @@ use git_internal::{ }; use jupiter::{storage::Storage, utils::converter::FromMegaModel}; -use super::logic::MonoServiceLogic; +use super::{context::ServiceContext, logic::MonoServiceLogic}; use crate::{ - application::api_service::{ApiHandler, cache::GitObjectCache, tree_ops}, + application::{ + api_service::{ApiHandler, cache::GitObjectCache, tree_ops}, + build_trigger::SharedBuildDispatch, + }, infra::TransportContext, model::git::{CreateEntryInfo, CreateEntryResult, EditFilePayload, EditFileResult}, }; +/// Git-domain API service (tags, commits, sync, buck upload, entry edit). +pub type GitApplicationService = MonoApiService; + #[derive(Clone)] pub struct MonoApiService { - pub storage: Storage, - pub git_object_cache: Arc, + ctx: ServiceContext, } impl MonoApiService { pub fn new(ctx: TransportContext) -> Self { Self { - storage: ctx.storage, - git_object_cache: ctx.git_object_cache, + ctx: ServiceContext::from_transport(ctx), } } + + pub fn storage(&self) -> &Storage { + self.ctx.storage() + } + + pub(crate) fn build_dispatch(&self) -> Option { + self.ctx.build_dispatch() + } + + pub fn git_object_cache(&self) -> Arc { + self.ctx.git_object_cache().clone() + } + + pub(crate) async fn trigger_build_for_cl( + &self, + editor: &crate::application::code_edit::on_edit::OneditCodeEdit, + cl: &callisto::mega_cl::Model, + username: &str, + ) -> Result<(), GitError> { + let Some(build_dispatch) = self.build_dispatch() else { + return Ok(()); + }; + editor + .trigger_build_and_check( + self.storage().clone(), + self.git_object_cache(), + build_dispatch, + cl, + username, + ) + .await?; + Ok(()) + } } impl From for MonoApiService { @@ -52,15 +89,15 @@ impl From for MonoApiService { #[async_trait] impl ApiHandler for MonoApiService { fn get_context(&self) -> Storage { - self.storage.clone() + self.storage().clone() } fn object_cache(&self) -> &GitObjectCache { - &self.git_object_cache + self.ctx.git_object_cache() } async fn get_root_commit(&self) -> Result { - let storage = self.storage.mono_storage(); + let storage = self.storage().mono_storage(); let refs = storage.get_main_ref("/").await.unwrap().unwrap(); self.get_commit_by_hash(&refs.ref_commit_hash).await } @@ -84,7 +121,7 @@ impl ApiHandler for MonoApiService { let refs = refs.unwrap_or("").trim(); if refs.is_empty() { - let storage = self.storage.mono_storage(); + let storage = self.storage().mono_storage(); let refs = storage.get_main_ref("/").await.unwrap().unwrap(); return self.get_tree_by_hash(&refs.ref_tree_hash).await; } @@ -107,7 +144,7 @@ impl ApiHandler for MonoApiService { async fn get_tree_by_hash(&self, hash: &str) -> Result { let model = self - .storage + .storage() .mono_storage() .get_tree_by_hash(hash) .await? @@ -117,7 +154,7 @@ impl ApiHandler for MonoApiService { async fn get_commit_by_hash(&self, hash: &str) -> Result { let model = self - .storage + .storage() .mono_storage() .get_commit_by_hash(hash) .await? @@ -134,7 +171,7 @@ impl ApiHandler for MonoApiService { Some(tree) => { let mut item_to_commit = HashMap::new(); - let storage = self.storage.mono_storage(); + let storage = self.storage().mono_storage(); let tree_hashes = tree .tree_items .iter() @@ -182,7 +219,7 @@ impl ApiHandler for MonoApiService { async fn get_commits_by_hashes(&self, c_hashes: Vec) -> Result, GitError> { let commits = self - .storage + .storage() .mono_storage() .get_commits_by_hashes(&c_hashes) .await diff --git a/ceres/src/application/api_service/mono/stack.rs b/ceres/src/application/api_service/mono/stack.rs new file mode 100644 index 000000000..5d041f5ac --- /dev/null +++ b/ceres/src/application/api_service/mono/stack.rs @@ -0,0 +1,35 @@ +//! Shared construction of the Git + CL application service pair. + +use std::sync::Arc; + +use jupiter::storage::Storage; + +use super::{ + context::{AdminApplicationService, ClApplicationService, ServiceContext}, + service::MonoApiService, +}; +use crate::{ + application::{api_service::cache::GitObjectCache, build_trigger::SharedBuildDispatch}, + infra::TransportContext, +}; + +/// Build the canonical `MonoApiService` + `ClApplicationService` pair for a storage context. +pub fn build_mono_stack( + storage: Storage, + git_object_cache: Arc, + build_dispatch: Option, +) -> ( + MonoApiService, + ClApplicationService, + AdminApplicationService, +) { + let ctx = ServiceContext::new(storage, git_object_cache, build_dispatch); + let git = MonoApiService::new(TransportContext { + storage: ctx.storage().clone(), + git_object_cache: ctx.git_object_cache().clone(), + build_dispatch: ctx.build_dispatch(), + }); + let admin = AdminApplicationService::new(ctx.clone()); + let cl = ClApplicationService::new(ctx, git.clone(), admin.clone()); + (git, cl, admin) +} diff --git a/ceres/src/application/api_service/mono/sync.rs b/ceres/src/application/api_service/mono/sync.rs index 6665a3f09..50a794b53 100644 --- a/ceres/src/application/api_service/mono/sync.rs +++ b/ceres/src/application/api_service/mono/sync.rs @@ -29,9 +29,9 @@ impl MonoApiService { const ROOT_LOCK_TTL_MS: u64 = 30_000; let path_buf = PathBuf::from(path); - let storage = self.storage.mono_storage(); + let storage = self.storage().mono_storage(); let redlock = Arc::new(RedLock::new( - self.git_object_cache.connection.clone(), + self.git_object_cache().connection.clone(), ROOT_LOCK_KEY.to_string(), ROOT_LOCK_TTL_MS, )); @@ -60,7 +60,7 @@ impl MonoApiService { &commit_msg, ); - let txn = self.storage.begin_db_transaction().await?; + let txn = self.storage().begin_db_transaction().await?; match storage .attach_to_monorepo_parent_in_txn( &txn, @@ -109,9 +109,9 @@ impl MonoApiService { repo_path_str: &str, ref_name: &str, ) -> Result { - let import_dir = self.storage.config().monorepo.import_dir.clone(); + let import_dir = self.storage().config().monorepo.import_dir.clone(); if PathBuf::from(repo_path_str).starts_with(&import_dir) { - let storage = self.storage.git_db_storage(); + let storage = self.storage().git_db_storage(); match storage.find_git_repo_exact_match(repo_path_str).await? { Some(repo_model) => Ok(storage .get_ref(repo_model.id) @@ -123,7 +123,7 @@ impl MonoApiService { None => Ok(ZERO_ID.to_string()), } } else { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); if let Some(r) = mono_storage .get_ref_at_path(repo_path_str, ref_name) .await? @@ -145,6 +145,7 @@ impl MonoApiService { pub async fn sync_third_party_repo( &self, + transport: &TransportRuntime, owner: &str, repo: &str, mega_path: PathBuf, @@ -159,7 +160,7 @@ impl MonoApiService { let url = format!("https://github.com/{owner}/{repo}.git"); let remote_client = ThirdPartyClient::new(&url); - let import_dir = self.storage.config().monorepo.import_dir.clone(); + let import_dir = self.storage().config().monorepo.import_dir.clone(); let fetch_depth = if mega_path.starts_with(&import_dir) { None } else { @@ -193,7 +194,7 @@ impl MonoApiService { ref_hash.clone(), ref_name.clone(), )]; - let state = TransportRuntime::new(self.storage.clone(), self.git_object_cache.clone()); + let state = transport.clone(); let bytes = protocol .git_receive_pack_stream( &state, @@ -227,7 +228,7 @@ impl MonoApiService { let path = base_path.join(&item.name); if item.is_tree() { let child = self - .storage + .storage() .mono_storage() .get_tree_by_hash(&item.id.to_string()) .await? diff --git a/ceres/src/application/api_service/mono/tag.rs b/ceres/src/application/api_service/mono/tag.rs index da9017219..2d30987a7 100644 --- a/ceres/src/application/api_service/mono/tag.rs +++ b/ceres/src/application/api_service/mono/tag.rs @@ -40,7 +40,7 @@ impl MonoApiService { message: Option, ) -> Result { validate_tag_name(&name)?; - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); let tagger_info = format_tagger_info(tagger_name, tagger_email); self.validate_target_commit_mono(target.as_ref()).await?; @@ -75,7 +75,7 @@ impl MonoApiService { repo_path: Option, pagination: Pagination, ) -> Result<(Vec, u64), GitError> { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); let (annotated_page, annotated_total) = match mono_storage.get_tags_by_page(pagination.clone()).await { Ok(v) => v, @@ -122,7 +122,7 @@ impl MonoApiService { _repo_path: Option, name: String, ) -> Result, GitError> { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); match mono_storage.get_tag_by_name(&name).await { Ok(Some(tag)) => return Ok(Some(self.tag_model_to_info(tag))), Ok(None) => {} @@ -149,7 +149,7 @@ impl MonoApiService { _repo_path: Option, name: String, ) -> Result<(), GitError> { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); match mono_storage.get_tag_by_name(&name).await { Ok(Some(_tag)) => { let full_ref = tags_full_ref(&name); @@ -198,7 +198,7 @@ impl MonoApiService { message: Option, full_ref: String, ) -> Result { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); let (tag_id_hex, object_id) = build_git_internal_tag(name.clone(), target, tagger_info.clone(), message.clone())?; @@ -247,7 +247,7 @@ impl MonoApiService { tagger_info: String, full_ref: String, ) -> Result { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); let path_str = repo_path.unwrap_or_else(|| "/".to_string()); let object_id = target.unwrap_or_default(); @@ -285,7 +285,7 @@ impl MonoApiService { } async fn resolve_tree_hash_for_commit(&self, commit_id: &str) -> Result { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); match mono_storage.get_commit_by_hash(commit_id).await { Ok(Some(commit_model)) => Ok(commit_model.tree.clone()), Ok(None) => { @@ -307,7 +307,7 @@ impl MonoApiService { } async fn validate_target_commit_mono(&self, target: Option<&String>) -> Result<(), GitError> { - let mono_storage = self.storage.mono_storage(); + let mono_storage = self.storage().mono_storage(); if let Some(t) = target { match mono_storage.get_commit_by_hash(t).await { Ok(commit_opt) => { diff --git a/ceres/src/application/api_service/mono/user.rs b/ceres/src/application/api_service/mono/user.rs index a1ce4f984..450b0d454 100644 --- a/ceres/src/application/api_service/mono/user.rs +++ b/ceres/src/application/api_service/mono/user.rs @@ -1,6 +1,6 @@ use common::errors::MegaError; -use super::service::MonoApiService; +use super::context::UserApplicationService; use crate::model::{ notification::{ NotificationEventTypeInfo, UpdateUserNotificationConfig, UserNotificationConfig, @@ -9,7 +9,7 @@ use crate::model::{ user::{ListSSHKey, ListToken}, }; -impl MonoApiService { +impl UserApplicationService { pub async fn save_ssh_key( &self, username: String, @@ -17,37 +17,54 @@ impl MonoApiService { ssh_key: &str, fingerprint: &str, ) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .user_storage() .save_ssh_key(username, title, ssh_key, fingerprint) .await } pub async fn delete_ssh_key(&self, username: String, key_id: i64) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .user_storage() .delete_ssh_key(username, key_id) .await } pub async fn list_user_ssh_keys(&self, username: String) -> Result, MegaError> { - let keys = self.storage.user_storage().list_user_ssh(username).await?; + let keys = self + .ctx + .storage() + .user_storage() + .list_user_ssh(username) + .await?; Ok(keys.into_iter().map(|k| k.into()).collect()) } pub async fn generate_user_token(&self, username: String) -> Result { - self.storage.user_storage().generate_token(username).await + self.ctx + .storage() + .user_storage() + .generate_token(username) + .await } pub async fn delete_user_token(&self, username: String, key_id: i64) -> Result<(), MegaError> { - self.storage + self.ctx + .storage() .user_storage() .delete_token(username, key_id) .await } pub async fn list_user_tokens(&self, username: String) -> Result, MegaError> { - let tokens = self.storage.user_storage().list_token(username).await?; + let tokens = self + .ctx + .storage() + .user_storage() + .list_token(username) + .await?; Ok(tokens.into_iter().map(|t| t.into()).collect()) } @@ -55,7 +72,8 @@ impl MonoApiService { &self, ) -> Result, MegaError> { Ok(self - .storage + .ctx + .storage() .notification_storage() .list_event_types() .await? @@ -75,7 +93,7 @@ impl MonoApiService { username: &str, email: &str, ) -> Result { - let stg = self.storage.notification_storage(); + let stg = self.ctx.storage().notification_storage(); stg.upsert_user_settings(username, email).await?; let settings = stg @@ -107,7 +125,7 @@ impl MonoApiService { email: &str, payload: UpdateUserNotificationConfig, ) -> Result<(), MegaError> { - let stg = self.storage.notification_storage(); + let stg = self.ctx.storage().notification_storage(); stg.upsert_user_settings(username, email).await?; if let Some(enabled) = payload.enabled { @@ -129,6 +147,10 @@ impl MonoApiService { &self, token: &str, ) -> Result, MegaError> { - self.storage.bots_storage().find_bot_by_token(token).await + self.ctx + .storage() + .bots_storage() + .find_bot_by_token(token) + .await } } diff --git a/ceres/src/application/build_trigger/buck_upload_handler.rs b/ceres/src/application/build_trigger/buck_upload_handler.rs index 5395e0cdf..d6a068cba 100644 --- a/ceres/src/application/build_trigger/buck_upload_handler.rs +++ b/ceres/src/application/build_trigger/buck_upload_handler.rs @@ -4,15 +4,11 @@ use std::sync::Arc; use async_trait::async_trait; use chrono::Utc; use common::errors::MegaError; -use jupiter::storage::Storage; use super::changes_calculator::MonoChangesCalculator; -use crate::application::{ - api_service::{cache::GitObjectCache, mono::MonoApiService}, - build_trigger::{ - BuckFileUploadPayload, BuildTrigger, BuildTriggerPayload, BuildTriggerType, TriggerContext, - TriggerHandler, - }, +use crate::application::build_trigger::{ + BuckFileUploadPayload, BuildTrigger, BuildTriggerPayload, BuildTriggerType, ChangesPort, + TriggerContext, TriggerHandler, }; /// Handler for Buck file upload triggers. @@ -21,12 +17,9 @@ pub struct BuckFileUploadHandler { } impl BuckFileUploadHandler { - pub fn new(storage: Storage, git_object_cache: Arc) -> Self { + pub fn new(changes_port: Arc) -> Self { Self { - changes_calculator: MonoChangesCalculator::new(MonoApiService { - storage, - git_object_cache, - }), + changes_calculator: MonoChangesCalculator::new(changes_port), } } } diff --git a/ceres/src/application/build_trigger/changes_calculator.rs b/ceres/src/application/build_trigger/changes_calculator.rs index ef874d8e4..d8aa97a28 100644 --- a/ceres/src/application/build_trigger/changes_calculator.rs +++ b/ceres/src/application/build_trigger/changes_calculator.rs @@ -1,14 +1,12 @@ use std::path::{Component, Path, PathBuf}; +pub use std::sync::Arc; -pub use api_model::buck2::{status::Status, types::ProjectRelativePath}; +use api_model::buck2::{status::Status, types::ProjectRelativePath}; use common::errors::MegaError; use git_internal::hash::ObjectHash; use super::changes_port::ChangesPort; -use crate::{ - application::{api_service::mono::MonoApiService, build_trigger::TriggerContext}, - model::change_list::ClDiffFile, -}; +use crate::{application::build_trigger::TriggerContext, model::change_list::ClDiffFile}; fn is_safe_normalized_path(path: &str) -> bool { path.is_empty() @@ -187,8 +185,8 @@ pub struct ChangesCalculator { port: P, } -/// Changes calculator backed by [`MonoApiService`]. -pub type MonoChangesCalculator = ChangesCalculator; +/// Changes calculator backed by a shared [`ChangesPort`]. +pub type MonoChangesCalculator = ChangesCalculator>; impl ChangesCalculator

{ pub fn new(port: P) -> Self { diff --git a/ceres/src/application/build_trigger/changes_port.rs b/ceres/src/application/build_trigger/changes_port.rs index 7a600e0c6..6040c320b 100644 --- a/ceres/src/application/build_trigger/changes_port.rs +++ b/ceres/src/application/build_trigger/changes_port.rs @@ -1,10 +1,10 @@ -use std::path::PathBuf; +use std::{path::PathBuf, sync::Arc}; use async_trait::async_trait; use common::errors::MegaError; use git_internal::hash::ObjectHash; -use crate::{application::api_service::mono::MonoApiService, model::change_list::ClDiffFile}; +use crate::{application::api_service::mono::ClApplicationService, model::change_list::ClDiffFile}; /// Port for computing CL file diffs used by build trigger handlers. #[async_trait] @@ -22,12 +22,12 @@ pub trait ChangesPort: Send + Sync { } #[async_trait] -impl ChangesPort for MonoApiService { +impl ChangesPort for ClApplicationService { async fn get_commit_blobs( &self, commit_hash: &str, ) -> Result, MegaError> { - MonoApiService::get_commit_blobs(self, commit_hash).await + self.get_commit_blobs(commit_hash).await } async fn cl_files_list( @@ -35,6 +35,42 @@ impl ChangesPort for MonoApiService { old_files: Vec<(PathBuf, ObjectHash)>, new_files: Vec<(PathBuf, ObjectHash)>, ) -> Result, MegaError> { - MonoApiService::cl_files_list(self, old_files, new_files).await + self.cl_files_list(old_files, new_files).await + } +} + +#[async_trait] +impl ChangesPort for Arc { + async fn get_commit_blobs( + &self, + commit_hash: &str, + ) -> Result, MegaError> { + self.as_ref().get_commit_blobs(commit_hash).await + } + + async fn cl_files_list( + &self, + old_files: Vec<(PathBuf, ObjectHash)>, + new_files: Vec<(PathBuf, ObjectHash)>, + ) -> Result, MegaError> { + self.as_ref().cl_files_list(old_files, new_files).await + } +} + +#[async_trait] +impl ChangesPort for Arc { + async fn get_commit_blobs( + &self, + commit_hash: &str, + ) -> Result, MegaError> { + self.as_ref().get_commit_blobs(commit_hash).await + } + + async fn cl_files_list( + &self, + old_files: Vec<(PathBuf, ObjectHash)>, + new_files: Vec<(PathBuf, ObjectHash)>, + ) -> Result, MegaError> { + self.as_ref().cl_files_list(old_files, new_files).await } } diff --git a/ceres/src/application/build_trigger/dispatcher.rs b/ceres/src/application/build_trigger/dispatcher.rs index 7bea91e20..cfa2f195f 100644 --- a/ceres/src/application/build_trigger/dispatcher.rs +++ b/ceres/src/application/build_trigger/dispatcher.rs @@ -1,16 +1,13 @@ -use std::sync::Arc; - use api_model::buck2::{api::TaskBuildRequest, status::Status, types::ProjectRelativePath}; use common::errors::MegaError; use jupiter::storage::Storage; -use orion_client::OrionBuildClient; -use crate::application::build_trigger::{BuildTrigger, BuildTriggerPayload}; +use crate::application::build_trigger::{BuildTrigger, BuildTriggerPayload, SharedBuildDispatch}; -/// Handles dispatching build triggers to the build execution layer (Orion). +/// Handles dispatching build triggers to the build execution layer. pub struct BuildDispatcher { storage: Storage, - orion_client: Arc, + build_dispatch: SharedBuildDispatch, } fn payload_to_task_request(payload: &BuildTriggerPayload) -> Result { @@ -40,10 +37,10 @@ fn payload_to_task_request(payload: &BuildTriggerPayload) -> Result) -> Self { + pub fn new(storage: Storage, build_dispatch: SharedBuildDispatch) -> Self { Self { storage, - orion_client, + build_dispatch, } } @@ -54,12 +51,12 @@ impl BuildDispatcher { })?; // Determine task_id based on whether build system is enabled - let task_id: Option = if self.orion_client.enable_build() { + let task_id: Option = if self.build_dispatch.enable_build() { let req = payload_to_task_request(&trigger.payload)?; - let task_id_str = self.orion_client.on_post_receive(req).await.map_err(|e| { - tracing::error!("Failed to dispatch build to Orion: {}", e); - MegaError::Other(format!("Failed to dispatch build to Orion: {}", e)) + let task_id_str = self.build_dispatch.dispatch_build(req).await.map_err(|e| { + tracing::error!("Failed to dispatch build: {}", e); + e })?; let task_uuid = uuid::Uuid::parse_str(&task_id_str).map_err(|e| { @@ -69,7 +66,7 @@ impl BuildDispatcher { Some(task_uuid) } else { - tracing::info!("BuildDispatcher: Build system disabled, skipping Orion call"); + tracing::info!("BuildDispatcher: Build system disabled, skipping dispatch"); None }; @@ -100,14 +97,38 @@ mod tests { use std::{sync::Arc, time::Duration}; use api_model::buck2::ws::WSMessage; + use async_trait::async_trait; use axum::{Json, Router, extract::State, routing::post}; use chrono::Utc; use common::config::BuildConfig; + use orion_client::OrionBuildClient; use tempfile::tempdir; use tokio::{net::TcpListener, sync::mpsc}; use super::*; - use crate::application::build_trigger::{BuildTriggerType, TriggerSource, WebEditPayload}; + use crate::application::build_trigger::{ + BuildDispatchPort, BuildTriggerType, TriggerSource, WebEditPayload, + }; + + struct TestOrionDispatch(Arc); + + #[async_trait] + impl BuildDispatchPort for TestOrionDispatch { + fn enable_build(&self) -> bool { + self.0.enable_build() + } + + async fn dispatch_build(&self, req: TaskBuildRequest) -> Result { + self.0 + .on_post_receive(req) + .await + .map_err(|e| MegaError::Other(e.to_string())) + } + } + + fn test_dispatch(client: Arc) -> SharedBuildDispatch { + Arc::new(TestOrionDispatch(client)) + } #[derive(Clone)] struct MockOrionState { @@ -187,7 +208,7 @@ mod tests { orion_server: orion_base, ..Default::default() })); - let dispatcher = BuildDispatcher::new(storage.clone(), orion_client); + let dispatcher = BuildDispatcher::new(storage.clone(), test_dispatch(orion_client)); let trigger = web_edit_trigger(repo, cl_link, changes.clone()); let trigger_id = dispatcher @@ -262,7 +283,7 @@ mod tests { orion_server: "http://127.0.0.1:0".to_string(), ..Default::default() })); - let dispatcher = BuildDispatcher::new(storage.clone(), orion_client); + let dispatcher = BuildDispatcher::new(storage.clone(), test_dispatch(orion_client)); let trigger = web_edit_trigger( "/project/buck2_test", diff --git a/ceres/src/application/build_trigger/git_push_handler.rs b/ceres/src/application/build_trigger/git_push_handler.rs index 8c7836340..87f55cdfa 100644 --- a/ceres/src/application/build_trigger/git_push_handler.rs +++ b/ceres/src/application/build_trigger/git_push_handler.rs @@ -3,15 +3,11 @@ use std::sync::Arc; use async_trait::async_trait; use chrono::Utc; use common::errors::MegaError; -use jupiter::storage::Storage; use super::changes_calculator::MonoChangesCalculator; -use crate::application::{ - api_service::{cache::GitObjectCache, mono::MonoApiService}, - build_trigger::{ - BuildTrigger, BuildTriggerPayload, BuildTriggerType, GitPushPayload, TriggerContext, - TriggerHandler, - }, +use crate::application::build_trigger::{ + BuildTrigger, BuildTriggerPayload, BuildTriggerType, ChangesPort, GitPushPayload, + TriggerContext, TriggerHandler, }; /// Handler for Git push triggers. @@ -20,12 +16,9 @@ pub struct GitPushHandler { } impl GitPushHandler { - pub fn new(storage: Storage, git_object_cache: Arc) -> Self { + pub fn new(changes_port: Arc) -> Self { Self { - changes_calculator: MonoChangesCalculator::new(MonoApiService { - storage, - git_object_cache, - }), + changes_calculator: MonoChangesCalculator::new(changes_port), } } } diff --git a/ceres/src/application/build_trigger/manual_handler.rs b/ceres/src/application/build_trigger/manual_handler.rs index 942132299..fa8173485 100644 --- a/ceres/src/application/build_trigger/manual_handler.rs +++ b/ceres/src/application/build_trigger/manual_handler.rs @@ -6,12 +6,9 @@ use common::errors::MegaError; use jupiter::storage::Storage; use super::changes_calculator::MonoChangesCalculator; -use crate::application::{ - api_service::{cache::GitObjectCache, mono::MonoApiService}, - build_trigger::{ - BuildTrigger, BuildTriggerPayload, BuildTriggerType, ManualPayload, TriggerContext, - TriggerHandler, - }, +use crate::application::build_trigger::{ + BuildTrigger, BuildTriggerPayload, BuildTriggerType, ChangesPort, ManualPayload, + TriggerContext, TriggerHandler, }; /// Handler for manual build triggers. @@ -21,13 +18,10 @@ pub struct ManualHandler { } impl ManualHandler { - pub fn new(storage: Storage, git_object_cache: Arc) -> Self { + pub fn new(storage: Storage, changes_port: Arc) -> Self { Self { storage: storage.clone(), - changes_calculator: MonoChangesCalculator::new(MonoApiService { - storage: storage.clone(), - git_object_cache, - }), + changes_calculator: MonoChangesCalculator::new(changes_port), } } diff --git a/ceres/src/application/build_trigger/mod.rs b/ceres/src/application/build_trigger/mod.rs index 69a12c788..b92794826 100644 --- a/ceres/src/application/build_trigger/mod.rs +++ b/ceres/src/application/build_trigger/mod.rs @@ -3,9 +3,6 @@ use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use common::errors::MegaError; use jupiter::storage::Storage; -use orion_client::OrionBuildClient; - -use crate::application::api_service::cache::GitObjectCache; mod buck_upload_handler; mod changes_calculator; @@ -14,12 +11,15 @@ mod dispatcher; mod git_push_handler; mod manual_handler; mod model; +mod port; mod ref_resolver; mod retry_handler; mod web_edit_handler; // Export all models from the single model file +pub use changes_port::ChangesPort; pub use model::*; +pub use port::{BuildDispatchPort, SharedBuildDispatch}; pub use ref_resolver::{RefResolver, RefType, ResolvedRef}; pub mod service; use buck_upload_handler::BuckFileUploadHandler; @@ -50,38 +50,25 @@ impl TriggerRegistry { /// Create a new TriggerRegistry with all handlers registered. pub fn new( storage: Storage, - git_object_cache: Arc, - orion_client: Arc, + build_dispatch: SharedBuildDispatch, + changes_port: Arc, ) -> Self { let mut registry = Self { handlers: HashMap::new(), - dispatcher: Arc::new(BuildDispatcher::new(storage.clone(), orion_client)), + dispatcher: Arc::new(BuildDispatcher::new( + storage.clone(), + build_dispatch.clone(), + )), }; - // Register core handlers (Git Push, Manual, Retry) - registry.register(Box::new(GitPushHandler::new( - storage.clone(), - git_object_cache.clone(), - ))); + registry.register(Box::new(GitPushHandler::new(changes_port.clone()))); registry.register(Box::new(ManualHandler::new( storage.clone(), - git_object_cache.clone(), - ))); - registry.register(Box::new(RetryHandler::new( - storage.clone(), - git_object_cache.clone(), + changes_port.clone(), ))); - registry.register(Box::new(WebEditHandler::new( - storage.clone(), - git_object_cache.clone(), - ))); - registry.register(Box::new(BuckFileUploadHandler::new( - storage.clone(), - git_object_cache.clone(), - ))); - - // Note: Webhook and Schedule handlers are reserved for future implementation - // but not registered yet as they are not part of the current requirements + registry.register(Box::new(RetryHandler::new(changes_port.clone()))); + registry.register(Box::new(WebEditHandler::new(changes_port.clone()))); + registry.register(Box::new(BuckFileUploadHandler::new(changes_port))); registry } @@ -104,7 +91,6 @@ impl TriggerRegistry { &context.commit_hash[..8.min(context.commit_hash.len())] ); - // Find the appropriate handler let handler = self.handlers.get(&context.trigger_type).ok_or_else(|| { MegaError::Other(format!( "No handler for trigger type: {:?}", @@ -112,10 +98,8 @@ impl TriggerRegistry { )) })?; - // Handle the trigger let trigger = handler.handle(&context).await?; - // Dispatch the build and return the trigger ID let trigger_id = self.dispatcher.dispatch(trigger).await?; tracing::info!( diff --git a/ceres/src/application/build_trigger/port.rs b/ceres/src/application/build_trigger/port.rs new file mode 100644 index 000000000..1ad4c8b64 --- /dev/null +++ b/ceres/src/application/build_trigger/port.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use api_model::buck2::api::TaskBuildRequest; +use async_trait::async_trait; +use common::errors::MegaError; + +/// Dispatches build tasks to an external execution layer (e.g. Orion). +#[async_trait] +pub trait BuildDispatchPort: Send + Sync { + fn enable_build(&self) -> bool; + + async fn dispatch_build(&self, req: TaskBuildRequest) -> Result; +} + +pub type SharedBuildDispatch = Arc; diff --git a/ceres/src/application/build_trigger/retry_handler.rs b/ceres/src/application/build_trigger/retry_handler.rs index b4ffb782b..e45713612 100644 --- a/ceres/src/application/build_trigger/retry_handler.rs +++ b/ceres/src/application/build_trigger/retry_handler.rs @@ -3,15 +3,11 @@ use std::sync::Arc; use async_trait::async_trait; use chrono::Utc; use common::errors::MegaError; -use jupiter::storage::Storage; use super::changes_calculator::MonoChangesCalculator; -use crate::application::{ - api_service::{cache::GitObjectCache, mono::MonoApiService}, - build_trigger::{ - BuildTrigger, BuildTriggerPayload, BuildTriggerType, RetryPayload, TriggerContext, - TriggerHandler, - }, +use crate::application::build_trigger::{ + BuildTrigger, BuildTriggerPayload, BuildTriggerType, ChangesPort, RetryPayload, TriggerContext, + TriggerHandler, }; /// Handler for retry build triggers. @@ -20,12 +16,9 @@ pub struct RetryHandler { } impl RetryHandler { - pub fn new(storage: Storage, git_object_cache: Arc) -> Self { + pub fn new(changes_port: Arc) -> Self { Self { - changes_calculator: MonoChangesCalculator::new(MonoApiService { - storage, - git_object_cache, - }), + changes_calculator: MonoChangesCalculator::new(changes_port), } } } diff --git a/ceres/src/application/build_trigger/service.rs b/ceres/src/application/build_trigger/service.rs index f48bcad01..35855e88f 100644 --- a/ceres/src/application/build_trigger/service.rs +++ b/ceres/src/application/build_trigger/service.rs @@ -30,14 +30,13 @@ use std::sync::Arc; use api_model::common::Pagination; use common::errors::MegaError; use jupiter::storage::Storage; -use orion_client::OrionBuildClient; use super::model::{ BuildParams, GitPushEvent, ListTriggersParams, TriggerContext, TriggerRecord, TriggerResponse, }; use crate::application::{ api_service::cache::GitObjectCache, - build_trigger::{RefResolver, TriggerRegistry}, + build_trigger::{ChangesPort, RefResolver, SharedBuildDispatch, TriggerRegistry}, code_edit::utils as edit_utils, }; @@ -45,7 +44,7 @@ use crate::application::{ pub struct BuildTriggerService { storage: Storage, registry: TriggerRegistry, - orion_client: Arc, + build_dispatch: SharedBuildDispatch, } impl BuildTriggerService { @@ -61,20 +60,19 @@ impl BuildTriggerService { pub fn new( storage: Storage, - git_object_cache: Arc, - orion_client: Arc, + build_dispatch: SharedBuildDispatch, + changes_port: Arc, ) -> Self { - let registry = - TriggerRegistry::new(storage.clone(), git_object_cache, orion_client.clone()); + let registry = TriggerRegistry::new(storage.clone(), build_dispatch.clone(), changes_port); Self { storage, registry, - orion_client, + build_dispatch, } } pub fn is_enabled(&self) -> bool { - self.orion_client.enable_build() + self.build_dispatch.enable_build() } fn check_build_enabled(&self) -> Result<(), MegaError> { @@ -108,14 +106,23 @@ impl BuildTriggerService { pub async fn build_by_context( storage: Storage, - git_cache: Arc, - orion_client: Arc, + git_object_cache: Arc, + build_dispatch: SharedBuildDispatch, context: TriggerContext, ) -> Result, MegaError> { - if !orion_client.enable_build() { + if !build_dispatch.enable_build() { return Ok(None); } - let registry = TriggerRegistry::new(storage, git_cache, orion_client); + let (_, cl, _) = crate::application::api_service::mono::stack::build_mono_stack( + storage.clone(), + git_object_cache, + Some(build_dispatch.clone()), + ); + let registry = TriggerRegistry::new( + storage, + build_dispatch, + Arc::new(cl) as Arc, + ); let id = registry.trigger_build(context).await?; Ok(Some(id)) diff --git a/ceres/src/application/build_trigger/web_edit_handler.rs b/ceres/src/application/build_trigger/web_edit_handler.rs index d955f2d4c..d8e2cfcde 100644 --- a/ceres/src/application/build_trigger/web_edit_handler.rs +++ b/ceres/src/application/build_trigger/web_edit_handler.rs @@ -3,15 +3,11 @@ use std::sync::Arc; use async_trait::async_trait; use chrono::Utc; use common::errors::MegaError; -use jupiter::storage::Storage; use super::changes_calculator::MonoChangesCalculator; -use crate::application::{ - api_service::{cache::GitObjectCache, mono::MonoApiService}, - build_trigger::{ - BuildTrigger, BuildTriggerPayload, BuildTriggerType, TriggerContext, TriggerHandler, - WebEditPayload, - }, +use crate::application::build_trigger::{ + BuildTrigger, BuildTriggerPayload, BuildTriggerType, ChangesPort, TriggerContext, + TriggerHandler, WebEditPayload, }; fn fallback_cl_link(commit_hash: &str, now_millis: i64) -> String { @@ -41,12 +37,9 @@ pub struct WebEditHandler { } impl WebEditHandler { - pub fn new(storage: Storage, git_object_cache: Arc) -> Self { + pub fn new(changes_port: Arc) -> Self { Self { - changes_calculator: MonoChangesCalculator::new(MonoApiService { - storage, - git_object_cache, - }), + changes_calculator: MonoChangesCalculator::new(changes_port), } } } diff --git a/ceres/src/application/code_edit/model.rs b/ceres/src/application/code_edit/model.rs index 8498186b3..6d0f317d6 100644 --- a/ceres/src/application/code_edit/model.rs +++ b/ceres/src/application/code_edit/model.rs @@ -8,12 +8,11 @@ use jupiter::{ storage::{Storage, mono_storage::MonoStorage}, utils::converter::FromMegaModel, }; -use orion_client::OrionBuildClient; use crate::{ application::{ api_service::{ApiHandler, cache::GitObjectCache}, - build_trigger::{BuildTriggerService, TriggerContext}, + build_trigger::{BuildTriggerService, SharedBuildDispatch, TriggerContext}, code_edit::{model, utils as edit_utils}, webhook::{WebhookEvent, dispatch_cl_webhook}, }, @@ -76,7 +75,7 @@ pub(crate) trait TriggerContextBuilder { &self, storage: Storage, git_cache: Arc, - orion_client: Arc, + build_dispatch: SharedBuildDispatch, cl: &mega_cl::Model, username: &str, ) -> Result<(), MegaError> { @@ -84,7 +83,7 @@ pub(crate) trait TriggerContextBuilder { let username = username.to_string(); let context = self.get_context(&cl_model, &username).await?; tokio::spawn(async move { - BuildTriggerService::build_by_context(storage, git_cache, orion_client, context).await + BuildTriggerService::build_by_context(storage, git_cache, build_dispatch, context).await }); Ok(()) } @@ -365,12 +364,12 @@ impl< &self, storage: Storage, git_cache: Arc, - orion_client: Arc, + build_dispatch: SharedBuildDispatch, cl: &mega_cl::Model, username: &str, ) -> Result<(), MegaError> { self.builder - .trigger_build(storage, git_cache, orion_client, cl, username) + .trigger_build(storage, git_cache, build_dispatch, cl, username) .await } @@ -395,11 +394,11 @@ impl< &self, storage: Storage, git_cache: Arc, - orion_client: Arc, + build_dispatch: SharedBuildDispatch, cl: &mega_cl::Model, username: &str, ) -> Result<(), MegaError> { - self.trigger_build(storage.clone(), git_cache, orion_client, cl, username) + self.trigger_build(storage.clone(), git_cache, build_dispatch, cl, username) .await?; self.trigger_check(storage, username, cl).await?; Ok(()) diff --git a/ceres/src/application/code_edit/on_edit.rs b/ceres/src/application/code_edit/on_edit.rs index e9ecb2cf3..089624f92 100644 --- a/ceres/src/application/code_edit/on_edit.rs +++ b/ceres/src/application/code_edit/on_edit.rs @@ -86,7 +86,7 @@ impl model::TriggerContextBuilder for OneditTrigerBuilder { &self, storage: Storage, git_cache: std::sync::Arc, - orion_client: std::sync::Arc, + build_dispatch: std::sync::Arc, cl: &mega_cl::Model, username: &str, ) -> Result<(), MegaError> { @@ -114,7 +114,7 @@ impl model::TriggerContextBuilder for OneditTrigerBuilder { Some(cl_model.id), Some(username), ); - BuildTriggerService::build_by_context(storage, git_cache, orion_client, context).await + BuildTriggerService::build_by_context(storage, git_cache, build_dispatch, context).await }); Ok(()) } diff --git a/ceres/src/application/code_edit/on_push.rs b/ceres/src/application/code_edit/on_push.rs index b4f8a0c92..5d6ef18e6 100644 --- a/ceres/src/application/code_edit/on_push.rs +++ b/ceres/src/application/code_edit/on_push.rs @@ -3,11 +3,10 @@ use std::sync::Arc; use callisto::{mega_cl, mega_refs}; use common::errors::MegaError; use jupiter::storage::Storage; -use orion_client::OrionBuildClient; use crate::application::{ api_service::{cache::GitObjectCache, mono::MonoApiService}, - build_trigger::{BuildTriggerService, TriggerContext}, + build_trigger::{BuildTriggerService, SharedBuildDispatch, TriggerContext}, code_edit::{ model::{self, CLRefUpdateVisitor}, utils as edit_utils, @@ -62,7 +61,7 @@ impl model::TriggerContextBuilder for OnpushTrigerBuilder { &self, storage: Storage, git_cache: Arc, - orion_client: Arc, + build_dispatch: SharedBuildDispatch, cl: &mega_cl::Model, username: &str, ) -> Result<(), MegaError> { @@ -92,7 +91,7 @@ impl model::TriggerContextBuilder for OnpushTrigerBuilder { Some(cl_model.id), Some(username), ); - BuildTriggerService::build_by_context(storage, git_cache, orion_client, context).await + BuildTriggerService::build_by_context(storage, git_cache, build_dispatch, context).await }); Ok(()) diff --git a/ceres/src/application/code_edit/post_receive/import.rs b/ceres/src/application/code_edit/post_receive/import.rs index 9f846a773..aa21b9ef6 100644 --- a/ceres/src/application/code_edit/post_receive/import.rs +++ b/ceres/src/application/code_edit/post_receive/import.rs @@ -17,9 +17,11 @@ use crate::{ transport::protocol::import_refs::{CommandType, RefCommand}, }; +#[allow(clippy::too_many_arguments)] pub async fn dispatch_import_receive_pack_finalized( storage: Storage, - git_object_cache: Arc, + _git_object_cache: Arc, + mono_api_service: &MonoApiService, repo_path: PathBuf, repo_id: i64, commands: Vec, @@ -31,10 +33,6 @@ pub async fn dispatch_import_receive_pack_finalized( None => return Ok(()), }; - let mono_api_service = MonoApiService { - storage: storage.clone(), - git_object_cache, - }; let mono_storage = storage.mono_storage(); let latest_commit: Commit = Commit::from_git_model( @@ -65,7 +63,7 @@ pub async fn dispatch_import_receive_pack_finalized( let expected_tree = root_ref.ref_tree_hash.clone(); let root_ref_id = root_ref.id; - let save_trees = tree_ops::search_and_create_tree(&mono_api_service, &repo_path).await?; + let save_trees = tree_ops::search_and_create_tree(mono_api_service, &repo_path).await?; let new_commit = Commit::from_tree_id( save_trees diff --git a/ceres/src/application/code_edit/post_receive/mono.rs b/ceres/src/application/code_edit/post_receive/mono.rs index 8b7140eec..071216a78 100644 --- a/ceres/src/application/code_edit/post_receive/mono.rs +++ b/ceres/src/application/code_edit/post_receive/mono.rs @@ -10,24 +10,27 @@ use callisto::{mega_cl, mega_code_review_anchor}; use common::{errors::MegaError, utils::ZERO_ID}; use futures::{StreamExt, stream}; use jupiter::storage::Storage; -use orion_client::OrionBuildClient; use crate::{ application::{ api_service::{ ApiHandler, - cache::GitObjectCache, - mono::{MonoApiService, cl_merge}, + mono::{ClApplicationService, MonoApiService, cl_merge}, }, + build_trigger::SharedBuildDispatch, code_edit::{on_push::OnpushCodeEdit, utils::get_changed_files}, }, bus::{ApplicationEventHandler, TransportEvent}, }; /// Handles CL creation, bootstrap, build triggers, and code-review reanchoring after mono push. +#[allow(clippy::too_many_arguments)] pub async fn dispatch_mono_receive_pack_finalized( storage: Storage, - git_object_cache: Arc, + git_object_cache: Arc, + build_dispatch: Option, + git: &MonoApiService, + cl: &ClApplicationService, repo_path: PathBuf, base_branch: String, from_hash: String, @@ -35,47 +38,45 @@ pub async fn dispatch_mono_receive_pack_finalized( username: Option, ) -> Result<(), MegaError> { let username = username.unwrap_or_else(|| String::from("Anonymous")); - let mono_api_service = MonoApiService { - storage: storage.clone(), - git_object_cache: git_object_cache.clone(), - }; let repo_path_str = repo_path .to_str() .ok_or_else(|| MegaError::Other("invalid repo path".to_string()))?; - let editor = OnpushCodeEdit::from(repo_path_str, &base_branch, &from_hash, &mono_api_service); - let cl = editor + let editor = OnpushCodeEdit::from(repo_path_str, &base_branch, &from_hash, git); + let cl_model = editor .update_or_create_cl(&storage, &from_hash, &to_hash, &username) .await?; if from_hash == ZERO_ID && repo_path_str.starts_with("/project/") { - cl_merge::bootstrap_monorepo_path(&mono_api_service, repo_path_str, Some(&cl)).await?; + cl_merge::bootstrap_monorepo_path(git, repo_path_str, Some(&cl_model)).await?; } - let orion_client = Arc::new(OrionBuildClient::new(storage.config().build.clone())); - if orion_client.enable_build() { + if let Some(build_dispatch) = build_dispatch + && build_dispatch.enable_build() + { editor .trigger_build_and_check( storage.clone(), - git_object_cache.clone(), - orion_client, - &cl, + git_object_cache, + build_dispatch, + &cl_model, &username, ) .await?; } - reanchor_code_review_threads(&storage, &mono_api_service, &cl, &to_hash).await + reanchor_code_review_threads(&storage, git, cl, &cl_model, &to_hash).await } async fn reanchor_code_review_threads( storage: &Storage, - mono_api_service: &MonoApiService, + git: &MonoApiService, + cl_svc: &ClApplicationService, cl: &mega_cl::Model, to_hash: &str, ) -> Result<(), MegaError> { let cl_link = cl.link.clone(); - let changed_files = get_changed_files(mono_api_service, cl).await?; + let changed_files = get_changed_files(git, cl).await?; let files_with_threads = storage .code_review_thread_storage() .get_files_with_threads_by_link(&cl_link) @@ -117,7 +118,8 @@ async fn reanchor_code_review_threads( .get_anchors_by_thread_ids(&pending_reanchor_thread_ids) .await?; - let mono_api_service = Arc::new(mono_api_service.clone()); + let git = Arc::new(git.clone()); + let cl_svc = Arc::new(cl_svc.clone()); let mut anchors_map: HashMap> = HashMap::new(); for anchor in anchors { anchors_map @@ -130,7 +132,8 @@ async fn reanchor_code_review_threads( .into_iter() .map(|thread| { let cl_link = cl_link.clone(); - let mono_api_service = Arc::clone(&mono_api_service); + let git = Arc::clone(&git); + let cl_svc = Arc::clone(&cl_svc); let anchors_map = anchors_map.clone(); let to_hash = to_hash.to_string(); let storage = storage.clone(); @@ -149,7 +152,7 @@ async fn reanchor_code_review_threads( } }; - let (diff_content, _) = mono_api_service + let (diff_content, _) = cl_svc .paged_content_diff(&cl_link, Pagination::default()) .await?; @@ -161,7 +164,7 @@ async fn reanchor_code_review_threads( let latest_blob = if let Some(blob) = blob_cache.get(&file_path) { blob.clone() } else { - let blob = mono_api_service + let blob = git .get_blob_as_string(PathBuf::from(&file_path), Some(&to_hash)) .await? .expect("latest blob must exist"); @@ -198,18 +201,15 @@ async fn reanchor_code_review_threads( Ok(()) } -/// Application handler that dispatches transport events using storage + cache context. +/// Application handler that dispatches transport events using injected git + CL services. pub struct RuntimeApplicationHandler { - storage: Storage, - git_object_cache: Arc, + git: MonoApiService, + cl: ClApplicationService, } impl RuntimeApplicationHandler { - pub fn new(storage: Storage, git_object_cache: Arc) -> Self { - Self { - storage, - git_object_cache, - } + pub fn new(git: MonoApiService, cl: ClApplicationService) -> Self { + Self { git, cl } } } @@ -225,8 +225,11 @@ impl ApplicationEventHandler for RuntimeApplicationHandler { username, } => { dispatch_mono_receive_pack_finalized( - self.storage.clone(), - self.git_object_cache.clone(), + self.git.storage().clone(), + self.git.git_object_cache(), + self.git.build_dispatch(), + &self.git, + &self.cl, repo_path, base_branch, from_hash, @@ -243,8 +246,9 @@ impl ApplicationEventHandler for RuntimeApplicationHandler { extra_timings, } => { super::import::dispatch_import_receive_pack_finalized( - self.storage.clone(), - self.git_object_cache.clone(), + self.git.storage().clone(), + self.git.git_object_cache(), + &self.git, repo_path, repo_id, commands, diff --git a/ceres/src/application/webhook/admin.rs b/ceres/src/application/webhook/admin.rs index 0cd0c26e8..4b05b3f72 100644 --- a/ceres/src/application/webhook/admin.rs +++ b/ceres/src/application/webhook/admin.rs @@ -9,11 +9,11 @@ use jupiter::{ }; use crate::{ - application::api_service::mono::MonoApiService, + application::api_service::mono::WebhookApplicationService, model::webhook::{CreateWebhookRequest, WebhookResponse, parse_webhook_event_types}, }; -impl MonoApiService { +impl WebhookApplicationService { pub async fn create_webhook( &self, payload: CreateWebhookRequest, @@ -43,7 +43,7 @@ impl MonoApiService { }; let created: WebhookWithEventTypes = self - .storage + .storage() .webhook_storage() .create_webhook(model, event_types) .await?; @@ -55,7 +55,7 @@ impl MonoApiService { pagination: Pagination, ) -> Result<(Vec, u64), MegaError> { let (webhooks, total) = self - .storage + .storage() .webhook_storage() .list_webhooks(pagination) .await?; @@ -63,6 +63,6 @@ impl MonoApiService { } pub async fn delete_webhook(&self, id: i64) -> Result<(), MegaError> { - self.storage.webhook_storage().delete_webhook(id).await + self.storage().webhook_storage().delete_webhook(id).await } } diff --git a/ceres/src/bus/runtime.rs b/ceres/src/bus/runtime.rs index 10b4307a7..548ffaa97 100644 --- a/ceres/src/bus/runtime.rs +++ b/ceres/src/bus/runtime.rs @@ -3,8 +3,12 @@ use std::sync::Arc; use jupiter::storage::Storage; use super::handler::ApplicationEventHandler; -use crate::{ - application::code_edit::post_receive::RuntimeApplicationHandler, infra::cache::GitObjectCache, +use crate::application::{ + api_service::{ + cache::GitObjectCache, + mono::{ClApplicationService, MonoApiService}, + }, + code_edit::post_receive::RuntimeApplicationHandler, }; #[derive(Clone)] @@ -15,10 +19,14 @@ pub struct TransportRuntime { } impl TransportRuntime { - pub fn new(storage: Storage, git_object_cache: Arc) -> Self { - let application: Arc = Arc::new( - RuntimeApplicationHandler::new(storage.clone(), git_object_cache.clone()), - ); + pub fn new( + storage: Storage, + git_object_cache: Arc, + git: MonoApiService, + cl: ClApplicationService, + ) -> Self { + let application: Arc = + Arc::new(RuntimeApplicationHandler::new(git, cl)); Self { storage, git_object_cache, diff --git a/ceres/src/infra/context.rs b/ceres/src/infra/context.rs index dbe6b928f..da77b1916 100644 --- a/ceres/src/infra/context.rs +++ b/ceres/src/infra/context.rs @@ -3,12 +3,14 @@ use std::sync::Arc; use jupiter::storage::Storage; use super::cache::GitObjectCache; +use crate::application::build_trigger::SharedBuildDispatch; /// Transport-agnostic handles shared by Git transport handlers and application services. #[derive(Clone)] pub struct TransportContext { pub storage: Storage, pub git_object_cache: Arc, + pub build_dispatch: Option, } impl TransportContext { @@ -16,6 +18,19 @@ impl TransportContext { Self { storage, git_object_cache, + build_dispatch: None, + } + } + + pub fn with_build_dispatch( + storage: Storage, + git_object_cache: Arc, + build_dispatch: SharedBuildDispatch, + ) -> Self { + Self { + storage, + git_object_cache, + build_dispatch: Some(build_dispatch), } } } diff --git a/ceres/src/lib.rs b/ceres/src/lib.rs index 764c6c406..1ac05d3e5 100644 --- a/ceres/src/lib.rs +++ b/ceres/src/lib.rs @@ -9,9 +9,11 @@ pub mod merge_checker; pub mod model; pub mod transport; -pub use application::api_service::{ - ADMIN_FILE, EffectiveResourcePermission, MonoApiService, MonoAppServices, MonoServiceLogic, - RefUpdate, TreeUpdateResult, cl_merge, +pub use application::{ + api_service::{ + ADMIN_FILE, EffectiveResourcePermission, MonoApiService, MonoAppServices, MonoServiceLogic, + RefUpdate, TreeUpdateResult, cl_merge, + }, + build_trigger::{BuildDispatchPort, SharedBuildDispatch}, }; pub use bus::{ApplicationEventHandler, TransportEvent, TransportRuntime}; -pub use transport::ProtocolApiState; diff --git a/ceres/src/transport/mod.rs b/ceres/src/transport/mod.rs index 150b5ef77..ff7686e4b 100644 --- a/ceres/src/transport/mod.rs +++ b/ceres/src/transport/mod.rs @@ -1,5 +1,2 @@ pub mod pack; pub mod protocol; -pub mod state; - -pub use state::{ProtocolApiState, TransportRuntime}; diff --git a/ceres/src/transport/pack/import_repo.rs b/ceres/src/transport/pack/import_repo.rs index 593921228..82e86ed8f 100644 --- a/ceres/src/transport/pack/import_repo.rs +++ b/ceres/src/transport/pack/import_repo.rs @@ -34,8 +34,8 @@ use tokio::sync::mpsc::{self, Sender}; use tokio_stream::wrappers::ReceiverStream; use crate::{ - application::api_service::cache::GitObjectCache, bus::{ApplicationEventHandler, TransportEvent}, + infra::cache::GitObjectCache, transport::{ pack::RepoHandler, protocol::{ diff --git a/ceres/src/transport/state.rs b/ceres/src/transport/state.rs deleted file mode 100644 index c43b7cfc4..000000000 --- a/ceres/src/transport/state.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Transport-layer shared state (storage, cache, application event handler). - -pub use crate::bus::TransportRuntime; - -/// Backward-compatible alias for [`TransportRuntime`]. -pub type ProtocolApiState = TransportRuntime; diff --git a/docker/README.md b/docker/README.md index d1f3b1cb8..351dd1247 100644 --- a/docker/README.md +++ b/docker/README.md @@ -55,7 +55,7 @@ docker compose version ### 1 Clone the repo ```bash -git clone https://github.com/web3infra-foundation/mega.git +git clone https://github.com/gitmono-dev/mega.git cd mega ``` diff --git a/docker/deployment/.env.example b/docker/deployment/.env.example index a7f9d2e8d..bc917216a 100644 --- a/docker/deployment/.env.example +++ b/docker/deployment/.env.example @@ -50,11 +50,11 @@ S3_SECRET_ACCESS_KEY=rustfsadmin # All images are pulled from Amazon ECR Public: public.ecr.aws/m8q5m4u3/mega # You can override these to use local images or different tags -MEGA_ENGINE_IMAGE=ghcr.io/web3infra-foundation/mono-engine:latest +MEGA_ENGINE_IMAGE=ghcr.io/gitmono-dev/mono-engine:latest # Unified web image (same tag for all environments; URLs injected at container start) MEGA_UI_IMAGE=public.ecr.aws/m8q5m4u3/mega/mega-ui:latest -ORION_SERVER_IMAGE=ghcr.io/web3infra-foundation/orion-server:latest -CAMPSITE_API_IMAGE=ghcr.io/web3infra-foundation/campsite-api:latest +ORION_SERVER_IMAGE=ghcr.io/gitmono-dev/orion-server:latest +CAMPSITE_API_IMAGE=ghcr.io/gitmono-dev/campsite-api:latest # ---------------------------------------------------------------------------- # Mega UI Runtime Configuration (unified image) diff --git a/docs/development.md b/docs/development.md index bd4ff97dc..62fd88038 100644 --- a/docs/development.md +++ b/docs/development.md @@ -10,7 +10,7 @@ ## Clone and build ```bash -git clone https://github.com/web3infra-foundation/mega.git +git clone https://github.com/gitmono-dev/mega.git cd mega git submodule update --init --recursive cargo build -p mono diff --git a/jupiter/src/service/cl_service.rs b/jupiter/src/service/cl_service.rs index ab8230076..c13361326 100644 --- a/jupiter/src/service/cl_service.rs +++ b/jupiter/src/service/cl_service.rs @@ -56,4 +56,12 @@ impl CLService { .new_cl_draft(path, link, title, base_branch, from_hash, username) .await } + + pub fn cl_store(&self) -> ClStorage { + self.cl_storage.clone() + } + + pub fn conversation_store(&self) -> ConversationStorage { + self.conversation_storage.clone() + } } diff --git a/jupiter/src/service/issue_service.rs b/jupiter/src/service/issue_service.rs index fcfde96dc..4e0fcfc32 100644 --- a/jupiter/src/service/issue_service.rs +++ b/jupiter/src/service/issue_service.rs @@ -85,4 +85,12 @@ impl IssueService { let cls = self.cl_storage.get_cl_suggestions_by_query(query).await?; Ok((issues, cls)) } + + pub fn issue_store(&self) -> IssueStorage { + self.issue_storage.clone() + } + + pub fn conversation_store(&self) -> ConversationStorage { + self.conversation_storage.clone() + } } diff --git a/mono/src/api/api_common/comment.rs b/mono/src/api/api_common/comment.rs index 52c361439..1c87ac7e0 100644 --- a/mono/src/api/api_common/comment.rs +++ b/mono/src/api/api_common/comment.rs @@ -25,7 +25,8 @@ pub async fn check_comment_ref( let username = user.username; for ref_link in links { state - .monorepo() + .services() + .conversation() .add_issue_mention_reference(source_link, &ref_link, &username) .await?; } diff --git a/mono/src/api/api_common/group_permission.rs b/mono/src/api/api_common/group_permission.rs index 2c103d342..f0ad8d0f8 100644 --- a/mono/src/api/api_common/group_permission.rs +++ b/mono/src/api/api_common/group_permission.rs @@ -8,7 +8,12 @@ use http::StatusCode; use crate::api::{MonoApiServiceState, error::ApiError, oauth::model::LoginUser}; pub async fn ensure_admin(state: &MonoApiServiceState, user: &LoginUser) -> Result<(), ApiError> { - if state.monorepo().check_is_admin(&user.username).await? { + if state + .services() + .admin() + .check_is_admin(&user.username) + .await? + { return Ok(()); } @@ -29,7 +34,8 @@ pub async fn resolve_resource_context( resource_id: &str, ) -> Result<(ResourceTypeValue, String), ApiError> { state - .monorepo() + .services() + .admin() .resolve_resource_context(resource_type, resource_id) .await .map_err(ApiError::from) diff --git a/mono/src/api/api_common/label_assignee.rs b/mono/src/api/api_common/label_assignee.rs index 0cbabdb2c..f0e31055a 100644 --- a/mono/src/api/api_common/label_assignee.rs +++ b/mono/src/api/api_common/label_assignee.rs @@ -17,7 +17,8 @@ pub async fn label_update( } = payload; state - .monorepo() + .services() + .issue() .update_item_labels(&user.username, item_id, &item_type, label_ids, &link) .await?; @@ -37,7 +38,8 @@ pub async fn assignees_update( } = payload; state - .monorepo() + .services() + .issue() .update_item_assignees(&user.username, item_id, &item_type, assignees, &link) .await?; diff --git a/mono/src/api/api_router.rs b/mono/src/api/api_router.rs index 91a552f76..41b40e30b 100644 --- a/mono/src/api/api_router.rs +++ b/mono/src/api/api_router.rs @@ -70,7 +70,7 @@ pub async fn get_blob_file( state: State, Path(oid): Path, ) -> Result { - let api_handler = state.monorepo(); + let api_handler = state.git(); let data = api_handler.get_raw_blob_by_hash(&oid).await?; let file_name = format!("inline; filename=\"{oid}\""); diff --git a/mono/src/api/mod.rs b/mono/src/api/mod.rs index ae49543bc..1d70c3695 100644 --- a/mono/src/api/mod.rs +++ b/mono/src/api/mod.rs @@ -5,21 +5,19 @@ use std::{ use axum::extract::FromRef; use ceres::{ + TransportRuntime, application::{ api_service::{ ApiHandler, - cache::GitObjectCache, import_api_service::ImportApiService, mono::{MonoApiService, MonoAppServices}, }, - artifact::ArtifactApplicationService, - build_trigger::service::BuildTriggerService, + build_trigger::BuildDispatchPort, }, - transport::{ProtocolApiState, protocol::repo::Repo}, + transport::protocol::repo::Repo, }; use common::errors::MegaError; use jupiter::storage::{Storage, user_storage::UserStorage}; -use orion_client::OrionBuildClient; use saturn::entitystore::EntityStore; use tower_sessions::MemoryStore; @@ -36,31 +34,25 @@ pub mod router; #[derive(Clone)] pub struct MonoApiServiceState { services: MonoAppServices, - storage: Storage, - git_object_cache: Arc, session_store: Option, listen_addr: String, entity_store: EntityStore, - orion_client: Arc, } impl MonoApiServiceState { pub fn new( storage: Storage, - git_object_cache: Arc, + git_object_cache: Arc, + build_dispatch: Arc, session_store: Option, listen_addr: String, entity_store: EntityStore, - orion_client: Arc, ) -> Self { Self { - services: MonoAppServices::new(storage.clone(), git_object_cache.clone()), - storage, - git_object_cache, + services: MonoAppServices::new(storage, git_object_cache, Some(build_dispatch)), session_store, listen_addr, entity_store, - orion_client, } } @@ -68,34 +60,14 @@ impl MonoApiServiceState { &self.listen_addr } - pub(crate) fn lfs_db_storage(&self) -> jupiter::storage::lfs_db_storage::LfsDbStorage { - self.storage.lfs_db_storage() - } - - pub(crate) fn lfs_service(&self) -> jupiter::service::lfs_service::LfsService { - self.storage.lfs_service.clone() - } - - pub fn monorepo(&self) -> MonoApiService { - self.services.monorepo().clone() + pub fn git(&self) -> &MonoApiService { + self.services.git() } pub fn services(&self) -> &MonoAppServices { &self.services } - pub fn artifact_app_service(&self) -> ArtifactApplicationService { - ArtifactApplicationService::from_storage(&self.storage) - } - - pub fn build_trigger_service(&self) -> BuildTriggerService { - BuildTriggerService::new( - self.storage.clone(), - self.git_object_cache.clone(), - self.orion_client.clone(), - ) - } - pub(crate) async fn api_handler(&self, path: &Path) -> Result, MegaError> { let path = if path.has_root() { path.to_path_buf() @@ -107,20 +79,21 @@ impl MonoApiServiceState { .to_str() .ok_or_else(|| MegaError::bad_request("Invalid repository path"))?; - let import_dir = self.monorepo().import_dir(); + let monorepo = self.git(); + let import_dir = monorepo.import_dir(); if path.starts_with(&import_dir) && path != import_dir - && let Some(model) = self.monorepo().find_git_repo_like_path(path_str).await? + && let Some(model) = monorepo.find_git_repo_like_path(path_str).await? { let repo: Repo = model.into(); - return Ok(Box::new(ImportApiService { - storage: self.storage.clone(), + return Ok(Box::new(ImportApiService::new( + monorepo.storage().clone(), repo, - git_object_cache: self.git_object_cache.clone(), - })); + monorepo.git_object_cache(), + ))); } - Ok(Box::new(self.monorepo()) as Box) + Ok(Box::new(monorepo.clone()) as Box) } } @@ -138,7 +111,7 @@ impl FromRef for OAuthApiStore { impl FromRef for UserStorage { fn from_ref(state: &MonoApiServiceState) -> Self { - state.storage.user_storage() + state.services.storage().user_storage() } } @@ -148,14 +121,8 @@ impl FromRef for EntityStore { } } -impl From<&MonoApiServiceState> for MonoApiService { - fn from(state: &MonoApiServiceState) -> Self { - state.monorepo() - } -} - -impl FromRef for ProtocolApiState { - fn from_ref(state: &MonoApiServiceState) -> ProtocolApiState { - ProtocolApiState::new(state.storage.clone(), state.git_object_cache.clone()) +impl FromRef for TransportRuntime { + fn from_ref(state: &MonoApiServiceState) -> TransportRuntime { + state.services.transport_runtime().clone() } } diff --git a/mono/src/api/notes/note_router.rs b/mono/src/api/notes/note_router.rs index cdb3237d6..d1d1852c8 100644 --- a/mono/src/api/notes/note_router.rs +++ b/mono/src/api/notes/note_router.rs @@ -30,7 +30,7 @@ async fn show_note( Path(id): Path, ) -> Result, ApiError> { // TODO: authorize(note, :show?) - let response = state.monorepo().get_note_sync_state(id).await?; + let response = state.services().note().get_note_sync_state(id).await?; Ok(Json(response)) } @@ -50,7 +50,8 @@ async fn update_note( ) -> Result, ApiError> { // TODO: authorize note access (like in show_note) state - .monorepo() + .services() + .note() .update_note_sync_state( id, json.description_html.as_str(), diff --git a/mono/src/api/oauth/mod.rs b/mono/src/api/oauth/mod.rs index ede071b6e..f59bca428 100644 --- a/mono/src/api/oauth/mod.rs +++ b/mono/src/api/oauth/mod.rs @@ -112,7 +112,12 @@ where // Delegate token validation to Jupiter storage (BotsStorage) let state_ref = MonoApiServiceState::from_ref(state); - match state_ref.monorepo().find_bot_by_token(raw_token).await { + match state_ref + .services() + .user() + .find_bot_by_token(raw_token) + .await + { Ok(Some((bot, token))) => Ok(BotAuth(BotIdentity::from_models(bot, token))), Ok(None) => { tracing::warn!("BotIdentity: bot token not found, revoked, or expired"); diff --git a/mono/src/api/router/admin_router.rs b/mono/src/api/router/admin_router.rs index b988fd82d..6cb6ddbb2 100644 --- a/mono/src/api/router/admin_router.rs +++ b/mono/src/api/router/admin_router.rs @@ -44,7 +44,11 @@ async fn is_admin_me( user: LoginUser, State(state): State, ) -> Result>, ApiError> { - let is_admin = state.monorepo().check_is_admin(&user.username).await?; + let is_admin = state + .services() + .admin() + .check_is_admin(&user.username) + .await?; Ok(Json(CommonResult::success(Some(IsAdminResponse { is_admin, @@ -71,7 +75,7 @@ async fn admin_list( ) -> Result>, ApiError> { ensure_admin(&state, &user).await?; - let admins = state.monorepo().get_all_admins().await?; + let admins = state.services().admin().get_all_admins().await?; Ok(Json(CommonResult::success(Some(AdminListResponse { admins, diff --git a/mono/src/api/router/artifacts_router.rs b/mono/src/api/router/artifacts_router.rs index a648ef624..8a2db860a 100644 --- a/mono/src/api/router/artifacts_router.rs +++ b/mono/src/api/router/artifacts_router.rs @@ -66,7 +66,7 @@ pub async fn discovery( State(state): State, Path(_repo): Path, ) -> Result, ApiError> { - Ok(Json(state.artifact_app_service().discovery_response())) + Ok(Json(state.services().artifact().discovery_response())) } /// List committed artifact sets for a repo (paginated). @@ -96,7 +96,8 @@ pub async fn list_artifact_sets( ) -> Result, ApiError> { let repo = decode_path_segment(&repo); let body = state - .artifact_app_service() + .services() + .artifact() .list_artifact_sets(&repo, &q) .await .map_err(ApiError::from)?; @@ -128,7 +129,8 @@ pub async fn get_artifact_set( let repo = decode_path_segment(&repo); let artifact_set_id = decode_path_segment(&artifact_set_id); let body = state - .artifact_app_service() + .services() + .artifact() .get_artifact_set_detail(&repo, &artifact_set_id, &q) .await .map_err(ApiError::from)?; @@ -161,7 +163,8 @@ pub async fn resolve_artifact_file( ) -> Result, ApiError> { let repo = decode_path_segment(&repo); let body = state - .artifact_app_service() + .services() + .artifact() .resolve_artifact_file(&repo, &q) .await .map_err(ApiError::from)?; @@ -198,7 +201,7 @@ pub async fn download_object( ) -> Result { let repo = decode_path_segment(&repo); let oid = decode_path_segment(&oid); - let svc = &state.artifact_app_service(); + let svc = &state.services().artifact(); let model = svc .artifact_object_model_for_committed_repo_download(&repo, &oid) @@ -332,7 +335,7 @@ pub async fn head_artifact_object( ) -> Result { let repo = decode_path_segment(&repo); let oid = decode_path_segment(&oid); - let svc = &state.artifact_app_service(); + let svc = &state.services().artifact(); let model = svc .artifact_object_model_for_committed_repo_download(&repo, &oid) .await @@ -372,7 +375,8 @@ pub async fn batch( Json(req): Json, ) -> Result, ApiError> { let body = state - .artifact_app_service() + .services() + .artifact() .batch_artifacts(&req) .await .map_err(ApiError::from)?; @@ -402,7 +406,8 @@ pub async fn commit( ) -> Result, ApiError> { let repo = decode_path_segment(&repo); let body = state - .artifact_app_service() + .services() + .artifact() .commit_artifacts(&repo, &req) .await .map_err(ApiError::from)?; @@ -458,7 +463,8 @@ pub async fn upload_object_fallback( } state - .artifact_app_service() + .services() + .artifact() .upload_artifact_object_bytes(&oid, body_bytes) .await .map_err(ApiError::from)?; diff --git a/mono/src/api/router/bot_router.rs b/mono/src/api/router/bot_router.rs index 71b56a2cb..83a5d8e6e 100644 --- a/mono/src/api/router/bot_router.rs +++ b/mono/src/api/router/bot_router.rs @@ -23,7 +23,7 @@ const MAX_EXPIRES_IN_SECS: i64 = 365 * 24 * 3600 * 10; const MIN_EXPIRES_IN_SECS: i64 = 1; async fn ensure_bot_exists(state: &MonoApiServiceState, bot_id: i64) -> Result<(), ApiError> { - let bot = state.monorepo().get_bot_by_id(bot_id).await?; + let bot = state.services().admin().get_bot_by_id(bot_id).await?; if bot.is_none() { return Err(ApiError::not_found(anyhow!("Bot not found"))); } @@ -62,7 +62,7 @@ async fn install_bot( Path(id): Path, Json(json): Json, ) -> Result>, ApiError> { - let bot = state.monorepo().install_bot(id, json).await?; + let bot = state.services().admin().install_bot(id, json).await?; Ok(Json(CommonResult::success(Some(bot)))) } @@ -83,7 +83,7 @@ async fn list_installed_bot( state: State, Path(id): Path, ) -> Result>>, ApiError> { - let models = state.monorepo().list_installed_bots(id).await?; + let models = state.services().admin().list_installed_bots(id).await?; Ok(Json(CommonResult::success(Some(models)))) } @@ -106,7 +106,8 @@ async fn change_installation_status( Json(json): Json, ) -> Result>, ApiError> { let model = state - .monorepo() + .services() + .admin() .change_bot_installation_status(id, installation_id, json) .await?; @@ -131,7 +132,8 @@ async fn uninstall_bot( Json(target_type): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .admin() .uninstall_bot(id, target_type, installation_id) .await?; @@ -184,7 +186,8 @@ async fn create_bot_token( }; let resp = state - .monorepo() + .services() + .admin() .generate_bot_token(bot_id, &req.token_name, expires_at) .await?; @@ -216,7 +219,7 @@ async fn list_bot_tokens( ensure_admin(&state, &user).await?; ensure_bot_exists(&state, bot_id).await?; - let items = state.monorepo().list_bot_tokens(bot_id).await?; + let items = state.services().admin().list_bot_tokens(bot_id).await?; Ok(Json(CommonResult::success(Some(items)))) } @@ -247,7 +250,11 @@ async fn revoke_bot_token( ensure_admin(&state, &user).await?; ensure_bot_exists(&state, bot_id).await?; - state.monorepo().revoke_bot_token(bot_id, token_id).await?; + state + .services() + .admin() + .revoke_bot_token(bot_id, token_id) + .await?; Ok(Json(CommonResult::success(None))) } @@ -277,7 +284,11 @@ async fn revoke_all_bot_tokens( ensure_admin(&state, &user).await?; ensure_bot_exists(&state, bot_id).await?; - state.monorepo().revoke_all_bot_tokens(bot_id).await?; + state + .services() + .admin() + .revoke_all_bot_tokens(bot_id) + .await?; Ok(Json(CommonResult::success(None))) } diff --git a/mono/src/api/router/buck_router.rs b/mono/src/api/router/buck_router.rs index 8f10a402c..82b6e9f54 100644 --- a/mono/src/api/router/buck_router.rs +++ b/mono/src/api/router/buck_router.rs @@ -49,7 +49,7 @@ async fn create_session( Json(payload): Json, ) -> Result>, ApiError> { let service_resp = state - .monorepo() + .git() .create_buck_session(&user.username, &payload.path) .await?; @@ -89,7 +89,7 @@ async fn upload_manifest( Json(payload): Json, ) -> Result>, ApiError> { let response = state - .monorepo() + .git() .process_buck_manifest(&user.username, &cl_link, payload) .await .map_err(ApiError::from)?; @@ -135,7 +135,7 @@ async fn upload_file( })?; // Get max_file_size from BuckService - let max_size = state.monorepo().buck_max_file_size(); + let max_size = state.git().buck_max_file_size(); if file_size > max_size { return Err(ApiError::with_status( StatusCode::PAYLOAD_TOO_LARGE, @@ -145,7 +145,7 @@ async fn upload_file( // Acquire permits through BuckService let (_global_permit, _large_file_permit) = state - .monorepo() + .git() .buck_try_acquire_upload_permits(file_size) .map_err(|e| { tracing::warn!( @@ -199,7 +199,7 @@ async fn upload_file( .map_err(|e| ApiError::bad_request(anyhow::anyhow!("Failed to read body: {}", e)))?; let svc_resp = state - .monorepo() + .git() .upload_buck_file( &user.username, &cl_link, @@ -251,7 +251,7 @@ async fn complete_upload( ) -> Result>, ApiError> { let payload = payload.map(|p| p.0).unwrap_or(CompletePayload {}); let response = state - .monorepo() + .git() .complete_buck_upload(&user.username, &cl_link, payload) .await .map_err(ApiError::from)?; diff --git a/mono/src/api/router/build_trigger_router.rs b/mono/src/api/router/build_trigger_router.rs index a960b67a2..d976e9432 100644 --- a/mono/src/api/router/build_trigger_router.rs +++ b/mono/src/api/router/build_trigger_router.rs @@ -49,7 +49,7 @@ async fn create_trigger( state: State, Json(req): Json, ) -> Result>, ApiError> { - let service = state.build_trigger_service(); + let service = state.services().build_trigger(); let response = service .create_manual_trigger(req.repo_path, req.ref_name, req.params, user.username) .await?; @@ -80,7 +80,7 @@ async fn list_triggers( state: State, Json(json): Json>, ) -> Result>>, ApiError> { - let service = state.build_trigger_service(); + let service = state.services().build_trigger(); let (items, total) = service .list_triggers(json.additional, json.pagination) .await?; @@ -115,7 +115,7 @@ async fn get_trigger( state: State, Path(id): Path, ) -> Result>, ApiError> { - let service = state.build_trigger_service(); + let service = state.services().build_trigger(); let response = service.get_trigger(id).await?; Ok(Json(CommonResult::success(Some(response)))) } @@ -143,7 +143,7 @@ async fn retry_trigger( state: State, Path(id): Path, ) -> Result>, ApiError> { - let service = state.build_trigger_service(); + let service = state.services().build_trigger(); let response = service.retry_trigger(id, user.username).await?; Ok(Json(CommonResult::success(Some(response)))) } diff --git a/mono/src/api/router/cl_router.rs b/mono/src/api/router/cl_router.rs index 984797dcc..3a6230cb2 100644 --- a/mono/src/api/router/cl_router.rs +++ b/mono/src/api/router/cl_router.rs @@ -63,7 +63,11 @@ async fn reopen_cl( Path(link): Path, state: State, ) -> Result>, ApiError> { - state.monorepo().reopen_cl(&link, &user.username).await?; + state + .services() + .cl() + .reopen_cl(&link, &user.username) + .await?; Ok(Json(CommonResult::success(None))) } @@ -84,7 +88,11 @@ async fn close_cl( Path(link): Path, state: State, ) -> Result>, ApiError> { - state.monorepo().close_cl(&link, &user.username).await?; + state + .services() + .cl() + .close_cl(&link, &user.username) + .await?; Ok(Json(CommonResult::success(None))) } @@ -106,7 +114,8 @@ async fn merge( state: State, ) -> Result>, ApiError> { state - .monorepo() + .services() + .cl() .merge_open_cl(&user.username, &link) .await?; Ok(Json(CommonResult::success(None))) @@ -129,7 +138,7 @@ async fn merge_no_auth( Path(link): Path, state: State, ) -> Result>, ApiError> { - state.monorepo().merge_open_cl_no_auth(&link).await?; + state.services().cl().merge_open_cl_no_auth(&link).await?; Ok(Json(CommonResult::success(Some( "Merge completed successfully".to_string(), )))) @@ -150,7 +159,8 @@ async fn fetch_cl_list( Json(json): Json>, ) -> Result>>, ApiError> { let res = state - .monorepo() + .services() + .cl() .get_cl_list(json.additional, json.pagination) .await?; Ok(Json(CommonResult::success(Some(res)))) @@ -174,7 +184,8 @@ async fn cl_detail( state: State, ) -> Result>, ApiError> { let cl_details = state - .monorepo() + .services() + .cl() .get_cl_details(&link, user.username) .await?; Ok(Json(CommonResult::success(Some(cl_details)))) @@ -196,7 +207,8 @@ async fn cl_mui_tree( state: State, ) -> Result>>, ApiError> { let files = state - .monorepo() + .services() + .cl() .get_sorted_changed_file_list(&link, None) .await?; let mui_trees = build_forest(files); @@ -222,7 +234,8 @@ async fn cl_files_changed_by_page( Json(json): Json>, ) -> Result>, ApiError> { let (items, total) = state - .monorepo() + .services() + .cl() .paged_content_diff_for_cl(&link, json.pagination) .await?; let res = CommonResult::success(Some(FilesChangedPage { @@ -247,12 +260,12 @@ async fn cl_files_list( Path(link): Path, state: State, ) -> Result>>, ApiError> { - let cl = state.monorepo().get_cl_model(&link).await?; + let cl = state.services().cl().get_cl_model(&link).await?; - let stg = state.monorepo(); - let old_files = stg.get_commit_blobs(&cl.from_hash).await?; - let new_files = stg.get_commit_blobs(&cl.to_hash).await?; - let cl_diff_files = stg.cl_files_list(old_files, new_files.clone()).await?; // TODO + let cl_svc = state.services().cl(); + let old_files = cl_svc.get_commit_blobs(&cl.from_hash).await?; + let new_files = cl_svc.get_commit_blobs(&cl.to_hash).await?; + let cl_diff_files = cl_svc.cl_files_list(old_files, new_files.clone()).await?; // TODO let res = cl_diff_files .into_iter() @@ -280,7 +293,7 @@ async fn update_branch_status( Path(link): Path, state: State, ) -> Result>, ApiError> { - let res = state.monorepo().update_branch_status(&link).await?; + let res = state.services().cl().update_branch_status(&link).await?; Ok(Json(CommonResult::success(Some(res)))) } @@ -302,7 +315,8 @@ async fn update_branch( state: State, ) -> Result>, ApiError> { let new_head = state - .monorepo() + .services() + .cl() .update_branch_with_webhook(&user.username, &link) .await?; Ok(Json(CommonResult::success(Some(new_head)))) @@ -324,7 +338,7 @@ async fn merge_box( Path(link): Path, state: State, ) -> Result>, ApiError> { - let res = state.monorepo().get_merge_box(&link).await?; + let res = state.services().cl().get_merge_box(&link).await?; Ok(Json(CommonResult::success(Some(res)))) } @@ -348,7 +362,8 @@ async fn save_comment( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .cl() .save_cl_comment(&link, &user.username, &payload.content) .await?; api_common::comment::check_comment_ref(user, state, &payload.content, &link).await @@ -374,7 +389,8 @@ async fn edit_title( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .cl() .edit_cl_title(&link, &payload.content) .await?; Ok(Json(CommonResult::success(None))) @@ -436,7 +452,8 @@ async fn update_cl_status( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .cl() .update_cl_status(&link, &user.username, &payload) .await?; Ok(Json(CommonResult::success(None))) diff --git a/mono/src/api/router/code_review_router.rs b/mono/src/api/router/code_review_router.rs index 2d514a008..1c818c79e 100644 --- a/mono/src/api/router/code_review_router.rs +++ b/mono/src/api/router/code_review_router.rs @@ -44,7 +44,11 @@ async fn code_review_comment_list( Path(link): Path, state: State, ) -> Result>, ApiError> { - let comments = state.monorepo().get_code_review_comments(&link).await?; + let comments = state + .services() + .code_review() + .get_code_review_comments(&link) + .await?; Ok(Json(CommonResult::success(Some(comments)))) } @@ -68,7 +72,8 @@ async fn initialize_code_review_comment( Json(paload): Json, ) -> Result>, ApiError> { let thread = state - .monorepo() + .services() + .code_review() .create_code_review_comment(&link, user.username, paload) .await?; @@ -94,7 +99,8 @@ async fn reply_code_review_comment( Json(payload): Json, ) -> Result>, ApiError> { let comment = state - .monorepo() + .services() + .code_review() .reply_code_review_comment(thread_id, user.username, payload) .await?; @@ -120,7 +126,8 @@ async fn update_code_review_comment( Json(payload): Json, ) -> Result>, ApiError> { let comment = state - .monorepo() + .services() + .code_review() .update_code_review_comment(comment_id, &user.username, payload) .await?; @@ -144,7 +151,8 @@ async fn resolve_code_review_thread( state: State, ) -> Result>, ApiError> { let thread = state - .monorepo() + .services() + .code_review() .resolve_code_review_thread(thread_id) .await?; @@ -168,7 +176,8 @@ async fn reopen_code_review_thread( state: State, ) -> Result>, ApiError> { let thread = state - .monorepo() + .services() + .code_review() .reopen_code_review_thread(thread_id) .await?; @@ -192,7 +201,8 @@ async fn delete_code_review_thread( state: State, ) -> Result>, ApiError> { state - .monorepo() + .services() + .code_review() .delete_code_review_thread(thread_id) .await?; @@ -217,7 +227,8 @@ async fn delete_code_review_comment( state: State, ) -> Result>, ApiError> { state - .monorepo() + .services() + .code_review() .delete_code_review_comment(comment_id, &user.username) .await?; diff --git a/mono/src/api/router/commit_router.rs b/mono/src/api/router/commit_router.rs index df002f3e9..a33f95010 100644 --- a/mono/src/api/router/commit_router.rs +++ b/mono/src/api/router/commit_router.rs @@ -46,7 +46,7 @@ async fn update_commit_binding( Json(request): Json, ) -> Result>, ApiError> { let response = state - .monorepo() + .git() .upsert_commit_binding(&sha, request.username.clone(), request.is_anonymous) .await?; @@ -91,12 +91,11 @@ async fn list_commit_history( )) })?; - let repo_selector = - if let Some(model) = state.monorepo().find_git_repo_like_path(path_str).await? { - PathBuf::from(model.repo_path) - } else { - abs_path.clone() - }; + let repo_selector = if let Some(model) = state.git().find_git_repo_like_path(path_str).await? { + PathBuf::from(model.repo_path) + } else { + abs_path.clone() + }; // Create handler using the repository selector (repo root), not the subdirectory. let handler = state.api_handler(&repo_selector).await?; diff --git a/mono/src/api/router/conv_router.rs b/mono/src/api/router/conv_router.rs index 72993fe5c..d5ccee3c9 100644 --- a/mono/src/api/router/conv_router.rs +++ b/mono/src/api/router/conv_router.rs @@ -41,7 +41,8 @@ async fn comment_reactions( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .conversation() .add_comment_reaction( Some(payload.content), comment_id, @@ -70,7 +71,8 @@ async fn delete_comment_reaction( state: State, ) -> Result>, ApiError> { state - .monorepo() + .services() + .conversation() .delete_comment_reaction(&id, &user.username) .await?; Ok(Json(CommonResult::success(None))) @@ -92,7 +94,11 @@ async fn delete_comment( Path(comment_id): Path, state: State, ) -> Result>, ApiError> { - state.monorepo().remove_conversation(comment_id).await?; + state + .services() + .conversation() + .remove_conversation(comment_id) + .await?; Ok(Json(CommonResult::success(None))) } @@ -116,7 +122,8 @@ async fn edit_comment( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .conversation() .update_comment(comment_id, Some(payload.content)) .await?; Ok(Json(CommonResult::success(None))) diff --git a/mono/src/api/router/dynamic_sidebar_router.rs b/mono/src/api/router/dynamic_sidebar_router.rs index c44e9eaea..6ceebc7da 100644 --- a/mono/src/api/router/dynamic_sidebar_router.rs +++ b/mono/src/api/router/dynamic_sidebar_router.rs @@ -34,7 +34,7 @@ pub fn routers() -> OpenApiRouter { async fn sidebar_menu_list( state: State, ) -> Result>, ApiError> { - let items = state.monorepo().list_sidebars().await?; + let items = state.services().sidebar().list_sidebars().await?; Ok(Json(CommonResult::success(Some(items)))) } @@ -53,7 +53,8 @@ async fn new_sidebar( Json(json): Json, ) -> Result>, ApiError> { let res = state - .monorepo() + .services() + .sidebar() .new_sidebar( json.public_id, json.label, @@ -84,7 +85,8 @@ async fn update_sidebar_by_id( Json(json): Json, ) -> Result>, ApiError> { let res = state - .monorepo() + .services() + .sidebar() .update_sidebar( id, json.public_id, @@ -120,7 +122,7 @@ async fn sync_sidebar( state: State, Json(payloads): Json>, ) -> Result>>, ApiError> { - let res = state.monorepo().sync_sidebars(payloads).await?; + let res = state.services().sidebar().sync_sidebars(payloads).await?; Ok(Json(CommonResult::success(Some(res)))) } @@ -141,6 +143,6 @@ async fn delete_sidebar_by_id( state: State, Path(id): Path, ) -> Result>, ApiError> { - let res = state.monorepo().delete_sidebar(id).await?; + let res = state.services().sidebar().delete_sidebar(id).await?; Ok(Json(CommonResult::success(Some(res)))) } diff --git a/mono/src/api/router/gpg_router.rs b/mono/src/api/router/gpg_router.rs index 2943204c3..f7565a106 100644 --- a/mono/src/api/router/gpg_router.rs +++ b/mono/src/api/router/gpg_router.rs @@ -30,7 +30,11 @@ async fn remove_gpg( ) -> Result>, ApiError> { // let uid = "exampleid".to_string(); let uid = user.campsite_user_id.clone(); - state.monorepo().remove_gpg_key(uid, req.key_id).await?; + state + .services() + .user() + .remove_gpg_key(uid, req.key_id) + .await?; Ok(Json(CommonResult::success(None))) } @@ -51,7 +55,11 @@ async fn add_gpg( // let uid = "exampleid".to_string(); let uid = user.campsite_user_id.clone(); println!("Adding GPG key for user: {}", req.gpg_content.clone()); - state.monorepo().add_gpg_key(uid, req.gpg_content).await?; + state + .services() + .user() + .add_gpg_key(uid, req.gpg_content) + .await?; Ok(Json(CommonResult::success(None))) } @@ -69,7 +77,7 @@ async fn list_gpg( ) -> Result>>, ApiError> { // let uid = "exampleid".to_string(); let uid = user.campsite_user_id; - let res = state.monorepo().list_user_gpg_keys(uid).await?; + let res = state.services().user().list_user_gpg_keys(uid).await?; Ok(Json(CommonResult::success(Some(res)))) } diff --git a/mono/src/api/router/group_router.rs b/mono/src/api/router/group_router.rs index 2dcdb1c7c..ab6e36688 100644 --- a/mono/src/api/router/group_router.rs +++ b/mono/src/api/router/group_router.rs @@ -63,7 +63,7 @@ async fn create_group( ) -> Result>, ApiError> { ensure_admin(&state, &user).await?; - let group = state.monorepo().create_group(req).await?; + let group = state.services().admin().create_group(req).await?; Ok(Json(CommonResult::success(Some(group.into())))) } @@ -87,7 +87,11 @@ async fn list_groups( ) -> Result>>, ApiError> { ensure_admin(&state, &user).await?; - let (items, total) = state.monorepo().list_groups(json.pagination).await?; + let (items, total) = state + .services() + .admin() + .list_groups(json.pagination) + .await?; let items = items.into_iter().map(Into::into).collect(); Ok(Json(CommonResult::success(Some(CommonPage { @@ -118,7 +122,8 @@ async fn get_group( ensure_admin(&state, &user).await?; let group = state - .monorepo() + .services() + .admin() .get_group_by_id(group_id) .await? .ok_or_else(|| { @@ -159,7 +164,8 @@ async fn update_group( ensure_admin(&state, &user).await?; let updated = state - .monorepo() + .services() + .admin() .update_group( group_id, UpdateGroupRequest { @@ -193,7 +199,7 @@ async fn delete_group( ) -> Result>, ApiError> { ensure_admin(&state, &user).await?; - let stats = state.monorepo().delete_group(group_id).await?; + let stats = state.services().admin().delete_group(group_id).await?; Ok(Json(CommonResult::success(Some(DeleteGroupResponse { group_id, @@ -228,7 +234,8 @@ async fn add_group_members( ensure_admin(&state, &user).await?; let members = state - .monorepo() + .services() + .admin() .add_group_members(group_id, req.usernames) .await?; let members = members.into_iter().map(Into::into).collect(); @@ -258,7 +265,8 @@ async fn remove_group_member( ) -> Result>, ApiError> { ensure_admin(&state, &user).await?; let removed = state - .monorepo() + .services() + .admin() .remove_group_member(group_id, &username) .await?; @@ -294,7 +302,8 @@ async fn list_group_members( ensure_admin(&state, &user).await?; let (items, total) = state - .monorepo() + .services() + .admin() .list_group_members(group_id, json.pagination) .await?; let items = items.into_iter().map(Into::into).collect(); @@ -333,7 +342,8 @@ async fn set_resource_permissions( resolve_resource_context(&state, resource_type.as_str(), &resource_id).await?; let saved = state - .monorepo() + .services() + .admin() .set_resource_permission(resource_type_value.into(), &resource_id, req.permissions) .await?; let saved = saved.into_iter().map(Into::into).collect(); @@ -367,7 +377,8 @@ async fn get_resource_permissions( resolve_resource_context(&state, resource_type.as_str(), &resource_id).await?; let permissions = state - .monorepo() + .services() + .admin() .get_resource_permissions(resource_type_value.into(), &resource_id) .await?; let permissions = permissions.into_iter().map(Into::into).collect(); @@ -403,7 +414,8 @@ async fn update_resource_permissions( resolve_resource_context(&state, resource_type.as_str(), &resource_id).await?; let updated = state - .monorepo() + .services() + .admin() .update_resource_permissions(resource_type_value.into(), &resource_id, req.permissions) .await?; let updated = updated.into_iter().map(Into::into).collect(); @@ -437,7 +449,8 @@ async fn delete_resource_permissions( resolve_resource_context(&state, resource_type.as_str(), &resource_id).await?; let deleted_count = state - .monorepo() + .services() + .admin() .delete_resource_permissions(resource_type_value.into(), &resource_id) .await?; @@ -470,7 +483,7 @@ async fn get_user_groups( ) -> Result>, ApiError> { ensure_admin(&state, &user).await?; - let groups = state.monorepo().get_user_groups(&username).await?; + let groups = state.services().admin().get_user_groups(&username).await?; let groups = groups.into_iter().map(Into::into).collect(); Ok(Json(CommonResult::success(Some(UserGroupsResponse { @@ -506,7 +519,8 @@ async fn get_user_effective_permission( resolve_resource_context(&state, resource_type.as_str(), &resource_id).await?; let effective = state - .monorepo() + .services() + .admin() .get_user_effective_permission(&username, resource_type_value.into(), &resource_id) .await?; let response = build_user_effective_permission_response( diff --git a/mono/src/api/router/issue_router.rs b/mono/src/api/router/issue_router.rs index 0dd43c0e5..624644ef4 100644 --- a/mono/src/api/router/issue_router.rs +++ b/mono/src/api/router/issue_router.rs @@ -47,7 +47,8 @@ async fn fetch_issue_list( Json(json): Json>, ) -> Result>>, ApiError> { let (items, total) = state - .monorepo() + .services() + .issue() .get_issue_list(json.additional, json.pagination) .await?; Ok(Json(CommonResult::success(Some(CommonPage { @@ -74,7 +75,8 @@ async fn issue_detail( state: State, ) -> Result>, ApiError> { let issue_details = state - .monorepo() + .services() + .issue() .get_issue_details(&link, user.username) .await?; Ok(Json(CommonResult::success(Some(issue_details)))) @@ -96,11 +98,13 @@ async fn new_issue( Json(json): Json, ) -> Result>, ApiError> { let res = state - .monorepo() + .services() + .issue() .save_issue(&user.username, &json.title) .await?; state - .monorepo() + .services() + .conversation() .add_conversation( &res.link, &user.username, @@ -128,9 +132,10 @@ async fn close_issue( Path(link): Path, state: State, ) -> Result>, ApiError> { - state.monorepo().close_issue(&link).await?; + state.services().issue().close_issue(&link).await?; state - .monorepo() + .services() + .conversation() .add_conversation( &link, &user.username, @@ -158,9 +163,10 @@ async fn reopen_issue( Path(link): Path, state: State, ) -> Result>, ApiError> { - state.monorepo().reopen_issue(&link).await?; + state.services().issue().reopen_issue(&link).await?; state - .monorepo() + .services() + .conversation() .add_conversation( &link, &user.username, @@ -191,7 +197,8 @@ async fn save_comment( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .conversation() .add_conversation( &link, &user.username, @@ -258,7 +265,8 @@ async fn edit_title( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .issue() .edit_issue_title(&link, &payload.content) .await?; Ok(Json(CommonResult::success(None))) @@ -279,7 +287,8 @@ async fn issue_suggester( state: State, ) -> Result>>, ApiError> { let res = state - .monorepo() + .services() + .issue() .get_issue_suggestions(&payload.query) .await?; Ok(Json(CommonResult::success(Some(res)))) diff --git a/mono/src/api/router/label_router.rs b/mono/src/api/router/label_router.rs index 0d407b1ab..9bf2c8155 100644 --- a/mono/src/api/router/label_router.rs +++ b/mono/src/api/router/label_router.rs @@ -35,7 +35,8 @@ async fn fetch_label_list( Json(json): Json>, ) -> Result>>, ApiError> { let (items, total) = state - .monorepo() + .services() + .issue() .list_labels_by_page(json.pagination, &json.additional) .await?; Ok(Json(CommonResult::success(Some(CommonPage { @@ -60,7 +61,8 @@ async fn new_label( Json(json): Json, ) -> Result>, ApiError> { let res = state - .monorepo() + .services() + .issue() .new_label(&json.name, &json.color, &json.description) .await?; Ok(Json(CommonResult::success(Some(res)))) @@ -82,6 +84,6 @@ async fn fetch_label( state: State, Path(id): Path, ) -> Result>, ApiError> { - let label = state.monorepo().get_label_by_id(id).await?; + let label = state.services().issue().get_label_by_id(id).await?; Ok(Json(CommonResult::success(label))) } diff --git a/mono/src/api/router/lfs_router.rs b/mono/src/api/router/lfs_router.rs index 31388336d..988c74dc5 100644 --- a/mono/src/api/router/lfs_router.rs +++ b/mono/src/api/router/lfs_router.rs @@ -48,12 +48,9 @@ use axum::{ http::{Request, StatusCode}, response::Response, }; -use ceres::lfs::{ - handler, - lfs_structs::{ - BatchRequest, BatchResponse, LockList, LockListQuery, LockRequest, LockResponse, - RequestObject, UnlockRequest, UnlockResponse, VerifiableLockList, VerifiableLockRequest, - }, +use ceres::lfs::lfs_structs::{ + BatchRequest, BatchResponse, LockList, LockListQuery, LockRequest, LockResponse, RequestObject, + UnlockRequest, UnlockResponse, VerifiableLockList, VerifiableLockRequest, }; use common::errors::GitLFSError; use futures::TryStreamExt; @@ -150,7 +147,7 @@ pub async fn list_locks( Query(query): Query, ) -> Result, (StatusCode, String)> { let result: Result = - handler::lfs_retrieve_lock(state.lfs_db_storage(), query).await; + state.services().lfs().lfs_retrieve_lock(query).await; match result { Ok(lock_list) => { let body = serde_json::to_string(&lock_list).unwrap_or_default(); @@ -191,7 +188,7 @@ pub async fn list_locks_for_verification( state: State, Json(json): Json, ) -> Result, (StatusCode, String)> { - let result = handler::lfs_verify_lock(state.lfs_db_storage(), json).await; + let result = state.services().lfs().lfs_verify_lock(json).await; match result { Ok(lock_list) => { let body = serde_json::to_string(&lock_list).unwrap_or_default(); @@ -227,7 +224,7 @@ pub async fn create_lock( state: State, Json(json): Json, ) -> Result, (StatusCode, String)> { - let result = handler::lfs_create_lock(state.lfs_db_storage(), json).await; + let result = state.services().lfs().lfs_create_lock(json).await; match result { Ok(lock) => { let lock_response = LockResponse { @@ -272,7 +269,7 @@ pub async fn delete_lock( Path(id): Path, Json(json): Json, ) -> Result { - let result = handler::lfs_delete_lock(state.lfs_db_storage(), &id, json).await; + let result = state.services().lfs().lfs_delete_lock(&id, json).await; match result { Ok(lock) => { @@ -314,7 +311,11 @@ pub async fn lfs_process_batch( state: State, Json(json): Json, ) -> Result, (StatusCode, String)> { - let result = handler::lfs_process_batch(&state.lfs_service(), json, state.listen_addr()).await; + let result = state + .services() + .lfs() + .lfs_process_batch(json, state.listen_addr()) + .await; match result { Ok(res) => { @@ -354,7 +355,11 @@ pub async fn lfs_download_object( state: State, Path(oid): Path, ) -> Result { - let result = handler::lfs_download_object(state.lfs_service(), oid.clone()).await; + let result = state + .services() + .lfs() + .lfs_download_object(oid.clone()) + .await; match result { Ok(byte_stream) => Ok(Response::builder() .header("Content-Type", LFS_STREAM_CONTENT_TYPE) @@ -407,7 +412,11 @@ pub async fn lfs_upload_object( .await .unwrap(); - let result = handler::lfs_upload_object(&state.lfs_service(), &req_obj, body_bytes).await; + let result = state + .services() + .lfs() + .lfs_upload_object(&req_obj, body_bytes) + .await; match result { Ok(_) => Ok(Response::builder() .header("Content-Type", LFS_CONTENT_TYPE) diff --git a/mono/src/api/router/merge_queue_router.rs b/mono/src/api/router/merge_queue_router.rs index 0faa00250..557b288ef 100644 --- a/mono/src/api/router/merge_queue_router.rs +++ b/mono/src/api/router/merge_queue_router.rs @@ -41,7 +41,8 @@ async fn add_to_queue( Json(request): Json, ) -> Result>, ApiError> { match state - .monorepo() + .services() + .cl() .add_to_merge_queue_response(request.cl_link) .await { @@ -65,7 +66,12 @@ async fn remove_from_queue( state: State, Path(cl_link): Path, ) -> Result>, ApiError> { - match state.monorepo().remove_from_merge_queue(&cl_link).await { + match state + .services() + .cl() + .remove_from_merge_queue(&cl_link) + .await + { Ok(true) => Ok(Json(CommonResult::success(Some(json!({ "success": true, "message": "Removed from queue" @@ -86,7 +92,7 @@ async fn remove_from_queue( async fn get_queue_list( state: State, ) -> Result>, ApiError> { - let response = state.monorepo().get_merge_queue_list().await?; + let response = state.services().cl().get_merge_queue_list().await?; Ok(Json(CommonResult::success(Some(response)))) } @@ -105,7 +111,7 @@ async fn get_cl_queue_status( state: State, Path(cl_link): Path, ) -> Result>, ApiError> { - let response = state.monorepo().get_cl_queue_status(&cl_link).await?; + let response = state.services().cl().get_cl_queue_status(&cl_link).await?; Ok(Json(CommonResult::success(Some(response)))) } @@ -126,7 +132,7 @@ async fn retry_queue_item( state: State, Path(cl_link): Path, ) -> Result>, ApiError> { - match state.monorepo().retry_merge_queue_item(&cl_link).await { + match state.services().cl().retry_merge_queue_item(&cl_link).await { Ok(true) => Ok(Json(CommonResult::success(Some(json!({ "success": true, "message": "Item retried" @@ -150,7 +156,7 @@ async fn retry_queue_item( async fn get_queue_stats( state: State, ) -> Result>, ApiError> { - let response = state.monorepo().get_merge_queue_stats().await?; + let response = state.services().cl().get_merge_queue_stats().await?; Ok(Json(CommonResult::success(Some(response)))) } @@ -165,7 +171,11 @@ async fn get_queue_stats( async fn cancel_all_pending( state: State, ) -> Result>, ApiError> { - state.monorepo().cancel_all_pending_merge_queue().await?; + state + .services() + .cl() + .cancel_all_pending_merge_queue() + .await?; Ok(Json(CommonResult::success(Some(json!({ "success": true, "message": "All pending items cancelled" diff --git a/mono/src/api/router/permission_router.rs b/mono/src/api/router/permission_router.rs index 19d917509..0756e094d 100644 --- a/mono/src/api/router/permission_router.rs +++ b/mono/src/api/router/permission_router.rs @@ -49,7 +49,8 @@ async fn get_my_permission( resolve_resource_context(&state, &resource_type, &resource_id).await?; let effective = state - .monorepo() + .services() + .admin() .get_user_effective_permission(&actor, resource_type_value.into(), &normalized_id) .await?; diff --git a/mono/src/api/router/preview_router.rs b/mono/src/api/router/preview_router.rs index 03f6e929f..24a43a291 100644 --- a/mono/src/api/router/preview_router.rs +++ b/mono/src/api/router/preview_router.rs @@ -32,7 +32,7 @@ async fn upsert_commit_binding( } }); state - .monorepo() + .git() .upsert_commit_binding(commit_id, final_username.clone(), final_username.is_none()) .await?; Ok(()) @@ -296,10 +296,10 @@ async fn path_can_be_cloned( state: State, ) -> Result>, ApiError> { let path: PathBuf = query.path.clone().into(); - let import_dir = state.monorepo().import_dir(); + let import_dir = state.git().import_dir(); let path_str = path.to_str().unwrap(); let res = if path.starts_with(&import_dir) { - match state.monorepo().find_git_repo_like_path(path_str).await? { + match state.git().find_git_repo_like_path(path_str).await? { Some(model) => model.repo_path == path_str, None => false, } diff --git a/mono/src/api/router/repo_router.rs b/mono/src/api/router/repo_router.rs index a1cc9a16b..79ab48e0d 100644 --- a/mono/src/api/router/repo_router.rs +++ b/mono/src/api/router/repo_router.rs @@ -38,8 +38,15 @@ async fn clone_third_party_repo( let path = MonoServiceLogic::validate_github_sync_path(&payload.path)?; let path = PathBuf::from(path); state - .monorepo() - .sync_third_party_repo(&payload.owner, &payload.repo, path, &user.username) + .services() + .git() + .sync_third_party_repo( + state.services().transport_runtime(), + &payload.owner, + &payload.repo, + path, + &user.username, + ) .await?; Ok(Json(CommonResult::success(None))) diff --git a/mono/src/api/router/reviewer_router.rs b/mono/src/api/router/reviewer_router.rs index fdc5a9625..38e8477b7 100644 --- a/mono/src/api/router/reviewer_router.rs +++ b/mono/src/api/router/reviewer_router.rs @@ -48,7 +48,8 @@ async fn add_reviewers( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .reviewer() .add_reviewers(&link, payload.reviewer_usernames.clone()) .await?; @@ -62,7 +63,8 @@ async fn add_reviewers( for reviewer in payload.reviewer_usernames { state - .monorepo() + .services() + .conversation() .add_conversation( &link, &user.username, @@ -97,7 +99,8 @@ async fn remove_reviewers( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .reviewer() .remove_reviewers(&link, &payload.reviewer_usernames) .await?; @@ -111,7 +114,8 @@ async fn remove_reviewers( for reviewer in &payload.reviewer_usernames { state - .monorepo() + .services() + .conversation() .add_conversation( &link, &user.username, @@ -139,7 +143,7 @@ async fn list_reviewers( Path(link): Path, state: State, ) -> Result>, ApiError> { - let reviewers = state.monorepo().list_reviewers(&link).await?; + let reviewers = state.services().reviewer().list_reviewers(&link).await?; Ok(Json(CommonResult::success(Some(reviewers)))) } @@ -163,19 +167,27 @@ async fn reviewer_approve( state: State, Json(payload): Json, ) -> Result>, ApiError> { - if state.monorepo().cl_merge_status(&link).await? == MergeStatus::Draft { + if state + .services() + .conversation() + .cl_merge_status(&link) + .await? + == MergeStatus::Draft + { return Err(ApiError::from(MegaError::Other( ERR_CL_NOT_READY_FOR_REVIEW.to_owned(), ))); } state - .monorepo() + .services() + .reviewer() .reviewer_change_state(&link, &user.username, payload.approved) .await?; state - .monorepo() + .services() + .conversation() .add_conversation( &link, &user.username, @@ -207,7 +219,11 @@ async fn review_resolve( Path(link): Path, Json(payload): Json, ) -> Result>, ApiError> { - let res = state.monorepo().is_reviewer(&link, &user.username).await?; + let res = state + .services() + .reviewer() + .is_reviewer(&link, &user.username) + .await?; if !res { return Err(ApiError::from(MegaError::Other( @@ -216,12 +232,14 @@ async fn review_resolve( } state - .monorepo() + .services() + .conversation() .change_review_state(&link, &payload.conversation_id, payload.resolved) .await?; state - .monorepo() + .services() + .conversation() .add_conversation( &link, &user.username, diff --git a/mono/src/api/router/tag_router.rs b/mono/src/api/router/tag_router.rs index 1de6d4be1..60af9eda1 100644 --- a/mono/src/api/router/tag_router.rs +++ b/mono/src/api/router/tag_router.rs @@ -24,7 +24,7 @@ async fn resolve_target_commit_id( target_opt: Option<&str>, ) -> Result { Ok(state - .monorepo() + .git() .resolve_target_commit_id(path_context, target_opt) .await?) } diff --git a/mono/src/api/router/user_router.rs b/mono/src/api/router/user_router.rs index 9018db574..8a1ad7f11 100644 --- a/mono/src/api/router/user_router.rs +++ b/mono/src/api/router/user_router.rs @@ -78,7 +78,8 @@ async fn add_key( json.title }; state - .monorepo() + .services() + .user() .save_ssh_key( user.username, &title, @@ -107,7 +108,8 @@ async fn remove_key( Path(key_id): Path, ) -> Result>, ApiError> { state - .monorepo() + .services() + .user() .delete_ssh_key(user.username, key_id) .await?; Ok(Json(CommonResult::success(None))) @@ -126,7 +128,11 @@ async fn list_key( user: LoginUser, state: State, ) -> Result>>, ApiError> { - let res = state.monorepo().list_user_ssh_keys(user.username).await?; + let res = state + .services() + .user() + .list_user_ssh_keys(user.username) + .await?; Ok(Json(CommonResult::success(Some(res)))) } @@ -143,7 +149,11 @@ async fn generate_token( user: LoginUser, state: State, ) -> Result>, ApiError> { - let res = state.monorepo().generate_user_token(user.username).await?; + let res = state + .services() + .user() + .generate_user_token(user.username) + .await?; Ok(Json(CommonResult::success(Some(res)))) } @@ -165,7 +175,8 @@ async fn remove_token( Path(key_id): Path, ) -> Result>, ApiError> { state - .monorepo() + .services() + .user() .delete_user_token(user.username, key_id) .await?; Ok(Json(CommonResult::success(None))) @@ -184,7 +195,11 @@ async fn list_token( user: LoginUser, state: State, ) -> Result>>, ApiError> { - let data = state.monorepo().list_user_tokens(user.username).await?; + let data = state + .services() + .user() + .list_user_tokens(user.username) + .await?; Ok(Json(CommonResult::success(Some(data)))) } @@ -199,7 +214,11 @@ async fn list_notification_types( _user: LoginUser, state: State, ) -> Result>>, ApiError> { - let types = state.monorepo().list_notification_event_types().await?; + let types = state + .services() + .user() + .list_notification_event_types() + .await?; Ok(Json(CommonResult::success(Some(types)))) } @@ -216,7 +235,8 @@ async fn get_notification_config( state: State, ) -> Result>, ApiError> { let config = state - .monorepo() + .services() + .user() .get_user_notification_config(&user.username, &user.email) .await?; @@ -237,7 +257,8 @@ async fn update_notification_config( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .user() .update_user_notification_config(&user.username, &user.email, payload) .await?; @@ -267,7 +288,8 @@ async fn get_cla_sign_status( state: State, ) -> Result>, ApiError> { let (cla_signed, cla_signed_at) = state - .monorepo() + .services() + .user() .get_or_init_cla_sign_status(&user.username) .await?; @@ -293,7 +315,8 @@ async fn change_sign_status( state: State, ) -> Result>, ApiError> { let (cla_signed, cla_signed_at) = state - .monorepo() + .services() + .user() .change_cla_sign_status(&user.username) .await?; @@ -318,7 +341,7 @@ async fn get_cla_content( _user: LoginUser, state: State, ) -> Result>, ApiError> { - let content = state.monorepo().get_cla_content().await?; + let content = state.services().user().get_cla_content().await?; Ok(Json(CommonResult::success(Some(ClaContentRes { content })))) } @@ -338,7 +361,8 @@ async fn update_cla_content( Json(payload): Json, ) -> Result>, ApiError> { state - .monorepo() + .services() + .user() .update_cla_content(&payload.content) .await?; Ok(Json(CommonResult::success(Some(ClaContentRes { diff --git a/mono/src/api/router/webhook_router.rs b/mono/src/api/router/webhook_router.rs index 98e8afc97..daa8470d4 100644 --- a/mono/src/api/router/webhook_router.rs +++ b/mono/src/api/router/webhook_router.rs @@ -29,7 +29,7 @@ async fn create_webhook( state: State, Json(payload): Json, ) -> Result>, ApiError> { - let created = state.monorepo().create_webhook(payload).await?; + let created = state.services().webhook().create_webhook(payload).await?; Ok(Json(CommonResult::success(Some(created)))) } @@ -51,7 +51,7 @@ async fn list_webhooks( Query(query): Query, ) -> Result>>, ApiError> { let pagination = build_webhook_pagination(query)?; - let (items, total) = state.monorepo().list_webhooks(pagination).await?; + let (items, total) = state.services().webhook().list_webhooks(pagination).await?; Ok(Json(CommonResult::success(Some(CommonPage { total, items, @@ -73,7 +73,7 @@ async fn delete_webhook( state: State, Path(id): Path, ) -> Result>, ApiError> { - state.monorepo().delete_webhook(id).await?; + state.services().webhook().delete_webhook(id).await?; Ok(Json(CommonResult::success(None))) } diff --git a/mono/src/git_protocol/http.rs b/mono/src/git_protocol/http.rs index 244873b1c..60eca07bf 100644 --- a/mono/src/git_protocol/http.rs +++ b/mono/src/git_protocol/http.rs @@ -8,11 +8,9 @@ use axum::{ use base64::Engine; use bytes::{Bytes, BytesMut}; use ceres::{ + TransportRuntime, infra::pack_stream::into_pack_byte_stream, - transport::{ - ProtocolApiState, - protocol::{PushUserInfo, ServiceType, SmartSession, TransportProtocol, smart}, - }, + transport::protocol::{PushUserInfo, ServiceType, SmartSession, TransportProtocol, smart}, }; use common::errors::{ProtocolError, mega_to_protocol_error}; use futures::{TryStreamExt, stream}; @@ -32,7 +30,7 @@ use crate::{ // where $servicename MUST be the service name the client wishes to contact to complete the operation. // The request MUST NOT contain additional query parameters. pub async fn git_info_refs( - state: &ProtocolApiState, + state: &TransportRuntime, params: InfoRefsParams, repo_path: std::path::PathBuf, ) -> Result, ProtocolError> { @@ -80,7 +78,7 @@ fn basic_auth_password_from_authorization_value(value: &str) -> Option { /// Uses [`crate::api::oauth::login_user_from_mono_access_token`] (same as [`crate::api::oauth::AccessTokenUser`]). /// Supports both Bearer tokens and Basic Auth (with token as password). async fn git_receive_pack_auth( - state: &ProtocolApiState, + state: &TransportRuntime, pack_protocol: &mut SmartSession, headers: &http::HeaderMap, ) -> Result { @@ -132,7 +130,7 @@ async fn git_receive_pack_auth( /// /// Finally, the constructed response with the response body is returned. pub async fn git_upload_pack( - state: &ProtocolApiState, + state: &TransportRuntime, req: Request, repo_path: std::path::PathBuf, ) -> Result, ProtocolError> { @@ -199,7 +197,7 @@ pub async fn git_upload_pack( /// - `Response`: The HTTP response with the result of the receive-pack operation. /// - `(StatusCode, String)`: A tuple with an HTTP status code and an error message in case of failure. pub async fn git_receive_pack( - state: &ProtocolApiState, + state: &TransportRuntime, req: Request, repo_path: std::path::PathBuf, ) -> Result, ProtocolError> { diff --git a/mono/src/git_protocol/ssh.rs b/mono/src/git_protocol/ssh.rs index 2c14493bb..7a6370df7 100644 --- a/mono/src/git_protocol/ssh.rs +++ b/mono/src/git_protocol/ssh.rs @@ -2,14 +2,12 @@ use std::{collections::HashMap, path::PathBuf, str::FromStr, sync::Arc}; use bytes::{Bytes, BytesMut}; use ceres::{ + TransportRuntime, infra::pack_stream::into_pack_byte_stream, lfs::lfs_structs::Link, - transport::{ - ProtocolApiState, - protocol::{ - ServiceType, SmartSession, TransportProtocol, - smart::{self}, - }, + transport::protocol::{ + ServiceType, SmartSession, TransportProtocol, + smart::{self}, }, }; use chrono::{DateTime, Duration, Utc}; @@ -34,7 +32,7 @@ pub struct SshServer { pub clients: Arc>, pub id: usize, pub smart_protocol: Option, - pub state: ProtocolApiState, + pub state: TransportRuntime, pub data_combined: BytesMut, } diff --git a/mono/src/lib.rs b/mono/src/lib.rs index dfe755a4d..49bca4f50 100644 --- a/mono/src/lib.rs +++ b/mono/src/lib.rs @@ -5,4 +5,5 @@ mod commands; pub mod email; pub mod git_protocol; pub mod notification; +pub mod orion_build_dispatch; pub mod server; diff --git a/mono/src/orion_build_dispatch.rs b/mono/src/orion_build_dispatch.rs new file mode 100644 index 000000000..17cc2bac1 --- /dev/null +++ b/mono/src/orion_build_dispatch.rs @@ -0,0 +1,38 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use ceres::application::build_trigger::BuildDispatchPort; +use common::errors::MegaError; +use orion_client::OrionBuildClient; + +/// Adapts [`OrionBuildClient`] to [`BuildDispatchPort`] for mono assembly. +pub struct OrionBuildDispatch { + inner: Arc, +} + +impl OrionBuildDispatch { + pub fn new(inner: Arc) -> Self { + Self { inner } + } + + pub fn into_arc(self) -> Arc { + Arc::new(self) + } +} + +#[async_trait] +impl BuildDispatchPort for OrionBuildDispatch { + fn enable_build(&self) -> bool { + self.inner.enable_build() + } + + async fn dispatch_build( + &self, + req: api_model::buck2::api::TaskBuildRequest, + ) -> Result { + self.inner + .on_post_receive(req) + .await + .map_err(|e| MegaError::Other(format!("Failed to dispatch build to Orion: {e}"))) + } +} diff --git a/mono/src/server/http_server.rs b/mono/src/server/http_server.rs index b03a9eb97..d00f75b06 100644 --- a/mono/src/server/http_server.rs +++ b/mono/src/server/http_server.rs @@ -11,8 +11,8 @@ use axum::{ routing::any, }; use ceres::{ + TransportRuntime, application::{api_service::cache::GitObjectCache, artifact::ArtifactApplicationService}, - transport::ProtocolApiState, }; use common::errors::ProtocolError; use http::{HeaderName, HeaderValue, Method}; @@ -44,6 +44,7 @@ use crate::{ bootstrap::AppContext, email::{NoopMailer, SmtpMailer}, git_protocol::InfoRefsParams, + orion_build_dispatch::OrionBuildDispatch, server::{CommonHttpOptions, trace_context}, }; @@ -384,9 +385,13 @@ pub async fn app(ctx: AppContext, host: String, port: u16) -> Router { prefix: "git-object-rkyv:v1".to_string(), }); + let orion_client = Arc::new(OrionBuildClient::new(storage.config().build.clone())); + let build_dispatch = OrionBuildDispatch::new(orion_client.clone()).into_arc(); + let api_state = MonoApiServiceState::new( storage.clone(), git_object_cache, + build_dispatch, Some(match oauth_config.api_store_backend { common::config::OauthApiStoreBackend::Campsite => { OAuthApiStore::Campsite(CampsiteApiStore::new(oauth_config.campsite_api_domain)) @@ -397,7 +402,6 @@ pub async fn app(ctx: AppContext, host: String, port: u16) -> Router { }), format!("http://{host}:{port}"), EntityStore::new(), - Arc::new(OrionBuildClient::new(storage.config().build.clone())), ); let origins: Vec = oauth_config @@ -435,7 +439,7 @@ pub async fn app(ctx: AppContext, host: String, port: u16) -> Router { move |req: Request| async move { match handle_smart_protocol( req, - Arc::new(ProtocolApiState::from_ref(&api_state)), + Arc::new(TransportRuntime::from_ref(&api_state)), ) .await { @@ -517,7 +521,7 @@ fn rewrite_lfs_request_uri(mut req: Request) -> Request { async fn handle_smart_protocol( req: Request, - state: Arc, + state: Arc, ) -> Result { let full_path = req.uri().path(); if is_disallowed_root_repo_path(full_path) { diff --git a/mono/src/server/ssh_server.rs b/mono/src/server/ssh_server.rs index b2ca5e59d..2bf653ea1 100644 --- a/mono/src/server/ssh_server.rs +++ b/mono/src/server/ssh_server.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; use bytes::BytesMut; -use ceres::{application::api_service::cache::GitObjectCache, transport::ProtocolApiState}; +use ceres::{TransportRuntime, application::api_service::cache::GitObjectCache}; use clap::Args; use ed25519_dalek::pkcs8::spki::der::pem::LineEnding; use russh::{ @@ -12,7 +12,10 @@ use russh::{ use tokio::sync::Mutex; use vault::integration::vault_core::VaultCoreInterface; -use crate::{bootstrap::AppContext, git_protocol::ssh::SshServer, server::CommonHttpOptions}; +use crate::{ + bootstrap::AppContext, git_protocol::ssh::SshServer, orion_build_dispatch::OrionBuildDispatch, + server::CommonHttpOptions, +}; #[derive(Args, Clone, Debug)] pub struct SshOptions { @@ -51,13 +54,21 @@ pub async fn start_server(ctx: AppContext, command: &SshOptions) { custom: SshCustom { ssh_port }, } = command; - let state = ProtocolApiState::new( + let orion_client = Arc::new(orion_client::OrionBuildClient::new( + ctx.storage.config().build.clone(), + )); + let build_dispatch = OrionBuildDispatch::new(orion_client).into_arc(); + + let git_cache = Arc::new(GitObjectCache { + connection: ctx.connection.clone(), + prefix: "git-object-rkyv:v1".to_string(), + }); + let (git, cl, _) = ceres::application::api_service::mono::build_mono_stack( ctx.storage.clone(), - Arc::new(GitObjectCache { - connection: ctx.connection.clone(), - prefix: "git-object-rkyv:v1".to_string(), - }), + git_cache.clone(), + Some(build_dispatch.clone()), ); + let state = TransportRuntime::new(ctx.storage.clone(), git_cache, git, cl); let mut ssh_server = SshServer { clients: Arc::new(Mutex::new(HashMap::new())), state, diff --git a/orion-scheduler/README.md b/orion-scheduler/README.md index 965a102f3..7628c7be2 100644 --- a/orion-scheduler/README.md +++ b/orion-scheduler/README.md @@ -302,7 +302,7 @@ orion-scheduler-vX.Y.Z-linux-amd64/ ```bash VERSION=v0.1.0 BUNDLE=orion-scheduler-${VERSION}-linux-amd64 -REPO=web3infra-foundation/mega +REPO=gitmono-dev/mega curl -LO https://github.com/${REPO}/releases/download/orion-scheduler-${VERSION}/${BUNDLE}.tar.gz curl -LO https://github.com/${REPO}/releases/download/orion-scheduler-${VERSION}/${BUNDLE}.tar.gz.sha256 diff --git a/orion-scheduler/systemd/orion-scheduler.service b/orion-scheduler/systemd/orion-scheduler.service index 3b83be148..ff4bb7368 100644 --- a/orion-scheduler/systemd/orion-scheduler.service +++ b/orion-scheduler/systemd/orion-scheduler.service @@ -1,6 +1,6 @@ [Unit] Description=Orion Scheduler (qlean-based KVM micro-VM controller) -Documentation=https://github.com/web3infra-foundation/mega/tree/main/orion-scheduler +Documentation=https://github.com/gitmono-dev/mega/tree/main/orion-scheduler After=network-online.target Wants=network-online.target diff --git a/orion/README.md b/orion/README.md index cb8031150..e42a4c9c7 100644 --- a/orion/README.md +++ b/orion/README.md @@ -60,7 +60,7 @@ Download and verify: ```bash VERSION=v0.1.1 BUNDLE=orion-${VERSION}-linux-amd64 -REPO=web3infra-foundation/mega +REPO=gitmono-dev/mega curl -LO https://github.com/${REPO}/releases/download/orion-${VERSION}/${BUNDLE}.tar.gz curl -LO https://github.com/${REPO}/releases/download/orion-${VERSION}/${BUNDLE}.tar.gz.sha256 diff --git a/scripts/init_mega/README.md b/scripts/init_mega/README.md index d0512dacc..259e7ec4f 100644 --- a/scripts/init_mega/README.md +++ b/scripts/init_mega/README.md @@ -89,7 +89,7 @@ In a temporary directory, it performs: Also in a temporary directory: 1. Clone `libra`: - - `git clone https://github.com/web3infra-foundation/libra.git .` + - `git clone https://github.com/gitmono-dev/libra.git .` 2. Use `third-party/` in the temporary directory as the scan root, and call the existing in-repo import script: - `python3 scripts/import-buck2-deps/import-buck2-deps.py --scan-root /third-party --git-base-url {base_url} ...`