The system design has changed, but the refactor is not over.

In future commits, I will strive to maintain a consistent style.
This commit is contained in:
Shiroyasha 2024-07-01 03:43:06 +03:00
parent 7594e3d615
commit a1f9eba502
Signed by: shiroyashik
GPG key ID: E4953D3940D7860A
25 changed files with 410 additions and 367 deletions

61
src/api/figura/auth.rs Normal file
View file

@ -0,0 +1,61 @@
use axum::{debug_handler, extract::{Query, State}, response::{IntoResponse, Response}, routing::get, Router};
use reqwest::StatusCode;
use ring::digest::{self, digest};
use tracing::{error, info};
use crate::{auth::{has_joined, Userinfo}, utils::rand, AppState};
use super::types::auth::*;
pub fn router() -> Router<AppState> {
Router::new()
.route("/id", get(id))
.route("/verify", get(verify))
}
#[debug_handler]
async fn id(
// First stage of authentication
Query(query): Query<Id>,
State(state): State<AppState>,
) -> String {
let server_id =
hex::encode(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0..20]);
let state = state.user_manager;
state.pending_insert(server_id.clone(), query.username);
server_id
}
#[debug_handler]
async fn verify(
// Second stage of authentication
Query(query): Query<Verify>,
State(state): State<AppState>,
) -> Response {
let server_id = query.id.clone();
let username = state.user_manager.pending_remove(&server_id).unwrap().1; // TODO: Add error check
let userinfo = match has_joined(&server_id, &username).await {
Ok(d) => d,
Err(e) => {
error!("[Authentication] {e}");
return (StatusCode::INTERNAL_SERVER_ERROR, "internal verify error".to_string()).into_response();
},
};
if let Some((uuid, auth_system)) = userinfo {
info!("[Authentication] {username} logged in using {auth_system:?}");
let authenticated = state.user_manager;
authenticated.insert(
uuid,
server_id.clone(),
Userinfo {
username,
uuid,
auth_system,
token: Some(server_id.clone()),
},
);
(StatusCode::OK, server_id.to_string()).into_response()
} else {
info!("[Authentication] failed to verify {username}");
(StatusCode::BAD_REQUEST, "failed to verify".to_string()).into_response()
}
}

7
src/api/figura/mod.rs Normal file
View file

@ -0,0 +1,7 @@
mod types;
mod websocket;
pub mod auth;
pub mod profile;
pub mod info;
pub use websocket::handler as ws;

View file

@ -15,9 +15,9 @@ use uuid::Uuid;
use crate::{ use crate::{
auth::Token, auth::Token,
utils::{calculate_file_sha256, format_uuid, get_correct_array}, utils::{calculate_file_sha256, format_uuid, get_correct_array},
ws::S2CMessage,
AppState, AppState,
}; };
use super::types::S2CMessage;
pub async fn user_info( pub async fn user_info(
Path(uuid): Path<Uuid>, Path(uuid): Path<Uuid>,

View file

@ -0,0 +1,11 @@
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Id {
pub username: String,
}
#[derive(Deserialize)]
pub struct Verify {
pub id: String,
}

View file

@ -1,6 +1,7 @@
mod c2s; mod c2s;
mod errors; mod errors;
mod s2c; mod s2c;
pub mod auth;
pub use c2s::C2SMessage; pub use c2s::C2SMessage;
pub use errors::MessageLoadError; pub use errors::MessageLoadError;

View file

@ -15,10 +15,8 @@ use tokio::sync::{
}; };
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::AppState;
ws::{C2SMessage, S2CMessage}, use super::types::{C2SMessage, S2CMessage};
AppState,
};
pub async fn handler(ws: WebSocketUpgrade, State(state): State<AppState>) -> Response { pub async fn handler(ws: WebSocketUpgrade, State(state): State<AppState>) -> Response {
ws.on_upgrade(|socket| handle_socket(socket, state)) ws.on_upgrade(|socket| handle_socket(socket, state))

2
src/api/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod figura;
pub mod v1;

22
src/api/v1/auth.rs Normal file
View file

@ -0,0 +1,22 @@
use axum::{extract::State, http::StatusCode, response::{IntoResponse, Response}};
use crate::{auth::Token, AppState};
pub async fn status(
Token(token): Token,
State(state): State<AppState>
) -> Response {
match token {
Some(token) => {
if state.user_manager.is_authenticated(&token) {
(StatusCode::OK, "ok".to_string()).into_response()
} else {
(StatusCode::UNAUTHORIZED, "unauthorized".to_string()).into_response()
}
}
None => {
(StatusCode::BAD_REQUEST, "bad request".to_string()).into_response()
}
}
}

View file

@ -1,29 +1,14 @@
use axum::{ use axum::{
extract::{Query, State}, extract::{Query, State},
http::StatusCode, http::StatusCode,
response::{IntoResponse, Response}, response::{IntoResponse, Response}
routing::{get, post},
Router
}; };
use serde::Deserialize;
use tracing::debug; use tracing::debug;
use uuid::Uuid;
use crate::{auth::Token, AppState}; use crate::{auth::Token, AppState};
use super::types::UserUuid;
pub fn router() -> Router<AppState> { pub(super) async fn verify(
Router::new()
.route("/verify", get(verify))
.route("/raw", post(raw))
.route("/sub/raw", post(sub_raw))
}
#[derive(Deserialize)]
struct UserUuid {
uuid: Option<Uuid>,
}
async fn verify(
Token(token): Token, Token(token): Token,
State(state): State<AppState>, State(state): State<AppState>,
) -> Response { ) -> Response {
@ -32,7 +17,7 @@ async fn verify(
.unwrap_or_else(|x| x) .unwrap_or_else(|x| x)
} }
async fn raw( pub(super) async fn raw(
Token(token): Token, Token(token): Token,
Query(query): Query<UserUuid>, Query(query): Query<UserUuid>,
State(state): State<AppState>, State(state): State<AppState>,
@ -68,7 +53,7 @@ async fn raw(
} }
} }
async fn sub_raw( pub(super) async fn sub_raw(
Token(token): Token, Token(token): Token,
Query(query): Query<UserUuid>, Query(query): Query<UserUuid>,
State(state): State<AppState>, State(state): State<AppState>,

16
src/api/v1/mod.rs Normal file
View file

@ -0,0 +1,16 @@
use axum::{routing::{get, post}, Router};
use crate::AppState;
mod http2ws;
mod users;
mod auth;
mod types;
pub fn router() -> Router<AppState> {
Router::new()
.route("/verify", get(http2ws::verify))
.route("/raw", post(http2ws::raw))
.route("/sub/raw", post(http2ws::sub_raw))
.route("/auth/", get(auth::status))
.route("/users/create", post(users::create_user))
}

7
src/api/v1/types.rs Normal file
View file

@ -0,0 +1,7 @@
use serde::Deserialize;
use uuid::Uuid;
#[derive(Deserialize)]
pub(super) struct UserUuid {
pub uuid: Option<Uuid>,
}

24
src/api/v1/users.rs Normal file
View file

@ -0,0 +1,24 @@
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Response},
Json
};
use tracing::debug;
use crate::{auth::{Token, Userinfo}, AppState};
pub(super) async fn create_user(
Token(token): Token,
State(state): State<AppState>,
Json(json): Json<Userinfo>
) -> Response {
debug!("Json: {json:?}");
match state.config.lock().await.clone().verify_token(&token) {
Ok(_) => {},
Err(e) => return e,
}
state.user_manager.insert_user(json.uuid, json);
(StatusCode::OK, "ok".to_string()).into_response()
}

View file

@ -1,309 +0,0 @@
use std::{str::FromStr, sync::Arc};
use crate::utils::*;
use anyhow::anyhow;
use axum::{
async_trait, debug_handler, extract::{FromRequestParts, Query, State}, http::{request::Parts, StatusCode}, response::{IntoResponse, Response}, routing::{get, post}, Json, Router
};
use dashmap::DashMap;
use ring::digest::{self, digest};
use serde::Deserialize;
use tracing::{debug, error, info, trace};
use uuid::Uuid;
use crate::AppState;
pub fn router() -> Router<AppState> {
Router::new()
.route("/id", get(id))
.route("/verify", get(verify))
}
pub fn router_v1() -> Router<AppState> {
Router::new()
.route("/create", post(create_user))
}
// Web
#[derive(Deserialize)]
struct Id {
username: String,
}
#[debug_handler]
async fn id(
// First stage of authentication
Query(query): Query<Id>,
State(state): State<AppState>,
) -> String {
let server_id =
hex::encode(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0..20]);
let state = state.user_manager;
state.pending_insert(server_id.clone(), query.username);
server_id
}
#[derive(Deserialize)]
struct Verify {
id: String,
}
#[debug_handler]
async fn verify(
// Second stage of authentication
Query(query): Query<Verify>,
State(state): State<AppState>,
) -> Response {
let server_id = query.id.clone();
let username = state.user_manager.pending_remove(&server_id).unwrap().1; // TODO: Add error check
let userinfo = match has_joined(&server_id, &username).await {
Ok(d) => d,
Err(e) => {
error!("[Authentication] {e}");
return (StatusCode::INTERNAL_SERVER_ERROR, "internal verify error".to_string()).into_response();
},
};
if let Some((uuid, auth_system)) = userinfo {
info!("[Authentication] {username} logged in using {auth_system:?}");
let authenticated = state.user_manager;
authenticated.insert(
uuid,
server_id.clone(),
Userinfo {
username,
uuid,
auth_system,
token: Some(server_id.clone()),
},
);
(StatusCode::OK, server_id.to_string()).into_response()
} else {
info!("[Authentication] failed to verify {username}");
(StatusCode::BAD_REQUEST, "failed to verify".to_string()).into_response()
}
}
pub async fn status(
Token(token): Token,
State(state): State<AppState>
) -> Response {
match token {
Some(token) => {
if state.user_manager.is_authenticated(&token) {
(StatusCode::OK, "ok".to_string()).into_response()
} else {
(StatusCode::UNAUTHORIZED, "unauthorized".to_string()).into_response()
}
}
None => {
(StatusCode::BAD_REQUEST, "bad request".to_string()).into_response()
}
}
}
pub async fn create_user(
Token(token): Token,
State(state): State<AppState>,
Json(json): Json<Userinfo>
) -> Response {
debug!("Json: {json:?}");
match state.config.lock().await.clone().verify_token(&token) {
Ok(_) => {},
Err(e) => return e,
}
state.user_manager.insert_user(json.uuid, json);
(StatusCode::OK, "ok".to_string()).into_response()
}
// Web End
// It's an extractor that pulls a token from the Header.
#[derive(PartialEq, Debug)]
pub struct Token(pub Option<String>);
#[async_trait]
impl<S> FromRequestParts<S> for Token
where
S: Send + Sync,
{
type Rejection = StatusCode;
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
let token = parts
.headers
.get("token")
.and_then(|value| value.to_str().ok());
trace!(token = ?token);
match token {
Some(token) => Ok(Self(Some(token.to_string()))),
None => Ok(Self(None)),
}
}
}
// End Extractor
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AuthSystem {
Internal,
ElyBy,
Mojang,
}
impl ToString for AuthSystem {
fn to_string(&self) -> String {
match self {
AuthSystem::Internal => String::from("internal"),
AuthSystem::ElyBy => String::from("elyby"),
AuthSystem::Mojang => String::from("mojang"),
}
}
}
impl FromStr for AuthSystem {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"internal" => Ok(Self::Internal),
"elyby" => Ok(Self::ElyBy),
"mojang" => Ok(Self::Mojang),
_ => Err(anyhow!("No auth system called: {s}"))
}
}
}
impl AuthSystem {
fn get_url(&self) -> String {
match self {
AuthSystem::Internal => panic!("Can't get internal URL!"),
AuthSystem::ElyBy => String::from("http://minecraft.ely.by/session/hasJoined"),
AuthSystem::Mojang => String::from("https://sessionserver.mojang.com/session/minecraft/hasJoined"),
}
}
}
// Work with external APIs
/// Get UUID from JSON response
#[inline]
fn get_id_json(json: &serde_json::Value) -> anyhow::Result<Uuid> {
trace!("json: {json:#?}"); // For debugging, we'll get to this later!
let uuid = Uuid::parse_str(json.get("id").unwrap().as_str().unwrap())?;
Ok(uuid)
}
#[inline]
async fn fetch_json(
auth_system: AuthSystem,
server_id: &str,
username: &str,
) -> anyhow::Result<Option<(Uuid, AuthSystem)>> {
let client = reqwest::Client::new();
let url = auth_system.get_url();
let res = client
.get(url)
.query(&[("serverId", server_id), ("username", username)])
.send()
.await?;
debug!("{res:?}");
match res.status().as_u16() {
200 => {
let json = serde_json::from_str::<serde_json::Value>(&res.text().await?)?;
let uuid = get_id_json(&json)?;
Ok(Some((uuid, auth_system)))
}
401 => Ok(None), // Ely.By None
204 => Ok(None), // Mojang None
_ => Err(anyhow!("Unknown code: {}", res.status().as_u16())),
}
}
pub async fn has_joined(
server_id: &str,
username: &str,
) -> anyhow::Result<Option<(Uuid, AuthSystem)>> {
let (elyby, mojang) = (
fetch_json(AuthSystem::ElyBy,server_id, username).await?,
fetch_json(AuthSystem::Mojang, server_id, username).await?
);
if elyby.is_none() && mojang.is_none() {
Ok(None)
} else if mojang.is_some() {
Ok(mojang)
} else if elyby.is_some() {
Ok(elyby)
} else {
panic!("Impossible error!")
}
}
// End of work with external APIs
// User manager
#[derive(Debug, Clone)]
pub struct UManager {
/// Users with incomplete authentication
pending: Arc<DashMap<String, String>>, // <SHA1 serverId, USERNAME> TODO: Add automatic purge
/// Authenticated users
authenticated: Arc<DashMap<String, Uuid>>, // <SHA1 serverId, Userinfo> NOTE: In the future, try it in a separate LockRw branch
/// Registered users
registered: Arc<DashMap<Uuid, Userinfo>>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Userinfo {
pub username: String,
pub uuid: Uuid,
pub auth_system: AuthSystem,
pub token: Option<String>,
}
impl UManager {
pub fn new() -> Self {
Self {
pending: Arc::new(DashMap::new()),
registered: Arc::new(DashMap::new()),
authenticated: Arc::new(DashMap::new()),
}
}
pub fn pending_insert(&self, server_id: String, username: String) {
self.pending.insert(server_id, username);
}
pub fn pending_remove(&self, server_id: &str) -> std::option::Option<(std::string::String, std::string::String)> {
self.pending.remove(server_id)
}
pub fn insert(&self, uuid: Uuid, token: String, userinfo: Userinfo) -> Option<Userinfo> {
self.authenticated.insert(token, uuid);
self.registered.insert(uuid, userinfo)
}
pub fn insert_user(&self, uuid: Uuid, userinfo: Userinfo) -> Option<Userinfo> {
self.registered.insert(uuid, userinfo)
}
pub fn get(
&self,
token: &String,
) -> Option<dashmap::mapref::one::Ref<'_, Uuid, Userinfo>> {
let uuid = self.authenticated.get(token)?;
self.registered.get(uuid.value())
}
pub fn get_by_uuid(
&self,
uuid: &Uuid,
) -> Option<dashmap::mapref::one::Ref<'_, Uuid, Userinfo>> {
self.registered.get(uuid)
}
pub fn is_authenticated(&self, token: &String) -> bool {
self.authenticated.contains_key(token)
}
pub fn _is_registered(&self, uuid: &Uuid) -> bool {
self.registered.contains_key(uuid)
}
pub fn remove(&self, uuid: &Uuid) {
let token = self.registered.remove(uuid).unwrap().1.token.unwrap();
self.authenticated.remove(&token);
}
}
// End of User manager

151
src/auth/auth.rs Normal file
View file

@ -0,0 +1,151 @@
use std::sync::Arc;
use anyhow::anyhow;
use axum::{
async_trait, extract::FromRequestParts, http::{request::Parts, StatusCode}
};
use dashmap::DashMap;
use tracing::{debug, trace};
use uuid::Uuid;
use super::types::*;
// It's an extractor that pulls a token from the Header.
#[derive(PartialEq, Debug)]
pub struct Token(pub Option<String>);
#[async_trait]
impl<S> FromRequestParts<S> for Token
where
S: Send + Sync,
{
type Rejection = StatusCode;
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
let token = parts
.headers
.get("token")
.and_then(|value| value.to_str().ok());
trace!(token = ?token);
match token {
Some(token) => Ok(Self(Some(token.to_string()))),
None => Ok(Self(None)),
}
}
}
// End Extractor
// Work with external APIs
/// Get UUID from JSON response
#[inline]
fn get_id_json(json: &serde_json::Value) -> anyhow::Result<Uuid> {
trace!("json: {json:#?}"); // For debugging, we'll get to this later!
let uuid = Uuid::parse_str(json.get("id").unwrap().as_str().unwrap())?;
Ok(uuid)
}
#[inline]
async fn fetch_json(
auth_system: AuthSystem,
server_id: &str,
username: &str,
) -> anyhow::Result<Option<(Uuid, AuthSystem)>> {
let client = reqwest::Client::new();
let url = auth_system.get_url();
let res = client
.get(url)
.query(&[("serverId", server_id), ("username", username)])
.send()
.await?;
debug!("{res:?}");
match res.status().as_u16() {
200 => {
let json = serde_json::from_str::<serde_json::Value>(&res.text().await?)?;
let uuid = get_id_json(&json)?;
Ok(Some((uuid, auth_system)))
}
401 => Ok(None), // Ely.By None
204 => Ok(None), // Mojang None
_ => Err(anyhow!("Unknown code: {}", res.status().as_u16())),
}
}
pub async fn has_joined(
server_id: &str,
username: &str,
) -> anyhow::Result<Option<(Uuid, AuthSystem)>> {
let (elyby, mojang) = (
fetch_json(AuthSystem::ElyBy,server_id, username).await?,
fetch_json(AuthSystem::Mojang, server_id, username).await?
);
if elyby.is_none() && mojang.is_none() {
Ok(None)
} else if mojang.is_some() {
Ok(mojang)
} else if elyby.is_some() {
Ok(elyby)
} else {
panic!("Impossible error!")
}
}
// End of work with external APIs
// User manager
#[derive(Debug, Clone)]
pub struct UManager {
/// Users with incomplete authentication
pending: Arc<DashMap<String, String>>, // <SHA1 serverId, USERNAME> TODO: Add automatic purge
/// Authenticated users TODO: Change name to sessions
authenticated: Arc<DashMap<String, Uuid>>, // <SHA1 serverId, Userinfo> NOTE: In the future, try it in a separate LockRw branch
/// Registered users
registered: Arc<DashMap<Uuid, Userinfo>>,
}
impl UManager {
pub fn new() -> Self {
Self {
pending: Arc::new(DashMap::new()),
registered: Arc::new(DashMap::new()),
authenticated: Arc::new(DashMap::new()),
}
}
pub fn pending_insert(&self, server_id: String, username: String) {
self.pending.insert(server_id, username);
}
pub fn pending_remove(&self, server_id: &str) -> std::option::Option<(std::string::String, std::string::String)> {
self.pending.remove(server_id)
}
pub fn insert(&self, uuid: Uuid, token: String, userinfo: Userinfo) -> Option<Userinfo> {
self.authenticated.insert(token, uuid);
self.registered.insert(uuid, userinfo)
}
pub fn insert_user(&self, uuid: Uuid, userinfo: Userinfo) -> Option<Userinfo> {
self.registered.insert(uuid, userinfo)
}
pub fn get(
&self,
token: &String,
) -> Option<dashmap::mapref::one::Ref<'_, Uuid, Userinfo>> {
let uuid = self.authenticated.get(token)?;
self.registered.get(uuid.value())
}
pub fn get_by_uuid(
&self,
uuid: &Uuid,
) -> Option<dashmap::mapref::one::Ref<'_, Uuid, Userinfo>> {
self.registered.get(uuid)
}
pub fn is_authenticated(&self, token: &String) -> bool {
self.authenticated.contains_key(token)
}
pub fn _is_registered(&self, uuid: &Uuid) -> bool {
self.registered.contains_key(uuid)
}
pub fn remove(&self, uuid: &Uuid) {
let token = self.registered.remove(uuid).unwrap().1.token.unwrap();
self.authenticated.remove(&token);
}
}
// End of User manager

