aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--README.md54
-rw-r--r--TODO2
-rw-r--r--src/database.rs84
-rw-r--r--src/database/types.rs79
-rw-r--r--src/main.rs18
7 files changed, 223 insertions, 18 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 00b2820..7ad81ff 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1143,7 +1143,7 @@ dependencies = [
[[package]]
name = "rocket_test"
-version = "0.2.4"
+version = "0.2.5"
dependencies = [
"chrono",
"rocket",
diff --git a/Cargo.toml b/Cargo.toml
index 7ca56e2..1ef8bfc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rocket_test"
-version = "0.2.4"
+version = "0.2.5"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/README.md b/README.md
index 58f9989..f4e1002 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,13 @@ DELETE `/delete_user`
- Return type: JSON
- Returns: `Result`
+POST `set_user_data`
+ Sets data in the user fields
+- Data type: JSON
+- Data: `{user: UID, field: ReceiveDataField, data: String}`
+- Return type: JSON
+- Returns: `Result`
+
GET `/get_user/<id>`
Gets a user from id
- Return type: JSON
@@ -60,10 +67,7 @@ GET `/get_message_list/<from_id>/<to_id>`
POST `/send_message`
Sends a message
- Data type: JSON
-- Data:
- - `"sender": u64`
- - `"message": String`
- - `"reply_to": Option<u64>`
+- Data: ReceiveMessage
- Return type: JSON
- Returns: `UID` of message
@@ -72,4 +76,44 @@ DELETE `/delete_message`
- Data type: JSON
- Data: `UID`
- Return type: JSON
-- Returns: `Result` \ No newline at end of file
+- Returns: `Result`
+
+# Types
+- ReceiveDataField
+ - DisplayName
+ - StatusText
+ - Desc
+
+- ReceiveMessage
+ - message: String
+ - sender: UID
+ - reply_to: Option<UID>
+
+- UID
+ - u64
+
+- Date
+ - u64 (date since epoch in UTC)
+
+- Status
+ - Online
+ - Offline
+ - Away
+ - Busy
+
+- User
+ - id: UID
+ - username: String (Max 20 chars)
+ - displayname: String (Max 30 chars)
+ - desc: String (Max 500 chars)
+ - statustext: String (Max 60 chars)
+ - status: Status
+ - deleted: bool
+
+- Message
+ - id: UID
+ - message: String (Max 6000 chars)
+ - reply_to: UID (of another message)
+ - deleted: bool
+ - date: Date
+ - sender: UID (of a User) \ No newline at end of file
diff --git a/TODO b/TODO
index f997783..33dd38b 100644
--- a/TODO
+++ b/TODO
@@ -10,4 +10,4 @@ Config
/api - list functions/routes
-Delete is a bit funky - doesn't always report correctly \ No newline at end of file
+Delete + set_set_data is a bit funky - doesn't always report correctly \ No newline at end of file
diff --git a/src/database.rs b/src/database.rs
index 0faa0d3..74d9c1a 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -168,21 +168,90 @@ impl Database {
}
}
+ pub fn set_user_field(&self, f: DataField, user: UID, data: String) -> Result<&'static str, &'static str> {
+ let query = format!("UPDATE users SET {}=:data WHERE id IS :id AND deleted IS false", f);
+ let statement = self.db.prepare(query).unwrap().into_iter()
+ .bind::<&[(_, sqlite::Value)]>(&[
+ (":id", user.into()),
+ (":data", data.into()),
+ ]);
+
+ let change_count = self.db.change_count();
+
+ for x in statement.unwrap() {
+ match x {
+ Ok(_) => (),
+ Err(n) => {
+ match n.code.unwrap_or(0) {
+ _ => return Err("Unknown error")
+ }
+ },
+ }
+ }
+
+ if change_count > 0 {
+ Ok("Updated")
+ } else {
+ Err("Unabled to update")
+ }
+ }
+
+ pub fn set_user_status(&self, user: UID, status: Status) -> Result<&'static str, &'static str> {
+ let query = "UPDATE users SET status=:status WHERE id IS :id AND deleted IS false";
+ let statement = self.db.prepare(query).unwrap().into_iter()
+ .bind::<&[(_, sqlite::Value)]>(&[
+ (":id", user.into()),
+ (":status", i64::from(status).into()),
+ ]);
+
+ let change_count = self.db.change_count();
+
+ for x in statement.unwrap() {
+ match x {
+ Ok(_) => (),
+ Err(n) => {
+ match n.code.unwrap_or(0) {
+ _ => return Err("Unknown error")
+ }
+ },
+ }
+ }
+
+ if change_count > 0 {
+ Ok("Updated")
+ } else {
+ Err("Unabled to update")
+ }
+ }
+
pub fn get_user(&self, id: UID) -> Option<User> {
let query = "SELECT * FROM users WHERE id IS :id";
let statement = self.db.prepare(query).unwrap().into_iter().bind::<&[(_, sqlite::Value)]>(&[(":id", id.into())]).unwrap();
for row in statement.map(|row| row.unwrap()) {
- let username = row.read::<&str, _>("username");
+ let username = row.read::<&str, _>("username").to_string();
let id = row.read::<i64, _>("id").into();
+ let desc = match row.read::<Option<&str>, _>("desc") {
+ Some(n) => n.to_string(),
+ None => "".to_string(),
+ };
+ let displayname = match row.read::<Option<&str>, _>("displayname") {
+ Some(n) => n.to_string(),
+ None => "".to_string(),
+ };
+ let statustext = match row.read::<Option<&str>, _>("statustext") {
+ Some(n) => n.to_string(),
+ None => "".to_string(),
+ };
+ let status = row.read::<i64, _>("status");
let deleted = if row.read::<i64, _>("deleted") > 0 {
true
} else {
false
};
- return Some(User::construct(String::from(username), id, deleted))
+ return Some(User::construct(String::from(username), id, deleted, desc, displayname, statustext, status))
}
None
@@ -213,7 +282,7 @@ impl Database {
let id: UID = self.get_user_count().into();
- let query = "INSERT INTO users (id, username, deleted) VALUES (:id, :name, false)";
+ let query = "INSERT INTO users (id, username, deleted, status, displayname) VALUES (:id, :name, false, 0, :name)";
let statement = self.db.prepare(query).unwrap().into_iter()
.bind::<&[(_, sqlite::Value)]>(&[(":id", id.into()),(":name", name.into())]);
@@ -291,13 +360,18 @@ impl Database {
};
// Setup the db
- let query = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY NOT NULL, username UNIQUE NOT NULL, deleted BOOL NOT NULL) WITHOUT ROWID";
+ match db.execute("PRAGMA foreign_keys = ON") {
+ Ok(_) => (),
+ Err(_) => panic!("Could not enable foreign_keys"),
+ };
+
+ let query = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY NOT NULL, username TEXT UNIQUE NOT NULL CHECK (length(username) <= 20), deleted BOOL NOT NULL, displayname TEXT CHECK (length(displayname) <= 30), desc TEXT CHECK (length(desc) <= 500), statustext TEXT CHECK (length(statustext) <= 60), status INTEGER NOT NULL) WITHOUT ROWID";
match db.execute(query) {
Ok(_) => (),
Err(n) => panic!("{n}"),
}
- let query = "CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY NOT NULL, sender INTEGER NOT NULL, date INTEGER NOT NULL, message TEXT, reply_to INTEGER, deleted BOOL NOT NULL) WITHOUT ROWID";
+ let query = "CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY NOT NULL, sender INTEGER NOT NULL, date INTEGER NOT NULL, message TEXT CHECK (length(message) <= 6000), reply_to INTEGER, deleted BOOL NOT NULL, FOREIGN KEY (sender) REFERENCES users(id), FOREIGN KEY (reply_to) REFERENCES messages(id)) WITHOUT ROWID";
match db.execute(query) {
Ok(_) => (),
Err(n) => panic!("{n}"),
diff --git a/src/database/types.rs b/src/database/types.rs
index 96efd7b..4656e49 100644
--- a/src/database/types.rs
+++ b/src/database/types.rs
@@ -28,26 +28,91 @@ impl std::convert::From<Date> for sqlite::Value {
}
}
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+pub enum Status {
+ Offline,
+ Online,
+ Away,
+ Busy,
+}
+impl std::convert::From<Status> for i64 {
+ fn from(s: Status) -> i64 {
+ match s {
+ Status::Offline => 0,
+ Status::Online => 1,
+ Status::Away => 2,
+ Status::Busy => 3,
+ }
+ }
+}
+impl std::convert::From<i64> for Status {
+ fn from(u: i64) -> Status {
+ match u {
+ 1 => Status::Online,
+ 2 => Status::Away,
+ 3 => Status::Busy,
+ _ => Status::Offline
+ }
+ }
+}
+
+#[derive(Deserialize, Copy, Clone)]
+pub enum DataField {
+ Desc,
+ StatusText,
+ DisplayName,
+}
+impl std::convert::From<DataField> for &str {
+ fn from(f: DataField) -> &'static str {
+ match f {
+ DataField::Desc => "desc",
+ DataField::StatusText => "statustext",
+ DataField::DisplayName => "displayname",
+ }
+ }
+}
+impl std::fmt::Display for DataField {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ DataField::Desc => write!(f, "desc"),
+ DataField::StatusText => write!(f, "statustext"),
+ DataField::DisplayName => write!(f, "displayname"),
+ }
+ }
+}
+
#[derive(Serialize, Clone, Debug)]
pub struct User {
- username: String,
+ username: String, //Limit 20
id: UID,
deleted: bool,
+ desc: String, // Limit 500
+ displayname: String, // Limit 30
+ statustext: String, //Limit 60
+ status: Status,
}
impl User {
pub fn new(username: String, id: UID) -> User {
User {
- username,
+ username: username.clone(),
id,
deleted: false,
+ desc: String::from(""),
+ displayname: username,
+ statustext: String::from(""),
+ status: Status::Offline,
}
}
- pub fn construct(username: String, id: UID, deleted: bool) -> User {
+ pub fn construct(username: String, id: UID, deleted: bool, desc: String, displayname: String, statustext: String, status: i64) -> User {
User {
username,
id,
deleted,
+ desc,
+ displayname,
+ statustext,
+ status: Status::from(status),
}
}
@@ -163,4 +228,10 @@ impl ReceiveMessage {
date: Date::now(),
}
}
-} \ No newline at end of file
+}
+
+#[derive(Deserialize, Clone)]
+pub struct ReceiveUpdateField {pub user: UID, pub field: DataField, pub data: String}
+
+#[derive(Deserialize, Clone)]
+pub struct ReceiveStatus {pub user: UID, pub status: Status} \ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 1955963..af0234a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,7 +5,7 @@ mod database;
use rocket::serde::json::Json;
use rocket::State;
use std::sync::Mutex;
-use database::types::{Message, User, Info, UID, ReceiveMessage};
+use database::types::*;
struct SharedDB {
@@ -119,6 +119,20 @@ fn delete_user(id: Json<UID>, db: &State<SharedDB>) -> Json<Response<&'static st
Json(Response(lock.delete_user(id.0)))
}
+#[post("/set_user_data", format = "application/json", data = "<data>")]
+fn set_user_data(data: Json<ReceiveUpdateField>, db: &State<SharedDB>) -> Json<Response<&'static str, &'static str>> {
+ let lock = db.sdb.lock().unwrap();
+
+ Json(Response(lock.set_user_field(data.field, data.user, data.data.clone())))
+}
+
+#[post("/set_user_status", format = "application/json", data = "<data>")]
+fn set_user_status(data: Json<ReceiveStatus>, db: &State<SharedDB>) -> Json<Response<&'static str, &'static str>> {
+ let lock = db.sdb.lock().unwrap();
+
+ Json(Response(lock.set_user_status(data.user, data.status)))
+}
+
#[get("/ping")]
fn ping() -> Json<Response<&'static str, &'static str>> {
Json(Response(Ok("pong")))
@@ -151,6 +165,8 @@ fn rocket() -> _ {
delete_message,
delete_user,
get_message_list,
+ set_user_data,
+ set_user_status,
])
.mount("/", routes![api_index])
.manage(SharedDB{sdb: Mutex::new(database::Database::new())})