fly-sdk 0.1.3

Unofficial Fly.io SDK for Rust
Documentation
use crate::API_BASE_URL;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::error::Error;
use tracing::debug;

#[derive(Debug, Deserialize, Serialize)]
pub struct Secret {
    pub label: String,
    pub publickey: Vec<i32>,
    #[serde(rename = "type")]
    pub stype: String,
}

#[derive(Debug, Serialize)]
pub struct SecretValue {
    pub value: Vec<i32>,
}

impl SecretValue {
    pub fn new(value: Vec<i32>) -> Self {
        Self { value }
    }
}

pub struct SecretsManager {
    client: Client,
    api_token: String,
}

impl SecretsManager {
    pub fn new(client: Client, api_token: String) -> Self {
        Self { client, api_token }
    }

    pub async fn list_secrets(&self, app_name: &str) -> Result<Vec<Secret>, Box<dyn Error>> {
        let url = format!("{API_BASE_URL}/apps/{}/secrets", app_name);
        let response = self
            .client
            .get(&url)
            .bearer_auth(&self.api_token)
            .send()
            .await?;

        if response.status().is_success() {
            let secrets = response.json::<Vec<Secret>>().await?;
            debug!("Successfully fetched secrets: {:?}", secrets);
            Ok(secrets)
        } else {
            Err(format!("Failed to fetch secrets: {}", response.status()).into())
        }
    }

    pub async fn create_secret(
        &self,
        app_name: &str,
        secret_label: &str,
        secret_type: &str,
        value_request: SecretValue,
    ) -> Result<Secret, Box<dyn Error>> {
        debug!("Creating secret: {}", secret_label);
        let url = format!(
            "{API_BASE_URL}/apps/{}/secrets/{}/type/{}",
            app_name, secret_label, secret_type
        );
        let response = self
            .client
            .post(&url)
            .bearer_auth(&self.api_token)
            .json(&value_request)
            .send()
            .await?;

        let status = response.status();
        if status.is_success() {
            let secret = response.json::<Secret>().await?;
            Ok(secret)
        } else {
            let error_text = response.text().await?;
            Err(format!("Failed to create secret: {} - {}", status, error_text).into())
        }
    }

    pub async fn generate_secret(
        &self,
        app_name: &str,
        secret_label: &str,
        secret_type: &str,
    ) -> Result<(), Box<dyn Error>> {
        debug!("Generating secret: {}", secret_label);
        let url = format!(
            "{API_BASE_URL}/apps/{}/secrets/{}/type/{}/generate",
            app_name, secret_label, secret_type
        );
        let response = self
            .client
            .post(&url)
            .bearer_auth(&self.api_token)
            .send()
            .await?;

        if response.status().is_success() {
            debug!("Successfully generated secret: {}", secret_label);
            Ok(())
        } else {
            Err(format!("Failed to generate secret: {}", response.status()).into())
        }
    }

    pub async fn destroy_secret(
        &self,
        app_name: &str,
        secret_label: &str,
    ) -> Result<(), Box<dyn Error>> {
        debug!("Deleting secret: {}", secret_label);
        let url = format!("{API_BASE_URL}/apps/{}/secrets/{}", app_name, secret_label);
        let response = self
            .client
            .delete(&url)
            .bearer_auth(&self.api_token)
            .send()
            .await?;

        if response.status().is_success() {
            debug!("Successfully deleted secret: {}", secret_label);
            Ok(())
        } else {
            Err(format!("Failed to delete secret: {}", response.status()).into())
        }
    }
}