pib-service 0.16.1

Politik im Blick service for serving public governmental information in OParl format, with some extra addon information
// SPDX-FileCopyrightText: Politik im Blick developers
// SPDX-FileCopyrightText: Wolfgang Silbermayr <wolfgang@silbermayr.at>
//
// SPDX-License-Identifier: AGPL-3.0-or-later OR EUPL-1.2

use std::sync::Arc;

use dirs::data_local_dir;
use oparl_service_facade::OParlServiceHandle;
use oparl_types::DateTime;
use openidconnect::{ClientId, ClientSecret, IssuerUrl};
use pib_oparl_service::{PibOParlService, ServiceUrlBuilder, UrlBuilder};
use pib_service_api_auth::ApiAuth;
use pib_service_api_auth_oidc::OidcAuth;
use pib_service_api_permissions::Permissions;
use pib_service_api_permissions_inventory::InventoryPermissions;
use pib_service_api_types::config::oidc::OidcConfig;
use pib_service_backend::Backend;
use pib_service_db_migration::Migrator;
use pib_service_inventory::InventoryProvider;
use pib_service_inventory_db::DatabaseInventoryProvider;
use sea_orm::Database;
use sea_orm_migration::MigratorTrait as _;
use tokio::{select, sync::RwLock};

mod settings;

use crate::settings::{ApiOidcSettings, ApiSettings, Settings};

const PROGRAM_NAME: &str = env!("CARGO_PKG_NAME");

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    env_logger::init();

    let settings = Settings::load()?;

    let data_dir = data_local_dir().unwrap().join("pib-service");
    std::fs::create_dir_all(&data_dir)?;
    let db_path = data_dir.join("inventory.sqlite?mode=rwc");
    let db_url = format!("sqlite:{}", db_path.to_str().unwrap());
    let db = Database::connect(db_url).await?;
    Migrator::up(&db, None).await.unwrap();

    let name = Some(PROGRAM_NAME.into());
    let vendor = Some("https://codeberg.org/pib".parse()?);
    let product = Some("https://codeberg.org/pib/pib-service".parse()?);
    let now = DateTime::now();
    let created = now.clone();
    let modified = now.clone();
    let pib_about = Some(
        "An experimental OParl service developed by the \"Politik im Blick\" developers".into(),
    );

    let database_inventory_provider: Arc<dyn InventoryProvider> = Arc::new(
        DatabaseInventoryProvider::new(db, name, vendor, product, created, modified, pib_about),
    );

    let permissions: Arc<dyn Permissions> = Arc::new(InventoryPermissions::new(
        database_inventory_provider.clone(),
    ));

    let url_builder: Arc<dyn UrlBuilder> =
        Arc::new(ServiceUrlBuilder::new(settings.oparl.base_url));

    let service = OParlServiceHandle::new(Arc::new(RwLock::new(PibOParlService::new(
        database_inventory_provider.clone(),
        url_builder,
    ))));
    let service_future = oparl_web_service_axum::run(service, settings.http.bind.as_slice());

    if let Some(ApiSettings { oidc, http }) = settings.api.as_ref() {
        let ApiOidcSettings {
            issuer,
            client_id,
            client_secret,
        } = oidc.clone();
        let auth: Arc<dyn ApiAuth> = Arc::new(OidcAuth::new(
            IssuerUrl::new(issuer.clone())?,
            ClientId::new(client_id.clone()),
            ClientSecret::new(client_secret.clone()),
            database_inventory_provider.clone(),
        ));
        let oidc = OidcConfig {
            issuer,
            client_id,
            client_secret,
        };

        let service = Arc::new(Backend::new(database_inventory_provider, permissions, oidc));

        let service_api_future = pib_service_api::run(http.bind.as_slice(), service, auth);

        select! {
            result = service_future => result?,
            result = service_api_future => result?
        }
    } else {
        service_future.await?;
    }

    Ok(())
}