Initial
This commit is contained in:
commit
f7fefe300d
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[env]
|
||||||
|
RUST_LOG="debug,sqlx=info"
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/target
|
||||||
|
/**/target
|
||||||
|
/db.sqite
|
||||||
|
/src/db/models
|
3010
Cargo.lock
generated
Normal file
3010
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
Cargo.toml
Normal file
22
Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
[package]
|
||||||
|
name = "rtmc-be"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.92"
|
||||||
|
argon2 = { version = "0.5.3", features = ["simple"] }
|
||||||
|
axum = { version = "0.7.7", features = ["ws"] }
|
||||||
|
axum-extra = { version = "0.9.4", features = ["typed-header"] }
|
||||||
|
chrono = "0.4.38"
|
||||||
|
clap = "4.5.20"
|
||||||
|
env_logger = "0.11.5"
|
||||||
|
futures-util = "0.3.31"
|
||||||
|
headers = "0.4.0"
|
||||||
|
lazy_static = "1.5.0"
|
||||||
|
log = "0.4.22"
|
||||||
|
sea-orm = { version = "1.1.0", features = ["macros", "runtime-tokio-rustls", "sqlx-sqlite", "with-json", "with-uuid"] }
|
||||||
|
serde = { version = "1.0.214", features = ["derive"] }
|
||||||
|
serde_json = "1.0.132"
|
||||||
|
tokio = { version = "1.41.0", features = ["full"] }
|
||||||
|
uuid = { version = "1.11.0", features = ["serde", "v4"] }
|
64
README.md
Normal file
64
README.md
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# setup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo install sea-orm-cli
|
||||||
|
sea-orm-cli generate entity -o src/db/models # do this every time you edit the migrations, or first time on clone
|
||||||
|
cargo build
|
||||||
|
```
|
||||||
|
|
||||||
|
# api docs
|
||||||
|
|
||||||
|
u-token = user token
|
||||||
|
p-token = pc token
|
||||||
|
a-token = u-token or p-token
|
||||||
|
pid = cc:t pc id
|
||||||
|
|
||||||
|
GET - /ws (nothing required on first req, see ws docs for more info)
|
||||||
|
|
||||||
|
POST - /api/user/login (no token (duh), json body with email, pwd, returns json with token)
|
||||||
|
POST - /api/user/add (u-token in header, json body with full user info)
|
||||||
|
POST - /api/user/:id/edit (u-token in header, json body with data to replace with)
|
||||||
|
POST - /api/user/:id/remove (u-token in header, no body required)
|
||||||
|
GET - /api/user/:id/info (u-token in header, get back json)
|
||||||
|
|
||||||
|
GET - /api/cct/ (a-token in header, gets a list of all groups)
|
||||||
|
GET - /api/cct/:group/ (a-token in header, gets a list of all pid's)
|
||||||
|
GET - /api/cct/:group/:pid/ (a-token in header, gets a list of all values for that pc)
|
||||||
|
GET - /api/cct/:group/:pid/:val (a-token in header, gets a value from a pc in a group)
|
||||||
|
|
||||||
|
POST - /api/cct/group/add (a-token in header, adds a group)
|
||||||
|
POST - /api/cct/:group/edit (a-token in header, edits a group)
|
||||||
|
POST - /api/cct/:group/pc/add (a-token in header, adds a pc to a group)
|
||||||
|
POST - /api/cct/:group/:pid/edit (a-token in header, edits a pc in a group)
|
||||||
|
POST - /api/cct/:group/:pid/val/add (a-token in header, adds a value to a pc in a group)
|
||||||
|
POST - /api/cct/:group/:pid/:val/edit (a-token in header, edits value info in a pc in a group)
|
||||||
|
POST - /api/cct/:group/:pid/:val/set (a-token in header, sets a value in a pc in a group)
|
||||||
|
|
||||||
|
==========
|
||||||
|
ws
|
||||||
|
==========
|
||||||
|
|
||||||
|
`ws_msg_t`:
|
||||||
|
0: heartbeat ping
|
||||||
|
1: server to client
|
||||||
|
2: client to server
|
||||||
|
`msg_t`:
|
||||||
|
TODO!
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ // low level message
|
||||||
|
"ws_msg_t": 0, // `ws_msg_t`
|
||||||
|
"ws_msg": { // high level message
|
||||||
|
"auth": "Token", // Auth token
|
||||||
|
"msg": { // Data field
|
||||||
|
"keypad": {
|
||||||
|
"DoorOpenEv": { // "DataType": "Value, can be a struct or anything"
|
||||||
|
"user": "uid",
|
||||||
|
"timestamp": 123456,
|
||||||
|
"door_id": "1234-abcd-1234-abcd"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
2201
migration/Cargo.lock
generated
Normal file
2201
migration/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
migration/Cargo.toml
Normal file
22
migration/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
[package]
|
||||||
|
name = "migration"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "migration"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-std = { version = "1", features = ["attributes", "tokio1"] }
|
||||||
|
|
||||||
|
[dependencies.sea-orm-migration]
|
||||||
|
version = "1.1.0"
|
||||||
|
features = [
|
||||||
|
# Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI.
|
||||||
|
# View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime.
|
||||||
|
# e.g.
|
||||||
|
"runtime-tokio-rustls", # `ASYNC_RUNTIME` feature
|
||||||
|
"sqlx-sqlite", # `DATABASE_DRIVER` feature
|
||||||
|
]
|
41
migration/README.md
Normal file
41
migration/README.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Running Migrator CLI
|
||||||
|
|
||||||
|
- Generate a new migration file
|
||||||
|
```sh
|
||||||
|
cargo run -- generate MIGRATION_NAME
|
||||||
|
```
|
||||||
|
- Apply all pending migrations
|
||||||
|
```sh
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
```sh
|
||||||
|
cargo run -- up
|
||||||
|
```
|
||||||
|
- Apply first 10 pending migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- up -n 10
|
||||||
|
```
|
||||||
|
- Rollback last applied migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- down
|
||||||
|
```
|
||||||
|
- Rollback last 10 applied migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- down -n 10
|
||||||
|
```
|
||||||
|
- Drop all tables from the database, then reapply all migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- fresh
|
||||||
|
```
|
||||||
|
- Rollback all applied migrations, then reapply all migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- refresh
|
||||||
|
```
|
||||||
|
- Rollback all applied migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- reset
|
||||||
|
```
|
||||||
|
- Check the status of all migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- status
|
||||||
|
```
|
19
migration/src/lib.rs
Normal file
19
migration/src/lib.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
pub use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
mod m20241102_133640_users;
|
||||||
|
mod m20241102_133644_computers;
|
||||||
|
mod m20241102_150432_pc_groups;
|
||||||
|
|
||||||
|
pub struct Migrator;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigratorTrait for Migrator {
|
||||||
|
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
||||||
|
vec![
|
||||||
|
Box::new(m20241102_133640_users::Migration),
|
||||||
|
Box::new(m20241102_133644_computers::Migration),
|
||||||
|
Box::new(m20241102_150432_pc_groups::Migration),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
68
migration/src/m20241102_133640_users.rs
Normal file
68
migration/src/m20241102_133640_users.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(User::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(ColumnDef::new(User::Id)
|
||||||
|
.uuid()
|
||||||
|
.unique_key()
|
||||||
|
.not_null()
|
||||||
|
.primary_key()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(User::Username)
|
||||||
|
.string()
|
||||||
|
.unique_key()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(User::Email)
|
||||||
|
.string()
|
||||||
|
.unique_key()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(User::PwdHash)
|
||||||
|
.string()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(User::PwdSalt)
|
||||||
|
.string()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(User::IsAdministrator)
|
||||||
|
.boolean()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(User::CreatedAt)
|
||||||
|
.date_time()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(User::Table).to_owned())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum User {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Username,
|
||||||
|
Email,
|
||||||
|
PwdHash,
|
||||||
|
PwdSalt,
|
||||||
|
IsAdministrator,
|
||||||
|
CreatedAt
|
||||||
|
}
|
63
migration/src/m20241102_133644_computers.rs
Normal file
63
migration/src/m20241102_133644_computers.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
// Replace the sample below with your own migration scripts
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(Computer::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(ColumnDef::new(Computer::Id)
|
||||||
|
.uuid()
|
||||||
|
.not_null()
|
||||||
|
.primary_key()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Computer::Name)
|
||||||
|
.string()
|
||||||
|
.unique_key()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Computer::Group)
|
||||||
|
.string()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Computer::Type)
|
||||||
|
.string()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Computer::AccessToken)
|
||||||
|
.string()
|
||||||
|
.unique_key()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Computer::CreatedAt)
|
||||||
|
.date_time()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(Computer::Table).to_owned())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum Computer {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
Group,
|
||||||
|
Type,
|
||||||
|
AccessToken,
|
||||||
|
CreatedAt
|
||||||
|
}
|
46
migration/src/m20241102_150432_pc_groups.rs
Normal file
46
migration/src/m20241102_150432_pc_groups.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use sea_orm_migration::{prelude::*, schema::*};
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(PcGroup::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(ColumnDef::new(PcGroup::Id)
|
||||||
|
.uuid()
|
||||||
|
.unique_key()
|
||||||
|
.primary_key()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(PcGroup::Name)
|
||||||
|
.string()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(PcGroup::AvailableActions)
|
||||||
|
.json()
|
||||||
|
.not_null()
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(PcGroup::Table).to_owned())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum PcGroup {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
AvailableActions,
|
||||||
|
}
|
6
migration/src/main.rs
Normal file
6
migration/src/main.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() {
|
||||||
|
cli::run_cli(migration::Migrator).await;
|
||||||
|
}
|
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(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user