Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ blackcandy config

The config file is written with user-only permissions on Unix systems.

To revoke the current server session and remove the stored credentials, log out:

```sh
blackcandy logout
```

For isolated test runs, point `BLACKCANDY_CONFIG` at a temporary file:

```sh
Expand Down
4 changes: 4 additions & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ impl ApiClient {
.await
}

pub async fn logout(&self) -> Result<()> {
self.delete_empty("my/session").await
}

pub async fn system(&self) -> Result<SystemInfo> {
self.get_without_auth("system", &[]).await
}
Expand Down
13 changes: 13 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ impl Config {
}
}

/// Remove the stored config file. Returns `true` if a file was deleted and
/// `false` if there was nothing to remove.
pub fn remove_config() -> Result<bool> {
let path = config_path()?;
if !path.exists() {
return Ok(false);
}

fs::remove_file(&path)
.with_context(|| format!("failed to remove config at {}", path.display()))?;
Ok(true)
}

pub fn config_path() -> Result<PathBuf> {
if let Ok(path) = std::env::var("BLACKCANDY_CONFIG") {
return Ok(PathBuf::from(path));
Expand Down
27 changes: 25 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::io::{self, Write};

use crate::{
api::{ApiClient, SongQuery, validate_limit},
config::{Config, config_path},
config::{Config, config_path, remove_config},
};

#[derive(Debug, Parser)]
Expand All @@ -25,6 +25,8 @@ struct Cli {
enum Command {
/// Log in to a Black Candy server and store an API token.
Login(LoginArgs),
/// Log out by removing the stored credentials.
Logout,
/// Show the configured server and current config path.
Config,
/// Show Black Candy server version information.
Expand Down Expand Up @@ -194,6 +196,7 @@ async fn main() -> Result<()> {

match cli.command {
Command::Login(args) => login(args).await,
Command::Logout => logout().await,
Command::Config => show_config(),
command => {
let config = Config::load()?;
Expand Down Expand Up @@ -237,6 +240,24 @@ async fn login(args: LoginArgs) -> Result<()> {
Ok(())
}

async fn logout() -> Result<()> {
let config = Config::load()?;
if config.api_token.is_some() {
let client = configured_client(&config)?;
client.logout().await?;
}

if remove_config()? {
println!(
"Logged out. Removed config at {}.",
config_path()?.display()
);
} else {
println!("Not logged in; nothing to remove.");
}
Ok(())
}

fn prompt_line(prompt: &str) -> Result<String> {
print!("{prompt}");
io::stdout().flush()?;
Expand Down Expand Up @@ -299,7 +320,9 @@ async fn run_authenticated(command: Command, config: Config, client: ApiClient)
Command::Queue(args) => run_queue(args, &client).await?,
Command::Favorite(args) => run_favorite(args, &client).await?,
Command::Playlist(args) => run_playlist(args, &client).await?,
Command::Login(_) | Command::Config => unreachable!("handled before authentication setup"),
Command::Login(_) | Command::Logout | Command::Config => {
unreachable!("handled before authentication setup")
}
}

Ok(())
Expand Down