From 65c3614c48281779a294d3dff141b66b06b90583 Mon Sep 17 00:00:00 2001 From: Mahmoud Moravej Date: Fri, 8 Mar 2024 17:39:41 -0500 Subject: [PATCH] fix multi engine and redirect issues --- app/@types/graphql/schema.ts | 16 +- app/components/OrganizationForm.tsx | 159 ++++++++++-------- .../graphql/organizationFragment.gql | 1 + .../route.tsx | 9 +- app/routes/auth.google.signin.callback.tsx | 19 ++- app/routes/auth.google.signup.callback.tsx | 18 +- app/services/auth.strategies/google.signin.ts | 18 +- app/services/auth.strategies/google.signup.ts | 16 +- app/utils/graphql.ts | 20 +++ 9 files changed, 174 insertions(+), 102 deletions(-) diff --git a/app/@types/graphql/schema.ts b/app/@types/graphql/schema.ts index a694f4e..602578e 100644 --- a/app/@types/graphql/schema.ts +++ b/app/@types/graphql/schema.ts @@ -20,8 +20,8 @@ export type Scalars = { export type Activity = { __typename?: 'Activity'; - aiEngine: AiEngine; - aiEngineId: Scalars['Int']['output']; + aiEngineType: AiEngineType; + aiEngineTypeId: Scalars['Int']['output']; channelActivityId: Scalars['String']['output']; channelActivityUrl?: Maybe; channelId: Scalars['Int']['output']; @@ -83,8 +83,8 @@ export type Advice = { activitiesTotal: Scalars['Int']['output']; activityPrompt?: Maybe; activitySummary?: Maybe; - aiEngine: AiEngine; - aiEngineId: Scalars['Int']['output']; + aiEngineType: AiEngineType; + aiEngineTypeId: Scalars['Int']['output']; analyzedActivitiesTotal: Scalars['Int']['output']; analyzedAt: Scalars['ISO8601DateTime']['output']; contentReadyVisionsTotal: Scalars['Int']['output']; @@ -630,6 +630,7 @@ export type Organization = { githubToken?: Maybe; id: Scalars['Int']['output']; isPersonal: Scalars['Boolean']['output']; + isSystem: Scalars['Boolean']['output']; name: Scalars['String']['output']; owner: User; useSystemAiEngine: Scalars['Boolean']['output']; @@ -1187,16 +1188,16 @@ export type FindOrganizationQueryVariables = Exact<{ }>; -export type FindOrganizationQuery = { __typename?: 'Query', organization: { __typename?: 'Organization', id: number, name: string, isPersonal: boolean, githubOrgs?: string | null, useSystemGithubToken: boolean, githubToken?: string | null, useSystemAiEngine: boolean, owner: { __typename?: 'User', email: string }, aiEngines?: { __typename?: 'AiEngineConnection', nodes?: Array<{ __typename?: 'AiEngine', id: number, settings?: string | null, type: { __typename?: 'AiEngineType', id: number, title: string } } | null> | null } | null } }; +export type FindOrganizationQuery = { __typename?: 'Query', organization: { __typename?: 'Organization', id: number, name: string, isPersonal: boolean, isSystem: boolean, githubOrgs?: string | null, useSystemGithubToken: boolean, githubToken?: string | null, useSystemAiEngine: boolean, owner: { __typename?: 'User', email: string }, aiEngines?: { __typename?: 'AiEngineConnection', nodes?: Array<{ __typename?: 'AiEngine', id: number, settings?: string | null, type: { __typename?: 'AiEngineType', id: number, title: string } } | null> | null } | null } }; -export type OrganizationFragmentFragment = { __typename?: 'Organization', id: number, name: string, isPersonal: boolean, githubOrgs?: string | null, useSystemGithubToken: boolean, githubToken?: string | null, useSystemAiEngine: boolean, owner: { __typename?: 'User', email: string }, aiEngines?: { __typename?: 'AiEngineConnection', nodes?: Array<{ __typename?: 'AiEngine', id: number, settings?: string | null, type: { __typename?: 'AiEngineType', id: number, title: string } } | null> | null } | null }; +export type OrganizationFragmentFragment = { __typename?: 'Organization', id: number, name: string, isPersonal: boolean, isSystem: boolean, githubOrgs?: string | null, useSystemGithubToken: boolean, githubToken?: string | null, useSystemAiEngine: boolean, owner: { __typename?: 'User', email: string }, aiEngines?: { __typename?: 'AiEngineConnection', nodes?: Array<{ __typename?: 'AiEngine', id: number, settings?: string | null, type: { __typename?: 'AiEngineType', id: number, title: string } } | null> | null } | null }; export type UpdateOrganizationMutationVariables = Exact<{ input: OrganizationUpdateInput; }>; -export type UpdateOrganizationMutation = { __typename?: 'Mutation', organizationUpdate?: { __typename?: 'OrganizationUpdatePayload', organization: { __typename?: 'Organization', id: number, name: string, isPersonal: boolean, githubOrgs?: string | null, useSystemGithubToken: boolean, githubToken?: string | null, useSystemAiEngine: boolean, owner: { __typename?: 'User', email: string }, aiEngines?: { __typename?: 'AiEngineConnection', nodes?: Array<{ __typename?: 'AiEngine', id: number, settings?: string | null, type: { __typename?: 'AiEngineType', id: number, title: string } } | null> | null } | null } } | null }; +export type UpdateOrganizationMutation = { __typename?: 'Mutation', organizationUpdate?: { __typename?: 'OrganizationUpdatePayload', organization: { __typename?: 'Organization', id: number, name: string, isPersonal: boolean, isSystem: boolean, githubOrgs?: string | null, useSystemGithubToken: boolean, githubToken?: string | null, useSystemAiEngine: boolean, owner: { __typename?: 'User', email: string }, aiEngines?: { __typename?: 'AiEngineConnection', nodes?: Array<{ __typename?: 'AiEngine', id: number, settings?: string | null, type: { __typename?: 'AiEngineType', id: number, title: string } } | null> | null } | null } } | null }; export type FindVisionQueryVariables = Exact<{ id: Scalars['ID']['input']; @@ -1308,6 +1309,7 @@ export const OrganizationFragmentFragmentDoc = gql` email } isPersonal + isSystem githubOrgs useSystemGithubToken githubToken diff --git a/app/components/OrganizationForm.tsx b/app/components/OrganizationForm.tsx index cc872b6..8233308 100644 --- a/app/components/OrganizationForm.tsx +++ b/app/components/OrganizationForm.tsx @@ -16,9 +16,10 @@ import { ChangeEventHandler } from "react"; export type OrganizationFormData = Omit & { isPersonal: boolean; + isSystem: boolean; } & { aiEngines: - | { id: number; title: string; settings: string }[] + | { id: number; title: string; settings?: string | null }[] | null | undefined; }; @@ -58,7 +59,7 @@ export function OrganizationForm({
- {!organization.isPersonal && ( + {!organization.isPersonal && !organization.isSystem && ( <> Name @@ -71,6 +72,7 @@ export function OrganizationForm({ }} crossOrigin={undefined} value={organization.name ?? ""} + readOnly={organization.isSystem} onChange={({ target }) => { updateData({ ...organization, name: target.value }); }} @@ -87,51 +89,58 @@ export function OrganizationForm({ }} crossOrigin={undefined} value={organization.ownerEmail ?? ""} + readOnly={organization.isSystem} onChange={({ target }) => { updateData({ ...organization, ownerEmail: target.value }); }} /> )} - - Github Organizations (comma separated) - - { - updateData({ ...organization, githubOrgs: target.value }); - }} - /> - - - - - - - - - - + {!organization.isSystem && ( + <> + + Github Organizations (comma separated) + + { + updateData({ ...organization, githubOrgs: target.value }); + }} + /> + + )} + {!organization.isSystem && ( + + + + + + + + + + + )} {!organization.useSystemGithubToken && ( <> @@ -159,34 +168,36 @@ export function OrganizationForm({ /> )} - - - - - - - - - - + {!organization.isSystem && ( + + + + + + + + + + + )} {!organization.useSystemAiEngine && ( <> - My AI Engines Settings + AI Engines Settings {organization?.aiEngines?.map(({ id, title, settings }) => ( @@ -206,11 +217,18 @@ export function OrganizationForm({ }} value={formatJson(settings)} onChange={({ target }) => { + const aiEngines = [ + ...(organization.aiEngines ?? []), + ]; + const currentEngine = aiEngines.find( + (o) => o.id == id, + ); + if (currentEngine) + currentEngine.settings = target.value; + updateData({ ...organization, - aiEngines: [ - { id, title, settings: target.value }, - ], + aiEngines, }); }} /> @@ -232,7 +250,8 @@ export function OrganizationForm({ ); } -function formatJson(json: string) { +function formatJson(json?: string | null) { + if (json == null) return ""; try { return JSON.stringify(JSON.parse(json), null, 2); } catch { diff --git a/app/routes/_dashboard.organizations.$id.edit/graphql/organizationFragment.gql b/app/routes/_dashboard.organizations.$id.edit/graphql/organizationFragment.gql index 975892c..e4ccf2a 100644 --- a/app/routes/_dashboard.organizations.$id.edit/graphql/organizationFragment.gql +++ b/app/routes/_dashboard.organizations.$id.edit/graphql/organizationFragment.gql @@ -5,6 +5,7 @@ fragment OrganizationFragment on Organization { email } isPersonal + isSystem githubOrgs useSystemGithubToken githubToken diff --git a/app/routes/_dashboard.organizations.$id.edit/route.tsx b/app/routes/_dashboard.organizations.$id.edit/route.tsx index 6fb7d8e..66577c4 100644 --- a/app/routes/_dashboard.organizations.$id.edit/route.tsx +++ b/app/routes/_dashboard.organizations.$id.edit/route.tsx @@ -63,7 +63,12 @@ export default function OrganizationEdit() { return ( - Modifying {organization.isPersonal ? "Account" : "Organization"}{" "} + Modifying{" "} + {organization.isPersonal + ? "Account" + : organization.isSystem + ? "System" + : "Organization"}{" "} Settings , ): OrganizationUpdate { - const { id: _, isPersonal: __, aiEngines, ...input } = data; + const { id: _, isPersonal: __, isSystem: ___, aiEngines, ...input } = data; return { ...input, diff --git a/app/routes/auth.google.signin.callback.tsx b/app/routes/auth.google.signin.callback.tsx index 2f322cb..79e9599 100644 --- a/app/routes/auth.google.signin.callback.tsx +++ b/app/routes/auth.google.signin.callback.tsx @@ -1,6 +1,8 @@ import { LoaderFunctionArgs, redirect } from "@remix-run/node"; import { AuthorizationError } from "remix-auth"; + import { authenticator } from "~/services/auth.server"; +import { parseAuthenticationMessage } from "~/utils"; export async function loader({ request }: LoaderFunctionArgs) { try { @@ -10,12 +12,19 @@ export async function loader({ request }: LoaderFunctionArgs) { }); } catch (error) { if (error instanceof Response) return error; - if (error instanceof AuthorizationError) { - return redirect("/signin", { - headers: { - "Set-Cookie": "signin-error=unauthorized; Path=/", + else if (error instanceof AuthorizationError) { + const errorMessageDetails = parseAuthenticationMessage(error.message); + + return redirect( + errorMessageDetails.organization_id != null + ? `/signin/${errorMessageDetails.organization_id}` + : "/signin", + { + headers: { + "Set-Cookie": "signin-error=unauthorized; Path=/", + }, }, - }); + ); } } } diff --git a/app/routes/auth.google.signup.callback.tsx b/app/routes/auth.google.signup.callback.tsx index b4c013f..0f508bd 100644 --- a/app/routes/auth.google.signup.callback.tsx +++ b/app/routes/auth.google.signup.callback.tsx @@ -1,7 +1,8 @@ import { LoaderFunctionArgs, redirect } from "@remix-run/node"; - import { AuthorizationError } from "remix-auth"; + import { authenticator } from "~/services/auth.server"; +import { parseAuthenticationMessage } from "~/utils"; export async function loader({ request }: LoaderFunctionArgs) { try { @@ -12,11 +13,18 @@ export async function loader({ request }: LoaderFunctionArgs) { } catch (error) { if (error instanceof Response) return error; else if (error instanceof AuthorizationError) { - return redirect("/signup", { - headers: { - "Set-Cookie": `signup-error=${error.message}; Path=/`, + const errorMessageDetails = parseAuthenticationMessage(error.message); + + return redirect( + errorMessageDetails.organization_id != null + ? `/signup/${errorMessageDetails.organization_id}` + : "/signup", + { + headers: { + "Set-Cookie": `signup-error=${errorMessageDetails.message}; Path=/`, + }, }, - }); + ); } } } diff --git a/app/services/auth.strategies/google.signin.ts b/app/services/auth.strategies/google.signin.ts index 204d5d1..6ae5344 100644 --- a/app/services/auth.strategies/google.signin.ts +++ b/app/services/auth.strategies/google.signin.ts @@ -3,11 +3,11 @@ import { GetLoggedInUserInfoQuery, } from "@app-types/graphql"; -import { AuthorizationError } from "remix-auth"; import { GoogleStrategy } from "remix-auth-google"; import type { User } from "~/models/user"; -import { getApolloClient } from "~/utils"; +import { buildAuthenticationMessage, getApolloClient } from "~/utils"; import cookie from "cookie"; +import { AuthorizationError } from "remix-auth"; export const googleStrategy = new GoogleStrategy( { @@ -57,11 +57,15 @@ export const googleStrategy = new GoogleStrategy( }; return user; } catch (error: any) { - const msg = - "Error fetching loggined in user info through API. Details: " + - JSON.stringify({ url: process.env.GRAPHQL_SCHEMA_URL, error: error }); - console.error(msg); - throw new AuthorizationError(msg, error); + const errorMsg = buildAuthenticationMessage({ + organization_id: parseInt(organization_id), + message: + "Error fetching loggined in user info through API. Details: " + + JSON.stringify({ url: process.env.GRAPHQL_SCHEMA_URL, error: error }), + }); + + console.error(errorMsg); + throw new AuthorizationError(errorMsg, error); } }, ); diff --git a/app/services/auth.strategies/google.signup.ts b/app/services/auth.strategies/google.signup.ts index 1298e4a..006e4c9 100644 --- a/app/services/auth.strategies/google.signup.ts +++ b/app/services/auth.strategies/google.signup.ts @@ -4,11 +4,11 @@ import { SignUpMutationVariables, } from "@app-types/graphql"; -import { AuthorizationError } from "remix-auth"; import { GoogleStrategy } from "remix-auth-google"; import type { User } from "~/models/user"; -import { getApolloClient } from "~/utils"; +import { buildAuthenticationMessage, getApolloClient } from "~/utils"; import cookie from "cookie"; +import { AuthorizationError } from "remix-auth"; export const googleSignUpStrategy = new GoogleStrategy( { @@ -48,7 +48,8 @@ export const googleSignUpStrategy = new GoogleStrategy( variables: { input: { signUpInput: { - organizationId: organization_id > 0 ? organization_id : undefined, + organizationId: + organization_id >= 0 ? organization_id : undefined, }, }, }, @@ -75,9 +76,12 @@ export const googleSignUpStrategy = new GoogleStrategy( JSON.stringify({ url: process.env.GRAPHQL_SCHEMA_URL, error: error }); console.error(msg); - const errorMsg = error.graphQLErrors - ? error.graphQLErrors[0].message - : "unauthorized"; + const errorMsg = buildAuthenticationMessage({ + organization_id, + message: error.graphQLErrors + ? error.graphQLErrors[0].message + : "unauthorized", + }); throw new AuthorizationError(errorMsg, error); } diff --git a/app/utils/graphql.ts b/app/utils/graphql.ts index a93036c..819f59f 100644 --- a/app/utils/graphql.ts +++ b/app/utils/graphql.ts @@ -62,3 +62,23 @@ export function getPureObject( export function noNull(value: T | null): value is NonNullable { return value !== null; } +export type AuthenticationErrorMessage = { + message: string; + organization_id?: number; +}; + +export function parseAuthenticationMessage( + message: string, +): AuthenticationErrorMessage { + try { + return JSON.parse(message) as AuthenticationErrorMessage; + } catch (error) { + return { message }; + } +} + +export function buildAuthenticationMessage( + message: AuthenticationErrorMessage, +) { + return JSON.stringify(message); +}