Skip to content

Commit

Permalink
Add authentication support
Browse files Browse the repository at this point in the history
  • Loading branch information
yukibtc committed Nov 22, 2022
1 parent a7c47bc commit e681b08
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 55 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ default = []
blocking = ["reqwest/blocking"]

[dependencies]
base64 = "0.13"
log = "0.4"
reqwest = { version = "0.11", features = ["json", "socks"] }
serde = { version = "1.0", features = ["derive"] }
Expand Down
32 changes: 14 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@ tokio = { version = "1", features = ["full"] }
```

```rust,no_run
use ntfy::{Dispatcher, Payload, Priority};
use ntfy::{Dispatcher, Payload, Priority, Auth};
#[tokio::main]
async fn main() {
let dispatcher = Dispatcher::new("https://ntfy.sh", Some("socks5h://127.0.0.1:9050")).unwrap();
let auth = Auth::new("username", "password");
let dispatcher = Dispatcher::new(
"https://ntfy.sh",
Some(auth),
Some("socks5h://127.0.0.1:9050"),
)
.unwrap();
let payload = Payload::new("mytopic", "Hello, World!")
.title("Alert") // Add optional title
Expand All @@ -26,25 +32,15 @@ async fn main() {
}
```

## Blocking example
More examples can be found in the [examples](./examples/) directory.

```toml
ntfy = { version = "0.1", features = ["blocking"] }
```

```rust,no_run
use ntfy::{Dispatcher, Payload, Priority};
fn main() {
let dispatcher = Dispatcher::new("https://ntfy.sh", Some("socks5h://127.0.0.1:9050")).unwrap();
## Crate Feature Flags

let payload = Payload::new("mytopic", "Hello, World!")
.title("Alert") // Add optional title
.priority(Priority::High); // Edit priority
The following crate feature flags are available:

dispatcher.send(&payload).unwrap();
}
```
| Feature | Default | Description |
| ------------------- | :-----: | -------------------------------------------------------------------------------------------------------------------------- |
| `blocking` | No | Needed if you want to use this library in not async/await context |

## License

Expand Down
10 changes: 8 additions & 2 deletions examples/hello_world_alert.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
// Copyright (c) 2022 Yuki Kishimoto
// Distributed under the MIT software license

use ntfy::{Dispatcher, Payload, Priority};
use ntfy::{Auth, Dispatcher, Payload, Priority};

#[tokio::main]
async fn main() {
let dispatcher = Dispatcher::new("https://ntfy.sh", Some("socks5h://127.0.0.1:9050")).unwrap();
let auth = Auth::new("username", "password");
let dispatcher = Dispatcher::new(
"https://ntfy.sh",
Some(auth),
Some("socks5h://127.0.0.1:9050"),
)
.unwrap();

/* let payload = Payload {
topic: String::from("mytopic"),
Expand Down
23 changes: 23 additions & 0 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2022 Yuki Kishimoto
// Distributed under the MIT software license

pub struct Auth {
username: String,
password: String,
}

impl Auth {
pub fn new<S>(username: S, password: S) -> Self
where
S: Into<String>,
{
Self {
username: username.into(),
password: password.into(),
}
}

pub fn as_base64(&self) -> String {
base64::encode(format!("{}:{}", self.username, self.password))
}
}
58 changes: 23 additions & 35 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#[macro_use]
extern crate serde;

use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue};
#[cfg(not(feature = "blocking"))]
use reqwest::Client as ReqwestClient;
#[cfg(not(feature = "blocking"))]
Expand All @@ -18,8 +19,12 @@ use reqwest::blocking::RequestBuilder;
use reqwest::{Proxy, StatusCode};
use thiserror::Error;

mod auth;
mod payload;
mod priority;

pub use self::auth::Auth;
pub use self::payload::Payload;
pub use self::priority::Priority;

#[derive(Clone)]
Expand All @@ -28,46 +33,14 @@ pub struct Dispatcher {
client: ReqwestClient,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Payload {
pub topic: String,
pub message: String,
#[serde(with = "priority")]
pub priority: Priority,
pub title: Option<String>,
}

impl Payload {
/// Create new payload
pub fn new(topic: &str, message: &str) -> Self {
Self {
topic: topic.into(),
message: message.into(),
priority: Priority::default(),
title: None,
}
}

/// Set priority
pub fn priority(self, priority: Priority) -> Self {
Self { priority, ..self }
}

/// Set title
pub fn title(self, title: &str) -> Self {
Self {
title: Some(title.into()),
..self
}
}
}

#[derive(Debug, Error)]
pub enum NtfyError {
#[error("Failed to deserialize: {0}")]
FailedToDeserialize(String),
#[error("Reqwest error: {0}")]
ReqwestError(reqwest::Error),
#[error("Invalid header value: {0}")]
InvalidHeaderValue(InvalidHeaderValue),
#[error("Empty Response")]
EmptyResponse,
#[error("Bad Result")]
Expand Down Expand Up @@ -102,9 +75,18 @@ pub enum NtfyError {

impl Dispatcher {
/// Create new dispatcher
pub fn new(url: &str, proxy: Option<&str>) -> Result<Self, NtfyError> {
pub fn new(url: &str, auth: Option<Auth>, proxy: Option<&str>) -> Result<Self, NtfyError> {
let mut client = ReqwestClient::builder();

if let Some(auth) = auth {
let mut headers = HeaderMap::new();
let mut auth_value = HeaderValue::from_str(&format!("Basic {}", auth.as_base64()))?;
auth_value.set_sensitive(true);
headers.insert("Authorization", auth_value);

client = client.default_headers(headers);
}

if let Some(proxy) = proxy {
client = client.proxy(Proxy::all(proxy)?);
}
Expand Down Expand Up @@ -199,3 +181,9 @@ impl From<reqwest::Error> for NtfyError {
NtfyError::ReqwestError(err)
}
}

impl From<InvalidHeaderValue> for NtfyError {
fn from(err: InvalidHeaderValue) -> Self {
NtfyError::InvalidHeaderValue(err)
}
}
38 changes: 38 additions & 0 deletions src/payload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2022 Yuki Kishimoto
// Distributed under the MIT software license

use crate::priority::{self, Priority};

#[derive(Debug, Serialize, Deserialize)]
pub struct Payload {
pub topic: String,
pub message: String,
#[serde(with = "priority")]
pub priority: Priority,
pub title: Option<String>,
}

impl Payload {
/// Create new payload
pub fn new(topic: &str, message: &str) -> Self {
Self {
topic: topic.into(),
message: message.into(),
priority: Priority::default(),
title: None,
}
}

/// Set priority
pub fn priority(self, priority: Priority) -> Self {
Self { priority, ..self }
}

/// Set title
pub fn title(self, title: &str) -> Self {
Self {
title: Some(title.into()),
..self
}
}
}

0 comments on commit e681b08

Please sign in to comment.