-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
341 additions
and
42 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
server: | ||
port: 6687 | ||
db_url: postgres://postgres:postgres@localhost:5432/chat | ||
auth: | ||
pk: | | ||
-----BEGIN PUBLIC KEY----- | ||
MCowBQYDK2VwAyEAfM+lwNHj6TRJ3EGP38lIJcOo9Dlt2u2JzcwWMbu7jQY= | ||
-----END PUBLIC KEY----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
use anyhow::{bail, Result}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::{env, fs::File}; | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
pub struct AppConfig { | ||
pub server: ServerConfig, | ||
pub auth: AuthConfig, | ||
} | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
pub struct AuthConfig { | ||
pub pk: String, | ||
} | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
pub struct ServerConfig { | ||
pub port: u16, | ||
pub db_url: String, | ||
} | ||
|
||
impl AppConfig { | ||
pub fn load() -> Result<Self> { | ||
// read from ./app.yml, or /etc/config/app.yml, or from env CHAT_CONFIG | ||
let ret = match ( | ||
File::open("notify.yml"), | ||
File::open("/etc/config/notify.yml"), | ||
env::var("NOTIFY_CONFIG"), | ||
) { | ||
(Ok(reader), _, _) => serde_yaml::from_reader(reader), | ||
(_, Ok(reader), _) => serde_yaml::from_reader(reader), | ||
(_, _, Ok(path)) => serde_yaml::from_reader(File::open(path)?), | ||
_ => bail!("Config file not found"), | ||
}; | ||
Ok(ret?) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
use axum::http::StatusCode; | ||
use axum::response::Json; | ||
use axum::response::{IntoResponse, Response}; | ||
use serde::{Deserialize, Serialize}; | ||
use thiserror::Error; | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
pub struct ErrorOutput { | ||
pub error: String, | ||
} | ||
|
||
#[derive(Error, Debug)] | ||
pub enum AppError { | ||
#[error("io error: {0}")] | ||
IoError(#[from] std::io::Error), | ||
|
||
#[error("jwt error: {0}")] | ||
JwtError(#[from] jwt_simple::Error), | ||
} | ||
|
||
impl ErrorOutput { | ||
pub fn new(error: impl Into<String>) -> Self { | ||
Self { | ||
error: error.into(), | ||
} | ||
} | ||
} | ||
|
||
impl IntoResponse for AppError { | ||
fn into_response(self) -> Response<axum::body::Body> { | ||
let status = match &self { | ||
Self::JwtError(_) => StatusCode::FORBIDDEN, | ||
Self::IoError(_) => StatusCode::INTERNAL_SERVER_ERROR, | ||
}; | ||
|
||
(status, Json(ErrorOutput::new(self.to_string()))).into_response() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,76 @@ | ||
mod config; | ||
mod error; | ||
mod notif; | ||
mod sse; | ||
|
||
use axum::{ | ||
middleware::from_fn_with_state, | ||
response::{Html, IntoResponse}, | ||
routing::get, | ||
Router, | ||
}; | ||
use chat_core::{Chat, Message}; | ||
use futures::StreamExt; | ||
use sqlx::postgres::PgListener; | ||
use chat_core::{ | ||
middlewares::{verify_token, TokenVerify}, | ||
DecodingKey, User, | ||
}; | ||
use dashmap::DashMap; | ||
use sse::sse_handler; | ||
use tracing::info; | ||
use std::{ops::Deref, sync::Arc}; | ||
use tokio::sync::broadcast; | ||
|
||
pub use config::AppConfig; | ||
pub use error::AppError; | ||
pub use notif::{setup_pg_listener, AppEvent}; | ||
|
||
pub type UserMap = Arc<DashMap<u64, broadcast::Sender<Arc<AppEvent>>>>; | ||
|
||
#[derive(Clone)] | ||
pub struct AppState(Arc<AppStateInner>); | ||
|
||
pub enum Event { | ||
NewChat(Chat), | ||
AddToChat(Chat), | ||
RemoveFromChat(Chat), | ||
NewMessage(Message), | ||
pub struct AppStateInner { | ||
pub config: AppConfig, | ||
users: UserMap, | ||
dk: DecodingKey, | ||
} | ||
|
||
const INDEX_HTML: &str = include_str!("../index.html"); | ||
|
||
pub fn get_router() -> Router { | ||
Router::new() | ||
.route("/", get(index_handler)) | ||
pub fn get_router() -> (Router, AppState) { | ||
let config = AppConfig::load().expect("Failed to load config"); | ||
let state = AppState::new(config); | ||
let app = Router::new() | ||
.route("/events", get(sse_handler)) | ||
.layer(from_fn_with_state(state.clone(), verify_token::<AppState>)) | ||
.route("/", get(index_handler)) | ||
.with_state(state.clone()); | ||
|
||
(app, state) | ||
} | ||
|
||
pub async fn setup_pg_listener() -> anyhow::Result<()> { | ||
let mut listener = | ||
PgListener::connect("postgresql://postgres:postgres@localhost:5432/chat").await?; | ||
listener.listen("chat_updated").await?; | ||
listener.listen("chat_message_created").await?; | ||
async fn index_handler() -> impl IntoResponse { | ||
Html(INDEX_HTML) | ||
} | ||
|
||
let mut stream = listener.into_stream(); | ||
impl TokenVerify for AppState { | ||
type Error = AppError; | ||
|
||
tokio::spawn(async move { | ||
while let Some(Ok(notif)) = stream.next().await { | ||
info!("Received notification: {:?}", notif); | ||
} | ||
}); | ||
fn verify(&self, token: &str) -> Result<User, Self::Error> { | ||
Ok(self.dk.verify(token)?) | ||
} | ||
} | ||
|
||
Ok(()) | ||
impl Deref for AppState { | ||
type Target = AppStateInner; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
async fn index_handler() -> impl IntoResponse { | ||
Html(INDEX_HTML) | ||
impl AppState { | ||
pub fn new(config: AppConfig) -> Self { | ||
let dk = DecodingKey::load(&config.auth.pk).expect("Failed to load public key"); | ||
let users = Arc::new(DashMap::new()); | ||
Self(Arc::new(AppStateInner { config, dk, users })) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.