use std::path::PathBuf;
use anyhow::{Context, Result, anyhow, bail};
use clap::ArgMatches;
use serde::Deserialize;
use super::base_path::{RequiredLocation, find_app_root_path};
use super::hmac::AuthMode;
use super::manifest::application::ApplicationManifestData;
use super::token::get_token;
use crate::constants::{get_billing_api_url, is_dev_build};
pub(crate) fn require_auth() -> Result<String> {
get_token()
}
pub(crate) fn resolve_auth() -> Result<AuthMode> {
let mode = AuthMode::detect();
if matches!(mode, AuthMode::Jwt) {
get_token()?; }
Ok(mode)
}
pub(crate) fn require_manifest(
matches: &ArgMatches,
) -> Result<(PathBuf, ApplicationManifestData)> {
let (app_root, _) = find_app_root_path(matches, RequiredLocation::Application)?;
let manifest_path = app_root.join(".forklaunch").join("manifest.toml");
let content = std::fs::read_to_string(&manifest_path)
.with_context(|| format!("Failed to read manifest at {:?}", manifest_path))?;
let manifest: ApplicationManifestData =
toml::from_str(&content).with_context(|| "Failed to parse manifest.toml")?;
Ok((app_root, manifest))
}
#[derive(Deserialize)]
struct TrialStatusResponse {
#[serde(rename = "isActive")]
is_active: bool,
#[serde(rename = "hasSubscription")]
has_subscription: bool,
#[serde(default, rename = "planName")]
plan_name: Option<String>,
}
pub(crate) fn require_active_account(auth_mode: &AuthMode) -> Result<()> {
if matches!(auth_mode, AuthMode::Hmac { .. }) {
return Ok(());
}
if is_dev_build() {
return Ok(());
}
let token = get_token()?;
let api_url = get_billing_api_url();
let client = reqwest::blocking::Client::new();
let response = client
.get(format!("{}/trial/status", api_url))
.header("Authorization", format!("Bearer {}", token))
.send();
match response {
Ok(resp) if resp.status().is_success() => match resp.json::<TrialStatusResponse>() {
Ok(status) if status.is_active || status.has_subscription => Ok(()),
Ok(status) => {
let plan = status
.plan_name
.map(|p| format!(" (current plan: {})", p))
.unwrap_or_default();
bail!(
"Your free trial has expired{}. Please upgrade at https://forklaunch.com/checkout?plan=pro to continue using the CLI.",
plan
);
}
Err(_) => {
eprintln!("Warning: Could not read account status response. Proceeding.");
Ok(())
}
},
Ok(resp) => {
eprintln!(
"Warning: Could not verify account status (HTTP {}). Proceeding.",
resp.status()
);
Ok(())
}
Err(_) => {
eprintln!("Warning: Could not reach account verification service. Proceeding.");
Ok(())
}
}
}
pub(crate) fn require_integration(manifest: &ApplicationManifestData) -> Result<String> {
manifest
.platform_application_id
.clone()
.ok_or_else(|| {
anyhow!("Application not integrated with platform.\nRun: forklaunch integrate --app <app-id>")
})
}