5
src/auth/mod.rs Normal file
View file

@ -0,0 +1,5 @@
mod auth;
mod types;
pub use auth::*;
pub use types::*;

56
src/auth/types.rs Normal file
View file

@ -0,0 +1,56 @@
use std::str::FromStr;
use serde::Deserialize;
use uuid::Uuid;
use anyhow::anyhow;
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Userinfo {
pub username: String,
pub uuid: Uuid,
pub auth_system: AuthSystem,
pub token: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AuthSystem {
Internal,
ElyBy,
Mojang,
}
impl ToString for AuthSystem {
fn to_string(&self) -> String {
match self {
AuthSystem::Internal => String::from("internal"),
AuthSystem::ElyBy => String::from("elyby"),
AuthSystem::Mojang => String::from("mojang"),
}
}
}
impl FromStr for AuthSystem {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"internal" => Ok(Self::Internal),
"elyby" => Ok(Self::ElyBy),
"mojang" => Ok(Self::Mojang),
_ => Err(anyhow!("No auth system called: {s}"))
}
}
}
impl AuthSystem {
pub(super) fn get_url(&self) -> String {
match self {
AuthSystem::Internal => panic!("Can't get internal URL!"),
AuthSystem::ElyBy => String::from("http://minecraft.ely.by/session/hasJoined"),
AuthSystem::Mojang => String::from("https://sessionserver.mojang.com/session/minecraft/hasJoined"),
}
}
}

