diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 61566582..11a14c48 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -8,11 +8,15 @@ Design docs live in `/.context/desktop-app-*.md` (spec, roadmap, architecture). ## Develop ```sh -# from this directory — launches the native window and spawns `next dev` on :3001 +# from this directory — launches the native window and spawns `next dev` on :3003 bun run tauri dev ``` -`bun run dev` on its own just starts the Next dev server on :3001 (no native window). +`bun run dev` on its own just starts the Next dev server on :3003 (no native window). + +In development, the desktop UI and Rust auth commands default to the local API at +`http://localhost:8787`. Set `NEXT_PUBLIC_BACKEND_URL` / `SUPERMEMORY_API_URL` +if you need a different API. ## Build diff --git a/apps/desktop/app/login/page.tsx b/apps/desktop/app/login/page.tsx index 34a9c57a..e38c630a 100644 --- a/apps/desktop/app/login/page.tsx +++ b/apps/desktop/app/login/page.tsx @@ -22,7 +22,13 @@ export default function LoginPage() { await storeToken(token) router.replace("/") } catch (err) { - setError(err instanceof Error ? err.message : "Could not sign in") + setError( + err instanceof Error + ? err.message + : typeof err === "string" + ? err + : "Could not sign in", + ) } finally { setIsSubmitting(false) } diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 021e70a7..0e479f1f 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "NEXT_PUBLIC_DESKTOP_DEV=1 next dev --port 3001", + "dev": "NEXT_PUBLIC_DESKTOP_DEV=1 NEXT_PUBLIC_BACKEND_URL=http://localhost:8787 next dev --port 3003", "build": "next build", "check-types": "tsc --noEmit", "tauri": "tauri" diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json index 32e45da5..95dd7d51 100644 --- a/apps/desktop/src-tauri/capabilities/default.json +++ b/apps/desktop/src-tauri/capabilities/default.json @@ -3,5 +3,5 @@ "identifier": "default", "description": "Core capabilities granted to the main window.", "windows": ["main"], - "permissions": ["core:default"] + "permissions": ["core:default", "core:window:allow-start-dragging"] } diff --git a/apps/desktop/src-tauri/src/auth.rs b/apps/desktop/src-tauri/src/auth.rs index 9637c7c9..229e4d86 100644 --- a/apps/desktop/src-tauri/src/auth.rs +++ b/apps/desktop/src-tauri/src/auth.rs @@ -1,8 +1,13 @@ use keyring::{Entry, Error as KeyringError}; -use serde::{Deserialize, Serialize}; +use serde::Serialize; +use serde_json::Value; const KEYCHAIN_SERVICE: &str = "ai.supermemory.desktop"; const KEYCHAIN_USER: &str = "supermemory-api-token"; +#[cfg(debug_assertions)] +const DEFAULT_API_URL: &str = "http://localhost:8787"; + +#[cfg(not(debug_assertions))] const DEFAULT_API_URL: &str = "https://api.supermemory.ai"; #[derive(Serialize)] @@ -14,18 +19,6 @@ pub struct AuthSession { pub api_url: String, } -#[derive(Deserialize)] -struct SessionResponse { - user: Option, -} - -#[derive(Deserialize)] -struct SessionUser { - id: Option, - email: Option, - name: Option, -} - fn token_entry() -> Result { Entry::new(KEYCHAIN_SERVICE, KEYCHAIN_USER) .map_err(|error| format!("Could not open keychain entry: {error}")) @@ -83,21 +76,33 @@ pub async fn whoami() -> Result { } let session = response - .json::() + .json::() .await .map_err(|error| format!("Could not parse session response: {error}"))?; - let user = session - .user - .ok_or_else(|| "Session response did not include a user".to_string())?; - let user_id = user - .id - .ok_or_else(|| "Session response did not include a user id".to_string())?; + let user_id = first_string( + &session, + &[ + &["user", "id"], + &["userId"], + &["id"], + &["session", "userId"], + &["session", "user", "id"], + ], + ) + .ok_or_else(|| "Session response did not include a user id".to_string())?; Ok(AuthSession { user_id, - email: user.email, - name: user.name, + email: first_string(&session, &[&["user", "email"], &["email"]]), + name: first_string(&session, &[&["user", "name"], &["name"]]), api_url, }) } + +fn first_string(value: &Value, paths: &[&[&str]]) -> Option { + paths.iter().find_map(|path| { + let found = path.iter().try_fold(value, |current, key| current.get(key))?; + found.as_str().map(ToString::to_string) + }) +} diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 666c12e4..22f4b94a 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -5,7 +5,7 @@ "identifier": "ai.supermemory.desktop", "build": { "beforeDevCommand": "bun run dev", - "devUrl": "http://localhost:3001", + "devUrl": "http://localhost:3003", "beforeBuildCommand": "bun run build", "frontendDist": "../out" },