commit
d10db5d31b
|
@ -2,4 +2,5 @@
|
||||||
.vscode
|
.vscode
|
||||||
users.json
|
users.json
|
||||||
message.zsh
|
message.zsh
|
||||||
|
TODO.md
|
||||||
users_db/
|
users_db/
|
||||||
|
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -1,3 +1,19 @@
|
||||||
|
## 0.6.0
|
||||||
|
- Remove deprecated API
|
||||||
|
- `/api/register` & `/api/login` now use JSON data
|
||||||
|
- Changing user info now uses Enum (Name, Pin, or Pronouns)
|
||||||
|
|
||||||
|
### 0.5.2
|
||||||
|
- When changing a username, it now deletes the previous user instead of creating a new one
|
||||||
|
- Database functions should now use some error handling
|
||||||
|
- Functions use `db_read_user()` instead of reading in the whole database
|
||||||
|
|
||||||
|
### 0.5.1
|
||||||
|
- `/api/logout` API to delete session token
|
||||||
|
- Add basic support for different message types
|
||||||
|
- Messages now use unix timestamps
|
||||||
|
- Backend finds timestamp
|
||||||
|
|
||||||
## 0.5.0
|
## 0.5.0
|
||||||
- Most actions should now fail on a NULL token
|
- Most actions should now fail on a NULL token
|
||||||
- Cookie should now expire after a week
|
- Cookie should now expire after a week
|
||||||
|
|
|
@ -228,9 +228,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-mac"
|
name = "crypto-mac"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6"
|
checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"subtle",
|
"subtle",
|
||||||
|
@ -301,9 +301,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.4.1"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb"
|
checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
|
@ -572,9 +572,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.97"
|
version = "0.2.98"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
|
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
|
@ -807,7 +807,7 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pogchat"
|
name = "pogchat"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -1062,7 +1062,7 @@ checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.27",
|
"proc-macro2 1.0.27",
|
||||||
"quote 1.0.9",
|
"quote 1.0.9",
|
||||||
"syn 1.0.73",
|
"syn 1.0.74",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1148,9 +1148,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.73"
|
version = "1.0.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.27",
|
"proc-macro2 1.0.27",
|
||||||
"quote 1.0.9",
|
"quote 1.0.9",
|
||||||
|
@ -1178,9 +1178,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.2.0"
|
version = "1.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
|
checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec_macros",
|
"tinyvec_macros",
|
||||||
]
|
]
|
||||||
|
@ -1259,9 +1259,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "universal-hash"
|
name = "universal-hash"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402"
|
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"subtle",
|
"subtle",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pogchat"
|
name = "pogchat"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
authors = ["Erin Nova <erin@the-system.eu.org>"]
|
authors = ["Erin Nova <erin@the-system.eu.org>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|
129
README.md
129
README.md
|
@ -1,54 +1,95 @@
|
||||||
# Chat Registration System
|
# Chat Registration System
|
||||||
|
|
||||||
The basic backend code needed to register & login to a chat system (to be built).
|
A simple chat system for built for maya's livestream.
|
||||||
Send it the unhashed username and pin, and it'll store it in the database with the pin hashed with SHA1.
|
Provides a simple API for user authentication, and chat functions.
|
||||||
|
Frontend & backend code stored here.
|
||||||
|
|
||||||
## API Documentation
|
## Auth API Documentation
|
||||||
|
|
||||||
`POST /api/register {"name":"<username>","pin":"<pin>","pronouns":"<pronouns>"}` Register a user if they don't already exist
|
Most API functions will return JSON in the following format:
|
||||||
|
|
||||||
`POST /api/register/<name>/<pin>/<pronouns>` Register the username with the pin provided if it doesn't already exist
|
`status`: either `ok` if action succeeded, or `fail` otherwise.
|
||||||
Returns status & reason json.
|
|
||||||
|
|
||||||
`GET /api/users/<name>` Check if the user exists
|
`reason`: More info about why the action failed.
|
||||||
Returns either
|
|
||||||
|
|
||||||
`{
|
### Register & Login:
|
||||||
"status": "fail",
|
|
||||||
"reason": "user not found",
|
|
||||||
}`
|
|
||||||
|
|
||||||
or
|
`POST /api/register` with JSON body values of: `name`, `pin`, `pronouns`.
|
||||||
|
|
||||||
`{
|
Will return JSON with `status` and `reason`.
|
||||||
"status": "ok",
|
|
||||||
"user": {
|
`POST /api/login` with JSON body values of: `name`, `pin`.
|
||||||
"name": "<name>",
|
|
||||||
"pronouns": "<pronouns>",
|
Will return JSON with `status` and `reason`.
|
||||||
|
|
||||||
|
Will set a private cookie named `token` which is used for authentication.
|
||||||
|
|
||||||
|
### Change User Information
|
||||||
|
|
||||||
|
User information such as name, pin, and pronouns, can be changed currently one at a time.
|
||||||
|
|
||||||
|
`POST /api/change` with JSON body values of: `name`, `changed_event`, `new_event`.
|
||||||
|
|
||||||
|
`name` the user's current username. used for authentication.
|
||||||
|
|
||||||
|
`changed_event` which event to change. value can be one of: `Name`, `Pin`, `Pronouns`.
|
||||||
|
|
||||||
|
`new_event` the new value for the changed event.
|
||||||
|
|
||||||
|
User is authenticated via token.
|
||||||
|
|
||||||
|
### Check if User is Still Logged in
|
||||||
|
|
||||||
|
Instead of having to save the pin and re-login every time to check wether they're logged in, you can just check via the token.
|
||||||
|
|
||||||
|
`GET /api/token/<name>` where `<name>` is the current username.
|
||||||
|
|
||||||
|
Will return JSON with `status` and `reason`.
|
||||||
|
|
||||||
|
### Logout
|
||||||
|
|
||||||
|
This API will remove the cookie from the client, as well as invalidating the token serverside.
|
||||||
|
|
||||||
|
`POST /api/logout` with JSON body values of: `name`.
|
||||||
|
|
||||||
|
Will use the current token as authentication.
|
||||||
|
|
||||||
|
Will return JSON with `status` and `reason`.
|
||||||
|
|
||||||
|
### Get Info About A User
|
||||||
|
|
||||||
|
This API will return info about a user on success.
|
||||||
|
|
||||||
|
`GET /api/users/<name>`
|
||||||
|
|
||||||
|
On success returns JSON in format:
|
||||||
|
|
||||||
|
`status`: `ok`
|
||||||
|
`user`:
|
||||||
|
`name`: user's name
|
||||||
|
`pronouns`: user's pronouns
|
||||||
|
`role`: the users role, one of either `Normal`, `Moderator`, or `Admin
|
||||||
|
|
||||||
|
eg:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
status: "ok",
|
||||||
|
user: {
|
||||||
|
name: "example",
|
||||||
|
pronouns: "they/them",
|
||||||
|
role: "Normal",
|
||||||
},
|
},
|
||||||
}`
|
}
|
||||||
|
```
|
||||||
|
|
||||||
`GET /api/token/<name>` Check if the current token matches the user provided
|
## Chat API Documentation
|
||||||
|
|
||||||
`GET /api/users/<name>/<pin>` Check if the user exists, and if the pin provided matches
|
`POST /api/message/send {"name":"username","body":"message body"}` Post a message with JSON body values of: `name` & `body`
|
||||||
Returns status & reason json.
|
|
||||||
|
|
||||||
`POST /api/users/change {"name":"<username>","pin":"<pin>","changed_event":"name/pin/pronouns","new_event":"<new name/pin/pronouns>"` Change a users details via a json post.
|
Will return JSON with `status` and `reason`.
|
||||||
|
|
||||||
eg. `POST /api/users/change {"name":"example","pin":"10","changed_event":"name","new_event":"test"` to change the user "example"'s name to "test"
|
`GET /api/message/messages.json` Returns a json file of all the messages
|
||||||
|
|
||||||
DEPRECATED `POST /api/users/change/<name>/<pin>/<new-name>/<new-pin>` Change a users pin/name
|
|
||||||
Returns status & reason json.
|
|
||||||
|
|
||||||
`POST /api/logout {"name":"<username>"}` to logout a user if the token matches
|
|
||||||
|
|
||||||
|
|
||||||
## Chat Documentation
|
|
||||||
|
|
||||||
`POST /api/message/send {"name":"username","body":"message body","date":"yyyy-mm-dd"}` Post a json message.
|
|
||||||
Returns status & reason json.
|
|
||||||
|
|
||||||
`GET /api/message/messages.json` Get a json file of all the messages
|
|
||||||
|
|
||||||
## Chat Planning
|
## Chat Planning
|
||||||
|
|
||||||
|
@ -65,10 +106,10 @@ Whenever user sends a message, client will send message & token and backend will
|
||||||
- [x] Basic messaging system
|
- [x] Basic messaging system
|
||||||
- [x] Finish up `chat::create_message()`
|
- [x] Finish up `chat::create_message()`
|
||||||
- [x] Create `chat::fetch_messages()`
|
- [x] Create `chat::fetch_messages()`
|
||||||
- [ ] Use unix timestamp for date
|
- [x] Use unix timestamp for date
|
||||||
- [ ] Create `chat::delete_message()`
|
- [ ] Create `chat::delete_message()`
|
||||||
- [x] Switch to using sled database to store users
|
- [x] Switch to using sled database to store users
|
||||||
- [ ] Error handling
|
- [x] Error handling
|
||||||
- [x] Token generation & storage
|
- [x] Token generation & storage
|
||||||
- [x] Sets cookie
|
- [x] Sets cookie
|
||||||
- [x] Store token in json
|
- [x] Store token in json
|
||||||
|
@ -79,11 +120,15 @@ Whenever user sends a message, client will send message & token and backend will
|
||||||
- [x] Fail on NULL token
|
- [x] Fail on NULL token
|
||||||
- [x] Pronouns
|
- [x] Pronouns
|
||||||
- [x] Set pronouns
|
- [x] Set pronouns
|
||||||
- [ ] Change pronouns
|
- [x] Change pronouns
|
||||||
- [ ] Multiple sets of pronouns
|
- [x] make changed_event Enum, use token instead of pin
|
||||||
- [ ] Some form of plural support?
|
- [ ] Some form of plural support?
|
||||||
- [ ] User management (banning, etc.)
|
- [ ] User management (banning, etc.)
|
||||||
|
- [x] User roles (admin, mod, etc.)
|
||||||
|
- [ ] Commands to affect users
|
||||||
- [ ] Blacklist words from chat/names
|
- [ ] Blacklist words from chat/names
|
||||||
- [ ] More advanced chat features
|
- [ ] More advanced chat features
|
||||||
- [ ] Different types of message events? eg. default, announcement, command
|
- [x] Different types of message events? eg. default, announcement, command
|
||||||
|
- [ ] Types will display differently? eg. announcements pinned to top?
|
||||||
|
- [ ] Have different commands?
|
||||||
- [ ] Emote support?
|
- [ ] Emote support?
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
http POST 'http://localhost:8000/api/message/send' name=erin body="nyaa uwu" date="2021-07-21"
|
http POST 'http://localhost:8000/api/mod' name=erin action=Ban target=sarah
|
||||||
|
|
524
src/auth.rs
524
src/auth.rs
|
@ -1,66 +1,33 @@
|
||||||
extern crate log;
|
extern crate log;
|
||||||
use crate::file_io::{db_add, db_write, db_read};
|
use crate::file_io::*;
|
||||||
use rocket::http::{Cookie, Cookies, SameSite};
|
use rocket::http::{Cookie, Cookies};
|
||||||
use crate::user::User;
|
use crate::user::*;
|
||||||
use rocket_contrib::json::{Json, JsonValue};
|
use rocket_contrib::json::{Json, JsonValue};
|
||||||
use random_string::generate;
|
use random_string::generate;
|
||||||
extern crate sha1;
|
extern crate sha1;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
pub fn index() -> &'static str {
|
|
||||||
"API Info:
|
|
||||||
|
|
||||||
`POST /api/register/<name>/<pin>/<pronouns>` Register the username with the pin provided if it doesn't already exist
|
|
||||||
|
|
||||||
`GET /api/users/<name>` Check if the user exists
|
|
||||||
|
|
||||||
`GET /api/users/<name>/<pin>` Check if the user exists, and if the pin provided matches
|
|
||||||
|
|
||||||
`POST /api/users/change/<name>/<pin>/<new-name>/<new-pin>` Change a users name and/or pin
|
|
||||||
|
|
||||||
`GET /api/about/name/<name>` Get the name of a user
|
|
||||||
|
|
||||||
`GET /api/about/pronouns/<name>` Get the pronouns of a user"
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct RegisterEvent {
|
|
||||||
pub name: String,
|
|
||||||
pub pin: String,
|
|
||||||
pub pronouns: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post request to register a user and pin
|
// Post request to register a user and pin
|
||||||
#[post("/register", format = "json", data = "<info>")]
|
#[post("/register", format = "json", data = "<data>")]
|
||||||
pub fn register_user(info: Json<RegisterEvent>) -> JsonValue {
|
pub fn register(data: Json<RegisterEvent>) -> JsonValue {
|
||||||
let users: Vec<User> = db_read(); // Create an array of users out of parsed json
|
// check if the user exists
|
||||||
for i in &users {
|
if let Some(user) = db_read_user(&data.name).ok().flatten() {
|
||||||
// loop through elements of the vector
|
warn!("Cannot create user {}! User is already in system.", data.name);
|
||||||
if i.name == info.name.to_lowercase() {
|
return json!({
|
||||||
warn!("Cannot create user {}! User is already in system.", i.name);
|
"status": "fail",
|
||||||
return json!({
|
"reason": "user already exists",
|
||||||
"status": "fail",
|
});
|
||||||
"reason": "user already exists",
|
} else {
|
||||||
});
|
let pin_hashed = sha1::Sha1::from(&data.pin).digest().to_string(); // hash the pin
|
||||||
|
|
||||||
|
let new_user: User = User {
|
||||||
|
name: data.name.to_string().to_lowercase(),
|
||||||
|
pin_hashed,
|
||||||
|
pronouns: data.pronouns.to_string().to_lowercase(),
|
||||||
|
session_token: "NULL".to_string(),
|
||||||
|
role: UserType::Normal,
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
let pin_hashed = sha1::Sha1::from(&info.pin.to_string()).digest().to_string(); // hash the pin
|
|
||||||
|
|
||||||
let new_user: User = User {
|
|
||||||
name: info.name.to_string().to_lowercase(),
|
|
||||||
pin_hashed: pin_hashed,
|
|
||||||
pronouns: info.pronouns.to_string().to_lowercase(),
|
|
||||||
session_token: "NULL".to_string(),
|
|
||||||
}; // append the user to the vec
|
|
||||||
|
|
||||||
/*
|
|
||||||
// append to the json file
|
|
||||||
match append_json(&new_user) {
|
|
||||||
Err(why) => panic!("couldn't append json: {}", why),
|
|
||||||
Ok(()) => info!("Succesfully appended to json"),
|
|
||||||
};*/
|
|
||||||
db_add(&new_user);
|
db_add(&new_user);
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
|
@ -72,25 +39,20 @@ pub fn register_user(info: Json<RegisterEvent>) -> JsonValue {
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"reason": format!("user {} registered", new_user.name.to_string().to_lowercase()),
|
"reason": format!("user {} registered", new_user.name.to_string().to_lowercase()),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_token(name: String, mut users: Vec<User>) -> String {
|
fn create_token(name: String, mut user: User) -> String {
|
||||||
let charset = "1234567890abcdefghijklmnopqrstuvwxyz";
|
let charset = "1234567890abcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
for i in 0..users.len() {
|
if user.name == name {
|
||||||
if users[i].name == name {
|
user.session_token = generate(12, charset);
|
||||||
users[i].session_token = generate(12, charset);
|
db_add(&user);
|
||||||
/*
|
info!("succesfully created token for user {}", name);
|
||||||
match write_json(&users) {
|
let token = user.session_token.clone();
|
||||||
Err(why) => panic!("coudln't write to file: {}", why),
|
return token;
|
||||||
Ok(()) => info!("succesfully wrote to file"),
|
|
||||||
};*/
|
|
||||||
db_write(&users);
|
|
||||||
info!("succesfully created token for user {}", name);
|
|
||||||
let token = users[i].session_token.clone();
|
|
||||||
return token;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
warn!("something bad happened while creating a token and idk what");
|
warn!("something bad happened while creating a token and idk what");
|
||||||
return "NULL".to_string();
|
return "NULL".to_string();
|
||||||
}
|
}
|
||||||
|
@ -98,159 +60,141 @@ fn create_token(name: String, mut users: Vec<User>) -> String {
|
||||||
// Check if user is properly logged in
|
// Check if user is properly logged in
|
||||||
#[get("/token/<name>")]
|
#[get("/token/<name>")]
|
||||||
pub fn check_token(name: String, mut cookies: Cookies) -> JsonValue {
|
pub fn check_token(name: String, mut cookies: Cookies) -> JsonValue {
|
||||||
let users: Vec<User> = db_read();
|
// check if the user is in the system
|
||||||
for i in &users {
|
if let Some(user) = db_read_user(&name).ok().flatten() {
|
||||||
if i.name == name.to_lowercase() {
|
// get the token from the cookie
|
||||||
let token = match cookies.get_private("token") {
|
let token = match cookies.get_private("token") {
|
||||||
None => {
|
None => {
|
||||||
warn!("couldn't get token cookie!");
|
warn!("couldn't get token cookie!");
|
||||||
return json!({
|
|
||||||
"status": "fail",
|
|
||||||
"reason": "could not read cookie",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
Some(token) => token,
|
|
||||||
};
|
|
||||||
if token.value() == "NULL" {
|
|
||||||
warn!("NULL token!");
|
|
||||||
return json!({
|
return json!({
|
||||||
"status": "fail",
|
"status": "fail",
|
||||||
"reason": "NULL token",
|
"reason": "could not read cookie",
|
||||||
});
|
});
|
||||||
} else if token.value() == i.session_token {
|
},
|
||||||
info!("user {} has correct session token", name);
|
Some(token) => token,
|
||||||
return json!({
|
};
|
||||||
"status": "ok",
|
|
||||||
"reason": "correct token",
|
// check the token value
|
||||||
});
|
if token.value() == "NULL" {
|
||||||
} else {
|
warn!("NULL token!");
|
||||||
info!("user {} has incorrect token!", name);
|
return json!({
|
||||||
return json!({
|
"status": "fail",
|
||||||
"status": "fail",
|
"reason": "NULL token",
|
||||||
"reason": "incorrect token",
|
});
|
||||||
});
|
} else if token.value() == user.session_token {
|
||||||
}
|
info!("user {} has correct session token", name);
|
||||||
|
return json!({
|
||||||
|
"status": "ok",
|
||||||
|
"reason": "correct token",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
info!("user {} has incorrect token!", name);
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "incorrect token",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
warn!("user {} not found", name);
|
warn!("user {} not found", name);
|
||||||
return json!({
|
return json!({
|
||||||
"status": "fail",
|
"status": "fail",
|
||||||
"reason": "user not found",
|
"reason": "user not found",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// logout event struct
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct LogoutEvent {
|
|
||||||
pub name: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logout API
|
// Logout API
|
||||||
#[post("/logout", format = "json", data = "<info>")]
|
#[post("/logout", format = "json", data = "<info>")]
|
||||||
pub fn logout(info: Json<LogoutEvent>, mut cookies: Cookies) -> JsonValue {
|
pub fn logout(info: Json<LogoutEvent>, mut cookies: Cookies) -> JsonValue {
|
||||||
let mut users: Vec<User> = db_read();
|
if let Some(mut user) = db_read_user(&info.name.to_lowercase()).ok().flatten() {
|
||||||
for i in 0..users.len() {
|
let token = match cookies.get_private("token") {
|
||||||
if info.name.to_lowercase() == users[i].name {
|
None => {
|
||||||
let token = match cookies.get_private("token") {
|
warn!("couldn't get token cookie!");
|
||||||
None => {
|
|
||||||
warn!("couldn't get token cookie!");
|
|
||||||
return json!({
|
|
||||||
"status": "fail",
|
|
||||||
"reason": "could not read cookie",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
Some(token) => token,
|
|
||||||
};
|
|
||||||
if token.value() == "NULL" {
|
|
||||||
warn!("NULL token!");
|
|
||||||
return json!({
|
return json!({
|
||||||
"status": "fail",
|
"status": "fail",
|
||||||
"reason": "NULL token",
|
"reason": "could not read cookie",
|
||||||
});
|
});
|
||||||
} else if token.value() == users[i].session_token {
|
},
|
||||||
|
Some(token) => token,
|
||||||
cookies.remove_private(Cookie::named("token"));
|
};
|
||||||
users[i].session_token = "NULL".to_string();
|
if token.value() == "NULL" {
|
||||||
info!("logged out user {}", info.name);
|
warn!("NULL token!");
|
||||||
return json!({
|
return json!({
|
||||||
"status": "ok",
|
"status": "fail",
|
||||||
"reason": "logged out",
|
"reason": "NULL token",
|
||||||
});
|
});
|
||||||
} else {
|
} else if token.value() == user.session_token {
|
||||||
warn!("token does not match! cannot logout");
|
cookies.remove_private(Cookie::named("token"));
|
||||||
return json!({
|
user.session_token = "NULL".to_string();
|
||||||
"status": "fail",
|
db_add(&user);
|
||||||
"reason": "token does not match",
|
info!("logged out user {}", info.name);
|
||||||
});
|
return json!({
|
||||||
}
|
"status": "ok",
|
||||||
|
"reason": "logged out",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
warn!("token does not match! cannot logout");
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "token does not match",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
warn!("failed to log out user {}, user not found", info.name);
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "user not found",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
warn!("logged out user {}, user not found", info.name);
|
|
||||||
return json!({
|
|
||||||
"status": "fail",
|
|
||||||
"reason": "user not found",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if pin matches user
|
// Check if pin matches user
|
||||||
#[get("/users/<name>/<pin>")]
|
#[post("/login", format = "json", data = "<data>")]
|
||||||
pub fn login(mut cookies: Cookies, name: String, pin: i32) -> JsonValue {
|
pub fn login(data: Json<LoginEvent>, mut cookies: Cookies) -> JsonValue {
|
||||||
let users: Vec<User> = db_read();
|
if let Some(user) = db_read_user(&data.name.to_lowercase()).ok().flatten() {
|
||||||
let hashed_pin_input = sha1::Sha1::from(&pin.to_string()).digest().to_string();
|
let hashed_pin_input = sha1::Sha1::from(&data.pin.to_string()).digest().to_string();
|
||||||
for i in &users {
|
|
||||||
// loop through the vector
|
|
||||||
if i.name == name.to_lowercase() {
|
|
||||||
if i.pin_hashed == hashed_pin_input {
|
|
||||||
info!("pin correct for user {}", i.name);
|
|
||||||
|
|
||||||
// Create token for user & set a cookie
|
if user.pin_hashed == hashed_pin_input { // check if pin hash matches
|
||||||
let token = create_token(i.name.clone(), users);
|
info!("pin correct for user {}", &user.name);
|
||||||
let cookie = Cookie::build("token", token)
|
|
||||||
.path("/")
|
|
||||||
.same_site(SameSite::Strict)
|
|
||||||
.secure(true)
|
|
||||||
.finish();
|
|
||||||
cookies.remove_private(Cookie::named("token"));
|
|
||||||
cookies.add_private(cookie);
|
|
||||||
info!("set the token cookie");
|
|
||||||
|
|
||||||
return json!({
|
// Create token for user & set a cookie
|
||||||
"status": "ok",
|
let token = create_token(user.name.clone(), user);
|
||||||
"reason": "pin matches",
|
let cookie = Cookie::build("token", token)
|
||||||
});
|
.path("/")
|
||||||
} else {
|
.finish();
|
||||||
cookies.remove_private(Cookie::named("token"));
|
cookies.remove_private(Cookie::named("token"));
|
||||||
info!("removed private cookie");
|
cookies.add_private(cookie);
|
||||||
warn!("pin incorrect for user {}", i.name);
|
info!("set the token cookie");
|
||||||
return json!({
|
|
||||||
"status": "fail",
|
return json!({
|
||||||
"reason": "incorrect pin",
|
"status": "ok",
|
||||||
});
|
"reason": "pin matches",
|
||||||
};
|
});
|
||||||
|
} else {
|
||||||
|
cookies.remove_private(Cookie::named("token"));
|
||||||
|
info!("removed private cookie");
|
||||||
|
warn!("pin incorrect for user {}", user.name);
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "incorrect pin",
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
cookies.remove_private(Cookie::named("token"));
|
||||||
|
info!("removed private cookie");
|
||||||
|
warn!(
|
||||||
|
"cannot check pin for user {} as they do not exist",
|
||||||
|
data.name.to_string().to_lowercase()
|
||||||
|
);
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": format!("user {} doesn't exist", data.name.to_string().to_lowercase()),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
cookies.remove_private(Cookie::named("token"));
|
|
||||||
info!("removed private cookie");
|
|
||||||
warn!(
|
|
||||||
"cannot check pin for user {} as they do not exist",
|
|
||||||
name.to_string().to_lowercase()
|
|
||||||
);
|
|
||||||
return json!({
|
|
||||||
"status": "fail",
|
|
||||||
"reason": format!("user {} doesn't exist", name.to_string().to_lowercase()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct ChangeEvent {
|
|
||||||
pub name: String,
|
|
||||||
pub pin: String,
|
|
||||||
pub changed_event: String,
|
|
||||||
pub new_event: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change info about a user
|
// Change info about a user
|
||||||
#[post("/users/change", format = "json", data = "<input>")]
|
#[post("/change", format = "json", data = "<input>")]
|
||||||
pub fn change_info(input: Json<ChangeEvent>, mut cookies: Cookies) -> JsonValue {
|
pub fn change_info(input: Json<ChangeEvent>, mut cookies: Cookies) -> JsonValue {
|
||||||
// read in the users & hash the pin
|
// read in the users & hash the pin
|
||||||
let mut users: Vec<User> = db_read();
|
let mut users: Vec<User> = db_read();
|
||||||
|
@ -274,134 +218,61 @@ pub fn change_info(input: Json<ChangeEvent>, mut cookies: Cookies) -> JsonValue
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop through the users
|
// find the user
|
||||||
for i in 0..users.len() {
|
if let Some(mut user) = db_read_user(&input.name).ok().flatten() {
|
||||||
if input.name.to_lowercase() == users[i].name { // if user found...
|
if token.value() == user.session_token { // & if token matches:
|
||||||
if token.value() == users[i].session_token { // & if token matches:
|
match input.changed_event {
|
||||||
if input.changed_event == "name" {
|
ChangeEventType::Name => {
|
||||||
|
// remove the user first
|
||||||
|
db_remove(&user);
|
||||||
// change the name
|
// change the name
|
||||||
users[i].name = input.new_event.clone();
|
user.name = input.new_event.clone();
|
||||||
info!("changed name of {} to {}", input.name, input.new_event);
|
info!("changed name of {} to {}", input.name, input.new_event);
|
||||||
db_write(&users);
|
db_add(&user);
|
||||||
return json!({
|
return json!({
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"reason": format!("changed name of {} to {}", input.name, input.new_event),
|
"reason": format!("changed name of {} to {}", input.name, input.new_event),
|
||||||
});
|
});
|
||||||
} else if input.changed_event == "pin" {
|
},
|
||||||
|
ChangeEventType::Pin => {
|
||||||
// change the pin
|
// change the pin
|
||||||
let new_hashed_pin = sha1::Sha1::from(&input.new_event).digest().to_string();
|
let new_hashed_pin = sha1::Sha1::from(&input.new_event).digest().to_string();
|
||||||
users[i].pin_hashed = new_hashed_pin.clone();
|
user.pin_hashed = new_hashed_pin.clone();
|
||||||
db_write(&users);
|
db_add(&user);
|
||||||
info!("changed pin of {}", input.name);
|
info!("changed pin of {}", input.name);
|
||||||
return json!({
|
return json!({
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"reason": "changed pin",
|
"reason": "changed pin",
|
||||||
});
|
});
|
||||||
} else if input.changed_event == "pronouns" {
|
},
|
||||||
|
ChangeEventType::Pronouns => {
|
||||||
// change the pronouns
|
// change the pronouns
|
||||||
users[i].pronouns = input.new_event.clone();
|
user.pronouns = input.new_event.clone();
|
||||||
info!("changed pronouns of {} to {}", input.name, input.new_event);
|
info!("changed pronouns of {} to {}", input.name, input.new_event);
|
||||||
db_write(&users);
|
db_add(&user);
|
||||||
return json!({
|
return json!({
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"reason": "successfully changed pronouns",
|
"reason": "successfully changed pronouns",
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
} else {
|
|
||||||
warn!("incorrect pin for user {}", input.name);
|
|
||||||
return json!({
|
|
||||||
"status": "fail",
|
|
||||||
"reason": "incorrect pin",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
} else {
|
||||||
};
|
warn!("incorrect pin for user {}", input.name);
|
||||||
warn!("couldn't change users info, user does not exist");
|
return json!({
|
||||||
return json!({
|
"status": "fail",
|
||||||
"status": "fail",
|
"reason": "incorrect pin",
|
||||||
"reason": "user doesn't exist",
|
});
|
||||||
});
|
};
|
||||||
}
|
} else {
|
||||||
|
warn!("couldn't change users info, user does not exist");
|
||||||
// Change a users pin/name
|
return json!({
|
||||||
#[post("/users/change/<name>/<pin>/<new_name>/<new_pin>")]
|
"status": "fail",
|
||||||
pub fn change(name: String, pin: i32, new_name: String, new_pin: i32) -> JsonValue {
|
"reason": "user doesn't exist",
|
||||||
let mut users: Vec<User> = db_read();
|
});
|
||||||
|
|
||||||
let hashed_pin_input = sha1::Sha1::from(&pin.to_string()).digest().to_string();
|
|
||||||
|
|
||||||
// Loop over elements in vector
|
|
||||||
for i in 0..users.len() {
|
|
||||||
if users[i].name == name.to_lowercase() {
|
|
||||||
// make sure name exists
|
|
||||||
if hashed_pin_input == users[i].pin_hashed {
|
|
||||||
// check if token is correct
|
|
||||||
// Check wether to change name or name+pin
|
|
||||||
if users[i].name == new_name.to_lowercase() {
|
|
||||||
// check if new name already exists
|
|
||||||
users[i].pin_hashed = sha1::Sha1::from(&new_pin.to_string()).digest().to_string();
|
|
||||||
/*
|
|
||||||
match write_json(&users) {
|
|
||||||
Err(why) => panic!("Cannot write to json! {}", why),
|
|
||||||
Ok(()) => info!("succesfully wrote to json file"),
|
|
||||||
}*/
|
|
||||||
db_write(&users);
|
|
||||||
info!("Changed pin of {}", name.to_string().to_lowercase());
|
|
||||||
return json!({
|
|
||||||
"status": "ok",
|
|
||||||
"reason": format!("changed {}'s pin", name.to_string().to_lowercase()),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// check if new name already exists
|
|
||||||
for n in &users {
|
|
||||||
if n.name == new_name.to_lowercase() {
|
|
||||||
warn!(
|
|
||||||
"Could not change name of {} to {}, as new name is already taken.",
|
|
||||||
name.to_lowercase(),
|
|
||||||
new_name.to_lowercase()
|
|
||||||
);
|
|
||||||
return json!({
|
|
||||||
"status": "fail",
|
|
||||||
"reason": format!("new name {} is already taken", new_name.to_lowercase()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
users[i].name = new_name.to_string().to_lowercase();
|
|
||||||
users[i].pin_hashed =
|
|
||||||
sha1::Sha1::from(&new_pin.to_string()).digest().to_string();
|
|
||||||
/*
|
|
||||||
match write_json(&users) {
|
|
||||||
Err(why) => panic!("couldn't write to json file! {}", why),
|
|
||||||
Ok(()) => info!("succesfully wrote to json file"),
|
|
||||||
}*/
|
|
||||||
db_write(&users);
|
|
||||||
info!(
|
|
||||||
"Changed name of {} to {}. New pin hash is {}",
|
|
||||||
name.to_string(),
|
|
||||||
users[i].name.to_string(),
|
|
||||||
users[i].pin_hashed.to_string()
|
|
||||||
);
|
|
||||||
return json!({
|
|
||||||
"status": "ok",
|
|
||||||
"reason": "successfully changed name and/or pin",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn!("Incorrect token for user {}!", name.to_string());
|
|
||||||
return json!({
|
|
||||||
"status": "fail",
|
|
||||||
"reason": "incorrect token for user",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
warn!(
|
|
||||||
"User {} not found, could not change pin and/or name.",
|
|
||||||
name.to_string()
|
|
||||||
);
|
|
||||||
return json!({
|
return json!({
|
||||||
"status": "fail",
|
"status": "fail",
|
||||||
"reason": format!("user {} not found", name.to_string().to_lowercase()),
|
"reason": "idk",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,6 +290,7 @@ pub fn get_user(name: String) -> JsonValue {
|
||||||
"user": {
|
"user": {
|
||||||
"name": user.name,
|
"name": user.name,
|
||||||
"pronouns": user.pronouns,
|
"pronouns": user.pronouns,
|
||||||
|
"role": user.role,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
None => json!({
|
None => json!({
|
||||||
|
@ -427,3 +299,59 @@ pub fn get_user(name: String) -> JsonValue {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* User Management */
|
||||||
|
#[post("/mod", format = "json", data = "<data>")]
|
||||||
|
pub fn moderation_actions(data: Json<ModerationAction>, mut cookies: Cookies) -> JsonValue {
|
||||||
|
let token = match cookies.get_private("token") {
|
||||||
|
None => {
|
||||||
|
warn!("couldn't get token cookie!");
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "could not read cookie",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Some(token) => token,
|
||||||
|
};
|
||||||
|
if let Some(user) = db_read_user(&data.name.to_lowercase()).ok().flatten() {
|
||||||
|
if token.value() == "NULL" { // fail if token is NULL
|
||||||
|
warn!("NULL token!");
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "NULL token",
|
||||||
|
});
|
||||||
|
} else if user.session_token == token.value() { // if token matches
|
||||||
|
if user.role == UserType::Normal {
|
||||||
|
match data.action {
|
||||||
|
ModActions::Kick => {
|
||||||
|
info!("kicked user {}", data.target)
|
||||||
|
},
|
||||||
|
ModActions::Ban => info!("banned user {}", data.target),
|
||||||
|
_ => info!("F"),
|
||||||
|
};
|
||||||
|
return json!({
|
||||||
|
"status": "ok",
|
||||||
|
"reason": "completed action",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
warn!("user does not have sufficient permissions to perform that action!");
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "insufficient permissions",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
warn!("token does not match!");
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "token does not match",
|
||||||
|
})
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
warn!("user not found");
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "user not found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
52
src/chat.rs
52
src/chat.rs
|
@ -4,7 +4,7 @@ use once_cell::sync::Lazy;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use crate::file_io::db_read;
|
use crate::file_io::db_read;
|
||||||
use rocket::http::{Cookie, Cookies};
|
use rocket::http::{Cookie, Cookies};
|
||||||
use crate::message::{Message, MessageInput};
|
use crate::message::{Message, MessageInput, MessageType};
|
||||||
use rocket_contrib::json::{Json, JsonValue};
|
use rocket_contrib::json::{Json, JsonValue};
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -23,35 +23,24 @@ pub fn fetch_messages() -> Json<Vec<Message>> {
|
||||||
|
|
||||||
// Create full message object and write to file
|
// Create full message object and write to file
|
||||||
fn create_message(message: Json<MessageInput>, file: &str, user: &User) -> JsonValue {
|
fn create_message(message: Json<MessageInput>, file: &str, user: &User) -> JsonValue {
|
||||||
|
let event_type = match message.body.chars().nth(0).unwrap() {
|
||||||
|
'/' => MessageType::Command,
|
||||||
|
':' => MessageType::Emote,
|
||||||
|
_ => MessageType::Normal,
|
||||||
|
};
|
||||||
|
|
||||||
// create full message object
|
// create full message object
|
||||||
// append message to file
|
|
||||||
|
|
||||||
// Create proper datetime format out of string
|
|
||||||
let date_split: Vec<&str> = message.date.split("-").collect();
|
|
||||||
|
|
||||||
let year: i32 = match date_split[0].trim().parse() { // extract year
|
|
||||||
Err(why) => panic!("could not extract year from given date: {}", why),
|
|
||||||
Ok(year) => year,
|
|
||||||
};
|
|
||||||
|
|
||||||
let month: u32 = match date_split[1].trim().parse() { // extract month
|
|
||||||
Err(why) => panic!("could not extract month from given date: {}", why),
|
|
||||||
Ok(month) => month,
|
|
||||||
};
|
|
||||||
|
|
||||||
let day: u32 = match date_split[2].trim().parse() { // extract day
|
|
||||||
Err(why) => panic!("could not extract day from given date: {}", why),
|
|
||||||
Ok(month) => month,
|
|
||||||
};
|
|
||||||
|
|
||||||
let date: DateTime<Utc> = Utc.ymd(year, month, day).and_hms(9, 10, 11);
|
|
||||||
let message_obj: Message = Message {
|
let message_obj: Message = Message {
|
||||||
id: Uuid::new_v4(),
|
id: Uuid::new_v4(),
|
||||||
|
event_type,
|
||||||
user: user.name.to_owned(),
|
user: user.name.to_owned(),
|
||||||
body: message.body.to_string(),
|
body: message.body.to_string(),
|
||||||
created_at: date,
|
created_at: Utc::now(),
|
||||||
};
|
};
|
||||||
info!("created mesage: {:?}", message_obj);
|
info!("created mesage: {:?}", message_obj);
|
||||||
|
info!("Date is: {}", message_obj.created_at.to_rfc2822());
|
||||||
|
|
||||||
|
// append message to file
|
||||||
let mut messages = MESSAGES.lock().unwrap();
|
let mut messages = MESSAGES.lock().unwrap();
|
||||||
messages.push(message_obj.to_owned());
|
messages.push(message_obj.to_owned());
|
||||||
return json!({
|
return json!({
|
||||||
|
@ -107,3 +96,20 @@ pub fn send_message(message: Json<MessageInput<'_>>, mut cookies: Cookies) -> Js
|
||||||
};
|
};
|
||||||
check_token(token, message)
|
check_token(token, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete a message
|
||||||
|
/*
|
||||||
|
#[post("/message/delete", format = "json", data = "<message>")]
|
||||||
|
pub fn delete_message(message: Json<MessageInput<'_>>, mut cookies: Cookies) -> JsonValue {
|
||||||
|
let token = match cookies.get_private("token") {
|
||||||
|
None => {
|
||||||
|
warn!("couldn't get token cookie!");
|
||||||
|
return json!({
|
||||||
|
"status": "fail",
|
||||||
|
"reason": "could not read cookie",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Some(token) => token,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
138
src/file_io.rs
138
src/file_io.rs
|
@ -1,123 +1,7 @@
|
||||||
use std::fs::{File, OpenOptions};
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::io::{self, BufRead};
|
|
||||||
use std::path::Path;
|
|
||||||
extern crate log;
|
extern crate log;
|
||||||
use crate::user::User;
|
use crate::user::User;
|
||||||
use serde_json::Result;
|
|
||||||
|
|
||||||
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
|
type MyErrorType = Box<dyn std::error::Error>;
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let file = File::open(filename)?;
|
|
||||||
Ok(io::BufReader::new(file).lines())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to read json from file into the vector
|
|
||||||
pub fn read_json() -> Vec<User> {
|
|
||||||
// Create path to file
|
|
||||||
let path = Path::new("users.json");
|
|
||||||
let display = path.display();
|
|
||||||
|
|
||||||
let mut users: Vec<User> = Vec::new(); // Create an empty vector of users
|
|
||||||
|
|
||||||
// Read through the lines and append them to the array
|
|
||||||
if let Ok(lines) = read_lines(&path) {
|
|
||||||
for line in lines {
|
|
||||||
if let Ok(user) = line {
|
|
||||||
info!("read {} from json file {}", display, &user);
|
|
||||||
// Parse line from file into a data structure
|
|
||||||
let user: User = serde_json::from_str(&user).unwrap();
|
|
||||||
users.push(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to append the last value of the users vector to the file
|
|
||||||
pub fn append_json(user: &User) -> Result<()> {
|
|
||||||
// Create a file to write to
|
|
||||||
let path = Path::new("users.json");
|
|
||||||
let display = path.display();
|
|
||||||
|
|
||||||
let mut file = match OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.append(true)
|
|
||||||
.open(&path)
|
|
||||||
{
|
|
||||||
Err(why) => panic!("couldn't create {}: {}", display, why),
|
|
||||||
Ok(file) => file,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Serialize the last user value
|
|
||||||
let user_json = serde_json::to_string(&user)?;
|
|
||||||
|
|
||||||
// Write to the file
|
|
||||||
match file.write_all(user_json.as_bytes()) {
|
|
||||||
Err(why) => panic!("couldn't write to {}: {}", display, why),
|
|
||||||
Ok(_) => info!("succesfully wrote to {}", display),
|
|
||||||
};
|
|
||||||
// Add newline
|
|
||||||
match file.write_all("\n".as_bytes()) {
|
|
||||||
Err(why) => panic!("couldn't write to {}: {}", display, why),
|
|
||||||
Ok(_) => info!("succesfully wrote newline to {}", display),
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to write whole vector of users to file
|
|
||||||
pub fn write_json(users_list: &Vec<User>) -> Result<()> {
|
|
||||||
// Create a file to write to
|
|
||||||
let path = Path::new("users.json");
|
|
||||||
let display = path.display();
|
|
||||||
|
|
||||||
let mut file = match OpenOptions::new().write(true).create(true).open(&path) {
|
|
||||||
Err(why) => panic!("couldn't create {}: {}", display, why),
|
|
||||||
Ok(file) => file,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut users_json = String::new();
|
|
||||||
for i in 0..users_list.len() {
|
|
||||||
// Serialize the users
|
|
||||||
users_json += &serde_json::to_string(&users_list[i])?;
|
|
||||||
if i != users_list.len()-1 {
|
|
||||||
// don't append newline if it's the last element
|
|
||||||
users_json += "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to the file
|
|
||||||
match file.write_all(users_json.as_bytes()) {
|
|
||||||
Err(why) => panic!("couldn't write to {}: {}", display, why),
|
|
||||||
Ok(_) => info!("succesfully wrote to {}", display),
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// test sled funtion
|
|
||||||
pub fn test_sled() {
|
|
||||||
// create test user
|
|
||||||
let user = User {
|
|
||||||
name: "erin".to_string(),
|
|
||||||
pin_hashed: "nyaa".to_string(),
|
|
||||||
pronouns: "she/her".to_string(),
|
|
||||||
session_token: "NULL".to_string(),
|
|
||||||
};
|
|
||||||
// open database
|
|
||||||
let db: sled::Db = sled::open("my_db").unwrap();
|
|
||||||
let bytes = bincode::serialize(&user).unwrap();
|
|
||||||
db.insert(&user.name, bytes).unwrap();
|
|
||||||
match db.get(user.name).unwrap() {
|
|
||||||
Some(bytes) => {
|
|
||||||
let read_user: User = bincode::deserialize(&bytes).unwrap();
|
|
||||||
println!("username: {}, pronouns: {}", read_user.name, read_user.pronouns);
|
|
||||||
},
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add a user to the database
|
// add a user to the database
|
||||||
pub fn db_add(user: &User) {
|
pub fn db_add(user: &User) {
|
||||||
|
@ -138,6 +22,12 @@ pub fn db_write(users_list: &Vec<User>) {
|
||||||
info!("wrote all users to db");
|
info!("wrote all users to db");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove a user from the database
|
||||||
|
pub fn db_remove(user: &User) {
|
||||||
|
let db: sled::Db = sled::open("users_db").unwrap();
|
||||||
|
db.remove(&user.name);
|
||||||
|
}
|
||||||
|
|
||||||
// read all users from the database
|
// read all users from the database
|
||||||
pub fn db_read() -> Vec<User> {
|
pub fn db_read() -> Vec<User> {
|
||||||
let db: sled::Db = sled::open("users_db").unwrap();
|
let db: sled::Db = sled::open("users_db").unwrap();
|
||||||
|
@ -149,3 +39,17 @@ pub fn db_read() -> Vec<User> {
|
||||||
}
|
}
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read one user from the database
|
||||||
|
pub fn db_read_user(user: &str) -> std::result::Result<Option<User>, MyErrorType> {
|
||||||
|
let db: sled::Db = sled::open("users_db")?;
|
||||||
|
let entry = db.get(user)?;
|
||||||
|
if let Some(user_entry) = entry {
|
||||||
|
let read_user: User = bincode::deserialize(&user_entry)?;
|
||||||
|
info!("read user {} from db", read_user.name);
|
||||||
|
Ok(Some(read_user))
|
||||||
|
} else {
|
||||||
|
warn!("user {} not found in db!", user);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -28,16 +28,15 @@ fn main() {
|
||||||
.mount(
|
.mount(
|
||||||
"/api",
|
"/api",
|
||||||
routes![
|
routes![
|
||||||
auth::index,
|
|
||||||
auth::get_user,
|
auth::get_user,
|
||||||
auth::register_user,
|
auth::register,
|
||||||
auth::login,
|
auth::login,
|
||||||
auth::change,
|
|
||||||
chat::send_message,
|
chat::send_message,
|
||||||
chat::fetch_messages,
|
chat::fetch_messages,
|
||||||
auth::change_info,
|
auth::change_info,
|
||||||
auth::check_token,
|
auth::check_token,
|
||||||
auth::logout
|
auth::logout,
|
||||||
|
auth::moderation_actions
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.mount("/", StaticFiles::from("frontend"))
|
.mount("/", StaticFiles::from("frontend"))
|
||||||
|
|
|
@ -7,12 +7,21 @@ use uuid::Uuid;
|
||||||
pub struct MessageInput<'r> {
|
pub struct MessageInput<'r> {
|
||||||
pub name: &'r str,
|
pub name: &'r str,
|
||||||
pub body: &'r str,
|
pub body: &'r str,
|
||||||
pub date: &'r str,
|
pub date: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum MessageType {
|
||||||
|
Normal,
|
||||||
|
Announcement,
|
||||||
|
Emote,
|
||||||
|
Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
|
pub event_type: MessageType,
|
||||||
pub user: String,
|
pub user: String,
|
||||||
pub body: String,
|
pub body: String,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
|
|
75
src/user.rs
75
src/user.rs
|
@ -1,11 +1,76 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/* User Data */
|
||||||
|
// enum of different user types
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
|
pub enum UserType {
|
||||||
|
Normal,
|
||||||
|
Moderator,
|
||||||
|
Admin,
|
||||||
|
}
|
||||||
|
|
||||||
// Struct to store basic user data
|
// Struct to store basic user data
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub name: String,
|
pub name: String, // unique username
|
||||||
pub pin_hashed: String,
|
pub pin_hashed: String, // sha1 hash of the pin
|
||||||
pub pronouns: String,
|
pub pronouns: String, // user's pronouns
|
||||||
#[serde(rename = "sessionToken")]
|
pub session_token: String, // generated session token
|
||||||
pub session_token: String,
|
pub role: UserType, // type/role of user
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Moderation Data */
|
||||||
|
// enum of different moderator actions
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub enum ModActions {
|
||||||
|
Kick, // Log the user out of their current session
|
||||||
|
Ban, // Remove the user
|
||||||
|
Demote, // Demote a user to a lower role
|
||||||
|
Premote, // Premote a user to a higher role
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct to use for json input
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct ModerationAction {
|
||||||
|
pub name: String, // name of the moderator
|
||||||
|
pub action: ModActions, // what action to take
|
||||||
|
pub target: String, // who to take the action on
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Miscellaneous Events */
|
||||||
|
// logout event struct
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct LogoutEvent {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// register event struct
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct RegisterEvent {
|
||||||
|
pub name: String,
|
||||||
|
pub pin: String,
|
||||||
|
pub pronouns: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// login event struct
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct LoginEvent {
|
||||||
|
pub name: String,
|
||||||
|
pub pin: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// change event type
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub enum ChangeEventType {
|
||||||
|
Name,
|
||||||
|
Pin,
|
||||||
|
Pronouns,
|
||||||
|
}
|
||||||
|
|
||||||
|
// change info event struct
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct ChangeEvent {
|
||||||
|
pub name: String, // name of the user
|
||||||
|
pub changed_event: ChangeEventType, // which event to change
|
||||||
|
pub new_event: String, // the new value for the event
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue