mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 10:40:54 +00:00
Update APIs and DB interactions to reflect email confirmation step
This commit is contained in:
parent
d85ecc8302
commit
f8c7c925af
8 changed files with 1409 additions and 1285 deletions
|
@ -0,0 +1,6 @@
|
|||
DROP TABLE signups;
|
||||
|
||||
ALTER TABLE users
|
||||
DROP COLUMN metrics_id;
|
||||
|
||||
DROP SEQUENCE metrics_id_seq;
|
|
@ -8,16 +8,18 @@ CREATE TABLE IF NOT EXISTS "signups" (
|
|||
"metrics_id" INTEGER NOT NULL DEFAULT nextval('metrics_id_seq'),
|
||||
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"user_id" INTEGER REFERENCES users (id),
|
||||
"inviting_user_id" INTEGER REFERENCES users (id),
|
||||
|
||||
"platform_mac" BOOLEAN NOT NULL,
|
||||
"platform_linux" BOOLEAN NOT NULL,
|
||||
"platform_windows" BOOLEAN NOT NULL,
|
||||
"platform_unknown" BOOLEAN NOT NULL,
|
||||
|
||||
"editor_features" VARCHAR[] NOT NULL,
|
||||
"programming_languages" VARCHAR[] NOT NULL
|
||||
"editor_features" VARCHAR[],
|
||||
"programming_languages" VARCHAR[]
|
||||
);
|
||||
|
||||
CREATE INDEX "index_users_on_email_address" ON "users" ("email_address");
|
||||
CREATE UNIQUE INDEX "index_signups_on_email_address" ON "signups" ("email_address");
|
||||
CREATE INDEX "index_signups_on_email_confirmation_sent" ON "signups" ("email_confirmation_sent");
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
auth,
|
||||
db::{ProjectId, Signup, SignupInvite, SignupRedemption, User, UserId},
|
||||
db::{Invite, NewUserParams, ProjectId, Signup, User, UserId},
|
||||
rpc::{self, ResultExt},
|
||||
AppState, Error, Result,
|
||||
};
|
||||
|
@ -46,9 +46,9 @@ pub fn routes(rpc_server: &Arc<rpc::Server>, state: Arc<AppState>) -> Router<Bod
|
|||
.route("/user_activity/counts", get(get_active_user_counts))
|
||||
.route("/project_metadata", get(get_project_metadata))
|
||||
.route("/signups", post(create_signup))
|
||||
.route("/signup/redeem", post(redeem_signup))
|
||||
.route("/signups_invites", get(get_signup_invites))
|
||||
.route("/signups_invites_sent", post(record_signup_invites_sent))
|
||||
.route("/user_invites", post(create_invite_from_code))
|
||||
.route("/unsent_invites", get(get_unsent_invites))
|
||||
.route("/sent_invites", post(record_sent_invites))
|
||||
.layer(
|
||||
ServiceBuilder::new()
|
||||
.layer(Extension(state))
|
||||
|
@ -113,9 +113,9 @@ async fn get_users(
|
|||
#[derive(Deserialize, Debug)]
|
||||
struct CreateUserParams {
|
||||
github_login: String,
|
||||
invite_code: Option<String>,
|
||||
email_address: Option<String>,
|
||||
admin: bool,
|
||||
email_address: String,
|
||||
email_confirmation_code: Option<String>,
|
||||
invite_count: i32,
|
||||
}
|
||||
|
||||
async fn create_user(
|
||||
|
@ -123,29 +123,38 @@ async fn create_user(
|
|||
Extension(app): Extension<Arc<AppState>>,
|
||||
Extension(rpc_server): Extension<Arc<rpc::Server>>,
|
||||
) -> Result<Json<User>> {
|
||||
let user_id = if let Some(invite_code) = params.invite_code {
|
||||
let invitee_id = app
|
||||
.db
|
||||
.redeem_invite_code(
|
||||
&invite_code,
|
||||
¶ms.github_login,
|
||||
params.email_address.as_deref(),
|
||||
let (user_id, inviter_id) =
|
||||
// Creating a user via the normal signup process
|
||||
if let Some(email_confirmation_code) = params.email_confirmation_code {
|
||||
app.db
|
||||
.create_user_from_invite(
|
||||
&Invite {
|
||||
email_address: params.email_address,
|
||||
email_confirmation_code,
|
||||
},
|
||||
NewUserParams {
|
||||
github_login: params.github_login,
|
||||
invite_count: params.invite_count,
|
||||
},
|
||||
)
|
||||
.await?
|
||||
}
|
||||
// Creating a user as an admin
|
||||
else {
|
||||
(
|
||||
app.db
|
||||
.create_user(¶ms.github_login, ¶ms.email_address, false)
|
||||
.await?,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
};
|
||||
|
||||
if let Some(inviter_id) = inviter_id {
|
||||
rpc_server
|
||||
.invite_code_redeemed(&invite_code, invitee_id)
|
||||
.invite_code_redeemed(inviter_id, user_id)
|
||||
.await
|
||||
.trace_err();
|
||||
invitee_id
|
||||
} else {
|
||||
app.db
|
||||
.create_user(
|
||||
¶ms.github_login,
|
||||
params.email_address.as_deref(),
|
||||
params.admin,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
}
|
||||
|
||||
let user = app
|
||||
.db
|
||||
|
@ -175,7 +184,9 @@ async fn update_user(
|
|||
}
|
||||
|
||||
if let Some(invite_count) = params.invite_count {
|
||||
app.db.set_invite_count(user_id, invite_count).await?;
|
||||
app.db
|
||||
.set_invite_count_for_user(user_id, invite_count)
|
||||
.await?;
|
||||
rpc_server.invite_count_updated(user_id).await.trace_err();
|
||||
}
|
||||
|
||||
|
@ -428,30 +439,39 @@ async fn create_signup(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn redeem_signup(
|
||||
Json(redemption): Json<SignupRedemption>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<()> {
|
||||
app.db.redeem_signup(redemption).await?;
|
||||
Ok(())
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateInviteFromCodeParams {
|
||||
invite_code: String,
|
||||
email_address: String,
|
||||
}
|
||||
|
||||
async fn record_signup_invites_sent(
|
||||
Json(params): Json<Vec<SignupInvite>>,
|
||||
async fn create_invite_from_code(
|
||||
Json(params): Json<CreateInviteFromCodeParams>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<()> {
|
||||
app.db.record_signup_invites_sent(¶ms).await?;
|
||||
Ok(())
|
||||
) -> Result<Json<Invite>> {
|
||||
Ok(Json(
|
||||
app.db
|
||||
.create_invite_from_code(¶ms.invite_code, ¶ms.email_address)
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetSignupInvitesParams {
|
||||
pub struct GetUnsentInvitesParams {
|
||||
pub count: usize,
|
||||
}
|
||||
|
||||
async fn get_signup_invites(
|
||||
Query(params): Query<GetSignupInvitesParams>,
|
||||
async fn get_unsent_invites(
|
||||
Query(params): Query<GetUnsentInvitesParams>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<Vec<SignupInvite>>> {
|
||||
Ok(Json(app.db.get_signup_invites(params.count).await?))
|
||||
) -> Result<Json<Vec<Invite>>> {
|
||||
Ok(Json(app.db.get_unsent_invites(params.count).await?))
|
||||
}
|
||||
|
||||
async fn record_sent_invites(
|
||||
Json(params): Json<Vec<Invite>>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<()> {
|
||||
app.db.record_sent_invites(¶ms).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
1071
crates/collab/src/db_tests.rs
Normal file
1071
crates/collab/src/db_tests.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
db::{tests::TestDb, ProjectId, UserId},
|
||||
db::{ProjectId, TestDb, UserId},
|
||||
rpc::{Executor, Server, Store},
|
||||
AppState,
|
||||
};
|
||||
|
@ -4640,7 +4640,10 @@ async fn test_random_collaboration(
|
|||
|
||||
let mut server = TestServer::start(cx.foreground(), cx.background()).await;
|
||||
let db = server.app_state.db.clone();
|
||||
let host_user_id = db.create_user("host", None, false).await.unwrap();
|
||||
let host_user_id = db
|
||||
.create_user("host", "host@example.com", false)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut available_guests = vec![
|
||||
"guest-1".to_string(),
|
||||
"guest-2".to_string(),
|
||||
|
@ -4649,7 +4652,10 @@ async fn test_random_collaboration(
|
|||
];
|
||||
|
||||
for username in &available_guests {
|
||||
let guest_user_id = db.create_user(username, None, false).await.unwrap();
|
||||
let guest_user_id = db
|
||||
.create_user(username, &format!("{username}@example.com"), false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(*username, format!("guest-{}", guest_user_id));
|
||||
server
|
||||
.app_state
|
||||
|
@ -5157,7 +5163,7 @@ impl TestServer {
|
|||
} else {
|
||||
self.app_state
|
||||
.db
|
||||
.create_user(name, None, false)
|
||||
.create_user(name, &format!("{name}@example.com"), false)
|
||||
.await
|
||||
.unwrap()
|
||||
};
|
||||
|
|
|
@ -4,6 +4,8 @@ mod db;
|
|||
mod env;
|
||||
mod rpc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod db_tests;
|
||||
#[cfg(test)]
|
||||
mod integration_tests;
|
||||
|
||||
|
|
|
@ -541,27 +541,30 @@ impl Server {
|
|||
|
||||
pub async fn invite_code_redeemed(
|
||||
self: &Arc<Self>,
|
||||
code: &str,
|
||||
inviter_id: UserId,
|
||||
invitee_id: UserId,
|
||||
) -> Result<()> {
|
||||
let user = self.app_state.db.get_user_for_invite_code(code).await?;
|
||||
let store = self.store().await;
|
||||
let invitee_contact = store.contact_for_user(invitee_id, true);
|
||||
for connection_id in store.connection_ids_for_user(user.id) {
|
||||
self.peer.send(
|
||||
connection_id,
|
||||
proto::UpdateContacts {
|
||||
contacts: vec![invitee_contact.clone()],
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
self.peer.send(
|
||||
connection_id,
|
||||
proto::UpdateInviteInfo {
|
||||
url: format!("{}{}", self.app_state.invite_link_prefix, code),
|
||||
count: user.invite_count as u32,
|
||||
},
|
||||
)?;
|
||||
if let Some(user) = self.app_state.db.get_user_by_id(inviter_id).await? {
|
||||
if let Some(code) = &user.invite_code {
|
||||
let store = self.store().await;
|
||||
let invitee_contact = store.contact_for_user(invitee_id, true);
|
||||
for connection_id in store.connection_ids_for_user(inviter_id) {
|
||||
self.peer.send(
|
||||
connection_id,
|
||||
proto::UpdateContacts {
|
||||
contacts: vec![invitee_contact.clone()],
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
self.peer.send(
|
||||
connection_id,
|
||||
proto::UpdateInviteInfo {
|
||||
url: format!("{}{}", self.app_state.invite_link_prefix, &code),
|
||||
count: user.invite_count as u32,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue