DEVELOPMENT ENVIRONMENT

~liljamo/canwa

3a2ad3d4f4147f683ac78acab88597433a1430a2 — Jonni Liljamo 27 days ago aee45a0
feat: optionally read tokens from files
4 files changed, 32 insertions(+), 2 deletions(-)

M README.md
M src/config.rs
M src/service/gotify.rs
M src/service/matrix.rs
M README.md => README.md +8 -0
@@ 12,6 12,14 @@ A small API for relaying the same message to multiple services.
The following example creates a notifier that sends a message to Gotify and
Matrix.

### `token` fields
All `token` fields (service and notifier configs) can also be a path to a
file containing the token, e.g. `/run/secrets/canwa-matrix-token`

canwa will check if the value is a path that exists and reads it if it is.
If the value is not a path, or it is a path that cannot be read, it will be
assumed a raw string token.

### Example `canwa.toml`
```toml
interface = "0.0.0.0"

M src/config.rs => src/config.rs +20 -0
@@ 32,8 32,28 @@ pub trait ServiceConfig {
    fn as_any(&self) -> &dyn std::any::Any;
}

pub fn deserialize_token<'de, D>(deserializer: D) -> Result<String, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let token_or_path: String = serde::de::Deserialize::deserialize(deserializer)?;
    // If the value exists as a path and we can read it, read the file.
    let path = std::path::Path::new(&token_or_path);
    if path.exists() {
        match std::fs::read_to_string(path) {
            Ok(token) => return Ok(token.trim().into()),
            Err(err) => {
                tracing::warn!(msg="token exists as a filesystem path, but could not be read, assuming raw token", %err)
            }
        }
    }
    // Assume raw token.
    Ok(token_or_path)
}

#[derive(Clone, Deserialize)]
pub struct NotifierConfig {
    #[serde(deserialize_with = "deserialize_token")]
    pub token: String,
    pub services: Vec<String>,
}

M src/service/gotify.rs => src/service/gotify.rs +2 -1
@@ 8,13 8,14 @@
use async_trait::async_trait;
use serde::{Deserialize, Serialize};

use crate::config::ServiceConfig;
use crate::config::{ServiceConfig, deserialize_token};

use super::Service;

#[derive(Clone, Deserialize, Serialize)]
pub struct GotifyConfig {
    pub instance: String,
    #[serde(deserialize_with = "deserialize_token")]
    pub token: String,
}


M src/service/matrix.rs => src/service/matrix.rs +2 -1
@@ 9,13 9,14 @@ use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::json;

use crate::config::ServiceConfig;
use crate::config::{ServiceConfig, deserialize_token};

use super::Service;

#[derive(Clone, Deserialize, Serialize)]
pub struct MatrixConfig {
    pub instance: String,
    #[serde(deserialize_with = "deserialize_token")]
    pub token: String,
    pub room: String,
}