Add email + OTP sign-in flow for cloud sync
Replace the "drop a session token in a file" workflow with a real Chrome-style email login. The Cloudflare worker already had Better Auth's `email-otp` plugin wired into `SEND_EMAIL`; this commit builds the renderer-side counterpart. Worker side: - Move the OTP sender from `auth@elydora.com` to `browser@elydora.com` (wrangler.toml `allowed_sender_addresses` + better_auth.ts `EMAIL_OTP_FROM_ADDRESS`). Worker must be redeployed to pick this up. Client side (`ely_sync_client::email_otp`): - `send_email_otp(config, email)` POSTs `/api/auth/email-otp/send-verification-otp` with `{ email, type: "sign-in" }`. - `verify_email_otp(config, email, otp)` POSTs `/api/auth/sign-in/email-otp`, reads the Better Auth session token from the JSON body's `token` field with the `Set-Cookie: better-auth.session_token=…` header as the documented fallback channel, and returns it as a `BearerToken`. Shell side (`shell/auth.rs` + `shell/internal_pages/sync.rs`): - New `AuthFlowPhase` (Idle / SendingCode / AwaitingOtp / Verifying / Error) tracks the in-flight form. Two off-thread workers run the HTTP exchanges so the GPUI render loop never blocks. - Successful verify saves the bearer via `SyncEngine::install_bearer` and triggers an immediate snapshot upload, so the user is signed in + initial-synced in one click. - Sync settings page replaces the bare "Sync now" button row with an account card: when SignedOut → email field + Send code → OTP field + Verify / Resend; when signed in → an account chip + Sign out. - `trigger_cloud_sync_upload` no longer takes a `Context` param so the post-auth path can fire it from the inbox-drain pass without needing a window context.
This commit is contained in:
@@ -246,7 +246,7 @@ impl ElyShell {
|
||||
/// (the UI thread never blocks on the network), and the worker
|
||||
/// reports back through the shell's `sync_inbox` so the sync page
|
||||
/// reflects the new state without waiting for a manual refresh.
|
||||
pub(super) fn trigger_cloud_sync_upload(&mut self, _cx: &mut Context<Self>) {
|
||||
pub(crate) fn trigger_cloud_sync_upload(&mut self) {
|
||||
let ShellState::Ready(core) = &self.state else {
|
||||
return;
|
||||
};
|
||||
@@ -318,7 +318,11 @@ fn run_sync_upload(
|
||||
bytes: Vec<u8>,
|
||||
inbox: std::sync::mpsc::Sender<super::SyncStateUpdate>,
|
||||
) {
|
||||
let mut engine = match SyncEngine::for_profile_dir(&profile_dir, device_name, sync_platform()) {
|
||||
let mut engine = match SyncEngine::for_profile_dir(
|
||||
&profile_dir,
|
||||
device_name,
|
||||
super::sync_platform_label(),
|
||||
) {
|
||||
Ok(engine) => engine,
|
||||
Err(error) => {
|
||||
let message = error.to_string();
|
||||
@@ -364,15 +368,3 @@ fn run_sync_upload(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn sync_platform() -> &'static str {
|
||||
if cfg!(target_os = "macos") {
|
||||
"macos"
|
||||
} else if cfg!(target_os = "windows") {
|
||||
"windows"
|
||||
} else if cfg!(target_os = "linux") {
|
||||
"linux"
|
||||
} else {
|
||||
"other"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user