View file

@ -9,29 +9,44 @@ use tower_http::trace::TraceLayer;
use tracing::info; use tracing::info;
use uuid::Uuid; use uuid::Uuid;
// WebSocket worker // // WebSocket worker
mod ws; // mod ws;
use ws::handler; // use ws::handler;
// API: Auth // // API: Auth
// mod auth;
// use auth::{self as api_auth, UManager};
// // API: Server info
// mod info;
// use info as api_info;
// // API: Profile
// mod profile;
// use profile as api_profile;
// API
mod api;
use api::{
figura::{ws, info as api_info, profile as api_profile, auth as api_auth},
// v1::{},
};
// Auth
mod auth; mod auth;
use auth::{self as api_auth, UManager}; use auth::UManager;
// API: Server info // Config
mod info; mod state;
use info as api_info; use state::Config;
// API: Profile
mod profile;
use profile as api_profile;
// Utils // Utils
mod utils; mod utils;
use utils::update_advanced_users; use utils::update_advanced_users;
// Config // // Config
mod config; // mod config;
use config::Config; // use config::Config;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AppState { pub struct AppState {
@ -42,7 +57,7 @@ pub struct AppState {
/// Ping broadcasts for WebSocket connections /// Ping broadcasts for WebSocket connections
broadcasts: Arc<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>, broadcasts: Arc<DashMap<Uuid, broadcast::Sender<Vec<u8>>>>,
/// Current configuration /// Current configuration
config: Arc<Mutex<config::Config>>, config: Arc<Mutex<state::Config>>,
} }
const LOGGER_ENV: &'static str = "RUST_LOG"; const LOGGER_ENV: &'static str = "RUST_LOG";
@ -94,13 +109,9 @@ async fn main() -> Result<()> {
} }
}); });
let v1 = Router::new()
.nest("/", ws::http2ws_router())
.nest("/user", api_auth::router_v1());
let api = Router::new() let api = Router::new()
.nest("//auth", api_auth::router()) .nest("//auth", api_auth::router())
.nest("/v1", v1) .nest("/v1", api::v1::router())
.route("/limits", get(api_info::limits)) .route("/limits", get(api_info::limits))
.route("/version", get(api_info::version)) .route("/version", get(api_info::version))
.route("/motd", get(api_info::motd)) .route("/motd", get(api_info::motd))
@ -112,10 +123,9 @@ async fn main() -> Result<()> {
let app = Router::new() let app = Router::new()
.nest("/api", api) .nest("/api", api)
.route("/api/", get(api_auth::status)) .route("/ws", get(ws))
.route("/ws", get(handler))
.route("/health", get(|| async { "ok" })) .route("/health", get(|| async { "ok" }))
.route_layer(from_extractor::<api_auth::Token>()) .route_layer(from_extractor::<auth::Token>())
.with_state(state) .with_state(state)
.layer(TraceLayer::new_for_http().on_request(())); .layer(TraceLayer::new_for_http().on_request(()));

4
src/state/mod.rs Normal file
View file

@ -0,0 +1,4 @@
mod config;
mod state;
pub use config::*;

0
src/state/state.rs Normal file
View file

View file

@ -1,8 +0,0 @@
mod types;
mod websocket;
mod http;
pub use types::C2SMessage;
pub use types::S2CMessage;
pub use websocket::handler;
pub use http::router as http2ws_router;