wsb-daemon 0.275.7622

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
//! Gists: the list from the machine's mirror, a gist's contents fetched once
//! and then served from it, and edits written through GitHub.
//!
//! A gist is the account's, not a worktree's, so these are the machine's
//! routes.

use std::sync::Arc;

use axum::{
    extract::{Path, State},
    routing::{get, post},
    Json, Router,
};
use serde_json::json;

use crate::machine::MachineState;
use crate::web::github::{self, Refusal};
use wsb_github::client::{CreateGistRequest, UpdateGistRequest};

pub fn routes() -> Router<Arc<MachineState>> {
    Router::new()
        .route("/api/v1/gists", get(list_gists).post(create_gist))
        .route("/api/v1/gists/refresh", post(refresh_gists))
        .route("/api/v1/gists/:id", get(get_gist).patch(update_gist))
}

/// GET /api/v1/gists — the mirrored list, with how current it is.
pub async fn list_gists(State(machine): State<Arc<MachineState>>) -> Result<Json<serde_json::Value>, Refusal> {
    let service = github::of_machine(&machine)?.service.clone();
    let listed = github::blocking(move || service.gists()).await?;
    Ok(Json(json!({ "gists": listed.items, "sync": listed.sync })))
}

/// POST /api/v1/gists/refresh — bring the mirrored gists current now.
pub async fn refresh_gists(
    State(machine): State<Arc<MachineState>>,
) -> Result<Json<wsb_github::sync::SyncReport>, Refusal> {
    let github = github::of_machine(&machine)?;
    let client = github::client()?;
    Ok(Json(
        wsb_github::sync::sync_scopes(github.service.mirror(), &client, &[wsb_github::Scope::Gists], true).await,
    ))
}

/// GET /api/v1/gists/:id — one gist with its file contents.
///
/// The mirror answers when it holds the whole gist; a gist held only as a
/// list summary is read from GitHub once and kept.
pub async fn get_gist(
    State(machine): State<Arc<MachineState>>,
    Path(gist_id): Path<String>,
) -> Result<Json<wsb_github::service::GistAnswer>, Refusal> {
    let github = github::of_machine(&machine)?;
    let client = github::client().ok();
    github
        .service
        .gist(client.as_ref(), &gist_id)
        .await
        .map(Json)
        .map_err(github::failure)
}

pub async fn create_gist(
    State(machine): State<Arc<MachineState>>,
    Json(body): Json<CreateGistRequest>,
) -> Result<Json<serde_json::Value>, Refusal> {
    let github = github::of_machine(&machine)?;
    let client = github::client()?;
    let gist = github
        .service
        .create_gist(&client, &body)
        .await
        .map_err(github::failure)?;
    Ok(Json(json!({ "gist": gist })))
}

pub async fn update_gist(
    State(machine): State<Arc<MachineState>>,
    Path(gist_id): Path<String>,
    Json(body): Json<UpdateGistRequest>,
) -> Result<Json<serde_json::Value>, Refusal> {
    let github = github::of_machine(&machine)?;
    let client = github::client()?;
    let gist = github
        .service
        .update_gist(&client, &gist_id, &body)
        .await
        .map_err(github::failure)?;
    Ok(Json(json!({ "gist": gist })))
}