This commit is contained in:
2024-11-02 19:34:53 +02:00
commit f7fefe300d
32 changed files with 5835 additions and 0 deletions

31
src/api/mod.rs Normal file
View File

@@ -0,0 +1,31 @@
use crate::context::AppContext;
use axum::{
routing::{get, post},
http::StatusCode,
Json, Router,
};
pub mod routes;
pub mod ws;
async fn get_api_info() -> String {
format!("API v{}", env!("CARGO_PKG_VERSION"))
}
pub async fn start_api(ctx: AppContext) {
// build our application with a route
let app = Router::new()
// `GET /` goes to `root`
.route("/api/", get(get_api_info))
.route("/ws/", get(ws::WsHandler::ws_handler))
.with_state(ctx);
// `POST /users` goes to `create_user`
// .route("/users", post(create_user));
// run our app with hyper, listening globally on port 3000
log::info!("Listening on http://0.0.0.0:3000");
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}

View File

View File

View File

View File

@@ -0,0 +1,3 @@
mod login;
mod register;
mod event;

View File

View File

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

@@ -0,0 +1,2 @@
pub mod cct;
pub mod user;

View File

View File

View File

View File

@@ -0,0 +1,14 @@
use axum::extract::State;
use crate::context::AppContext;
pub mod add;
pub mod edit;
pub mod login;
pub mod remove;
async fn handler(State(ctx): State<AppContext>) -> String {
format!(":333")
}

View File

116
src/api/ws/mod.rs Normal file
View File

@@ -0,0 +1,116 @@
use std::{collections::HashMap, sync::mpsc::{self, Receiver, Sender}};
use axum::{body::HttpBody, extract::{ws::WebSocket, State, WebSocketUpgrade}, http::StatusCode, response::Response};
use axum_extra::TypedHeader;
use chrono::{DateTime, Utc};
use futures_util::{stream::{SplitSink, SplitStream}, StreamExt};
use headers::{authorization::Bearer, Authorization};
use sea_orm::prelude::Uuid;
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use crate::context::AppContext;
lazy_static::lazy_static!(
pub static ref WS_CLIENTS: Mutex<HashMap<Uuid, WsClient>> = Mutex::new(HashMap::new());
);
#[derive(Debug)]
pub struct WsClient {
rx: Receiver<WsMessage>,
tx: Sender<WsMessage>,
alive: bool,
last_heartbeat: DateTime<Utc>
}
impl WsClient {
pub fn new(rx: Receiver<WsMessage>, tx: Sender<WsMessage>) -> Self {
Self {
rx, tx,
alive: false,
last_heartbeat: chrono::Utc::now()
}
}
}
#[derive(Debug, Clone)]
pub struct WsHandler {
}
impl WsHandler {
pub fn new() -> Self {
Self {
}
}
pub async fn ws_handler(ws: WebSocketUpgrade, State(ctx): State<AppContext>, TypedHeader(token): TypedHeader<Authorization<Bearer>>) -> Response {
let token = token.0.token().to_string();
let Ok(token) = Uuid::parse_str(&token) else {
todo!()
// return Response::status(Body)
};
ws.on_upgrade(move |socket| Self::handle_socket(socket, token))
}
async fn handle_socket(socket: WebSocket, token: Uuid) {
let (sender, receiver) = socket.split();
let (r_tx, from_socket) = mpsc::channel();
let (to_socket, s_rx) = mpsc::channel();
async fn send(sender: SplitSink<WebSocket, axum::extract::ws::Message>, s_rx: mpsc::Receiver<WsMessage>) {
}
async fn recv(mut receiver: SplitStream<WebSocket>, tx: mpsc::Sender<WsMessage>) {
while let Some(msg) = receiver.next().await {
let Ok(msg) = msg else {
return;
};
let Ok(msg) = msg.to_text() else {
return;
};
}
}
tokio::spawn(recv(receiver, r_tx));
tokio::spawn(send(sender, s_rx));
Self::add_client(token, from_socket, to_socket).await;
}
async fn add_client(token: Uuid, rx: Receiver<WsMessage>, tx: Sender<WsMessage>) {
WS_CLIENTS.lock().await.insert(token, WsClient::new(rx, tx));
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WsMessage {
pub ws_msg_t: usize,
pub ws_msg: Message,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub auth: Uuid,
pub msg: MessageType
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MessageType {
Keypad(KeypadMessageType)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum KeypadMessageType {
DoorOpenEv {
user: Uuid,
timestamp: usize,
door_id: Uuid
}
}

26
src/context/mod.rs Normal file
View File

@@ -0,0 +1,26 @@
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
use crate::{api::ws::WsHandler, db::Database};
#[derive(Debug, Clone)]
pub struct AppContext {
db: Database,
ws: WsHandler
}
impl AppContext {
pub fn new(db: Database) -> Self {
Self {
db,
ws: WsHandler::new()
}
}
pub fn db(&mut self) -> &mut Database {
&mut self.db
}
pub fn ws(&mut self) -> &mut WsHandler {
&mut self.ws
}
}

36
src/db/mod.rs Normal file
View File

@@ -0,0 +1,36 @@
use std::time::Duration;
use sea_orm::{ConnectOptions, Database as SODatabase, DatabaseConnection};
mod models;
// to update the models run `sea-orm-cli generate entity -o src/db/models`
#[derive(Debug, Clone)]
pub struct Database {
db: DatabaseConnection
}
impl Database {
pub fn new() -> Self {
Self {
db: DatabaseConnection::Disconnected
}
}
pub async fn connect(&mut self) -> anyhow::Result<()> {
log::info!("Connecting to SQlite database at ./db.sqlite");
let mut opt = ConnectOptions::new("sqlite://db.sqlite?mode=rwc");
opt.max_connections(100)
.min_connections(5)
.connect_timeout(Duration::from_secs(8))
.acquire_timeout(Duration::from_secs(8))
.idle_timeout(Duration::from_secs(8))
.max_lifetime(Duration::from_secs(8))
.sqlx_logging(true)
.sqlx_logging_level(log::LevelFilter::Debug);
self.db = SODatabase::connect(opt).await?;
log::info!("Connection successful");
Ok(())
}
}

38
src/main.rs Normal file
View File

@@ -0,0 +1,38 @@
use api::ws::{KeypadMessageType, Message, WsMessage};
use sea_orm::prelude::Uuid;
mod api;
mod db;
mod context;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
env_logger::init();
let msg = WsMessage {
ws_msg_t: 0,
ws_msg: Message {
auth: Uuid::new_v4(),
msg: api::ws::MessageType::Keypad(
KeypadMessageType::DoorOpenEv {
user: Uuid::new_v4(),
timestamp: 0,
door_id: Uuid::new_v4()
}
)
}
};
dbg!(&msg);
let txt = serde_json::to_string_pretty(&msg)?;
println!("{txt}");
return Ok(());
// TODO: Start db
let mut db = db::Database::new();
db.connect().await?;
let ctx = context::AppContext::new(db);
api::start_api(ctx).await;
Ok(())
}