ordinary-app 0.8.2

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

use crate::server::OrdinaryAppRouter;
use async_compression::tokio::write::ZlibDecoder;
use axum::Router;
use axum::http::StatusCode;
use axum::routing::post;
use bytes::Bytes;
use ordinary_config::OrdinaryConfig;
use ordinary_utils::json::JsonValuable;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::sync::Arc;
use tokio::io::AsyncWriteExt;

#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct ClientEvent {
    /// timestamp
    pub ts: String,
    /// timezone
    pub tz: String,
    /// log level
    #[serde(skip_serializing)]
    pub lvl: String,
    /// version
    pub v: String,
    /// correlation id
    pub cid: String,
    /// memory id
    pub mid: String,
    /// path
    pub p: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    /// query
    pub q: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    /// fragment
    pub f: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    /// flags
    pub flg: Option<BTreeMap<String, String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    /// fields
    pub fld: Option<BTreeMap<String, String>>,
    #[serde(skip_serializing)]
    /// message
    pub msg: String,
}

pub fn setup_routes(config: &Arc<OrdinaryConfig>) -> Option<OrdinaryAppRouter> {
    if config.client_events == Some(true) {
        let router = Router::new().route(
            "/.ordinary/v1/events",
            post(|body: Bytes| async move {
                let mut decoder = ZlibDecoder::new(Vec::new());
                if let Err(err) = decoder.write_all(body.as_ref()).await {
                    tracing::error!(%err);
                    return StatusCode::INTERNAL_SERVER_ERROR;
                }
                if let Err(err) = decoder.shutdown().await {
                    tracing::error!(%err);
                    return StatusCode::INTERNAL_SERVER_ERROR;
                }

                let mut events_slice = decoder.into_inner();

                let events: Vec<ClientEvent> = match simd_json::from_slice(&mut events_slice) {
                    Ok(parsed) => parsed,
                    Err(err) => {
                        tracing::error!(%err);
                        return StatusCode::INTERNAL_SERVER_ERROR;
                    }
                };

                let client_span = tracing::info_span!("client");

                client_span.in_scope(|| {
                    for event in events {
                        #[cfg(tracing_unstable)]
                        {
                            let level = event.lvl.as_str();
                            let message = event.msg.as_str();

                            if let Ok(value) = serde_json::to_value(&event) {
                                let event = JsonValuable(value);

                                match level {
                                    "TRACE" => tracing::trace!(
                                        evt = tracing::field::valuable(&event),
                                        "{message}"
                                    ),
                                    "DEBUG" => tracing::debug!(
                                        evt = tracing::field::valuable(&event),
                                        "{message}"
                                    ),
                                    "INFO" => {
                                        tracing::info!(
                                            evt = tracing::field::valuable(&event),
                                            "{message}"
                                        );
                                    }
                                    "WARN" => {
                                        tracing::warn!(
                                            evt = tracing::field::valuable(&event),
                                            "{message}"
                                        );
                                    }
                                    _ => tracing::error!(
                                        evt = tracing::field::valuable(&event),
                                        "{message}"
                                    ),
                                }
                            }
                        }

                        #[cfg(not(tracing_unstable))]
                        {
                            match event.lvl.as_ref() {
                                "TRACE" => tracing::trace!(evt = debug(&event)),
                                "DEBUG" => tracing::debug!(evt = debug(&event)),
                                "INFO" => tracing::info!(evt = debug(&event)),
                                "WARN" => tracing::warn!(evt = debug(&event)),
                                _ => tracing::error!(evt = debug(&event)),
                            }
                        }
                    }
                });

                StatusCode::OK
            }),
        );
        return Some(router);
    }

    None
}