AkurAI Build
Menu

Public repository / d63e826eb482cb963477774b04fe8a84f23f0e45

AkurAI-Drive / backend/src/main.rs

Latest change beff27fc3a062a220cde556e7bb28da49f6d04ea - Drive login: recover oidc.rs (IdP PKCE + introspection) from deployed binary by AkurAI Agent

pub mod api;
pub mod auth;
pub mod config;
pub mod db;
pub mod device;
pub mod idp;
pub mod mcp;
pub mod oauth;
pub mod oidc;
pub mod pages;
pub mod session;
pub mod sigv4;
pub mod telemetry;
pub mod util;

use axum::{
    Json, Router,
    extract::Request,
    http::StatusCode,
    middleware::{self, Next},
    response::{IntoResponse, Response},
    routing::{get, post},
};
use serde_json::json;

#[tokio::main]
async fn main() {
    akurai_log::init("akurai-drive-api");

    tracing::info!("akurai-drive-api starting up");

    let missing = config::missing_required(config::get());
    if !missing.is_empty() {
        tracing::error!(
            "refusing to start: missing required environment variables: {}",
            missing.join(", ")
        );
        std::process::exit(1);
    }

    // Trigger DB initialization (opens the connection, sets pragmas, creates tables).
    db::with_db(|_conn| {});

    let cfg = config::get();

    let app = Router::new()
        .route("/healthz", get(healthz))
        .route("/api/health", get(api_health))
        .route("/auth/login", get(auth::login))
        .route("/auth/password", post(auth::password))
        .route("/auth/callback", get(auth::callback))
        .route("/auth/logout", get(auth::logout))
        .route("/auth/device", get(auth::device))
        .route("/mcp", post(mcp::handle))
        .route("/.well-known/oauth-protected-resource", get(mcp::metadata))
        .route(
            "/.well-known/oauth-protected-resource/mcp",
            get(mcp::metadata),
        )
        .route("/api/me", get(api::me))
        .route(
            "/api/storage",
            get(api::storage_list).post(api::storage_create),
        )
        .route("/api/buckets", get(api::buckets))
        .route("/api/files", get(api::files_list).delete(api::files_delete))
        .route("/api/files/presign", post(api::files_presign))
        .route("/api/client-logs", post(api::client_logs))
        .route("/js/monitor-client.js", get(pages::monitor_client))
        .route("/theme.js", get(pages::theme_js))
        .route("/themes.css", get(pages::themes_css))
        .route("/shared-shell.css", get(pages::shared_shell_css))
        .route("/themes.json", get(pages::themes_json))
        .route("/favicon.svg", get(pages::favicon))
        .route("/apple-touch-icon.png", get(pages::apple_touch_icon))
        .route("/app", get(pages::app))
        .fallback(not_found)
        .layer(middleware::from_fn(request_logger));

    tracing::info!("listening on {}", cfg.listen_addr);

    let listener = tokio::net::TcpListener::bind(&cfg.listen_addr)
        .await
        .expect("failed to bind listener");

    axum::serve(listener, app).await.expect("server error");
}

async fn request_logger(req: Request, next: Next) -> Response {
    let method = req.method().clone();
    let path = req.uri().path().to_owned();
    let response = next.run(req).await;
    let status = response.status();
    tracing::info!(%method, %path, %status, "request");
    response
}
async fn healthz() -> impl IntoResponse {
    Json(json!({"ok": true, "service": "akurai-drive-api"}))
}

// Fleet ops contract: GET /api/health -> {app, version, status}.
async fn api_health() -> impl IntoResponse {
    Json(json!({
        "app": "akurai-drive-api",
        "version": env!("CARGO_PKG_VERSION"),
        "status": "ok",
    }))
}

async fn not_found() -> impl IntoResponse {
    (StatusCode::NOT_FOUND, Json(json!({"error": "not found"})))
}