1use actix_cors::Cors;
2use actix_web::{App, HttpServer, web};
3use argon2::Argon2;
4use clap::Parser;
5use diesel_async::pooled_connection::AsyncDieselConnectionManager;
6use diesel_async::pooled_connection::deadpool::Pool;
7use error::Error;
8use objects::MailClient;
9use simple_logger::SimpleLogger;
10use std::time::SystemTime;
11mod config;
12use config::{Config, ConfigBuilder};
13use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations};
14
15pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
16
17type Conn =
18 deadpool::managed::Object<AsyncDieselConnectionManager<diesel_async::AsyncPgConnection>>;
19
20mod api;
21pub mod error;
22pub mod objects;
23pub mod schema;
24pub mod utils;
25
26#[derive(Parser, Debug)]
27#[command(version, about, long_about = None)]
28struct Args {
29 #[arg(short, long, default_value_t = String::from("/etc/gorb/config.toml"))]
30 config: String,
31}
32
33#[derive(Clone)]
34pub struct Data {
35 pub pool: deadpool::managed::Pool<
36 AsyncDieselConnectionManager<diesel_async::AsyncPgConnection>,
37 Conn,
38 >,
39 pub cache_pool: redis::Client,
40 pub config: Config,
41 pub argon2: Argon2<'static>,
42 pub start_time: SystemTime,
43 pub bunny_cdn: bunny_api_tokio::Client,
44 pub mail_client: MailClient,
45}
46
47#[tokio::main]
48async fn main() -> Result<(), Error> {
49 SimpleLogger::new()
50 .with_level(log::LevelFilter::Info)
51 .with_colors(true)
52 .env()
53 .init()
54 .unwrap();
55 let args = Args::parse();
56
57 let config = ConfigBuilder::load(args.config).await?.build();
58
59 let web = config.web.clone();
60
61 let pool_config =
63 AsyncDieselConnectionManager::<diesel_async::AsyncPgConnection>::new(config.database.url());
64 let pool = Pool::builder(pool_config).build()?;
65
66 let cache_pool = redis::Client::open(config.cache_database.url())?;
67
68 let mut bunny_cdn = bunny_api_tokio::Client::new("").await?;
69
70 let bunny = config.bunny.clone();
71
72 bunny_cdn
73 .storage
74 .init(bunny.api_key, bunny.endpoint, bunny.storage_zone)
75 .await?;
76
77 let mail = config.mail.clone();
78
79 let mail_client = MailClient::new(
80 mail.smtp.credentials(),
81 mail.smtp.server,
82 mail.address,
83 mail.tls,
84 )?;
85
86 let database_url = config.database.url();
87
88 tokio::task::spawn_blocking(move || {
89 use diesel::prelude::Connection;
90 use diesel_async::async_connection_wrapper::AsyncConnectionWrapper;
91
92 let mut conn =
93 AsyncConnectionWrapper::<diesel_async::AsyncPgConnection>::establish(&database_url)?;
94
95 conn.run_pending_migrations(MIGRATIONS)?;
96 Ok::<_, Box<dyn std::error::Error + Send + Sync>>(())
97 })
98 .await?
99 .unwrap();
100
101 let data = Data {
119 pool,
120 cache_pool,
121 config,
122 argon2: Argon2::default(),
124 start_time: SystemTime::now(),
125 bunny_cdn,
126 mail_client,
127 };
128
129 HttpServer::new(move || {
130 let cors = Cors::default()
132 .allowed_origin_fn(|_origin, _req_head| true)
139 .allow_any_method()
144 .allow_any_header()
149 .supports_credentials();
154
155 App::new()
156 .app_data(web::Data::new(data.clone()))
157 .wrap(cors)
158 .service(api::web(data.config.web.backend_url.path()))
159 })
160 .bind((web.ip, web.port))?
161 .run()
162 .await?;
163
164 Ok(())
165}