Skip to main content

fn0_deploy/
app_url.rs

1//! Resolves the public URL a deployed project is served at.
2//!
3//! There is no default subdomain. A project answers on the custom domain its
4//! owner registered in their own Cloudflare zone and on nothing else, so until
5//! one is attached and its origin certificate is held, the project has no URL
6//! to report rather than a fallback one.
7
8use anyhow::Result;
9
10use crate::domain::{DomainStatus, fetch_domain_status};
11
12pub struct ResolvedAppUrl {
13    pub url: Option<String>,
14    pub note: Option<String>,
15}
16
17impl ResolvedAppUrl {
18    fn unresolved(note: String) -> Self {
19        Self {
20            url: None,
21            note: Some(note),
22        }
23    }
24}
25
26pub async fn resolve_app_url(project_id: &str) -> Result<ResolvedAppUrl> {
27    let Some(creds) = crate::credentials::load()? else {
28        return Ok(ResolvedAppUrl::unresolved(
29            "not signed in, so the project's domain could not be checked".to_string(),
30        ));
31    };
32
33    Ok(match fetch_domain_status(&creds, project_id).await {
34        // The owner's own edge terminates the visitor connection, so the domain
35        // serves as soon as their DNS points at the origin — there is no fn0-side
36        // DV state to wait on. The origin certificate is what fn0 must hold.
37        Ok(DomainStatus::SelfHosted {
38            domain,
39            origin_certificate_ready: true,
40            ..
41        }) => ResolvedAppUrl {
42            url: Some(format!("https://{domain}")),
43            note: None,
44        },
45        Ok(DomainStatus::SelfHosted { domain, .. }) => ResolvedAppUrl::unresolved(format!(
46            "'{domain}' has no origin certificate yet; run `forte domain add {domain}`"
47        )),
48        Ok(DomainStatus::NotConfigured) => ResolvedAppUrl::unresolved(
49            "no domain is attached; run `forte domain add <hostname>`".to_string(),
50        ),
51        Ok(DomainStatus::NotLoggedIn) => ResolvedAppUrl::unresolved(
52            "control rejected the saved token; run `forte login` again".to_string(),
53        ),
54        Ok(DomainStatus::NotFound) => ResolvedAppUrl::unresolved(format!(
55            "project '{project_id}' was not found under your account; nothing may be deployed there"
56        )),
57        Ok(DomainStatus::InternalError) => {
58            ResolvedAppUrl::unresolved("control failed to report the domain".to_string())
59        }
60        Err(e) => ResolvedAppUrl::unresolved(format!(
61            "could not reach control to check the domain: {e}"
62        )),
63    })
64}