Skip to content
Clone

refactor(webapi): assemble domains once and expose the router

ZhenYi committed
parent 7a62b15 commit 350bd30 Browse files

12 files changed

+275 −155

File tree 12
Modified app/src/main.rs View app/src/main.rs
@@ -76,7 +76,7 @@
7676 )
7777 .await
7878 .context("building AppState")?;
79 let accounts = auth::Accounts::new(state.database.connection().clone(), state.cache.clone());
79 let deps = webapi::bootstrap::Deps::new(state.clone());
8080 info!("dependencies ready");
8181
8282 spawn_queue_depth_reporter(
@@ -87,10 +87,8 @@
8787 ],
8888 );
8989
90 tokio::spawn(chat::worker::run_background_tasks(
91 webapi::build_chat_service(&state, accounts.clone()),
92 ));
93 tokio::spawn(deletion::run_worker(state.clone(), accounts.clone()));
90 tokio::spawn(chat::worker::run_background_tasks(deps.chat.clone()));
91 tokio::spawn(deletion::run_worker(state.clone(), deps.accounts.clone()));
9492
9593 let relay = events::outbox::OutboxRelay::start(
9694 Some(auth_events),
@@ -114,12 +112,14 @@
114112
115113 let doc = api_doc();
116114 let app_state = state.clone();
115 let app_deps = deps.clone();
117116
118117 let server = HttpServer::new(move || {
119118 let state = app_state.clone();
119 let deps = app_deps.clone();
120120 let app = App::new()
121121 .app_data(web::Data::new(state.clone()))
122 .app_data(web::Data::new(accounts.clone()))
122 .app_data(web::Data::new(deps.accounts.clone()))
123123 .app_data(
124124 web::JsonConfig::default()
125125 .limit(webapi::MAX_REQUEST_BODY_BYTES)
@@ -142,7 +142,7 @@
142142 .finish()
143143 })))
144144 .service(SwaggerUi::new("/api/docs/{_:.*}").url("/api/docs/openapi.json", doc.clone()))
145 .service(webapi::scope(state))
145 .service(webapi::routes(&deps))
146146 .default_service(web::to(webapi::assets::serve))
147147 })
148148 .disable_signals()
Added lib/webapi/src/domains/account_deletion.rs View lib/webapi/src/domains/account_deletion.rs
@@ -0,0 +1,16 @@
1use export::AppState;
2use export::http::{Guard, Router};
3
4use crate::bootstrap::Deps;
5
6pub fn router(deps: &Deps) -> Router<()> {
7 Router::<AppState>::new()
8 .route(
9 "/me/deletion",
10 export::http::get(crate::account_deletion::account_deletion_status)
11 .post(crate::account_deletion::request_account_deletion)
12 .delete(crate::account_deletion::cancel_account_deletion),
13 )
14 .wrap(Guard::Session)
15 .with_state(deps.state.clone())
16}
Added lib/webapi/src/domains/auth.rs View lib/webapi/src/domains/auth.rs
@@ -0,0 +1,11 @@
1use export::http::Router;
2
3use crate::bootstrap::Deps;
4
5pub fn router(deps: &Deps) -> Router<()> {
6 auth::routes::router(deps.auth.clone()).with_state(deps.auth.clone())
7}
8
9pub fn pages(deps: &Deps) -> Router<()> {
10 auth::routes::pages_router(deps.auth.clone())
11}
Added lib/webapi/src/domains/chat.rs View lib/webapi/src/domains/chat.rs
@@ -0,0 +1,11 @@
1use export::http::Router;
2
3use crate::bootstrap::Deps;
4
5pub fn router(deps: &Deps) -> Router<()> {
6 chat::routes::router(deps.chat.clone(), deps.space.clone()).with_state(deps.chat.clone())
7}
8
9pub fn internal(deps: &Deps) -> Router<()> {
10 chat::routes::internal_router(deps.chat.clone()).with_state(deps.chat.clone())
11}
Added lib/webapi/src/domains/git.rs View lib/webapi/src/domains/git.rs
@@ -0,0 +1,11 @@
1use export::http::Router;
2
3use crate::bootstrap::Deps;
4
5pub fn router(deps: &Deps) -> Router<()> {
6 git::routes::router(deps.git.clone()).with_state(deps.git.clone())
7}
8
9pub fn internal(deps: &Deps) -> Router<()> {
10 git::routes::internal_router(deps.git.clone()).with_state(deps.git.clone())
11}
Added lib/webapi/src/domains/issues.rs View lib/webapi/src/domains/issues.rs
@@ -0,0 +1,7 @@
1use export::http::Router;
2
3use crate::bootstrap::Deps;
4
5pub fn router(deps: &Deps) -> Router<()> {
6 issues::routes::router(deps.issues.clone()).with_state(deps.issues.clone())
7}
Added lib/webapi/src/domains/mod.rs View lib/webapi/src/domains/mod.rs
@@ -0,0 +1,8 @@
1pub mod account_deletion;
2pub mod auth;
3pub mod chat;
4pub mod git;
5pub mod issues;
6pub mod pulls;
7pub mod space;
8pub mod user;
Added lib/webapi/src/domains/pulls.rs View lib/webapi/src/domains/pulls.rs
@@ -0,0 +1,7 @@
1use export::http::Router;
2
3use crate::bootstrap::Deps;
4
5pub fn router(deps: &Deps) -> Router<()> {
6 pulls::routes::router().with_state(deps.pulls.clone())
7}
Added lib/webapi/src/domains/space.rs View lib/webapi/src/domains/space.rs
@@ -0,0 +1,7 @@
1use export::http::Router;
2
3use crate::bootstrap::Deps;
4
5pub fn router(deps: &Deps) -> Router<()> {
6 space::routes::router(deps.space.clone()).with_state(deps.space.clone())
7}
Added lib/webapi/src/domains/user.rs View lib/webapi/src/domains/user.rs
@@ -0,0 +1,7 @@
1use export::http::Router;
2
3use crate::bootstrap::Deps;
4
5pub fn router(deps: &Deps) -> Router<()> {
6 user::routes::router(deps.user.clone()).with_state(deps.user.clone())
7}
Added lib/webapi/src/bootstrap.rs View lib/webapi/src/bootstrap.rs
@@ -0,0 +1,156 @@
1use std::sync::Arc;
2
3use export::AppState;
4
5/// Everything a request handler needs, built once at startup.
6///
7/// The actix worker closure only clones this value; no pool, client or
8/// service handle is created per worker thread.
9#[derive(Clone)]
10pub struct Deps {
11 pub state: AppState,
12 pub accounts: auth::Accounts,
13 pub access: space::Access,
14 pub notifications: export::notify::Notifications,
15 pub auth: auth::AuthService,
16 pub chat: chat::services::service::ChatService,
17 pub issues: issues::services::service::IssueService,
18 pub space: space::services::service::SpaceService,
19 pub user: user::services::service::UserService,
20 pub git: git::service::GitService,
21 pub pulls: pulls::services::service::PullService,
22 pub internal_service_key: String,
23}
24
25impl Deps {
26 pub fn new(state: AppState) -> Self {
27 let accounts =
28 auth::Accounts::new(state.database.connection().clone(), state.cache.clone());
29 let access = space::Access::new(state.database.connection().clone(), state.cache.clone());
30 let notifications =
31 export::notify::Notifications::new(state.database.clone(), state.redis.clone());
32 let auth = auth::AuthService::new(
33 accounts.clone(),
34 auth::services::AuthSettings::from_config(&state.config),
35 state.mail.clone(),
36 );
37 let chat = build_chat_service(&state, &accounts, &notifications, &access);
38 let issue_audience = issues::services::audience::Audience::new(
39 accounts.clone(),
40 notifications.clone(),
41 state.mail.clone(),
42 i18n::config_fallback(state.config.email.default_locale()),
43 );
44 let issues = issues::services::service::IssueService::new(
45 state.database.clone(),
46 state.storage.clone(),
47 issue_audience,
48 issues::services::service::IssueSettings::from_config(&state.config),
49 issues::services::admission::Admission::new(
50 access.clone(),
51 state.redis.clone(),
52 state.config.quota.clone(),
53 ),
54 );
55 let space = space::services::service::SpaceService::new(
56 access.clone(),
57 space::services::images::SpaceImages::new(
58 access.clone(),
59 state.storage.clone(),
60 state.redis.clone(),
61 ),
62 space::services::policy::SpacePolicy::new(
63 space::services::service::SpaceSettings::from_config(&state.config),
64 accounts.clone(),
65 state.redis.clone(),
66 ),
67 notifications.clone(),
68 );
69 let user = user::services::service::UserService::new(
70 state.database.clone(),
71 accounts.clone(),
72 access.clone(),
73 user::services::avatar::AvatarService::new(
74 state.database.clone(),
75 state.storage.clone(),
76 state.redis.clone(),
77 ),
78 );
79 let git_config = state.config.git.clone();
80 let kenshell = proto::KenShellClient::connect_with_key(
81 &git_config.kenshell_endpoint,
82 &git_config.kenshell_rpc_key,
83 std::time::Duration::from_secs(git_config.kenshell_timeout_secs.max(1)),
84 )
85 .unwrap_or_else(|error| {
86 panic!(
87 "invalid git.kenshell_endpoint `{}`: {error}",
88 git_config.kenshell_endpoint
89 )
90 });
91 let git = git::service::GitService::new(
92 state.database.clone(),
93 accounts.clone(),
94 access.clone(),
95 git::service::GitBackend::new(state.cache.clone(), git_config, Arc::new(kenshell)),
96 );
97 let pulls = pulls::services::service::PullService::new(
98 state.database.clone(),
99 git.clone(),
100 issues.clone(),
101 pulls::services::actors::Actors::new(
102 accounts.clone(),
103 state.redis.clone(),
104 state.config.quota.clone(),
105 ),
106 );
107 let internal_service_key = state.config.internal.service_key.clone();
108 Self {
109 state,
110 accounts,
111 access,
112 notifications,
113 auth,
114 chat,
115 issues,
116 space,
117 user,
118 git,
119 pulls,
120 internal_service_key,
121 }
122 }
123}
124
125fn build_chat_service(
126 state: &AppState,
127 accounts: &auth::Accounts,
128 notifications: &export::notify::Notifications,
129 access: &space::Access,
130) -> chat::services::service::ChatService {
131 let audience = chat::services::audience::Audience::new(
132 accounts.clone(),
133 notifications.clone(),
134 state.mail.clone(),
135 chat::share::realtime::Realtime::new(state.database.clone()),
136 i18n::config_fallback(state.config.email.default_locale()),
137 );
138 let participants = chat::services::presence::Participants::new(
139 state.database.clone(),
140 state.redis.clone(),
141 access.clone(),
142 audience.clone(),
143 chat::services::presence::ParticipantSettings::from_config(&state.config),
144 );
145 chat::services::service::ChatService::new(
146 state.database.clone(),
147 chat::services::media::Media::new(
148 state.storage.clone(),
149 state.redis.clone(),
150 state.database.clone(),
151 ),
152 audience,
153 chat::services::service::ChatSettings::from_config(&state.config),
154 participants,
155 )
156}
Modified lib/webapi/src/lib.rs View lib/webapi/src/lib.rs
@@ -1,6 +1,8 @@
11pub mod account_deletion;
22pub mod assets;
3pub mod bootstrap;
34pub mod cleanup;
5pub mod domains;
46pub mod middleware;
57pub mod openapi;
68
@@ -8,7 +10,6 @@
810
911use actix_web::http::Method;
1012use actix_web::{Scope, web};
11use export::AppState;
1213use export::http::{Guard, Router};
1314
1415pub const MAX_REQUEST_BODY_BYTES: usize = 2 * 1024 * 1024;
@@ -34,163 +35,41 @@
3435 cors
3536}
3637
37pub fn scope(state: AppState) -> Scope {
38 let accounts = auth::Accounts::new(state.database.connection().clone(), state.cache.clone());
39 let access = space::Access::new(state.database.connection().clone(), state.cache.clone());
40 let notifications =
41 export::notify::Notifications::new(state.database.clone(), state.redis.clone());
42 let auth = auth::AuthService::new(
43 accounts.clone(),
44 auth::services::AuthSettings::from_config(&state.config),
45 state.mail.clone(),
46 );
47 let chat = build_chat_service(&state, accounts.clone());
48 let issue_audience = issues::services::audience::Audience::new(
49 accounts.clone(),
50 notifications,
51 state.mail.clone(),
52 i18n::config_fallback(state.config.email.default_locale()),
53 );
54 let issues = issues::services::service::IssueService::new(
55 state.database.clone(),
56 state.storage.clone(),
57 issue_audience,
58 issues::services::service::IssueSettings::from_config(&state.config),
59 issues::services::admission::Admission::new(
60 access.clone(),
61 state.redis.clone(),
62 state.config.quota.clone(),
63 ),
64 );
65 let space = space::services::service::SpaceService::new(
66 access.clone(),
67 space::services::images::SpaceImages::new(
68 access.clone(),
69 state.storage.clone(),
70 state.redis.clone(),
71 ),
72 space::services::policy::SpacePolicy::new(
73 space::services::service::SpaceSettings::from_config(&state.config),
74 accounts.clone(),
75 state.redis.clone(),
76 ),
77 export::notify::Notifications::new(state.database.clone(), state.redis.clone()),
78 );
79 let user = user::services::service::UserService::new(
80 state.database.clone(),
81 accounts.clone(),
82 access.clone(),
83 user::services::avatar::AvatarService::new(
84 state.database.clone(),
85 state.storage.clone(),
86 state.redis.clone(),
87 ),
88 );
89 let git_config = state.config.git.clone();
90 let ks = proto::KenShellClient::connect_with_key(
91 &git_config.kenshell_endpoint,
92 &git_config.kenshell_rpc_key,
93 std::time::Duration::from_secs(git_config.kenshell_timeout_secs.max(1)),
94 )
95 .unwrap_or_else(|error| {
96 panic!(
97 "invalid git.kenshell_endpoint `{}`: {error}",
98 git_config.kenshell_endpoint
99 )
100 });
101 let git = git::service::GitService::new(
102 state.database.clone(),
103 accounts.clone(),
104 access,
105 git::service::GitBackend::new(state.cache.clone(), git_config, std::sync::Arc::new(ks)),
106 );
107 let pulls = pulls::services::service::PullService::new(
108 state.database.clone(),
109 git.clone(),
110 issues.clone(),
111 pulls::services::actors::Actors::new(
112 accounts.clone(),
113 state.redis.clone(),
114 state.config.quota.clone(),
115 ),
116 );
117 let internal_service_key = state.config.internal.service_key.clone();
118
119 let cleanup_router = cleanup::router(
120 state.database.clone(),
121 space.clone(),
122 issues.clone(),
123 git.clone(),
124 );
125
126 let account_deletion = Router::<AppState>::new()
127 .route(
128 "/me/deletion",
129 export::http::get(account_deletion::account_deletion_status)
130 .post(account_deletion::request_account_deletion)
131 .delete(account_deletion::cancel_account_deletion),
132 )
133 .wrap(Guard::Session)
134 .with_state(state.clone());
135 let accounts_for_http = accounts.clone();
38pub fn routes(deps: &bootstrap::Deps) -> Scope {
39 let state = &deps.state;
40 let accounts = deps.accounts.clone();
13641 let push_config = state.config.push.clone();
42 let internal = Router::<()>::new()
43 .merge(domains::git::internal(deps))
44 .merge(domains::chat::internal(deps))
45 .with_state(deps.internal_service_key.clone())
46 .wrap(Guard::ServiceKey);
13747 let public = Router::<()>::new()
13848 .setup(move |scope| {
13949 scope
140 .app_data(web::Data::new(accounts_for_http))
50 .app_data(web::Data::new(accounts))
14151 .app_data(web::Data::new(push_config))
14252 })
143 .merge(account_deletion)
144 .merge(::auth::routes::router(auth.clone()).with_state(auth.clone()))
145 .merge(chat::routes::router(chat.clone(), space.clone()).with_state(chat.clone()))
146 .merge(issues::routes::router(issues.clone()).with_state(issues))
147 .merge(space::routes::router(space.clone()).with_state(space))
148 .merge(user::routes::router(user.clone()).with_state(user))
149 .merge(git::routes::router(git.clone()).with_state(git.clone()))
150 .merge(pulls::routes::router().with_state(pulls))
151 .merge(cleanup_router);
152 let internal = Router::<()>::new()
153 .merge(git::routes::internal_router(git.clone()).with_state(git))
154 .merge(chat::routes::internal_router(chat.clone()).with_state(chat))
155 .with_state(internal_service_key)
156 .wrap(Guard::ServiceKey);
53 .merge(domains::account_deletion::router(deps))
54 .merge(domains::auth::router(deps))
55 .merge(domains::chat::router(deps))
56 .merge(domains::issues::router(deps))
57 .merge(domains::space::router(deps))
58 .merge(domains::user::router(deps))
59 .merge(domains::git::router(deps))
60 .merge(domains::pulls::router(deps))
61 .merge(cleanup::router(
62 state.database.clone(),
63 deps.space.clone(),
64 deps.issues.clone(),
65 deps.git.clone(),
66 ));
15767
15868 Router::<()>::new()
15969 .nest("/api/v1/internal", internal)
16070 .nest("/api/v1", public)
161 .nest("/pages", ::auth::routes::pages_router(auth.clone()))
162 .merge(export::health::router(state))
71 .nest("/pages", domains::auth::pages(deps))
72 .merge(export::health::router(state.clone()))
16373 .into_scope(web::scope(""))
16474 .default_service(web::to(assets::serve))
165}
166
167pub fn build_chat_service(
168 state: &AppState,
169 accounts: auth::Accounts,
170) -> chat::services::service::ChatService {
171 let audience = chat::services::audience::Audience::new(
172 accounts,
173 export::notify::Notifications::new(state.database.clone(), state.redis.clone()),
174 state.mail.clone(),
175 chat::share::realtime::Realtime::new(state.database.clone()),
176 i18n::config_fallback(state.config.email.default_locale()),
177 );
178 let participants = chat::services::presence::Participants::new(
179 state.database.clone(),
180 state.redis.clone(),
181 space::Access::new(state.database.connection().clone(), state.cache.clone()),
182 audience.clone(),
183 chat::services::presence::ParticipantSettings::from_config(&state.config),
184 );
185 chat::services::service::ChatService::new(
186 state.database.clone(),
187 chat::services::media::Media::new(
188 state.storage.clone(),
189 state.redis.clone(),
190 state.database.clone(),
191 ),
192 audience,
193 chat::services::service::ChatSettings::from_config(&state.config),
194 participants,
195 )
19675}