Skip to content

Commit

Permalink
Use invite page for handling redirects
Browse files Browse the repository at this point in the history
Signed-off-by: Dinika Saxena <[email protected]>
  • Loading branch information
Dinika authored and Dinika Saxena committed Apr 15, 2024
1 parent 4651d98 commit 0924d8f
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 164 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs

EXPOSE 8000

ENV PORT 8000
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]
162 changes: 0 additions & 162 deletions src/app/api/invite/route.ts

This file was deleted.

12 changes: 12 additions & 0 deletions src/app/invite/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ErrorBoundary } from 'react-error-boundary';

import SimpleErrorComponent from '@/components/GenericErrorFallback';
import InviteLoader from '@/components/Invites';

export default function InvitePage() {
return (
<ErrorBoundary FallbackComponent={SimpleErrorComponent}>
<InviteLoader />
</ErrorBoundary>
);
}
31 changes: 31 additions & 0 deletions src/components/Invites/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { captureException } from '@sentry/nextjs';

import { InviteResponse } from '@/types/virtual-lab/invites';
import { VlmError } from '@/types/virtual-lab/common';
import { virtualLabApi } from '@/config';

export const processInvite = async (
sessionToken: string,
inviteToken: string
): Promise<InviteResponse | VlmError> => {
return fetch(`${virtualLabApi.url}/invites?token=${inviteToken}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${sessionToken}`,
},
})
.then<InviteResponse | VlmError>((response) => {
// Valid response or client errors (40X)
return response.json();
})
.catch((err) => {
// Server errors (50X)
captureException(
new Error(`User could not accept invite ${inviteToken} because of an unknown error`),
{ extra: err }
);
return { error_code: 'INTERNAL_SERVER_ERROR', message: 'Vlm server is down' } as VlmError;
});
};
58 changes: 58 additions & 0 deletions src/components/Invites/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use client';

import { LoadingOutlined } from '@ant-design/icons';
import { Spin } from 'antd';
import { useAtomValue } from 'jotai';
import { useRouter, useSearchParams } from 'next/navigation';
import { useEffect } from 'react';
import { captureException } from '@sentry/nextjs';

import { processInvite } from './api';
import { getErrorUrl, getLabUrl, getProjectUrl } from './utils';
import sessionAtom from '@/state/session';
import { isVlmInviteResponse } from '@/types/virtual-lab/invites';

export default function InviteLoader() {
const session = useAtomValue(sessionAtom);
const inviteToken = useSearchParams().get('token');
const router = useRouter();

useEffect(() => {
if (!session?.accessToken) {
return router.push(getErrorUrl(null, session?.accessToken, inviteToken));
}
if (!inviteToken) {
return router.push(getErrorUrl(null, session?.accessToken, inviteToken));
}

processInvite(session.accessToken, inviteToken).then((response) => {
if (!isVlmInviteResponse(response)) {
router.push(getErrorUrl(response, session?.accessToken, inviteToken));
return;
}

switch (response.data.origin) {
case 'Lab':
router.push(getLabUrl(response.data));
return;
case 'Project':
router.push(getProjectUrl(response.data));
return;
default:
captureException(
new Error(
`User could not accept invite ${inviteToken} because unknown origin returned by server`
),
{ extra: response.data.origin }
);
router.push(getErrorUrl(response, session?.accessToken, inviteToken));
}
});
}, [session, inviteToken, router]);

return (
<div className="relative flex h-screen w-screen items-center justify-center bg-primary-8">
<Spin indicator={<LoadingOutlined style={{ color: '#fff', fontSize: 24 }} spin />} />
</div>
);
}
60 changes: 60 additions & 0 deletions src/components/Invites/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { captureException } from '@sentry/nextjs';

import { InviteData, InviteErrorCodes } from '@/types/virtual-lab/invites';
import { VlmError, isVlmError } from '@/types/virtual-lab/common';

const errorPath = '/';
const projectPath = (labId: string, projectId: string) =>
`/virtual-lab/lab/${labId}/project/${projectId!}/home`;
const labPath = (labId: string) => `/virtual-lab/lab/${labId}/lab`;

export const getLabUrl = (vlmData: InviteData): string => {
const { status, virtual_lab_id: labId, origin } = vlmData;
if (status === 'already_accepted') {
return `${errorPath}?errorcode=${InviteErrorCodes.INVITE_ALREADY_ACCEPTED}&origin=${origin}&lab_id=${labId}`;
}

return `${labPath(labId)}?invite_accepted=true`;
};

export const getProjectUrl = (vlmData: InviteData): string => {
const { status, virtual_lab_id: labId, project_id: projectId, origin } = vlmData;
if (status === 'already_accepted') {
return `${errorPath}?errorcode=${InviteErrorCodes.INVITE_ALREADY_ACCEPTED}&origin=${origin}&lab_id=${labId}&project_id=${projectId}`;
}

return `${projectPath(labId, projectId!)}?invite_accepted=true`;
};

export const getErrorUrl = (
response: VlmError | any,
accessToken?: string,
inviteToken?: string | null
): string => {
if (!accessToken) {
return `${errorPath}?errorcode=${InviteErrorCodes.UNAUTHORIZED}`;
}
if (!inviteToken) {
return `${errorPath}?errorcode=${InviteErrorCodes.INVALID_LINK}`;
}
if (isVlmError(response)) {
captureException(new Error(`User invite could not be accepted because of VLM Error`), {
extra: { vliError: response, invite: inviteToken },
});

if (response.error_code === 'AUTHORIZATION_ERROR') {
return `${errorPath}?errorcode=${InviteErrorCodes.UNAUTHORIZED}`;
}

if (response.error_code === 'TOKEN_EXPIRED') {
return `${errorPath}?errorcode=${InviteErrorCodes.TOKEN_EXPIRED}`;
}
return `${errorPath}?errorcode=${InviteErrorCodes.UNKNOWN}`;
}

captureException(new Error(`User invite could not be accepted because of Unknown error`), {
extra: { error: response, invite: inviteToken },
});

return `${errorPath}?errorcode=${InviteErrorCodes.UNKNOWN}`;
};
2 changes: 1 addition & 1 deletion src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const config = {
matcher: [
'/',
'/main',
'/api/invite',
'/invite',
'/(build|simulate|simulations|main|explore|experiment-designer|svc|virtual-lab)/(.*)',
],
};

0 comments on commit 0924d8f

Please sign in to comment.