Skip to content
Merged
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
96 changes: 94 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ futures-util = "0.3.31"
tokio = { version = "1.48.0", features = ["full"] }
tower-http = { version = "0.5", features = ["cors", "trace", "limit"] }

redis = "0.32.7"
redis = { version = "1.0.0", features = ["cluster-async", "tokio-comp"] }

tracing = "0.1.41"
tracing-subscriber = "0.3"
Expand Down
17 changes: 13 additions & 4 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use serde::Deserialize;

use crate::middleware::{Server, ServerClients};
use crate::{
db::{self, RedisClient},
middleware::{Server, ServerClients},
};

#[derive(Deserialize, Debug)]
pub struct SystemConfig {
pub available_servers: String, // TODO: This should be hosted in redis
pub port: u16,
pub redis_url: String,
}

impl SystemConfig {
Expand All @@ -17,23 +21,28 @@ impl SystemConfig {
}
}

#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct State {
pub available_servers: ServerClients,
pub redis_conn: RedisClient,
}

impl State {
pub fn new(config: &SystemConfig) -> anyhow::Result<Self> {
pub async fn new(config: &SystemConfig) -> Result<Self, Box<dyn std::error::Error>> {
let servers = config.available_servers.split(',').collect::<Vec<&str>>();

let available_servers: Vec<Server> = servers
.clone()
.into_iter()
.map(Server::new)
.collect::<anyhow::Result<Vec<Server>>>()?;
.collect::<Result<Vec<Server>, _>>()?;

let redis_conn =
db::RedisClient::init_redis(&config.redis_url, available_servers.clone()).await?;

Ok(State {
available_servers: ServerClients::new(available_servers),
redis_conn,
})
}
}
3 changes: 3 additions & 0 deletions src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod redis;

pub use redis::RedisClient;
38 changes: 38 additions & 0 deletions src/db/redis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use redis::{AsyncTypedCommands as _, cluster::ClusterClient, cluster_async::ClusterConnection};

use crate::{error::Error, middleware::Server};

#[derive(Clone)]
pub struct RedisClient(ClusterConnection);

impl RedisClient {
pub async fn init_redis(
redis_url: &str,
available_servers: Vec<Server>,
) -> Result<Self, Box<dyn std::error::Error>> {
let nodes = vec![redis_url];
let client = ClusterClient::new(nodes)?;
let mut connection = client.get_async_connection().await?;

// Preload server urls into Redis
for (server_index, server) in available_servers.clone().into_iter().enumerate() {
connection
.set(format!("server_{}", server_index), server.url.as_str())
.await?;
}

Ok(Self(connection))
}

pub async fn set(&mut self, key: &str, value: &str) -> Result<(), Error> {
Ok(self.0.set(key, value).await?)
}

pub async fn get(&mut self, key: &str) -> Result<Option<String>, Error> {
Ok(self.0.get(key).await?)
}

pub async fn delete(&mut self, key: &str) -> Result<usize, Error> {
Ok(self.0.del(key).await?)
}
}
7 changes: 6 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub enum Error {
Unauthorized,
#[error("Other: {0}")]
Other(#[from] anyhow::Error),
#[error("Redis Error: {0}")]
RedisError(#[from] redis::RedisError),
#[error("Method Not Allowed")]
MethodNotAllowed,
#[error("Invalid URL")]
Expand All @@ -24,7 +26,10 @@ impl IntoResponse for Error {
fn into_response(self) -> Response {
match self {
Error::NotFound => (StatusCode::NOT_FOUND, self).into_response(),
Error::InternalServerError | Error::Other(_) | Error::InvalidResponse => {
Error::InternalServerError
| Error::Other(_)
| Error::InvalidResponse
| Error::RedisError(_) => {
(StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error").into_response()
}
Error::Unauthorized => (StatusCode::UNAUTHORIZED, self).into_response(),
Expand Down
9 changes: 5 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ use crate::{
};

pub mod config;
pub mod db;
pub mod error;
pub mod middleware;
pub mod servers;
pub mod services;
mod middleware;
mod servers;
mod services;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -32,7 +33,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

let config = SystemConfig::from_env()?;

let state = State::new(&config)?;
let state = State::new(&config).await?;

let server = Router::new()
.route("/status", get(status))
Expand Down