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