oxyde_cloud_deploy/
deploy.rs

1use cargo_leptos::config::Opts;
2use oxyde_cloud_client::{Client, ReqwestJsonError, UploadFileError};
3use oxyde_cloud_common::config::CloudConfig;
4use std::env::VarError;
5use std::ffi::OsStr;
6use std::fs::read_dir;
7use std::path::{Path, PathBuf};
8use thiserror::Error;
9use walkdir::WalkDir;
10
11#[derive(Debug, Error)]
12pub enum Error {
13    #[error("Build error: {0}")]
14    Build(#[from] anyhow::Error),
15
16    #[error("IO error: {0}")]
17    Io(#[from] std::io::Error),
18
19    #[error("Check Name error: {0}")]
20    CheckName(#[from] ReqwestJsonError),
21
22    #[error("Upload error: {0}")]
23    Upload(#[from] UploadFileError),
24
25    #[error("Deployment done error: {0}")]
26    Done(#[from] reqwest::Error),
27
28    #[error("Error reading variable `OXYDE_CLOUD_API_KEY`: {0}")]
29    ApiKeyEnv(#[from] VarError),
30
31    #[error("Config loading error: {0}")]
32    Config(#[from] oxyde_cloud_common::config::Error),
33}
34
35pub async fn deploy_with_config_file(
36    config: &PathBuf,
37    cargo_leptos_opts: Opts,
38) -> Result<(), Error> {
39    let config = CloudConfig::load(&config).await?;
40    deploy(&config, cargo_leptos_opts).await?;
41    Ok(())
42}
43
44pub async fn deploy(config: &CloudConfig, cargo_leptos_opts: Opts) -> Result<(), Error> {
45    if let Some((_, database_url)) = config.env.get_key_value("DATABASE_URL") {
46        unsafe { std::env::set_var("DATABASE_URL", database_url.as_str().expect("DATABASE_URL should be a string")) ; }
47    };
48    
49    crate::build::build(cargo_leptos_opts.clone()).await.map_err(Error::Build)?;
50
51    let target_dir = "target";
52    let server_bin_dir = if cargo_leptos_opts.release {
53        "release"
54    } else {
55        "debug"
56    };
57    let frontend_dir = "site";
58
59    let api_key = std::env::var("OXYDE_CLOUD_API_KEY")?;
60    let client = Client::new(api_key.clone());
61
62    let frontend_path = Path::new(target_dir).join(frontend_dir);
63    let server_path = Path::new(target_dir).join(server_bin_dir);
64
65    let mut files = recursive_files_from_dir(frontend_path);
66    files.append(&mut server_files(server_path)?);
67
68    log::debug!(target:"cargo_leptos", "Found files: {:#?}", files);
69
70    log::info!(target:"cargo_leptos", "Deploying app {}", config.app.slug);
71
72    if let Err(err) = deploy_inner(config, client, &mut files).await {
73        log::error!(target:"cargo_leptos", "Deploy failed: {:?}", err);
74        return Err(err);
75    }
76
77    log::info!(target:"cargo_leptos", "Deployed app to {}", config.deployed_url());
78
79    Ok(())
80}
81
82async fn deploy_inner(
83    config: &CloudConfig,
84    client: Client,
85    files: &mut Vec<PathBuf>,
86) -> Result<(), Error> {
87    for file in files {
88        log::debug!(target:"cargo_leptos", "Uploading {}...", file.display());
89        client.clone().upload_file(&config.app.slug, file).await?;
90    }
91
92    log::debug!(target:"cargo_leptos", "Deploying app...");
93    client.upload_done(config).await?;
94
95    Ok(())
96}
97
98fn recursive_files_from_dir(dir: impl AsRef<Path>) -> Vec<PathBuf> {
99    WalkDir::new(dir)
100        .into_iter()
101        .filter_map(|e| e.ok().map(|e| e.into_path()))
102        .filter_map(|e| if e.is_file() { Some(e) } else { None })
103        .collect()
104}
105
106fn server_files(dir: impl AsRef<Path>) -> std::io::Result<Vec<PathBuf>> {
107    Ok(read_dir(dir)?
108        .filter_map(|d| {
109            d.ok().and_then(|e| {
110                let path = e.path();
111                if path.is_file() && path.extension() != Some(OsStr::new("d")) {
112                    Some(path)
113                } else {
114                    None
115                }
116            })
117        })
118        .collect())
119}