diff --git a/CHANGELOG.md b/CHANGELOG.md index 82bd88c..ffa2fe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added + +- Allow to add notes to generated invite codes. + ## [1.15.0] - 2023-02-27 ### Added diff --git a/docs/mitractl.md b/docs/mitractl.md index 9f33d9d..e4c0b0d 100644 --- a/docs/mitractl.md +++ b/docs/mitractl.md @@ -20,10 +20,10 @@ Generate RSA private key: mitractl generate-rsa-key ``` -Generate invite code: +Generate invite code (note is optional): ```shell -mitractl generate-invite-code +mitractl generate-invite-code ``` List generated invites: diff --git a/migrations/V0044__user_invite_code__add_note_and_created_at.sql b/migrations/V0044__user_invite_code__add_note_and_created_at.sql new file mode 100644 index 0000000..5e2b8d0 --- /dev/null +++ b/migrations/V0044__user_invite_code__add_note_and_created_at.sql @@ -0,0 +1,2 @@ +ALTER TABLE user_invite_code ADD COLUMN note VARCHAR(200); +ALTER TABLE user_invite_code ADD COLUMN created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP; diff --git a/migrations/schema.sql b/migrations/schema.sql index 37a672e..ed289ff 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -38,7 +38,9 @@ CREATE TABLE actor_profile ( CREATE TABLE user_invite_code ( code VARCHAR(100) PRIMARY KEY, - used BOOLEAN NOT NULL DEFAULT FALSE + used BOOLEAN NOT NULL DEFAULT FALSE, + note VARCHAR(200), + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE user_account ( diff --git a/mitra-cli/src/cli.rs b/mitra-cli/src/cli.rs index b8f61ee..b16c4cc 100644 --- a/mitra-cli/src/cli.rs +++ b/mitra-cli/src/cli.rs @@ -117,14 +117,19 @@ impl GenerateEthereumAddress { /// Generate invite code #[derive(Parser)] -pub struct GenerateInviteCode; +pub struct GenerateInviteCode { + note: Option, +} impl GenerateInviteCode { pub async fn execute( &self, db_client: &impl DatabaseClient, ) -> Result<(), Error> { - let invite_code = create_invite_code(db_client).await?; + let invite_code = create_invite_code( + db_client, + self.note.as_deref(), + ).await?; println!("generated invite code: {}", invite_code); Ok(()) } @@ -144,8 +149,12 @@ impl ListInviteCodes { println!("no invite codes found"); return Ok(()); }; - for code in invite_codes { - println!("{}", code); + for invite_code in invite_codes { + if let Some(note) = invite_code.note { + println!("{} ({})", invite_code.code, note); + } else { + println!("{}", invite_code.code); + }; }; Ok(()) } diff --git a/src/models/users/queries.rs b/src/models/users/queries.rs index 9638b42..4ac94b3 100644 --- a/src/models/users/queries.rs +++ b/src/models/users/queries.rs @@ -12,36 +12,43 @@ use crate::models::{ profiles::queries::create_profile, profiles::types::{DbActorProfile, ProfileCreateData}, }; -use super::types::{DbUser, Role, User, UserCreateData}; +use super::types::{ + DbInviteCode, + DbUser, + Role, + User, + UserCreateData, +}; use super::utils::generate_invite_code; pub async fn create_invite_code( db_client: &impl DatabaseClient, + note: Option<&str>, ) -> Result { let invite_code = generate_invite_code(); db_client.execute( " - INSERT INTO user_invite_code (code) - VALUES ($1) + INSERT INTO user_invite_code (code, note) + VALUES ($1, $2) ", - &[&invite_code], + &[&invite_code, ¬e], ).await?; Ok(invite_code) } pub async fn get_invite_codes( db_client: &impl DatabaseClient, -) -> Result, DatabaseError> { +) -> Result, DatabaseError> { let rows = db_client.query( " - SELECT code + SELECT user_invite_code FROM user_invite_code WHERE used = FALSE ", &[], ).await?; - let codes: Vec = rows.iter() - .map(|row| row.try_get("code")) + let codes = rows.iter() + .map(|row| row.try_get("user_invite_code")) .collect::>()?; Ok(codes) } @@ -299,6 +306,14 @@ mod tests { use crate::models::users::types::Role; use super::*; + #[tokio::test] + #[serial] + async fn test_create_invite_code() { + let db_client = &mut create_test_database().await; + let code = create_invite_code(db_client, Some("test")).await.unwrap(); + assert_eq!(code.len(), 32); + } + #[tokio::test] #[serial] async fn test_create_user() { diff --git a/src/models/users/types.rs b/src/models/users/types.rs index 011f0ac..89160bf 100644 --- a/src/models/users/types.rs +++ b/src/models/users/types.rs @@ -13,6 +13,16 @@ use crate::errors::ValidationError; use crate::identity::did::Did; use crate::models::profiles::types::DbActorProfile; +#[allow(dead_code)] +#[derive(FromSql)] +#[postgres(name = "user_invite_code")] +pub struct DbInviteCode { + pub code: String, + used: bool, + pub note: Option, + created_at: DateTime, +} + #[derive(PartialEq)] pub enum Permission { CreateFollowRequest,