44// SPDX-License-Identifier: AGPL-3.0-only
55// Please see LICENSE in the repository root for full details.
66
7- use std:: time:: Duration ;
7+ use std:: { sync :: Arc , time:: Duration } ;
88
99use anyhow:: Context ;
1010use mas_config:: {
@@ -17,11 +17,14 @@ use mas_email::{MailTransport, Mailer};
1717use mas_handlers:: passwords:: PasswordManager ;
1818use mas_policy:: PolicyFactory ;
1919use mas_router:: UrlBuilder ;
20+ use mas_storage:: RepositoryAccess ;
21+ use mas_storage_pg:: PgRepository ;
2022use mas_templates:: { SiteConfigExt , TemplateLoadingError , Templates } ;
2123use sqlx:: {
2224 ConnectOptions , PgConnection , PgPool ,
2325 postgres:: { PgConnectOptions , PgPoolOptions } ,
2426} ;
27+ use tokio_util:: { sync:: CancellationToken , task:: TaskTracker } ;
2528use tracing:: { Instrument , log:: LevelFilter } ;
2629
2730pub async fn password_manager_from_config (
@@ -346,6 +349,63 @@ pub async fn database_connection_from_config(
346349 . context ( "could not connect to the database" )
347350}
348351
352+ /// Update the policy factory dynamic data from the database and spawn a task to
353+ /// periodically update it
354+ // XXX: this could be put somewhere else?
355+ pub async fn load_policy_factory_dynamic_data_continuously (
356+ policy_factory : & Arc < PolicyFactory > ,
357+ pool : & PgPool ,
358+ cancellation_token : CancellationToken ,
359+ task_tracker : & TaskTracker ,
360+ ) -> Result < ( ) , anyhow:: Error > {
361+ let policy_factory = policy_factory. clone ( ) ;
362+ let pool = pool. clone ( ) ;
363+
364+ load_policy_factory_dynamic_data ( & policy_factory, & pool) . await ?;
365+
366+ task_tracker. spawn ( async move {
367+ let mut interval = tokio:: time:: interval ( Duration :: from_secs ( 60 ) ) ;
368+
369+ loop {
370+ tokio:: select! {
371+ ( ) = cancellation_token. cancelled( ) => {
372+ return ;
373+ }
374+ _ = interval. tick( ) => { }
375+ }
376+
377+ if let Err ( err) = load_policy_factory_dynamic_data ( & policy_factory, & pool) . await {
378+ tracing:: error!( "Failed to load policy factory dynamic data: {}" , err) ;
379+ cancellation_token. cancel ( ) ;
380+ return ;
381+ }
382+ }
383+ } ) ;
384+
385+ Ok ( ( ) )
386+ }
387+
388+ /// Update the policy factory dynamic data from the database
389+ #[ tracing:: instrument( name = "policy.load_dynamic_data" , skip_all, err( Debug ) ) ]
390+ pub async fn load_policy_factory_dynamic_data (
391+ policy_factory : & PolicyFactory ,
392+ pool : & PgPool ,
393+ ) -> Result < ( ) , anyhow:: Error > {
394+ let mut repo = PgRepository :: from_pool ( pool)
395+ . await
396+ . context ( "Failed to acquire database connection" ) ?;
397+
398+ if let Some ( data) = repo. policy_data ( ) . get ( ) . await ? {
399+ let id = data. id ;
400+ let updated = policy_factory. set_dynamic_data ( data) . await ?;
401+ if updated {
402+ tracing:: info!( policy_data. id = %id, "Loaded dynamic policy data from the database" ) ;
403+ }
404+ }
405+
406+ Ok ( ( ) )
407+ }
408+
349409#[ cfg( test) ]
350410mod tests {
351411 use rand:: SeedableRng ;
0 commit comments