Add basic user adding functionality
What works: - adding users - default settings - detect duplicate usernames
This commit is contained in:
parent
114bb63f45
commit
1ea3e5260c
608
Cargo.lock
generated
608
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
208
src/graphql.rs
208
src/graphql.rs
@ -1,10 +1,10 @@
|
|||||||
use juniper::{
|
use juniper::{
|
||||||
EmptyMutation,
|
|
||||||
EmptySubscription,
|
EmptySubscription,
|
||||||
ID,
|
ID,
|
||||||
RootNode,
|
RootNode,
|
||||||
graphql_object,
|
graphql_object,
|
||||||
GraphQLObject,
|
GraphQLObject,
|
||||||
|
GraphQLInputObject,
|
||||||
GraphQLEnum,
|
GraphQLEnum,
|
||||||
FieldResult,
|
FieldResult,
|
||||||
FieldError,
|
FieldError,
|
||||||
@ -28,35 +28,28 @@ use sqlx::{
|
|||||||
sqlite::SqliteRow,
|
sqlite::SqliteRow,
|
||||||
};
|
};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
fn into_user_limit(
|
fn into_user_limit(
|
||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
row: &SqliteRow,
|
row: &SqliteRow,
|
||||||
index: &str
|
index: &str
|
||||||
) -> SqlxResult<Option<Vec<String>>> {
|
) -> SqlxResult<Vec<String>> {
|
||||||
let row_value: Option<&str> = row.try_get(index)?;
|
let row_value: &str = row.try_get(index)?;
|
||||||
if let Some(s) = row_value {
|
into_toml_iter(row_value, index).map(|i| i.collect())
|
||||||
into_toml_iter(s, index).map(|i| Some(i.collect()))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_server_limit(
|
fn into_server_limit(
|
||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
row: &SqliteRow,
|
row: &SqliteRow,
|
||||||
index: &str
|
index: &str
|
||||||
) -> SqlxResult<Option<Vec<Url>>> {
|
) -> SqlxResult<Vec<Url>> {
|
||||||
let row_value: Option<&str> = row.try_get(index)?;
|
let row_value: &str = row.try_get(index)?;
|
||||||
if let Some(s) = row_value {
|
|
||||||
let mut url_vector = Vec::new();
|
let mut url_vector = Vec::new();
|
||||||
for s in into_toml_iter(s, index)? {
|
for s in into_toml_iter(row_value, index)? {
|
||||||
url_vector.push(Url::parse(s).map_err(into_column_decode_err(index))?);
|
url_vector.push(Url::parse(s).map_err(into_column_decode_err(index))?);
|
||||||
}
|
}
|
||||||
Ok(Some(url_vector))
|
Ok(url_vector)
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -93,6 +86,11 @@ where
|
|||||||
into_toml_iter(row.try_get(index)?, index)
|
into_toml_iter(row.try_get(index)?, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn id_to_uuid(id: &ID) -> Result<Uuid, uuid::Error> {
|
||||||
|
use std::str::FromStr;
|
||||||
|
Uuid::from_str(&id)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
@ -103,13 +101,14 @@ impl juniper::Context for Context {}
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Decode, Type)]
|
#[derive(Clone, Debug, Decode, Type)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
id: ID,
|
// can we change this to Uuid?
|
||||||
user_name: String,
|
pub id: ID,
|
||||||
display_name: Option<String>,
|
pub user_name: String,
|
||||||
activated: bool,
|
pub display_name: Option<String>,
|
||||||
created: DateTime<Utc>,
|
pub activated: bool,
|
||||||
last_online: Option<DateTime<Utc>>,
|
pub created: DateTime<Utc>,
|
||||||
preferences: UserPreferences,
|
pub last_online: Option<DateTime<Utc>>,
|
||||||
|
pub preferences: UserPreferences,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql_object(context = Context)]
|
#[graphql_object(context = Context)]
|
||||||
@ -163,6 +162,36 @@ impl User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, GraphQLInputObject)]
|
||||||
|
pub struct NewUser {
|
||||||
|
pub(crate) user_name: String,
|
||||||
|
pub(crate) display_name: Option<String>,
|
||||||
|
pub(crate) password_hash: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NewUser> for User {
|
||||||
|
fn from(new_user: NewUser) -> Self {
|
||||||
|
User {
|
||||||
|
// TODO: how do we generate uuids?
|
||||||
|
id: ID::new(Uuid::new_v4().to_string()),
|
||||||
|
user_name: new_user.user_name,
|
||||||
|
display_name: new_user.display_name,
|
||||||
|
activated: false,
|
||||||
|
created: Utc::now(),
|
||||||
|
last_online: None,
|
||||||
|
preferences: UserPreferences {
|
||||||
|
privacy_preferences: Default::default(),
|
||||||
|
security_preferences: SecurityPreferences {
|
||||||
|
account_tokens: Vec::new(),
|
||||||
|
password_hash: new_user.password_hash,
|
||||||
|
},
|
||||||
|
notification_preferences: Default::default(),
|
||||||
|
external_servers_preferences: Default::default(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, GraphQLObject, Decode, Encode, FromRow)]
|
#[derive(Clone, Debug, GraphQLObject, Decode, Encode, FromRow)]
|
||||||
pub struct UserPreferences {
|
pub struct UserPreferences {
|
||||||
privacy_preferences: PrivacyPreferences,
|
privacy_preferences: PrivacyPreferences,
|
||||||
@ -200,15 +229,33 @@ impl UserPreferences {
|
|||||||
#[derive(Clone, Debug, GraphQLObject)]
|
#[derive(Clone, Debug, GraphQLObject)]
|
||||||
pub struct PrivacyPreferences {
|
pub struct PrivacyPreferences {
|
||||||
discovery: RestrictionPolicy,
|
discovery: RestrictionPolicy,
|
||||||
discovery_user_limit: Option<Vec<String>>,
|
discovery_user_limit: Vec<String>,
|
||||||
discovery_server_limit: Option<Vec<Url>>,
|
discovery_server_limit: Vec<Url>,
|
||||||
last_seen: RestrictionPolicy,
|
last_seen: RestrictionPolicy,
|
||||||
last_seen_user_limit: Option<Vec<String>>,
|
last_seen_user_limit: Vec<String>,
|
||||||
last_seen_server_limit: Option<Vec<Url>>,
|
last_seen_server_limit: Vec<Url>,
|
||||||
last_seen_course: bool,
|
last_seen_course: bool,
|
||||||
info: RestrictionPolicy,
|
info: RestrictionPolicy,
|
||||||
info_user_limit: Option<Vec<String>>,
|
info_user_limit: Vec<String>,
|
||||||
info_server_limit: Option<Vec<Url>>,
|
info_server_limit: Vec<Url>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PrivacyPreferences {
|
||||||
|
fn default() -> Self {
|
||||||
|
PrivacyPreferences {
|
||||||
|
discovery: Default::default(),
|
||||||
|
discovery_user_limit: Default::default(),
|
||||||
|
discovery_server_limit: Default::default(),
|
||||||
|
last_seen: Default::default(),
|
||||||
|
last_seen_user_limit: Default::default(),
|
||||||
|
last_seen_server_limit: Default::default(),
|
||||||
|
last_seen_course: true,
|
||||||
|
info: Default::default(),
|
||||||
|
info_user_limit: Default::default(),
|
||||||
|
info_server_limit: Default::default(),
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrivacyPreferences {
|
impl PrivacyPreferences {
|
||||||
@ -238,7 +285,7 @@ impl FromRow<'_, SqliteRow> for PrivacyPreferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, GraphQLObject, Decode, Encode, FromRow)]
|
#[derive(Clone, Copy, Debug, Default, GraphQLObject, Decode, Encode, FromRow)]
|
||||||
pub struct NotificationPreferences {
|
pub struct NotificationPreferences {
|
||||||
lock_details: bool,
|
lock_details: bool,
|
||||||
do_not_disturb: bool,
|
do_not_disturb: bool,
|
||||||
@ -271,11 +318,11 @@ impl FromRow<'_, SqliteRow> for SecurityPreferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, GraphQLObject, Decode, Encode, FromRow)]
|
#[derive(Clone, Debug, Default, GraphQLObject, Decode, Encode, FromRow)]
|
||||||
pub struct ExternalServersPreferences {
|
pub struct ExternalServersPreferences {
|
||||||
privacy_preferences: PrivacyPreferences,
|
privacy_preferences: PrivacyPreferences,
|
||||||
external_servers: RestrictionPolicy,
|
external_servers: RestrictionPolicy,
|
||||||
external_servers_limit: Option<Vec<Url>>,
|
external_servers_limit: Vec<Url>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExternalServersPreferences {
|
impl ExternalServersPreferences {
|
||||||
@ -303,6 +350,12 @@ pub enum RestrictionPolicy {
|
|||||||
None = 3,
|
None = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for RestrictionPolicy {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Everyone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Query;
|
pub struct Query;
|
||||||
|
|
||||||
@ -329,8 +382,95 @@ impl Query {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Schema<'root_node> = RootNode<'root_node, Query, EmptyMutation<Context>, EmptySubscription<Context>>;
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Mutation;
|
||||||
|
|
||||||
|
#[graphql_object(context = Context)]
|
||||||
|
impl Mutation {
|
||||||
|
async fn createUser(context: &Context, new_user: NewUser) -> FieldResult<String> {
|
||||||
|
let user: User = new_user.into();
|
||||||
|
|
||||||
|
if sqlx::query(
|
||||||
|
format!(r#"SELECT id FROM users WHERE user_name="{}""#, user.user_name).as_str()
|
||||||
|
).fetch_all(&context.db).await?.len() > 0 {
|
||||||
|
return Err("username is already in use".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlx::query(format!(
|
||||||
|
r#"INSERT INTO users VALUES ("{}", "{}", {}, {}, {}, {})"#,
|
||||||
|
id_to_uuid(&user.id)?.to_simple(),
|
||||||
|
user.user_name,
|
||||||
|
user.display_name.map(|x| format!(r#""{}""#, x)).unwrap_or("NULL".to_string()),
|
||||||
|
user.activated as u8,
|
||||||
|
user.created.timestamp(),
|
||||||
|
match user.last_online {
|
||||||
|
None => "NULL".to_string(),
|
||||||
|
Some(d) => format!("{}", d.timestamp()),
|
||||||
|
},
|
||||||
|
).as_str()).execute(&context.db).await?;
|
||||||
|
|
||||||
|
let privacy_preferences = user.preferences.privacy_preferences;
|
||||||
|
sqlx::query(format!(
|
||||||
|
r#"INSERT INTO privacy_preferences VALUES ("{}", {}, "{}", "{}", {}, "{}", "{}", {}, {}, "{}", "{}")"#,
|
||||||
|
id_to_uuid(&user.id)?.to_simple(),
|
||||||
|
privacy_preferences.discovery as u8,
|
||||||
|
privacy_preferences.discovery_user_limit.join(","),
|
||||||
|
privacy_preferences.discovery_server_limit.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(","),
|
||||||
|
privacy_preferences.last_seen as u8,
|
||||||
|
privacy_preferences.last_seen_user_limit.join(","),
|
||||||
|
privacy_preferences.last_seen_server_limit.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(","),
|
||||||
|
privacy_preferences.last_seen_course as u8,
|
||||||
|
privacy_preferences.info as u8,
|
||||||
|
privacy_preferences.info_user_limit.join(","),
|
||||||
|
privacy_preferences.info_server_limit.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(","),
|
||||||
|
).as_str()).execute(&context.db).await?;
|
||||||
|
|
||||||
|
let notification_preferences = user.preferences.notification_preferences;
|
||||||
|
sqlx::query(format!(
|
||||||
|
r#"INSERT INTO notification_preferences VALUES ("{}", {}, {})"#,
|
||||||
|
id_to_uuid(&user.id)?.to_simple(),
|
||||||
|
notification_preferences.lock_details as u8,
|
||||||
|
notification_preferences.do_not_disturb as u8,
|
||||||
|
).as_str()).execute(&context.db).await?;
|
||||||
|
|
||||||
|
let security_preferences = user.preferences.security_preferences;
|
||||||
|
sqlx::query(format!(
|
||||||
|
r#"INSERT INTO security_preferences VALUES ("{}", "{}", "{}")"#,
|
||||||
|
id_to_uuid(&user.id)?.to_simple(),
|
||||||
|
security_preferences.account_tokens.iter().map(|id| id.as_ref()).collect::<Vec<_>>().join(","),
|
||||||
|
security_preferences.password_hash,
|
||||||
|
).as_str()).execute(&context.db).await?;
|
||||||
|
|
||||||
|
let external_servers_preferences = user.preferences.external_servers_preferences;
|
||||||
|
sqlx::query(format!(
|
||||||
|
r#"INSERT INTO external_servers_preferences VALUES ("{}", {}, "{}")"#,
|
||||||
|
id_to_uuid(&user.id)?.to_simple(),
|
||||||
|
external_servers_preferences.external_servers as u8,
|
||||||
|
external_servers_preferences.external_servers_limit.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(","),
|
||||||
|
).as_str()).execute(&context.db).await?;
|
||||||
|
|
||||||
|
let external_servers_privacy_preferences = external_servers_preferences.privacy_preferences;
|
||||||
|
sqlx::query(format!(
|
||||||
|
r#"INSERT INTO external_servers_privacy_preferences VALUES ("{}", {}, "{}", "{}", {}, "{}", "{}", {}, {}, "{}", "{}")"#,
|
||||||
|
id_to_uuid(&user.id)?.to_simple(),
|
||||||
|
external_servers_privacy_preferences.discovery as u8,
|
||||||
|
external_servers_privacy_preferences.discovery_user_limit.join(","),
|
||||||
|
external_servers_privacy_preferences.discovery_server_limit.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(","),
|
||||||
|
external_servers_privacy_preferences.last_seen as u8,
|
||||||
|
external_servers_privacy_preferences.last_seen_user_limit.join(","),
|
||||||
|
external_servers_privacy_preferences.last_seen_server_limit.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(","),
|
||||||
|
external_servers_privacy_preferences.last_seen_course as u8,
|
||||||
|
external_servers_privacy_preferences.info as u8,
|
||||||
|
external_servers_privacy_preferences.info_user_limit.join(","),
|
||||||
|
external_servers_privacy_preferences.info_server_limit.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(","),
|
||||||
|
).as_str()).execute(&context.db).await?;
|
||||||
|
|
||||||
|
Ok(format!("User @{} created", user.user_name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Schema<'root_node> = RootNode<'root_node, Query, Mutation, EmptySubscription<Context>>;
|
||||||
|
|
||||||
pub fn schema<'root_node>() -> Schema<'root_node> {
|
pub fn schema<'root_node>() -> Schema<'root_node> {
|
||||||
Schema::new(Query, EmptyMutation::new(), EmptySubscription::new())
|
Schema::new(Query, Mutation, EmptySubscription::new())
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user