diff --git a/Cargo.lock b/Cargo.lock index b425cf9b7..1bb820ac4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5303,6 +5303,18 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" +[[package]] +name = "shuttle-admin" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.0.4", + "dirs", + "reqwest", + "tokio", + "toml", +] + [[package]] name = "shuttle-codegen" version = "0.7.2" diff --git a/Cargo.toml b/Cargo.toml index 1e840d978..6ae4b77e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "admin", "cargo-shuttle", "codegen", "common", diff --git a/admin/Cargo.toml b/admin/Cargo.toml new file mode 100644 index 000000000..87ef178a7 --- /dev/null +++ b/admin/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "shuttle-admin" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.62" +clap = { version = "4.0.0", features = [ "derive", "env" ] } +dirs = "4.0.0" +reqwest = { version = "0.11.11", features = ["json"] } +tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread"] } +toml = "0.5.9" diff --git a/admin/src/args.rs b/admin/src/args.rs new file mode 100644 index 000000000..5f64d8977 --- /dev/null +++ b/admin/src/args.rs @@ -0,0 +1,17 @@ +use clap::{Parser, Subcommand}; + +#[derive(Parser, Debug)] +pub struct Args { + /// run this command against the api at the supplied url + #[arg(long, default_value = "https://api.shuttle.rs", env = "SHUTTLE_API")] + pub api_url: String, + + #[command(subcommand)] + pub command: Command, +} + +#[derive(Subcommand, Debug)] +pub enum Command { + /// Try to revive projects in the crashed state + Revive, +} diff --git a/admin/src/client.rs b/admin/src/client.rs new file mode 100644 index 000000000..7738fc512 --- /dev/null +++ b/admin/src/client.rs @@ -0,0 +1,28 @@ +use anyhow::{Context, Result}; + +pub struct Client { + api_url: String, + api_key: String, +} + +impl Client { + pub fn new(api_url: String, api_key: String) -> Self { + Self { api_url, api_key } + } + + pub async fn revive(&self) -> Result { + self.post("/admin/revive").await + } + + async fn post(&self, path: &str) -> Result { + reqwest::Client::new() + .post(format!("{}{}", self.api_url, path)) + .bearer_auth(&self.api_key) + .send() + .await + .context("failed to make post request")? + .text() + .await + .context("failed to post text body from response") + } +} diff --git a/admin/src/config.rs b/admin/src/config.rs new file mode 100644 index 000000000..52a295c44 --- /dev/null +++ b/admin/src/config.rs @@ -0,0 +1,15 @@ +use std::{fs, path::PathBuf}; + +pub fn get_api_key() -> String { + let data = fs::read_to_string(config_path()).expect("shuttle config file to exist"); + let toml: toml::Value = toml::from_str(&data).expect("to parse shuttle config file"); + + toml["api_key"].to_string() +} + +fn config_path() -> PathBuf { + dirs::config_dir() + .expect("system to have a config path") + .join("shuttle") + .join("config.toml") +} diff --git a/admin/src/lib.rs b/admin/src/lib.rs new file mode 100644 index 000000000..f1c9fb465 --- /dev/null +++ b/admin/src/lib.rs @@ -0,0 +1,3 @@ +pub mod args; +pub mod client; +pub mod config; diff --git a/admin/src/main.rs b/admin/src/main.rs new file mode 100644 index 000000000..91fac1a89 --- /dev/null +++ b/admin/src/main.rs @@ -0,0 +1,19 @@ +use clap::Parser; +use shuttle_admin::{ + args::{Args, Command}, + client::Client, + config::get_api_key, +}; + +#[tokio::main] +async fn main() { + let args = Args::parse(); + let api_key = get_api_key(); + let client = Client::new(args.api_url.clone(), api_key); + + let res = match args.command { + Command::Revive => client.revive().await.expect("revive to succeed"), + }; + + println!("{res}"); +}