Skip to main content

fn0_deploy/
admin.rs

1use crate::credentials;
2use anyhow::{Result, anyhow};
3use serde::{Deserialize, Serialize};
4
5pub struct AdminRunOutput {
6    pub status: u16,
7    pub content_type: Option<String>,
8    pub body: Vec<u8>,
9}
10
11#[derive(Serialize)]
12struct AdminRunInput<'a> {
13    project_id: &'a str,
14    task: &'a str,
15    input: serde_json::Value,
16}
17
18#[derive(Deserialize)]
19#[serde(tag = "t", rename_all_fields = "camelCase")]
20enum AdminRunResponse {
21    Ok {
22        status: u16,
23        body: serde_json::Value,
24    },
25    NotLoggedIn,
26    NotFound,
27    Forbidden,
28    NotDeployed,
29    UpstreamError {
30        status: u16,
31        body: serde_json::Value,
32    },
33    InternalError {
34        reason: String,
35    },
36}
37
38pub async fn admin_run(
39    project_id: &str,
40    task: &str,
41    input_body: Vec<u8>,
42    timeout_secs: u64,
43) -> Result<AdminRunOutput> {
44    let creds = credentials::require()?;
45
46    let input: serde_json::Value = if input_body.is_empty() {
47        serde_json::Value::Null
48    } else {
49        serde_json::from_slice(&input_body)
50            .map_err(|e| anyhow!("admin run input is not valid JSON: {e}"))?
51    };
52
53    let url = format!(
54        "{}/__forte_action/admin_run",
55        creds.control_url.trim_end_matches('/')
56    );
57
58    let client = reqwest::Client::builder()
59        .timeout(std::time::Duration::from_secs(timeout_secs))
60        .build()?;
61
62    let raw: AdminRunResponse = client
63        .post(&url)
64        .bearer_auth(&creds.token)
65        .json(&AdminRunInput {
66            project_id,
67            task,
68            input,
69        })
70        .send()
71        .await?
72        .error_for_status()
73        .map_err(|e| anyhow!("admin run control call failed: {e}"))?
74        .json()
75        .await?;
76
77    match raw {
78        AdminRunResponse::Ok { status, body } => Ok(AdminRunOutput {
79            status,
80            content_type: Some("application/json".to_string()),
81            body: serde_json::to_vec(&body)?,
82        }),
83        AdminRunResponse::UpstreamError { status, body } => Ok(AdminRunOutput {
84            status,
85            content_type: Some("application/json".to_string()),
86            body: serde_json::to_vec(&body)?,
87        }),
88        AdminRunResponse::NotLoggedIn => {
89            Err(anyhow!("control rejected token; run `fn0 login` again."))
90        }
91        AdminRunResponse::NotFound => Err(anyhow!("project '{project_id}' not found.")),
92        AdminRunResponse::Forbidden => Err(anyhow!(
93            "project '{project_id}' is not owned by the signed-in user."
94        )),
95        AdminRunResponse::NotDeployed => Err(anyhow!(
96            "project '{project_id}' has no deployed version yet."
97        )),
98        AdminRunResponse::InternalError { reason } => {
99            Err(anyhow!("control admin_run internal error: {reason}"))
100        }
101    }
102}