Repository: libsql/libsql
File: libsql-server/src/http/admin/mod.rs (Rust)

Code before:
    heartbeat_url: Option<String>,
    #[serde(default)]
    jwt_key: Option<String>,
}

async fn handle_post_config<M: MakeNamespace, C>(
    State(app_state): State<Arc<AppState<M, C>>>,
    Path(namespace): Path<String>,
    Json(req): Json<HttpDatabaseConfig>,
) -> crate::Result<()> {
    if let Some(jwt_key) = req.jwt_key.as_deref() {
        // Check that the jwt key is correct
        parse_jwt_key(jwt_key).context("Could not parse JWT decoding key")?;
    }
    let store = app_state
        .namespaces
        .config_store(NamespaceName::from_string(namespace)?)
        .await?;
    let mut config = (*store.get()).clone();
    config.block_reads = req.block_reads;
    config.block_writes = req.block_writes;
    config.block_reason = req.block_reason;
    if let Some(size) = req.max_db_size {
        config.max_db_pages = size.as_u64() / LIBSQL_PAGE_SIZE;
    }
    if let Some(url) = req.heartbeat_url {
        config.heartbeat_url = Some(Url::parse(&url)?);
    }
    config.jwt_key = req.jwt_key;

    store.store(config).await?;

    Ok(())
}

#[derive(Debug, Deserialize)]
struct CreateNamespaceReq {
    dump_url: Option<Url>,
    max_db_size: Option<bytesize::ByteSize>,
    heartbeat_url: Option<String>,
    bottomless_db_id: Option<String>,
    jwt_key: Option<String>,
}

async fn handle_create_namespace<M: MakeNamespace, C: Connector>(
    State(app_state): State<Arc<AppState<M, C>>>,
    Path(namespace): Path<String>,
    Json(req): Json<CreateNamespaceReq>,
) -> crate::Result<()> {
    if let Some(jwt_key) = req.jwt_key.as_deref() {
        // Check that the jwt key is correct

Review comment (suggestion):
I think we want to avoid using context here since its going to hide the inner message if we use display. This has bitten us in the past. We should rather, create a error variant for this error case that can add this contextual message and map the error to that either within parse_jwt_key or here.

Code after:
    tracing::trace!("diagnostics: {diagnostics:?}");
    Ok(Json(diagnostics))
}

#[derive(Debug, Deserialize, Serialize)]
struct HttpDatabaseConfig {
    block_reads: bool,
    block_writes: bool,
    #[serde(default)]
    block_reason: Option<String>,
    #[serde(default)]
    max_db_size: Option<bytesize::ByteSize>,
    #[serde(default)]
    heartbeat_url: Option<String>,
    #[serde(default)]
    jwt_key: Option<String>,
}

async fn handle_post_config<M: MakeNamespace, C>(
    State(app_state): State<Arc<AppState<M, C>>>,
    Path(namespace): Path<String>,
    Json(req): Json<HttpDatabaseConfig>,
) -> crate::Result<()> {
    if let Some(jwt_key) = req.jwt_key.as_deref() {
        // Check that the jwt key is correct
        parse_jwt_key(jwt_key)?;
    }
    let store = app_state
        .namespaces
        .config_store(NamespaceName::from_string(namespace)?)
        .await?;
    let mut config = (*store.get()).clone();
    config.block_reads = req.block_reads;
    config.block_writes = req.block_writes;
    config.block_reason = req.block_reason;
    if let Some(size) = req.max_db_size {
        config.max_db_pages = size.as_u64() / LIBSQL_PAGE_SIZE;
    }
    if let Some(url) = req.heartbeat_url {
        config.heartbeat_url = Some(Url::parse(&url)?);
    }
    config.jwt_key = req.jwt_key;

    store.store(config).await?;

    Ok(())
}

#[derive(Debug, Deserialize)]
struct CreateNamespaceReq {
    dump_url: Option<Url>,