product-os-server 0.0.55

Product OS : Server provides a full functioning advanced server capable of acting as a web server, command and control distributed network, authentication server, crawling server and more. Fully featured with high level of flexibility.
Documentation
//! Feature service integrations module
//!
//! This module provides convenience methods for integrating higher-level Product OS
//! services into the server, including:
//!
//! - **OIDC**: OAuth 2.0 / OpenID Connect server (requires `oidc` feature)
//! - **Authentication**: User authentication service (requires `authentication` feature)
//! - **Content Platform**: Content management service (requires `content` feature)
//! - **Service Handler**: Generic CRUD service handler (requires `service_handler` feature)
//!
//! All methods require the `controller` feature to be enabled as they register
//! features with the server's command-and-control system.

use std::prelude::v1::*;
use std::sync::Arc;

#[cfg(feature = "oidc")]
use product_os_oauth_oidc::ProductOSOIDCServer;

#[cfg(feature = "authentication")]
use product_os_authentication::{Authentication, ProductOSAuthentication};

#[cfg(feature = "content")]
use product_os_content::ProductOSContentPlatform;

use crate::error::ProductOSServerError;
use crate::ProductOSServer;

#[cfg(feature = "controller")]
use product_os_async_executor;

impl<E, X> ProductOSServer<(), E, X>
where
    E: product_os_async_executor::Executor<X> + product_os_async_executor::ExecutorPerform<X> + product_os_async_executor::Timer + 'static,
{
    /// Adds a generic CRUD service handler for a database table.
    ///
    /// This creates REST endpoints for the specified table with standard
    /// CRUD operations (Create, Read, Update, Delete).
    ///
    /// # Arguments
    ///
    /// * `name` - Name identifier for the service
    /// * `path` - URL path prefix for the service routes
    /// * `table_name` - Database table to operate on
    /// * `identifier_name` - Primary key column name
    ///
    /// # Errors
    ///
    /// Returns `ControllerError` if no controller is configured.
    #[cfg(all(feature = "service_handler", feature = "controller"))]
    pub async fn try_add_handler_service(&mut self, name: String, path: String, table_name: String, identifier_name: String) -> crate::error::Result<()> {
        let controller = self.try_get_controller()?;

        let handler = product_os_service_handler::ProductOSServiceHandler::new(name, path, table_name, identifier_name, controller);
        let handler_name = handler.get_identifier_name();
        handler.load_service(&mut self.router).await;

        #[cfg(feature = "core")]
        tracing::info!("Handler Service {} successfully added to server", handler_name);
        Ok(())
    }

    /// Adds an OIDC (OpenID Connect) server to the server.
    ///
    /// Sets up OAuth 2.0 / OIDC endpoints at the specified base path and
    /// initialises the required database tables.
    ///
    /// # Arguments
    ///
    /// * `identifier` - Unique identifier for this OIDC server instance
    /// * `base` - Optional base path (defaults to "/auth")
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - No controller is configured
    /// - The relational store is not available
    /// - Database setup fails
    #[cfg(all(feature = "oidc", feature = "controller"))]
    pub async fn try_add_oidc(&mut self, identifier: String, base: Option<String>) -> crate::error::Result<()> {
        let base_path = base.unwrap_or_else(|| "/auth".to_string());

        let relational_store = self.try_get_relational_store()
            .map_err(|e| ProductOSServerError::ControllerError {
                operation: "add_oidc".to_string(),
                message: alloc::format!("Failed to get relational store for OIDC: {}", e),
            })?;

        let controller_arc = self.try_get_controller()?;
        let mut controller = controller_arc.lock();

        let oidc_server = ProductOSOIDCServer::new(identifier, controller.get_configuration().get_oidc_configuration(), relational_store.clone());
        let oidc_server_arc = Arc::new(oidc_server);

        match product_os_oauth_oidc::setup_oidc_store(relational_store.clone()).await {
            Ok(_) => {
                #[cfg(feature = "core")]
                tracing::info!("Database setup for OIDC complete");
            }
            Err(e) => {
                #[cfg(feature = "core")]
                tracing::error!("Error setting up database for OIDC: {:?}", e);
                return Err(ProductOSServerError::ControllerError {
                    operation: "add_oidc".to_string(),
                    message: format!("Failed to complete database setup for OIDC: {:?}", e),
                });
            }
        }

        controller.add_feature(oidc_server_arc.clone(), base_path, &mut self.router).await;
        #[cfg(feature = "core")]
        tracing::info!("OIDC successfully added to server");
        Ok(())
    }

    /// Adds user authentication to the server.
    ///
    /// Sets up authentication endpoints at the specified base path and
    /// initialises the required database tables.
    ///
    /// # Arguments
    ///
    /// * `base` - Optional base path for auth endpoints (defaults to "/authentication")
    /// * `oidc_base` - Optional base path for OIDC integration (defaults to "/oidc")
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - No controller is configured
    /// - The relational store is not available
    /// - Database setup fails
    #[cfg(all(feature = "authentication", feature = "controller"))]
    pub async fn try_add_authentication(&mut self, base: Option<String>, oidc_base: Option<String>) -> crate::error::Result<()> {
        let base_path = base.unwrap_or_else(|| "/authentication".to_string());
        let oidc_base_path = oidc_base.unwrap_or_else(|| "/oidc".to_string());

        let relational_store = self.try_get_relational_store()
            .map_err(|e| ProductOSServerError::ControllerError {
                operation: "add_authentication".to_string(),
                message: alloc::format!("Failed to get relational store for authentication: {}", e),
            })?;

        let auth_config: Authentication = match &self.config.authentication {
            Some(value) => serde_json::from_value(value.clone()).map_err(|e| ProductOSServerError::ControllerError {
                operation: "add_authentication".to_string(),
                message: format!("Invalid authentication configuration: {e}"),
            })?,
            None => Authentication::default(),
        };

        let controller_arc = self.try_get_controller()?;
        let authentication = ProductOSAuthentication::new(
            &auth_config,
            relational_store.clone(),
            Some(controller_arc),
        );
        let auth_arc = Arc::new(authentication);

        match product_os_authentication::setup_auth_store(relational_store.clone(), auth_config.scopes, auth_arc.clone()).await {
            Ok(_) => {
                #[cfg(feature = "core")]
                tracing::info!("Database setup for user authentication complete");
            }
            Err(e) => {
                #[cfg(feature = "core")]
                tracing::error!("Error setting up database for user authentication: {:?}", e);
                return Err(ProductOSServerError::ControllerError {
                    operation: "add_authentication".to_string(),
                    message: format!("Failed to complete database setup for user authentication: {:?}", e),
                });
            }
        }

        let _ = oidc_base_path;
        self.try_add_feature(auth_arc, Some(base_path)).await?;
        #[cfg(feature = "core")]
        tracing::info!("Authentication successfully added to server");
        Ok(())
    }

    /// Adds a content management platform to the server.
    ///
    /// Sets up content platform endpoints and initialises the required database tables.
    /// Requires both relational and key-value stores.
    ///
    /// # Arguments
    ///
    /// * `base` - Optional base path for content endpoints (defaults to "")
    /// * `auth_base` - Optional base path for authentication integration (defaults to "/authentication")
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - No controller is configured
    /// - The relational store or key-value store is not available
    /// - Database setup fails
    #[cfg(all(feature = "content", feature = "controller"))]
    pub async fn try_add_content_platform(&mut self, base: Option<String>, auth_base: Option<String>) -> crate::error::Result<()> {
        let base_path = base.unwrap_or_default();
        let auth_base_path = auth_base.unwrap_or_else(|| "/authentication".to_string());

        let relational_store = self.try_get_relational_store()
            .map_err(|e| ProductOSServerError::ControllerError {
                operation: "add_content_platform".to_string(),
                message: alloc::format!("Failed to get relational store for content platform: {}", e),
            })?;

        let key_value_store = self.try_get_key_value_store()
            .map_err(|e| ProductOSServerError::ControllerError {
                operation: "add_content_platform".to_string(),
                message: alloc::format!("Failed to get key-value store for content platform: {}", e),
            })?;

        let controller_arc = self.try_get_controller()?;

        let content_platform = ProductOSContentPlatform::new(
            relational_store.clone(),
            Some(key_value_store.clone()),
            Some(controller_arc.clone()),
            auth_base_path,
        );
        let content_platform_arc = Arc::new(content_platform);

        match product_os_content::setup_content_store(relational_store.clone()).await {
            Ok(_) => {
                #[cfg(feature = "core")]
                tracing::info!("Database setup for content platform complete");
            }
            Err(e) => {
                #[cfg(feature = "core")]
                tracing::error!("Error setting up database for content platform: {:?}", e);
                return Err(ProductOSServerError::ControllerError {
                    operation: "add_content_platform".to_string(),
                    message: format!("Failed to complete database setup for content platform: {:?}", e),
                });
            }
        }

        let mut controller = controller_arc.lock();
        controller.add_feature(content_platform_arc.clone(), base_path, &mut self.router).await;
        #[cfg(feature = "core")]
        tracing::info!("Content Platform successfully added to server");
        Ok(())
    }
}