Initial
This commit is contained in:
31
src/api/mod.rs
Normal file
31
src/api/mod.rs
Normal 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();
|
||||
|
||||
}
|
||||
0
src/api/routes/cct/event/mod.rs
Normal file
0
src/api/routes/cct/event/mod.rs
Normal file
0
src/api/routes/cct/event/typ.rs
Normal file
0
src/api/routes/cct/event/typ.rs
Normal file
0
src/api/routes/cct/login.rs
Normal file
0
src/api/routes/cct/login.rs
Normal file
3
src/api/routes/cct/mod.rs
Normal file
3
src/api/routes/cct/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod login;
|
||||
mod register;
|
||||
mod event;
|
||||
0
src/api/routes/cct/register.rs
Normal file
0
src/api/routes/cct/register.rs
Normal file
0
src/api/routes/data/mod.rs
Normal file
0
src/api/routes/data/mod.rs
Normal file
2
src/api/routes/mod.rs
Normal file
2
src/api/routes/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod cct;
|
||||
pub mod user;
|
||||
0
src/api/routes/user/add.rs
Normal file
0
src/api/routes/user/add.rs
Normal file
0
src/api/routes/user/edit.rs
Normal file
0
src/api/routes/user/edit.rs
Normal file
0
src/api/routes/user/login.rs
Normal file
0
src/api/routes/user/login.rs
Normal file
14
src/api/routes/user/mod.rs
Normal file
14
src/api/routes/user/mod.rs
Normal 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")
|
||||
}
|
||||
0
src/api/routes/user/remove.rs
Normal file
0
src/api/routes/user/remove.rs
Normal file
116
src/api/ws/mod.rs
Normal file
116
src/api/ws/mod.rs
Normal 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
26
src/context/mod.rs
Normal 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
36
src/db/mod.rs
Normal 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
38
src/main.rs
Normal 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(())
|
||||
}
|
||||
Reference in New Issue
Block a user