freechat/json-api/db/src/member.rs
shockrah 975acfd606 Backend REST API changes:
* Fixed weird match with get_online_members
+ Now using special(smoll) Public Member struct for public member data fetches as.
They're size on networks should be pretty small so we can package a ton of them in a single request

DOCS changes:
- Removing references to unix timestamps in seconds
- Removing references to joindate
* Exact content-type values now specified
2021-03-20 22:44:22 -07:00

184 lines
6.4 KiB
Rust

use mysql_async::{params, Pool, Conn};
use mysql_async::prelude::Queryable;
use mysql_async::error::Error as SqlError;
use crate::{Response, no_conn, sql_err};
use crate::{UBigInt, Integer, VarChar};
use crate::{PublicMember, Member};
pub const STATUS_ONLINE: Integer = 0;
pub const STATUS_OFFLINE: Integer = 1;
pub const STATUS_AWAY: Integer = 2;
pub const STATUS_DO_NOT_DISTURB: Integer = 3;
/*
*
* conn = getconn
* result = conn.query
* return response based on result
*
*/
impl Member {
pub async fn get(p: &Pool, id: UBigInt) -> Response<Self> {
//! @returns Row on success
//! @returns Other on failure
if let Ok(conn) = p.get_conn().await {
let q = "SELECT secret, name, status, permissions FROM members WHERE id = :id";
type Row = Option<(VarChar, VarChar, Integer, UBigInt)>;
let db_res : Result<(Conn, Row), SqlError>
= conn.first_exec(q, params!{"id" => id}).await;
if let Ok((_, row)) = db_res {
return match row {
Some(row) => Response::Row(Self {
id,
secret: row.0,
name: row.1,
status: row.2,
permissions: row.3
}),
None => Response::Empty
}
}
return Response::Other(sql_err!("Fetch failed"));
}
return Response::Other(no_conn!("Member::FromDB::get"));
}
pub async fn filter(p: &Pool, status: Integer) -> Response<Self> {
//! @params status
return match (p.get_conn().await, status) {
(Ok(conn), STATUS_ONLINE) | (Ok(conn), STATUS_OFFLINE) |
(Ok(conn), STATUS_AWAY) | (Ok(conn), STATUS_DO_NOT_DISTURB) => {
// TODO: Allow people to query somewhere in the middle of this set
// i.e. we should be able to get user 100 -> 150 instead of just the
// first 1000 people
let q =
"SELECT id, name, status, permissions FROM members
WHERE status = :stat LIMIT 100"; // high limit for now
if let Ok(query) = conn.prep_exec(q, params!{"stat" => status}).await {
let mapping_r = query.map_and_drop(|row| {
let (id, name, status, permissions): (UBigInt, VarChar, Integer, UBigInt) =
mysql_async::from_row(row);
Member {
id,
secret: "".into(), // no show for obv reasons
name,
status,
permissions
}
}).await;
match mapping_r {
Ok((_, members)) => Response::Set(members),
Err(_) => Response::Other(sql_err!("db::Members::filter"))
}
}
else {
Response::Other(sql_err!("Initial query faile: db::Members::filter"))
}
}
_ => Response::Other(sql_err!("err"))
}
}
pub async fn add(p: &Pool, name: &str, secret: &str, perms: u64) -> Result<Response<Self>, SqlError> {
//! @param {pool} p
//! @param {&str} name of new user
//! @param {&str} encrypted secret : userland auth module should have a function for this
//! @param {u64} permissions mask
//! @returns : on_succes => Ok(Response<Member>)
//! @returns : on_partial_succes => Ok(Response<Member>)
//! @returns : on_failure => Err(SomeBS)
let conn = p.get_conn().await?;
let conn = conn.drop_exec(
"INSERT INTO members(secret, name, status, permissions)
VALUES(:secret, :name, :status, :permissions)",
mysql_async::params!{
"secret" => secret.clone(),
"name" => name.clone(),
"status" => STATUS_ONLINE,
"permissions" => perms
}).await?;
let (_, opt_id): (Conn, Option<UBigInt>) = conn.first_exec(
"SELECT id FROM members WHERE secret = :secret",
params!{
"secret" => secret.clone()
}).await?;
if let Some(id) = opt_id {
return Ok(Response::Row(Self {
id,
secret: secret.into(),
name: name.into(),
status: STATUS_ONLINE,
permissions: perms
}))
}
return Ok(Response::Empty);
}
pub async fn update_perms(p: &Pool, uid: UBigInt, permissions: UBigInt) -> Result<UBigInt, SqlError> {
//! @return on_sucess Ok(NewPermisionsMask)
//! @throws Err(SqlError)
let conn = p.get_conn().await?;
conn.drop_exec(
"UPDATE members SET permissions = :perms WHERE id = :id",
params!{
"id" => uid,
"perms" => permissions
}).await?;
Ok(permissions)
}
pub async fn update_nick(p: &Pool, uid: UBigInt, new_nick: &str) -> Result<(), SqlError> {
let conn = p.get_conn().await?;
conn.drop_exec(
"UPDATE members SET name = :nick WHERE id = id",
params!{
"id" => uid,
"nick" => new_nick
}).await?;
Ok(())
}
}
impl PublicMember {
pub async fn get_online(p: &Pool, status: Integer) -> Result<Response<Self>, SqlError> {
let valid_status = status == STATUS_ONLINE ||
status == STATUS_AWAY ||
status == STATUS_OFFLINE ||
status == STATUS_DO_NOT_DISTURB;
if !valid_status {
return Ok(Response::RestrictedInput(format!("Invalid status value")));
} else {
let conn = p.get_conn().await?;
let q = "SELECT id, name, permissions FROM members
WHERE status = :status LIMIT 1000";
let result = conn.prep_exec(q, params!{"status" => status}).await?;
let (_, data): (_, Vec<PublicMember>) = result.map_and_drop(|row| {
let (id, name, permissions): (UBigInt, VarChar, UBigInt) =
mysql_async::from_row(row);
PublicMember { id, name, permissions, status }
}).await?;
return Ok(Response::Set(data));
}
}
}