Initial Commit
This commit is contained in:
commit
3851d9df21
13 changed files with 2015 additions and 0 deletions
74
.forgejo/workflows/build.yml
Normal file
74
.forgejo/workflows/build.yml
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.cargo/bin
|
||||||
|
RUSTFLAGS: -C target-feature=-crt-static
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checkfmt:
|
||||||
|
name: Check format
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: alpine
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- nightly
|
||||||
|
steps:
|
||||||
|
- name: Install packages
|
||||||
|
run: apk add --no-cache rustup nodejs git
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install rustup
|
||||||
|
run: rustup-init -y --default-toolchain none
|
||||||
|
- name: Install toolchain
|
||||||
|
run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
|
- name: Check format
|
||||||
|
run: cargo fmt --check
|
||||||
|
lint:
|
||||||
|
name: Lint Uketoru
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: alpine
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- stable
|
||||||
|
steps:
|
||||||
|
- name: Install packages
|
||||||
|
run: apk add --no-cache rustup nodejs git build-base openssl-dev pkgconfig
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Initialize rustup
|
||||||
|
run: rustup-init -y --default-toolchain none
|
||||||
|
- name: Install toolchain
|
||||||
|
run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
|
- name: Lint Uketoru
|
||||||
|
run: cargo clippy --release -- -Dwarnings
|
||||||
|
build:
|
||||||
|
name: Build Uketoru
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: alpine
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- stable
|
||||||
|
steps:
|
||||||
|
- name: Install packages
|
||||||
|
run: apk add --no-cache rustup nodejs git build-base pkgconfig openssl-dev
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Initialize rustup
|
||||||
|
run: rustup-init -y --default-toolchain none
|
||||||
|
- name: Install toolchain
|
||||||
|
run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
|
- name: Build Uketoru
|
||||||
|
run: cargo build --release
|
78
.github/workflows/build.yml
vendored
Normal file
78
.github/workflows/build.yml
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- ready_for_review
|
||||||
|
- synchronize
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.cargo/bin
|
||||||
|
RUSTFLAGS: -C target-feature=-crt-static
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checkfmt:
|
||||||
|
name: Check format
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: alpine
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- nightly
|
||||||
|
steps:
|
||||||
|
- name: Install packages
|
||||||
|
run: apk add --no-cache rustup nodejs git
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install rustup
|
||||||
|
run: rustup-init -y --default-toolchain none
|
||||||
|
- name: Install toolchain
|
||||||
|
run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
|
- name: Check format
|
||||||
|
run: cargo fmt --check
|
||||||
|
lint:
|
||||||
|
name: Lint Uketoru
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: alpine
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- stable
|
||||||
|
steps:
|
||||||
|
- name: Install packages
|
||||||
|
run: apk add --no-cache rustup nodejs git build-base openssl-dev pkgconfig
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Initialize rustup
|
||||||
|
run: rustup-init -y --default-toolchain none
|
||||||
|
- name: Install toolchain
|
||||||
|
run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
|
- name: Lint Uketoru
|
||||||
|
run: cargo clippy --release -- -Dwarnings
|
||||||
|
build:
|
||||||
|
name: Build Uketoru
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: alpine
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- stable
|
||||||
|
steps:
|
||||||
|
- name: Install packages
|
||||||
|
run: apk add --no-cache rustup nodejs git build-base pkgconfig openssl-dev
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Initialize rustup
|
||||||
|
run: rustup-init -y --default-toolchain none
|
||||||
|
- name: Install toolchain
|
||||||
|
run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||||
|
- name: Build Uketoru
|
||||||
|
run: cargo build --release
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/config.toml
|
16
.rustfmt.toml
Normal file
16
.rustfmt.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
unstable_features = true
|
||||||
|
indent_style = "Block"
|
||||||
|
imports_indent = "Block"
|
||||||
|
imports_layout = "HorizontalVertical"
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
brace_style = "PreferSameLine"
|
||||||
|
match_arm_leading_pipes = "Never"
|
||||||
|
match_arm_blocks = false
|
||||||
|
condense_wildcard_suffixes = true
|
||||||
|
overflow_delimited_expr = false
|
||||||
|
spaces_around_ranges = true
|
||||||
|
reorder_imports = true
|
||||||
|
hard_tabs = false
|
||||||
|
max_width = 130
|
||||||
|
fn_call_width = 120
|
||||||
|
chain_width = 90
|
1478
Cargo.lock
generated
Normal file
1478
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "uketoru"
|
||||||
|
license = "MIT"
|
||||||
|
authors = [ "Xavier Moffett <sapphirus@azorium.net>" ]
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version="1.82"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
axum = { version = "0.7.7", default-features = false, features = [ "json", "http1", "tokio" ] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
|
lettre = "0.11.10"
|
||||||
|
toml = "0.8.19"
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (C) 2024 Xavier Moffett <sapphirus@azorium.net>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
11
README.md
Normal file
11
README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Uketoru [![Build Workflow](https://git.sapphirus.org/Sapphirus/Uketoru/badges/workflows/build.yml/badge.svg?label=build&logo=github+actions&logoColor=d1d7e0&style=flat-square)](https://git.sapphirus.org/Sapphirus/Uketoru/actions?workflow=build.yml)
|
||||||
|
|
||||||
|
Uketoru provides a simple, yet configurable middleware to relay messages to a SMTP server.
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
[SMS to URL Forwarder](https://github.com/bogkonstantin/android_income_sms_gateway_webhook)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT license](./LICENSE)
|
43
config.toml.example
Normal file
43
config.toml.example
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
[relay]
|
||||||
|
# Declare a token here to enable token authentication.
|
||||||
|
# Leaving this value undeclared will disable this functionality.
|
||||||
|
#
|
||||||
|
# token = "TOKEN_HERE"
|
||||||
|
#
|
||||||
|
# Listen value declaration structured with the hostname:port
|
||||||
|
#
|
||||||
|
# listen = "0.0.0.0:3000"
|
||||||
|
#
|
||||||
|
|
||||||
|
[smtp]
|
||||||
|
# SMTP Transport Type. Available types: TLS and StartTLS.
|
||||||
|
# By default, TLS is selected when this value is undeclared.
|
||||||
|
#
|
||||||
|
# transport = "StartTLS"
|
||||||
|
#
|
||||||
|
# Hostname for the SMTP Mailer this gateway is relaying messages towards.
|
||||||
|
# Note: This value is required.
|
||||||
|
#
|
||||||
|
server = "smtp.example.com"
|
||||||
|
|
||||||
|
# Declare this value if the SMTP Mailer is on a different port. e.g. SUBMISSION 587
|
||||||
|
#
|
||||||
|
# port = 587
|
||||||
|
#
|
||||||
|
# E-mail Address being used to relay messages.
|
||||||
|
# Note: This value is required.
|
||||||
|
#
|
||||||
|
address = "mail@example.com"
|
||||||
|
# Sender name for the relay address.
|
||||||
|
# The default naem is Uketoru.
|
||||||
|
#
|
||||||
|
# name = "Sender Name"
|
||||||
|
#
|
||||||
|
# Username credential used to authenticate against the SMTP mailer.
|
||||||
|
# If this value is undeclared, it will default to the e-mail adddress specified.
|
||||||
|
#
|
||||||
|
# username = "user"
|
||||||
|
#
|
||||||
|
# Pasword credential used to authenticate against the SMTP mailer.
|
||||||
|
#
|
||||||
|
#password = "password"
|
125
src/config.rs
Normal file
125
src/config.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Uketoru
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Xavier Moffett <sapphirus@azorium.net>
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use lettre::transport::smtp::authentication::Credentials;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{fs::File, io::Read, sync::LazyLock};
|
||||||
|
|
||||||
|
pub static CONFIG: LazyLock<Config> = LazyLock::new(load);
|
||||||
|
|
||||||
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
|
#[derive(Deserialize, Debug, Clone, Default)]
|
||||||
|
pub enum TransportType {
|
||||||
|
#[default]
|
||||||
|
TLS,
|
||||||
|
StartTLS,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
pub struct Smtp {
|
||||||
|
#[serde(default = "TransportType::default")]
|
||||||
|
transport: TransportType,
|
||||||
|
address: String,
|
||||||
|
to: Option<String>,
|
||||||
|
name: Option<String>,
|
||||||
|
username: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
password: String,
|
||||||
|
server: String,
|
||||||
|
port: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
pub struct Relay {
|
||||||
|
listen: Option<String>,
|
||||||
|
token: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
pub struct Config {
|
||||||
|
relay: Relay,
|
||||||
|
smtp: Smtp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn client(&self) -> &Smtp {
|
||||||
|
&self.smtp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn relay(&self) -> &Relay {
|
||||||
|
&self.relay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Relay {
|
||||||
|
pub fn token(&self) -> Option<&str> {
|
||||||
|
self.token.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listen_addr(&self) -> &str {
|
||||||
|
self.listen.as_deref().unwrap_or("0.0.0.0:3000")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Smtp {
|
||||||
|
pub fn transport(&self) -> &TransportType {
|
||||||
|
&self.transport
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn credentials(&self) -> Credentials {
|
||||||
|
match &self.username {
|
||||||
|
Some(username) => Credentials::new(username.to_owned(), self.password.to_owned()),
|
||||||
|
None => Credentials::new(self.address.to_owned(), self.password.to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn server(&self) -> &str {
|
||||||
|
&self.server
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn port(&self) -> u16 {
|
||||||
|
self.port.unwrap_or(25)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn address(&self) -> &str {
|
||||||
|
&self.address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to(&self) -> &str {
|
||||||
|
self.to.as_deref().unwrap_or(&self.address)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
self.name.as_deref().unwrap_or("Uketoru")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load() -> Config {
|
||||||
|
let mut string = String::new();
|
||||||
|
let mut config = File::open("config.toml").expect("Opening config.toml");
|
||||||
|
|
||||||
|
config.read_to_string(&mut string).expect("Reading config.toml");
|
||||||
|
toml::from_str::<Config>(&string).expect("Invalid config.toml")
|
||||||
|
}
|
46
src/main.rs
Normal file
46
src/main.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Uketoru
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Xavier Moffett <sapphirus@azorium.net>
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use axum::{middleware, routing::post, serve, Router};
|
||||||
|
use std::io::Error;
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
use crate::{config::CONFIG, relay::*};
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
mod relay;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Error> {
|
||||||
|
let router = Router::new()
|
||||||
|
.route("/api", post(message).get(default))
|
||||||
|
.fallback(default)
|
||||||
|
.layer(middleware::from_fn(validate));
|
||||||
|
let address = CONFIG.relay().listen_addr();
|
||||||
|
let listen = TcpListener::bind(address).await?;
|
||||||
|
|
||||||
|
println!("Uketoru listening on {address}");
|
||||||
|
serve(listen, router).await
|
||||||
|
}
|
95
src/relay.rs
Normal file
95
src/relay.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Uketoru
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Xavier Moffett <sapphirus@azorium.net>
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
body::Body,
|
||||||
|
extract::Request,
|
||||||
|
http::{header, StatusCode},
|
||||||
|
middleware::Next,
|
||||||
|
response::Response,
|
||||||
|
Json,
|
||||||
|
};
|
||||||
|
use lettre::{message::header::ContentType, Message, SmtpTransport, Transport};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::config::{TransportType, CONFIG};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct RelayMessage {
|
||||||
|
from: String,
|
||||||
|
text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn default() -> (StatusCode, Body) {
|
||||||
|
(StatusCode::NOT_FOUND, Body::from("Not Found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn validate(req: Request, next: Next) -> Result<Response, StatusCode> {
|
||||||
|
let token = match CONFIG.relay().token() {
|
||||||
|
Some(token) => token,
|
||||||
|
None => return Ok(next.run(req).await),
|
||||||
|
};
|
||||||
|
let auth = match req.headers().get(header::AUTHORIZATION) {
|
||||||
|
Some(header) => header.to_str().map_err(|_| StatusCode::BAD_REQUEST)?,
|
||||||
|
None => Err(StatusCode::UNAUTHORIZED)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !auth.starts_with("Bearer") || auth.len() < 7 {
|
||||||
|
Err(StatusCode::BAD_REQUEST)?
|
||||||
|
} else if &auth[7 ..] != token {
|
||||||
|
Err(StatusCode::UNAUTHORIZED)?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(next.run(req).await)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn message(Json(msg): Json<RelayMessage>) -> (StatusCode, Body) {
|
||||||
|
let client = CONFIG.client();
|
||||||
|
let message = Message::builder()
|
||||||
|
.from(format!("{} <{}>", msg.from, client.address()).parse().expect("Invalid address"))
|
||||||
|
.to(format!("{} <{}>", client.name(), client.to()).parse().expect("Invalid address"))
|
||||||
|
.subject(format!("Message received from {} via Uketoru", msg.from))
|
||||||
|
.header(ContentType::TEXT_PLAIN)
|
||||||
|
.body(msg.text)
|
||||||
|
.expect("Message builder");
|
||||||
|
let transport = match CONFIG.client().transport() {
|
||||||
|
TransportType::TLS => SmtpTransport::relay(CONFIG.client().server()),
|
||||||
|
TransportType::StartTLS => SmtpTransport::starttls_relay(CONFIG.client().server()),
|
||||||
|
};
|
||||||
|
let transport = match transport {
|
||||||
|
Ok(transport) => transport,
|
||||||
|
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, Body::from(format!("Internal Error: {err}"))),
|
||||||
|
};
|
||||||
|
|
||||||
|
match transport
|
||||||
|
.port(CONFIG.client().port())
|
||||||
|
.credentials(CONFIG.client().credentials())
|
||||||
|
.build()
|
||||||
|
.send(&message)
|
||||||
|
{
|
||||||
|
Ok(_) => (StatusCode::OK, Body::empty()),
|
||||||
|
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, Body::from(format!("Internal Error: {err}"))),
|
||||||
|
}
|
||||||
|
}
|
12
systemd/uketoru.service
Normal file
12
systemd/uketoru.service
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Uketoru SMTP relay
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=/etc/uketoru
|
||||||
|
ExecStart=/usr/bin/uketoru
|
||||||
|
KillMode=process
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
Loading…
Reference in a new issue