portfu_admin 1.3.3

Library of Admin tools build on toip of Portfu
Documentation
use portfu::macros::{get, put};
use portfu::pfcore::editable::EditResult;
use portfu::pfcore::{FromBody, Json, ServiceRegister};
use portfu::prelude::http::{Extensions, StatusCode};
use portfu::prelude::*;
use serde::Deserialize;
use std::io::{Error, ErrorKind};
use std::sync::Arc;

#[get("/pf_admin/editor/files")]
pub async fn list_editable_files(data: &mut ServiceData) -> Result<Vec<u8>, Error> {
    let mut editable = vec![];
    let services: Vec<Arc<Service>> = data.server.registry.read().await.services.to_vec();
    for service in &services {
        if let Some(handle) = &service.handler {
            if handle.is_editable() {
                editable.push(service.name());
            }
        }
    }
    serde_json::to_vec(&editable).map_err(|e| {
        Error::new(
            ErrorKind::InvalidData,
            format!("Failed to Convert to JSON: {e:?}"),
        )
    })
}

#[get("/pf_admin/editor/folders")]
pub async fn list_editable_folders(data: &mut ServiceData) -> Result<Vec<u8>, Error> {
    let mut editable = vec![];
    let services: Vec<Arc<Service>> = data.server.registry.read().await.services.to_vec();
    for service in &services {
        if let Some(handle) = &service.handler {
            if handle.is_editable() {
                editable.push(service.name());
            }
        }
    }
    serde_json::to_vec(&editable).map_err(|e| {
        Error::new(
            ErrorKind::InvalidData,
            format!("Failed to Convert to JSON: {e:?}"),
        )
    })
}

#[derive(Deserialize)]
pub struct LoadRequest {
    service_name: String,
}

#[get("/pf_admin/editor/load")]
pub async fn get_service_value(data: &mut ServiceData) -> Result<Vec<u8>, Error> {
    let load_request: Option<LoadRequest> = Json::from_body(&mut data.request.request.body())
        .await?
        .inner();
    if let Some(load_request) = load_request {
        let services: Vec<Arc<Service>> = data.server.registry.read().await.services.to_vec();
        for service in &services {
            if service.name == load_request.service_name {
                if let Some(handle) = service.handler.clone() {
                    if handle.is_editable() {
                        match handle.current_value().await {
                            EditResult::Failed(s) => {
                                *data.response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
                                return Ok(s.into_bytes());
                            }
                            EditResult::Success(v) => {
                                return Ok(v);
                            }
                            EditResult::NotEditable => {
                                *data.response.status_mut() = StatusCode::FORBIDDEN;
                                return Ok(vec![]);
                            }
                        }
                    } else {
                        *data.response.status_mut() = StatusCode::FORBIDDEN;
                        return Ok(vec![]);
                    }
                }
            }
        }
    }
    *data.response.status_mut() = StatusCode::NOT_FOUND;
    Ok(vec![])
}

#[derive(Deserialize)]
pub struct EditRequest {
    service_name: String,
    new_value: Vec<u8>,
    current_value: Option<Vec<u8>>,
}

#[put("/pf_admin/editor/create")]
pub async fn create_service(data: &mut ServiceData) -> Result<Vec<u8>, Error> {
    let edit_request: Option<EditRequest> = Json::from_body(&mut data.request.request.body())
        .await?
        .inner();
    if let Some(edit_request) = edit_request {
        let services: Vec<Arc<Service>> = data.server.registry.read().await.services.to_vec();
        for service in &services {
            if service.name == edit_request.service_name {
                if let Some(handle) = service.handler.clone() {
                    if handle.is_editable() {
                        match handle
                            .update_value(edit_request.new_value, edit_request.current_value)
                            .await
                        {
                            EditResult::Failed(s) => {
                                *data.response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
                                return Ok(s.into_bytes());
                            }
                            EditResult::Success(v) => {
                                return Ok(v);
                            }
                            EditResult::NotEditable => {
                                *data.response.status_mut() = StatusCode::FORBIDDEN;
                                return Ok(vec![]);
                            }
                        }
                    } else {
                        *data.response.status_mut() = StatusCode::FORBIDDEN;
                        return Ok(vec![]);
                    }
                }
            }
        }
    }
    *data.response.status_mut() = StatusCode::NOT_FOUND;
    Ok(vec![])
}

#[put("/pf_admin/editor/update")]
pub async fn update_service_value(data: &mut ServiceData) -> Result<Vec<u8>, Error> {
    let edit_request: Option<EditRequest> = Json::from_body(&mut data.request.request.body())
        .await?
        .inner();
    if let Some(edit_request) = edit_request {
        let services: Vec<Arc<Service>> = data.server.registry.read().await.services.to_vec();
        for service in &services {
            if service.name == edit_request.service_name {
                if let Some(handle) = service.handler.clone() {
                    if handle.is_editable() {
                        match handle
                            .update_value(edit_request.new_value, edit_request.current_value)
                            .await
                        {
                            EditResult::Failed(s) => {
                                *data.response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
                                return Ok(s.into_bytes());
                            }
                            EditResult::Success(v) => {
                                return Ok(v);
                            }
                            EditResult::NotEditable => {
                                *data.response.status_mut() = StatusCode::FORBIDDEN;
                                return Ok(vec![]);
                            }
                        }
                    } else {
                        *data.response.status_mut() = StatusCode::FORBIDDEN;
                        return Ok(vec![]);
                    }
                }
            }
        }
    }
    *data.response.status_mut() = StatusCode::NOT_FOUND;
    Ok(vec![])
}

pub struct ServiceEditor {
    services: ServiceGroup,
}
impl Default for ServiceEditor {
    fn default() -> Self {
        Self {
            services: ServiceGroup::default()
                .service(list_editable_files)
                .service(list_editable_folders)
                .service(get_service_value)
                .service(update_service_value),
        }
    }
}
impl ServiceRegister for ServiceEditor {
    fn register(self, service_registry: &mut ServiceRegistry, shared_state: Extensions) {
        self.services.register(service_registry, shared_state);
    }
}
impl From<ServiceEditor> for ServiceGroup {
    fn from(value: ServiceEditor) -> Self {
        value.services
    }
}