1use std::sync::{Arc, LazyLock};
9
10use mas_data_model::{Clock, SiteConfig};
11use mas_email::Mailer;
12use mas_matrix::HomeserverConnection;
13use mas_router::UrlBuilder;
14use mas_storage::{BoxRepository, RepositoryError, RepositoryFactory};
15use mas_storage_pg::PgRepositoryFactory;
16use new_queue::QueueRunnerError;
17use opentelemetry::metrics::Meter;
18use rand::SeedableRng;
19use sqlx::{Pool, Postgres};
20use tokio_util::{sync::CancellationToken, task::TaskTracker};
21
22pub use crate::new_queue::QueueWorker;
23
24mod cleanup;
25mod email;
26mod invite_email;
27mod matrix;
28mod new_queue;
29mod recovery;
30mod sessions;
31mod user;
32
33static METER: LazyLock<Meter> = LazyLock::new(|| {
34 let scope = opentelemetry::InstrumentationScope::builder(env!("CARGO_PKG_NAME"))
35 .with_version(env!("CARGO_PKG_VERSION"))
36 .with_schema_url(opentelemetry_semantic_conventions::SCHEMA_URL)
37 .build();
38
39 opentelemetry::global::meter_with_scope(scope)
40});
41
42#[derive(Clone)]
43struct State {
44 repository_factory: PgRepositoryFactory,
45 mailer: Mailer,
46 clock: Arc<dyn Clock>,
47 homeserver: Arc<dyn HomeserverConnection>,
48 url_builder: UrlBuilder,
49 site_config: SiteConfig,
50}
51
52impl State {
53 pub fn new(
54 repository_factory: PgRepositoryFactory,
55 clock: impl Clock + 'static,
56 mailer: Mailer,
57 homeserver: impl HomeserverConnection + 'static,
58 url_builder: UrlBuilder,
59 site_config: SiteConfig,
60 ) -> Self {
61 Self {
62 repository_factory,
63 mailer,
64 clock: Arc::new(clock),
65 homeserver: Arc::new(homeserver),
66 url_builder,
67 site_config,
68 }
69 }
70
71 pub fn pool(&self) -> Pool<Postgres> {
72 self.repository_factory.pool()
73 }
74
75 pub fn clock(&self) -> &dyn Clock {
76 &self.clock
77 }
78
79 pub fn mailer(&self) -> &Mailer {
80 &self.mailer
81 }
82
83 #[expect(clippy::unused_self, clippy::disallowed_methods)]
85 pub fn rng(&self) -> rand_chacha::ChaChaRng {
86 rand_chacha::ChaChaRng::from_rng(&mut rand::rng())
87 }
88
89 pub async fn repository(&self) -> Result<BoxRepository, RepositoryError> {
90 self.repository_factory.create().await
91 }
92
93 pub fn matrix_connection(&self) -> &dyn HomeserverConnection {
94 self.homeserver.as_ref()
95 }
96
97 pub fn url_builder(&self) -> &UrlBuilder {
98 &self.url_builder
99 }
100
101 pub fn site_config(&self) -> &SiteConfig {
102 &self.site_config
103 }
104}
105
106pub async fn init(
114 repository_factory: PgRepositoryFactory,
115 clock: impl Clock + 'static,
116 mailer: &Mailer,
117 homeserver: impl HomeserverConnection + 'static,
118 url_builder: UrlBuilder,
119 site_config: &SiteConfig,
120 cancellation_token: CancellationToken,
121) -> Result<QueueWorker, QueueRunnerError> {
122 let state = State::new(
123 repository_factory,
124 clock,
125 mailer.clone(),
126 homeserver,
127 url_builder,
128 site_config.clone(),
129 );
130 let mut worker = QueueWorker::new(state, cancellation_token).await?;
131
132 worker
133 .register_handler::<mas_storage::queue::CleanupRevokedOAuthAccessTokensJob>()
134 .register_handler::<mas_storage::queue::CleanupExpiredOAuthAccessTokensJob>()
135 .register_handler::<mas_storage::queue::CleanupRevokedOAuthRefreshTokensJob>()
136 .register_handler::<mas_storage::queue::CleanupConsumedOAuthRefreshTokensJob>()
137 .register_handler::<mas_storage::queue::CleanupUserRegistrationsJob>()
138 .register_handler::<mas_storage::queue::CleanupFinishedCompatSessionsJob>()
139 .register_handler::<mas_storage::queue::CleanupFinishedOAuth2SessionsJob>()
140 .register_handler::<mas_storage::queue::CleanupFinishedUserSessionsJob>()
141 .register_handler::<mas_storage::queue::CleanupOAuthAuthorizationGrantsJob>()
142 .register_handler::<mas_storage::queue::CleanupOAuthDeviceCodeGrantsJob>()
143 .register_handler::<mas_storage::queue::CleanupUserRecoverySessionsJob>()
144 .register_handler::<mas_storage::queue::CleanupUserEmailAuthenticationsJob>()
145 .register_handler::<mas_storage::queue::CleanupUpstreamOAuthSessionsJob>()
146 .register_handler::<mas_storage::queue::CleanupUpstreamOAuthLinksJob>()
147 .register_handler::<mas_storage::queue::CleanupQueueJobsJob>()
148 .register_handler::<mas_storage::queue::DeactivateUserJob>()
149 .register_handler::<mas_storage::queue::DeleteDeviceJob>()
150 .register_handler::<mas_storage::queue::ProvisionDeviceJob>()
151 .register_handler::<mas_storage::queue::ProvisionUserJob>()
152 .register_handler::<mas_storage::queue::ReactivateUserJob>()
153 .register_handler::<mas_storage::queue::SendAccountRecoveryEmailsJob>()
154 .register_handler::<mas_storage::queue::SendEmailAuthenticationCodeJob>()
155 .register_handler::<mas_storage::queue::SendInviteEmailJob>()
156 .register_handler::<mas_storage::queue::SyncDevicesJob>()
157 .register_handler::<mas_storage::queue::VerifyEmailJob>()
158 .register_handler::<mas_storage::queue::ExpireInactiveSessionsJob>()
159 .register_handler::<mas_storage::queue::ExpireInactiveCompatSessionsJob>()
160 .register_handler::<mas_storage::queue::ExpireInactiveOAuthSessionsJob>()
161 .register_handler::<mas_storage::queue::ExpireInactiveUserSessionsJob>()
162 .register_handler::<mas_storage::queue::PruneStalePolicyDataJob>()
163 .register_handler::<mas_storage::queue::CleanupInactiveOAuth2SessionIpsJob>()
164 .register_handler::<mas_storage::queue::CleanupInactiveCompatSessionIpsJob>()
165 .register_handler::<mas_storage::queue::CleanupInactiveUserSessionIpsJob>()
166 .register_deprecated_queue("cleanup-expired-tokens")
167 .add_schedule(
170 "cleanup-revoked-oauth-access-tokens",
171 "0 0 * * * *".parse()?,
173 mas_storage::queue::CleanupRevokedOAuthAccessTokensJob,
174 )
175 .add_schedule(
176 "cleanup-revoked-oauth-refresh-tokens",
177 "0 5 * * * *".parse()?,
179 mas_storage::queue::CleanupRevokedOAuthRefreshTokensJob,
180 )
181 .add_schedule(
182 "cleanup-consumed-oauth-refresh-tokens",
183 "0 5 * * * *".parse()?,
185 mas_storage::queue::CleanupConsumedOAuthRefreshTokensJob,
186 )
187 .add_schedule(
188 "cleanup-finished-compat-sessions",
189 "0 10 * * * *".parse()?,
191 mas_storage::queue::CleanupFinishedCompatSessionsJob,
192 )
193 .add_schedule(
194 "cleanup-finished-oauth2-sessions",
195 "0 15 * * * *".parse()?,
197 mas_storage::queue::CleanupFinishedOAuth2SessionsJob,
198 )
199 .add_schedule(
200 "cleanup-finished-user-sessions",
201 "0 20 * * * *".parse()?,
203 mas_storage::queue::CleanupFinishedUserSessionsJob,
204 )
205 .add_schedule(
206 "cleanup-inactive-oauth2-session-ips",
207 "0 25 * * * *".parse()?,
209 mas_storage::queue::CleanupInactiveOAuth2SessionIpsJob,
210 )
211 .add_schedule(
212 "cleanup-inactive-compat-session-ips",
213 "0 25 * * * *".parse()?,
215 mas_storage::queue::CleanupInactiveCompatSessionIpsJob,
216 )
217 .add_schedule(
218 "cleanup-inactive-user-session-ips",
219 "0 25 * * * *".parse()?,
221 mas_storage::queue::CleanupInactiveUserSessionIpsJob,
222 )
223 .add_schedule(
224 "cleanup-oauth-authorization-grants",
225 "0 30 * * * *".parse()?,
227 mas_storage::queue::CleanupOAuthAuthorizationGrantsJob,
228 )
229 .add_schedule(
230 "cleanup-oauth-device-code-grants",
231 "0 35 * * * *".parse()?,
233 mas_storage::queue::CleanupOAuthDeviceCodeGrantsJob,
234 )
235 .add_schedule(
236 "cleanup-upstream-oauth-sessions",
237 "0 40 * * * *".parse()?,
239 mas_storage::queue::CleanupUpstreamOAuthSessionsJob,
240 )
241 .add_schedule(
242 "cleanup-upstream-oauth-links",
243 "0 40 * * * *".parse()?,
245 mas_storage::queue::CleanupUpstreamOAuthLinksJob,
246 )
247 .add_schedule(
249 "cleanup-user-registrations",
250 "0 45 * * * *".parse()?,
252 mas_storage::queue::CleanupUserRegistrationsJob,
253 )
254 .add_schedule(
255 "cleanup-user-recovery-sessions",
256 "0 50 * * * *".parse()?,
258 mas_storage::queue::CleanupUserRecoverySessionsJob,
259 )
260 .add_schedule(
261 "cleanup-user-email-authentications",
262 "0 50 * * * *".parse()?,
264 mas_storage::queue::CleanupUserEmailAuthenticationsJob,
265 )
266 .add_schedule(
267 "cleanup-queue-jobs",
268 "0 55 * * * *".parse()?,
270 mas_storage::queue::CleanupQueueJobsJob,
271 )
272 .add_schedule(
273 "cleanup-expired-oauth-access-tokens",
274 "0 5 */4 * * *".parse()?,
276 mas_storage::queue::CleanupExpiredOAuthAccessTokensJob,
277 )
278 .add_schedule(
279 "expire-inactive-sessions",
280 "30 */15 * * * *".parse()?,
282 mas_storage::queue::ExpireInactiveSessionsJob,
283 )
284 .add_schedule(
285 "prune-stale-policy-data",
286 "0 0 2 * * *".parse()?,
288 mas_storage::queue::PruneStalePolicyDataJob,
289 );
290
291 Ok(worker)
292}
293
294#[expect(clippy::too_many_arguments, reason = "this is fine")]
300pub async fn init_and_run(
301 repository_factory: PgRepositoryFactory,
302 clock: impl Clock + 'static,
303 mailer: &Mailer,
304 homeserver: impl HomeserverConnection + 'static,
305 url_builder: UrlBuilder,
306 site_config: &SiteConfig,
307 cancellation_token: CancellationToken,
308 task_tracker: &TaskTracker,
309) -> Result<(), QueueRunnerError> {
310 let worker = init(
311 repository_factory,
312 clock,
313 mailer,
314 homeserver,
315 url_builder,
316 site_config,
317 cancellation_token,
318 )
319 .await?;
320
321 task_tracker.spawn(worker.run());
322
323 Ok(())
324}