summaryrefslogtreecommitdiff
path: root/webhook/src
diff options
context:
space:
mode:
authorMatthieuCoder <matthieu@matthieu-dev.xyz>2022-12-31 17:07:30 +0400
committerMatthieuCoder <matthieu@matthieu-dev.xyz>2022-12-31 17:07:30 +0400
commit65652932f77ce194a10cbc8dd42f3064e2c1a132 (patch)
tree4ca18a9317c4e561e917e9dd0cf39b695b43bc34 /webhook/src
parenta16bafdf5b0ec52fa0d73458597eee7c34ea5e7b (diff)
updates and bazel removal
Diffstat (limited to 'webhook/src')
-rw-r--r--webhook/src/config.rs19
-rw-r--r--webhook/src/handler/error.rs36
-rw-r--r--webhook/src/handler/handler.rs167
-rw-r--r--webhook/src/handler/make_service.rs34
-rw-r--r--webhook/src/handler/mod.rs7
-rw-r--r--webhook/src/handler/signature.rs41
-rw-r--r--webhook/src/handler/tests/handler.rs0
-rw-r--r--webhook/src/handler/tests/handler_integration.rs255
-rw-r--r--webhook/src/handler/tests/mod.rs4
-rw-r--r--webhook/src/handler/tests/signature.rs33
-rw-r--r--webhook/src/handler/tests/utils.rs15
-rw-r--r--webhook/src/main.rs45
12 files changed, 0 insertions, 656 deletions
diff --git a/webhook/src/config.rs b/webhook/src/config.rs
deleted file mode 100644
index a054d33..0000000
--- a/webhook/src/config.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use serde::Deserialize;
-
-#[derive(Debug, Deserialize, Clone, Default)]
-pub struct ServerSettings {
- pub port: u16,
- pub address: String,
-}
-
-#[derive(Debug, Deserialize, Clone, Default)]
-pub struct Discord {
- pub public_key: String,
- pub client_id: u32,
-}
-
-#[derive(Debug, Deserialize, Clone, Default)]
-pub struct Config {
- pub server: ServerSettings,
- pub discord: Discord,
-}
diff --git a/webhook/src/handler/error.rs b/webhook/src/handler/error.rs
deleted file mode 100644
index d4fee07..0000000
--- a/webhook/src/handler/error.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-use hyper::{header::ToStrError, Body, Response, StatusCode};
-
-pub struct WebhookError {
- pub code: StatusCode,
- pub message: String,
-}
-
-impl WebhookError {
- pub fn new(code: StatusCode, message: &str) -> WebhookError {
- WebhookError {
- code,
- message: message.to_string(),
- }
- }
-}
-
-impl Into<Response<Body>> for WebhookError {
- fn into(self) -> Response<Body> {
- Response::builder()
- .status(self.code)
- .body(self.message.into())
- .unwrap()
- }
-}
-
-impl From<hyper::Error> for WebhookError {
- fn from(_: hyper::Error) -> Self {
- WebhookError::new(StatusCode::BAD_REQUEST, "invalid request")
- }
-}
-
-impl From<ToStrError> for WebhookError {
- fn from(_: ToStrError) -> Self {
- WebhookError::new(StatusCode::BAD_REQUEST, "invalid request")
- }
-}
diff --git a/webhook/src/handler/handler.rs b/webhook/src/handler/handler.rs
deleted file mode 100644
index b25d270..0000000
--- a/webhook/src/handler/handler.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-use super::error::WebhookError;
-use super::signature::validate_signature;
-use crate::config::Config;
-use common::log::{debug, error};
-use common::nats_crate::Connection;
-use common::payloads::SerializeHelper;
-use ed25519_dalek::PublicKey;
-use hyper::{
- body::{to_bytes, Bytes},
- service::Service,
- Body, Method, Request, Response, StatusCode,
-};
-use serde::{Deserialize, Serialize};
-use std::{
- future::Future,
- pin::Pin,
- str::from_utf8,
- sync::Arc,
- task::{Context, Poll},
- time::Duration,
-};
-use twilight_model::application::interaction::Interaction;
-use twilight_model::gateway::event::Event;
-use twilight_model::gateway::payload::InteractionCreate;
-
-/// Hyper service used to handle the discord webhooks
-#[derive(Clone)]
-pub struct HandlerService {
- pub config: Arc<Config>,
- pub nats: Arc<Connection>,
- pub public_key: Arc<PublicKey>,
-}
-
-impl HandlerService {
- async fn check_request(&self, req: Request<Body>) -> Result<Bytes, WebhookError> {
- if req.method() == Method::POST {
- let signature = if let Some(sig) = req.headers().get("X-Signature-Ed25519") {
- sig.to_owned()
- } else {
- return Err(WebhookError::new(
- StatusCode::BAD_REQUEST,
- "missing signature header",
- ));
- };
-
- let timestamp = if let Some(timestamp) = req.headers().get("X-Signature-Timestamp") {
- timestamp.to_owned()
- } else {
- return Err(WebhookError::new(
- StatusCode::BAD_REQUEST,
- "missing timestamp header",
- ));
- };
- let data = to_bytes(req.into_body()).await?;
-
- if validate_signature(
- &self.public_key,
- &[timestamp.as_bytes().to_vec(), data.to_vec()].concat(),
- signature.to_str()?,
- ) {
- Ok(data)
- } else {
- Err(WebhookError::new(
- StatusCode::UNAUTHORIZED,
- "invalid signature",
- ))
- }
- } else {
- Err(WebhookError::new(StatusCode::NOT_FOUND, "not found"))
- }
- }
-
- async fn process_request(
- &mut self,
- req: Request<Body>,
- ) -> Result<Response<Body>, WebhookError> {
- match self.check_request(req).await {
- Ok(data) => {
- let utf8 = from_utf8(&data);
- match utf8 {
- Ok(data) => match serde_json::from_str::<Interaction>(data) {
- Ok(value) => match value {
- Interaction::Ping(_) => Ok(Response::builder()
- .header("Content-Type", "application/json")
- .body(serde_json::to_string(&Ping { t: 1 }).unwrap().into())
- .unwrap()),
- _ => {
- debug!("calling nats");
- // this should hopefully not fail ?
- let payload =
- serde_json::to_string(&common::payloads::CachePayload {
- tracing: common::payloads::Tracing {
- node_id: "".to_string(),
- span: None,
- },
- data: SerializeHelper(Event::InteractionCreate(
- Box::new(InteractionCreate(value)),
- )),
- })
- .unwrap();
-
- match self.nats.request_timeout(
- "nova.cache.dispatch.interaction_create",
- payload,
- Duration::from_secs(2),
- ) {
- Ok(response) => Ok(Response::builder()
- .header("Content-Type", "application/json")
- .body(Body::from(response.data))
- .unwrap()),
-
- Err(error) => {
- error!("failed to request nats: {}", error);
- Err(WebhookError::new(
- StatusCode::INTERNAL_SERVER_ERROR,
- "failed to request nats",
- ))
- }
- }
- }
- },
-
- Err(error) => {
- error!("invalid json body: {}", error);
- Err(WebhookError::new(
- StatusCode::BAD_REQUEST,
- "invalid json body",
- ))
- }
- },
-
- Err(_) => Err(WebhookError::new(StatusCode::BAD_REQUEST, "not utf-8 body")),
- }
- }
- Err(error) => Err(error),
- }
- }
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct Ping {
- #[serde(rename = "type")]
- t: i32,
-}
-
-/// Implementation of the service
-impl Service<Request<Body>> for HandlerService {
- type Response = Response<Body>;
- type Error = hyper::Error;
- type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
-
- fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
- Poll::Ready(Ok(()))
- }
-
- fn call(&mut self, req: Request<Body>) -> Self::Future {
- let mut clone = self.clone();
- Box::pin(async move {
- let response = clone.process_request(req).await;
-
- match response {
- Ok(r) => Ok(r),
- Err(e) => Ok(e.into()),
- }
- })
- }
-}
diff --git a/webhook/src/handler/make_service.rs b/webhook/src/handler/make_service.rs
deleted file mode 100644
index 9e90436..0000000
--- a/webhook/src/handler/make_service.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use super::handler::HandlerService;
-use crate::config::Config;
-use hyper::service::Service;
-use common::nats_crate::Connection;
-use std::{
- future::{ready, Ready},
- sync::Arc,
- task::{Context, Poll},
-};
-use ed25519_dalek::PublicKey;
-
-pub struct MakeSvc {
- pub settings: Arc<Config>,
- pub nats: Arc<Connection>,
- pub public_key: Arc<PublicKey>
-}
-
-impl<T> Service<T> for MakeSvc {
- type Response = HandlerService;
- type Error = std::io::Error;
- type Future = Ready<Result<Self::Response, Self::Error>>;
-
- fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
- Ok(()).into()
- }
-
- fn call(&mut self, _: T) -> Self::Future {
- ready(Ok(HandlerService {
- config: self.settings.clone(),
- nats: self.nats.clone(),
- public_key: self.public_key.clone()
- }))
- }
-}
diff --git a/webhook/src/handler/mod.rs b/webhook/src/handler/mod.rs
deleted file mode 100644
index 20a977a..0000000
--- a/webhook/src/handler/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-mod error;
-mod handler;
-pub mod make_service;
-mod signature;
-
-#[cfg(test)]
-pub mod tests;
diff --git a/webhook/src/handler/signature.rs b/webhook/src/handler/signature.rs
deleted file mode 100644
index 748fa6a..0000000
--- a/webhook/src/handler/signature.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use common::prometheus::{Counter, HistogramVec, labels, opts, register_counter, register_histogram_vec};
-use ed25519_dalek::PublicKey;
-use ed25519_dalek::Verifier;
-use ed25519_dalek::Signature;
-use std::convert::TryInto;
-
-lazy_static::lazy_static! {
- static ref SIGNATURE_TIME_HISTOGRAM: HistogramVec = register_histogram_vec!(
- "nova_webhook_signature_time",
- "The time taken by the signature verification",
- &["signature"]
- ).unwrap();
-
- static ref SIGNATURE_COUNTER: Counter = register_counter!(opts!(
- "nova_webhook_signatures_verify",
- "number of signatures verification issued by the service",
- labels! {"handler" => "webhook_main"}
- )).unwrap();
-}
-
-fn demo<T, const N: usize>(v: Vec<T>) -> [T; N] {
- v.try_into()
- .unwrap_or_else(|v: Vec<T>| panic!("Expected a Vec of length {} but it was {}", N, v.len()))
-}
-
-pub fn validate_signature(public_key: &PublicKey, data: &Vec<u8>, hex_signature: &str) -> bool {
- SIGNATURE_COUNTER.inc();
- let timer = SIGNATURE_TIME_HISTOGRAM.with_label_values(&["webhook_main"]).start_timer();
-
- let signature_result = hex::decode(hex_signature);
-
- let mut result = false;
- if let Ok(signature) = signature_result {
- let sig = Signature::from(demo(signature));
-
- result = public_key.verify(data, &sig).is_ok();
- }
-
- timer.observe_duration();
- result
-}
diff --git a/webhook/src/handler/tests/handler.rs b/webhook/src/handler/tests/handler.rs
deleted file mode 100644
index e69de29..0000000
--- a/webhook/src/handler/tests/handler.rs
+++ /dev/null
diff --git a/webhook/src/handler/tests/handler_integration.rs b/webhook/src/handler/tests/handler_integration.rs
deleted file mode 100644
index b5204fb..0000000
--- a/webhook/src/handler/tests/handler_integration.rs
+++ /dev/null
@@ -1,255 +0,0 @@
-use std::time::Duration;
-
-use ctor;
-use hyper::{Body, Method, Request, StatusCode};
-use lazy_static::lazy_static;
-use serde_json::json;
-use ed25519_dalek::Keypair;
-
-use common::{
- config::test_init,
- nats_crate::Connection,
- testcontainers::{Image, images::generic::WaitFor},
-};
-use common::{
- config::Settings,
- log::info,
- testcontainers::{clients::Cli, Container, Docker, images::generic::GenericImage},
-};
-
-use crate::{
- config::Config,
- handler::tests::utils::{generate_keypair, sign_message},
- start,
-};
-
-const fn nats_image<'a>() -> &'a str {
- #[cfg(all(unix, target_arch = "x86_64"))]
- return "amd64/nats";
- #[cfg(all(unix, target_arch = "aarch64"))]
- return "arm64v8/nats";
- #[cfg(all(target_arch = "x86_64", target_os = "windows"))]
- return "winamd64/nats";
-}
-
-static mut NATS: Option<Container<Cli, GenericImage>> = None;
-static mut SETTINGS: Option<Settings<Config>> = None;
-
-lazy_static! {
- static ref TEST_KEYPAIR: Keypair = generate_keypair();
- static ref DOCKER: Cli = Cli::default();
-}
-
-#[ctor::ctor]
-unsafe fn init() {
- test_init();
- let image = GenericImage::new(nats_image())
- .with_wait_for(WaitFor::message_on_stderr("Server is ready"));
-
- let container = DOCKER.run(image);
- container.start();
- container.image().wait_until_ready(&container);
- container.get_host_port(4222).unwrap();
-
- let port = container.get_host_port(4222).unwrap();
- NATS = Some(container);
- SETTINGS = Some(common::config::Settings {
- config: crate::config::Config {
- server: crate::config::ServerSettings {
- port: 5003,
- address: "0.0.0.0".to_string(),
- },
- discord: crate::config::Discord {
- public_key: hex::encode(TEST_KEYPAIR.public.clone()),
- client_id: 0,
- },
- },
- redis: common::redis::RedisConfiguration {
- url: "".to_string(),
- },
- monitoring: common::monitoring::MonitoringConfiguration {
- enabled: false,
- address: None,
- port: None,
- },
- nats: common::nats::NatsConfiguration {
- client_cert: None,
- root_cert: None,
- jetstream_api_prefix: None,
- max_reconnects: None,
- reconnect_buffer_size: None,
- tls: None,
- client_name: None,
- tls_required: None,
- host: format!("localhost:{}", port),
- },
- });
- let settings = (&mut SETTINGS).as_ref().unwrap();
-
- std::thread::spawn(move || {
- let runtime = tokio::runtime::Runtime::new().unwrap();
- runtime.block_on(start(settings.clone()));
- });
- std::thread::sleep(Duration::from_secs(3));
-}
-
-#[ctor::dtor]
-unsafe fn destroy() {
- let nats = (&mut NATS).as_ref().unwrap();
- nats.stop();
-}
-
-#[tokio::test]
-async fn respond_to_pings() {
- let ping = json!({ "type": 1, "id": "0", "application_id": "0", "token": "random token", "version": 1, "channel_id": "123" }).to_string();
- let timestamp = "my datetime :)";
- let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat();
- let signature = sign_message(signature_data, &TEST_KEYPAIR);
-
- let req = Request::builder()
- .method(Method::POST)
- .uri("http://localhost:5003/")
- .header("X-Signature-Ed25519", signature)
- .header("X-Signature-Timestamp", timestamp)
- .body(Body::from(ping.clone()))
- .expect("request builder");
-
- let client = hyper::client::Client::new();
- let result = client.request(req).await.unwrap();
-
- assert_eq!(result.status(), StatusCode::OK);
-}
-
-#[tokio::test]
-async fn deny_invalid_signatures() {
- let ping = json!({ "type": 1, "id": "0", "application_id": "0", "token": "random token", "version": 1, "channel_id": "123" }).to_string();
- let timestamp = "my datetime :)";
-
- let req = Request::builder()
- .method(Method::POST)
- .uri("http://localhost:5003/")
- .header("X-Signature-Ed25519", "inva&lid signature :)")
- .header("X-Signature-Timestamp", timestamp)
- .body(Body::from(ping.clone()))
- .expect("request builder");
- let client = hyper::client::Client::new();
- let result = client.request(req).await.unwrap();
- assert_eq!(result.status(), StatusCode::UNAUTHORIZED);
-}
-
-#[tokio::test]
-async fn response_500_when_no_nats_response() {
- let ping = json!({
- "type": 2,
- "id": "0",
- "application_id": "0",
- "token": "random token",
- "version": 1,
- "channel_id": "123",
- "data": {
- "id": "0",
- "name": "command"
- }
- }).to_string();
-
- let timestamp = "my datetime :)";
- let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat();
- let signature = sign_message(signature_data, &TEST_KEYPAIR);
-
- // we must timeout
- let req = Request::builder()
- .method(Method::POST)
- .uri("http://localhost:5003/")
- .header("X-Signature-Ed25519", signature)
- .header("X-Signature-Timestamp", timestamp)
- .body(Body::from(ping.clone()))
- .expect("request builder");
-
- let client = hyper::client::Client::new();
- let result = client.request(req).await.unwrap();
- assert_eq!(result.status(), StatusCode::INTERNAL_SERVER_ERROR);
-}
-
-#[tokio::test]
-async fn respond_from_nats_response() {
- let nats: Connection;
- unsafe {
- nats = SETTINGS.clone().unwrap().nats.into();
- }
- let sub = nats.subscribe("nova.cache.dispatch.interaction").unwrap();
- let ping = json!({
- "type": 2,
- "id": "0",
- "application_id": "0",
- "token": "random token",
- "version": 1,
- "channel_id": "123",
- "data": {
- "id": "0",
- "name": "command"
- }
- }).to_string();
-
- let timestamp = "my datetime :)";
- let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat();
- let signature = sign_message(signature_data, &TEST_KEYPAIR);
-
- sub.with_handler(move |msg| {
- info!("Received {}", &msg);
- msg.respond("ok :)").unwrap();
- Ok(())
- });
-
- let req = Request::builder()
- .method(Method::POST)
- .uri("http://localhost:5003/")
- .header("X-Signature-Ed25519", signature)
- .header("X-Signature-Timestamp", timestamp)
- .body(Body::from(ping.clone()))
- .expect("request builder");
-
- let client = hyper::client::Client::new();
- let result = client.request(req).await.unwrap();
- assert_eq!(result.status(), StatusCode::OK);
-}
-
-#[tokio::test]
-async fn response_400_when_invalid_json_body() {
- let ping = "{".to_string();
- let timestamp = "my datetime :)";
- let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat();
- let signature = sign_message(signature_data, &TEST_KEYPAIR);
-
- let req = Request::builder()
- .method(Method::POST)
- .uri("http://localhost:5003/")
- .header("X-Signature-Ed25519", signature)
- .header("X-Signature-Timestamp", timestamp)
- .body(Body::from(ping.clone()))
- .expect("request builder");
-
- let client = hyper::client::Client::new();
- let result = client.request(req).await.unwrap();
- assert_eq!(result.status(), StatusCode::BAD_REQUEST);
-}
-
-#[tokio::test]
-async fn response_400_when_invalid_utf8_body() {
- // invalid 2 octet sequence
- let ping = vec![0xc3, 0x28];
-
- let timestamp = "my datetime :)";
- let signature_data = [timestamp.as_bytes().to_vec(), ping.to_vec()].concat();
- let signature = sign_message(signature_data, &TEST_KEYPAIR);
-
- let req = Request::builder()
- .method(Method::POST)
- .uri("http://localhost:5003/")
- .header("X-Signature-Ed25519", signature)
- .header("X-Signature-Timestamp", timestamp)
- .body(Body::from(ping.clone()))
- .expect("request builder");
- let client = hyper::client::Client::new();
- let result = client.request(req).await.unwrap();
- assert_eq!(result.status(), StatusCode::BAD_REQUEST);
-}
diff --git a/webhook/src/handler/tests/mod.rs b/webhook/src/handler/tests/mod.rs
deleted file mode 100644
index 589ad52..0000000
--- a/webhook/src/handler/tests/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-pub mod handler_integration;
-pub mod signature;
-pub mod utils;
-pub mod handler;
diff --git a/webhook/src/handler/tests/signature.rs b/webhook/src/handler/tests/signature.rs
deleted file mode 100644
index 490143b..0000000
--- a/webhook/src/handler/tests/signature.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use crate::handler::signature::validate_signature;
-use ed25519_dalek::PublicKey;
-
-#[test]
-fn validate_signature_test() {
- let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002";
- let content = "message de test incroyable".as_bytes().to_vec();
- let public_key = PublicKey::from_bytes(&hex::decode("eefe0c24473737cb2035232e3b4eb91c206f0a14684168f3503f7d8316058d6f").unwrap()).unwrap();
-
- assert!(validate_signature(&public_key, &content, signature))
-}
-
-#[test]
-fn validate_signature_reverse_test() {
- let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002";
- let public_key = PublicKey::from_bytes(&hex::decode("c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044").unwrap()).unwrap();
-
- let content = "ceci est un test qui ne fonctionnera pas!"
- .as_bytes()
- .to_vec();
- assert!(!validate_signature(&public_key, &content, signature))
-}
-
-#[test]
-fn invalid_hex() {
- let signature = "zzz";
- let public_key = PublicKey::from_bytes(&hex::decode("c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044").unwrap()).unwrap();
-
- let content = "ceci est un test qui ne fonctionnera pas!"
- .as_bytes()
- .to_vec();
- assert!(!validate_signature(&public_key, &content, signature))
-} \ No newline at end of file
diff --git a/webhook/src/handler/tests/utils.rs b/webhook/src/handler/tests/utils.rs
deleted file mode 100644
index 5e59f09..0000000
--- a/webhook/src/handler/tests/utils.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-use rand::rngs::OsRng;
-use ed25519_dalek::{Signer, Keypair, Signature};
-
-pub fn generate_keypair() -> Keypair {
- let mut csprng = OsRng{};
- Keypair::generate(&mut csprng)
-}
-
-pub fn sign_message(
- message: Vec<u8>,
- keypair: &Keypair,
-) -> String {
- let signature: Signature = keypair.sign(&message);
- return hex::encode(signature.to_bytes());
-} \ No newline at end of file
diff --git a/webhook/src/main.rs b/webhook/src/main.rs
deleted file mode 100644
index 00d42ee..0000000
--- a/webhook/src/main.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use std::{net::ToSocketAddrs, sync::Arc};
-mod config;
-mod handler;
-use crate::handler::make_service::MakeSvc;
-
-use crate::config::Config;
-use common::config::Settings;
-use common::log::{error, info};
-use ed25519_dalek::PublicKey;
-use hyper::Server;
-
-#[tokio::main]
-async fn main() {
- let settings: Settings<Config> = Settings::new("webhook").unwrap();
- start(settings).await;
-}
-
-async fn start(settings: Settings<Config>) {
- let addr = format!(
- "{}:{}",
- settings.config.server.address, settings.config.server.port
- )
- .to_socket_addrs()
- .unwrap()
- .next()
- .unwrap();
-
- info!(
- "Starting server on {}:{}",
- settings.config.server.address, settings.config.server.port
- );
-
- let config = Arc::new(settings.config);
- let public_key =
- Arc::new(PublicKey::from_bytes(&hex::decode(&config.discord.public_key).unwrap()).unwrap());
- let server = Server::bind(&addr).serve(MakeSvc {
- settings: config,
- nats: Arc::new(settings.nats.into()),
- public_key: public_key,
- });
-
- if let Err(e) = server.await {
- error!("server error: {}", e);
- }
-}