Skip to content

chore: Allow multiple environment logins, new desktop authentication storage, pool configuration, swap environment support. #7714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 151 commits into from
Jul 22, 2025

Conversation

nadr0
Copy link
Contributor

@nadr0 nadr0 commented Jul 8, 2025

discussion #7668

Issue

  • Users need the ability to select the environment at the sign in page
  • We need to cache this result to know what environment their token was generated for

Implementation

  • Environment variables

    • VITE_KITTYCAD_BASE_DOMAIN to enumerate several URLs during runtime.
    • You ca only overwrite VITE_KITTYCAD_API_WEBSOCKET_URL by itself otherwise use the VITE_KITTYCAD_BASE_DOMAIN workflow.
    • .env.development and .env.production have been updated to account for this.
  • Environment related GUI

    • User menu
      • Change environment
      • Sign out of Environment
      • Sign out of all environments
    • Status bar
      • environment pop over detailing the environment
    • Sign in Page
      • Advanced sign in options for domain and pool
      • New copy outlining the sign in options
  • Global application commands

    • Change Pool
    • Swap environments
  • New login authentication keys on disk

    • removal of token.txt
    • Added environment.txt for last environment authenticated against (desktop only)
    • Added envs/<subdomain>.json to store token, pool, name for multiple desktop authentications
  • src/lib/desktop

    • New helper functions for writing environment configuration
    • New helper functions for updating environment configuration
  • @src/env

    • New runtime logic to determine your environment

nadr0 added 30 commits July 2, 2025 10:22
@jacebrowning jacebrowning requested a review from pierremtb July 22, 2025 14:15
Copy link
Contributor

@pierremtb pierremtb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright longer review than expected! Feels good playing with it locally.

Left lots of comments, most of them are nits and cool to move forward without addressing or creating separate issues for them. Before approving I'd like to make sure we look at @jacebrowning's feedback at #7714 (comment)


const choosePoolCommand: Command = {
name: 'choose-pool',
displayName: 'Choose pool',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to the comment above about the 'pool' naming

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@franknoirot Any opinions?

Comment on lines +4 to +11
export function generateDomainsFromBaseDomain(baseDomain: string) {
return {
API_URL: `https://api.${baseDomain}`,
SITE_URL: `https://${baseDomain}`,
WEBSOCKET_URL: `wss://api.${baseDomain}/ws/modeling/commands`,
APP_URL: `https://app.${baseDomain}`,
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generateDomainsFromBaseDomain function doesn't handle empty strings gracefully. When baseDomain is empty, it generates invalid URLs like https://api. which will cause API calls to fail. Consider adding a check for empty strings that returns default values or throws an appropriate error to prevent downstream failures.

Suggested change
export function generateDomainsFromBaseDomain(baseDomain: string) {
return {
API_URL: `https://api.${baseDomain}`,
SITE_URL: `https://${baseDomain}`,
WEBSOCKET_URL: `wss://api.${baseDomain}/ws/modeling/commands`,
APP_URL: `https://app.${baseDomain}`,
}
}
export function generateDomainsFromBaseDomain(baseDomain: string) {
if (!baseDomain) {
throw new Error('Base domain cannot be empty')
}
return {
API_URL: `https://api.${baseDomain}`,
SITE_URL: `https://${baseDomain}`,
WEBSOCKET_URL: `wss://api.${baseDomain}/ws/modeling/commands`,
APP_URL: `https://app.${baseDomain}`,
}
}

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean yes but no? What if I want the function to allow the empty string, it lets me check the default values of the return function.

Comment on lines +315 to +317
} else {
return new Error('Unable to logout, cannot find domain')
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function returns an Error object directly, but doesn't throw it or wrap it in a rejected Promise. This means the error won't propagate through the Promise chain and won't be caught by error handlers. Consider either:

  1. Throwing the error: throw new Error('Unable to logout, cannot find domain')
  2. Returning a rejected Promise: return Promise.reject(new Error('Unable to logout, cannot find domain'))

This will ensure the error is properly handled by the caller's error handling mechanisms.

Suggested change
} else {
return new Error('Unable to logout, cannot find domain')
}
} else {
return Promise.reject(new Error('Unable to logout, cannot find domain'))
}

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +4 to +7
return typeof navigator !== 'undefined' &&
typeof window.navigator !== 'undefined'
? navigator.userAgent.toLowerCase().indexOf('electron') > -1
: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The modified isDesktop() function returns true when navigator is undefined, which could lead to desktop-specific code running in non-desktop environments. Consider defaulting to false when the environment cannot be determined, as this would be safer. The current implementation assumes desktop when uncertain, which may cause unexpected behavior in server-side rendering or other environments where navigator is not available.

Suggested change
return typeof navigator !== 'undefined' &&
typeof window.navigator !== 'undefined'
? navigator.userAgent.toLowerCase().indexOf('electron') > -1
: true
return typeof navigator !== 'undefined' &&
typeof window.navigator !== 'undefined'
? navigator.userAgent.toLowerCase().indexOf('electron') > -1
: false

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

Copy link
Contributor

@pierremtb pierremtb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes! Looks really good locally

@pierremtb pierremtb enabled auto-merge (squash) July 22, 2025 20:28
@pierremtb pierremtb merged commit 33e41fb into main Jul 22, 2025
73 of 74 checks passed
@pierremtb pierremtb deleted the nadro/adhoc/environment-based-login branch July 22, 2025 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants