app.rs
1 use async_trait::async_trait; 2 use loco_openapi::prelude::*; 3 use loco_rs::{ 4 Result, 5 app::{AppContext, Hooks, Initializer}, 6 bgworker::{BackgroundWorker, Queue}, 7 boot::{BootResult, StartMode, create_app}, 8 config::Config, 9 controller::AppRoutes, 10 db::{self, truncate_table}, 11 environment::Environment, 12 task::Tasks, 13 }; 14 use migration::Migrator; 15 use std::path::Path; 16 17 #[allow(unused_imports)] 18 use crate::{ 19 controllers, initializers, models::_entities::users, tasks, workers::downloader::DownloadWorker, 20 }; 21 22 pub struct App; 23 #[async_trait] 24 impl Hooks for App { 25 fn app_name() -> &'static str { 26 env!("CARGO_CRATE_NAME") 27 } 28 29 fn app_version() -> String { 30 format!( 31 "{} ({})", 32 env!("CARGO_PKG_VERSION"), 33 option_env!("BUILD_SHA") 34 .or(option_env!("GITHUB_SHA")) 35 .unwrap_or("dev") 36 ) 37 } 38 39 async fn boot( 40 mode: StartMode, 41 environment: &Environment, 42 config: Config, 43 ) -> Result<BootResult> { 44 create_app::<Self, Migrator>(mode, environment, config).await 45 } 46 47 async fn initializers(_ctx: &AppContext) -> Result<Vec<Box<dyn Initializer>>> { 48 Ok(vec![ 49 Box::new(initializers::view_engine::ViewEngineInitializer), 50 Box::new(loco_openapi::OpenapiInitializerWithSetup::new( 51 |_ctx| { 52 #[derive(OpenApi)] 53 #[openapi( 54 modifiers(&SecurityAddon), 55 info( 56 title = "🧩 Microvisor Systems OpenAPI Spec 🧩", 57 description = "Beep Boop 🤖" 58 ) 59 )] 60 struct ApiDoc; 61 ApiDoc::openapi() 62 }, 63 None, // When using automatic schema collection only 64 // When using manual schema collection 65 // Manual schema collection can also be used at the same time as automatic schema collection 66 // Some(vec![controllers::album::api_routes()]), 67 )), 68 ]) 69 } 70 71 fn routes(_ctx: &AppContext) -> AppRoutes { 72 AppRoutes::with_default_routes() // controller routes below 73 .add_route(controllers::auth::routes()) 74 } 75 76 async fn after_routes(router: axum::Router, _ctx: &AppContext) -> Result<axum::Router> { 77 async fn scalar_ui() -> axum::response::Html<String> { 78 let cfg = serde_json::json!({ 79 "isLoading": true, 80 "theme": "deepSpace", 81 "hideClientButton": true, 82 "defaultOpenAllTags": true, 83 "expandAllResponses": true, 84 "favicon": "/nix-mfarabi.svg" , 85 "expandAllModelSections": true, 86 "url": "/api-docs/openapi.json", 87 }); 88 89 axum::response::Html(scalar_api_reference::scalar_html_default(&cfg)) 90 } 91 92 Ok(router.route("/scalar", axum::routing::get(scalar_ui))) 93 } 94 95 async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> { 96 queue.register(DownloadWorker::build(ctx)).await?; 97 Ok(()) 98 } 99 100 #[allow(unused_variables)] 101 fn register_tasks(tasks: &mut Tasks) { 102 tasks.register(tasks::upload::Upload); 103 tasks.register(tasks::ve_direct::VeDirect); 104 } 105 async fn truncate(ctx: &AppContext) -> Result<()> { 106 truncate_table(&ctx.db, users::Entity).await?; 107 Ok(()) 108 } 109 async fn seed(ctx: &AppContext, base: &Path) -> Result<()> { 110 db::seed::<users::ActiveModel>(&ctx.db, &base.join("users.yaml").display().to_string()) 111 .await?; 112 Ok(()) 113 } 114 }