diff --git a/backend/ee/onyx/server/tenants/billing.py b/backend/ee/onyx/server/tenants/billing.py index ec4ada922f3..e8d0e39ab60 100644 --- a/backend/ee/onyx/server/tenants/billing.py +++ b/backend/ee/onyx/server/tenants/billing.py @@ -46,6 +46,7 @@ def register_tenant_users(tenant_id: str, number_of_users: int) -> stripe.Subscr """ Send a request to the control service to register the number of users for a tenant. """ + if not STRIPE_PRICE_ID: raise Exception("STRIPE_PRICE_ID is not set") diff --git a/backend/onyx/auth/email_utils.py b/backend/onyx/auth/email_utils.py index 4cb36706ec5..346125de6ae 100644 --- a/backend/onyx/auth/email_utils.py +++ b/backend/onyx/auth/email_utils.py @@ -40,21 +40,24 @@ def send_email( def send_user_email_invite(user_email: str, current_user: User) -> None: - subject = "Invitation to Join Onyx Workspace" + subject = "Invitation to Join Onyx Organization" body = dedent( f"""\ Hello, - You have been invited to join a workspace on Onyx. + You have been invited to join an organization on Onyx. - To join the workspace, please visit the following link: + To join the organization, please visit the following link: - {WEB_DOMAIN}/auth/login + {WEB_DOMAIN}/auth/signup?email={user_email} + + You'll be asked to set a password or login with Google to complete your registration. Best regards, The Onyx Team """ ) + send_email(user_email, subject, body, current_user.email) diff --git a/backend/onyx/auth/users.py b/backend/onyx/auth/users.py index fb3e5e8eb65..5ef96c6f118 100644 --- a/backend/onyx/auth/users.py +++ b/backend/onyx/auth/users.py @@ -46,7 +46,6 @@ from httpx_oauth.oauth2 import BaseOAuth2 from httpx_oauth.oauth2 import OAuth2Token from pydantic import BaseModel -from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncSession from onyx.auth.api_key import get_hashed_api_key_from_request @@ -396,11 +395,9 @@ async def oauth_callback( # Explicitly set the Postgres schema for this session to ensure # OAuth account creation happens in the correct tenant schema - await db_session.execute(text(f'SET search_path = "{tenant_id}"')) # Add OAuth account await self.user_db.add_oauth_account(user, oauth_account_dict) - await self.on_after_register(user, request) else: @@ -419,7 +416,6 @@ async def oauth_callback( # NOTE: Most IdPs have very short expiry times, and we don't want to force the user to # re-authenticate that frequently, so by default this is disabled - if expires_at and TRACK_EXTERNAL_IDP_EXPIRY: oidc_expiry = datetime.fromtimestamp(expires_at, tz=timezone.utc) await self.user_db.update( diff --git a/backend/onyx/db/engine.py b/backend/onyx/db/engine.py index 4176b954205..0d1ebceea26 100644 --- a/backend/onyx/db/engine.py +++ b/backend/onyx/db/engine.py @@ -370,9 +370,23 @@ async def get_async_session_with_tenant( bind=engine, expire_on_commit=False, class_=AsyncSession ) # type: ignore + async def _set_search_path(session: AsyncSession, tenant_id: str) -> None: + await session.execute(text(f'SET search_path = "{tenant_id}"')) + async with async_session_factory() as session: + # Register an event listener that is called whenever a new transaction starts + @event.listens_for(session.sync_session, "after_begin") + def after_begin(session_: Any, transaction: Any, connection: Any) -> None: + # Because the event is sync, we can't directly await here. + # Instead we queue up an asyncio task to ensures + # the next statement sets the search_path + session_.do_orm_execute = lambda state: connection.exec_driver_sql( + f'SET search_path = "{tenant_id}"' + ) + try: - await session.execute(text(f'SET search_path = "{tenant_id}"')) + await _set_search_path(session, tenant_id) + if POSTGRES_IDLE_SESSIONS_TIMEOUT: await session.execute( text( diff --git a/web/src/app/auth/login/EmailPasswordForm.tsx b/web/src/app/auth/login/EmailPasswordForm.tsx index 97b494dc292..6f7a48cc98c 100644 --- a/web/src/app/auth/login/EmailPasswordForm.tsx +++ b/web/src/app/auth/login/EmailPasswordForm.tsx @@ -19,11 +19,13 @@ export function EmailPasswordForm({ shouldVerify, referralSource, nextUrl, + defaultEmail, }: { isSignup?: boolean; shouldVerify?: boolean; referralSource?: string; nextUrl?: string | null; + defaultEmail?: string | null; }) { const { user } = useUser(); const { popup, setPopup } = usePopup(); @@ -34,7 +36,7 @@ export function EmailPasswordForm({ {popup}