ordinary-api 0.6.0-pre.13

API server for Ordinary
Documentation
// Copyright (C) 2026 Ordinary Labs, LLC.
//
// SPDX-License-Identifier: AGPL-3.0-only

use crate::client::{OrdinaryApiClient, compress_zstd};
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD as b64};
use fs_err::read_to_string;
use ordinary_config::OrdinaryConfig;
use std::path::Path;

pub async fn update(api_client: &OrdinaryApiClient<'_>, proj_path: &str) -> anyhow::Result<()> {
    let correlation_id = api_client.correlation_id.map(|id| id.to_string());

    let config = OrdinaryConfig::get(proj_path)?;

    if let Some(content) = config.content {
        if let Some(update_config) = &content.update
            && let Some(lifecycle) = &update_config.lifecycle
            && let Some(before) = &lifecycle.before
        {
            OrdinaryConfig::exec_lifecycle_script(
                Path::new(proj_path),
                &None,
                "content update",
                "before",
                before,
            )?;
        }

        let content_path = Path::new(proj_path).join(content.file_path);
        let content_json = read_to_string(content_path)?;

        // todo: do validations down here too, to limit the work the server has to do

        let access_token = api_client.get_access(None, correlation_id.clone()).await?;

        tracing::info!("updating content on API server...");
        let mut req = api_client
            .client
            .put(format!("{}/v1/content", api_client.addr))
            .body(compress_zstd(content_json.as_bytes())?)
            .query(&[("d", config.domain)])
            .header("Content-Encoding", "zstd")
            .header("Content-Type", "application/json")
            .header(
                "Authorization",
                format!("Bearer {}", b64.encode(&access_token)),
            );

        if let Some(correlation_id) = correlation_id {
            req = req.header("x-correlation-id", correlation_id);
        }

        req.send().await?;

        tracing::info!("content updated.");

        if let Some(update_config) = &content.update
            && let Some(lifecycle) = &update_config.lifecycle
            && let Some(after) = &lifecycle.after
        {
            OrdinaryConfig::exec_lifecycle_script(
                Path::new(proj_path),
                &None,
                "content update",
                "before",
                after,
            )?;
        }
    } else {
        tracing::warn!("no \"content\" field in ordinary.json");
    }

    Ok(())
}