Skip to content

Commit

Permalink
Avoid throwing error when user has no session
Browse files Browse the repository at this point in the history
Throwing error end in Sentry with a NotFound. What we want to do is
redirect user to /login
  • Loading branch information
andresgutgon committed Oct 23, 2024
1 parent cda5630 commit 2ad5785
Show file tree
Hide file tree
Showing 17 changed files with 73 additions and 37 deletions.
1 change: 1 addition & 0 deletions apps/web/src/actions/copilot/refinePrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export const refinePromptAction = authProcedure
)

const sdk = await createSdk({
workspace: ctx.workspace,
apiKey: env.DATASET_GENERATOR_WORKSPACE_APIKEY,
projectId: env.COPILOT_PROJECT_ID,
}).then((r) => r.unwrap())
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/actions/copilot/requestSuggestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const requestSuggestionAction = authProcedure
)

const sdk = await createSdk({
workspace: ctx.workspace,
apiKey: env.DATASET_GENERATOR_WORKSPACE_APIKEY,
projectId: env.COPILOT_PROJECT_ID,
}).then((r) => r.unwrap())
Expand Down
6 changes: 4 additions & 2 deletions apps/web/src/actions/datasets/generateDataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { env } from '@latitude-data/env'
import { ChainEventDto } from '@latitude-data/sdk'
import slugify from '@sindresorhus/slugify'
import { createSdk } from '$/app/(private)/_lib/createSdk'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { getCurrentUserOrError } from '$/services/auth/getCurrentUser'
import { createStreamableValue } from 'ai/rsc'

type GenerateDatasetActionProps = {
Expand All @@ -39,12 +39,14 @@ export async function generateDatasetAction({
}

let response: Dataset | undefined
const { user, workspace } = await getCurrentUser()
const { user, workspace } = await getCurrentUserOrError()

const stream = createStreamableValue<
{ event: StreamEventTypes; data: ChainEventDto },
Error
>()
const sdk = await createSdk({
workspace,
apiKey: env.DATASET_GENERATOR_WORKSPACE_APIKEY,
projectId: env.DATASET_GENERATOR_PROJECT_ID,
__internal: { source: LogSources.Playground },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const generateSuggestedEvaluationsAction = authProcedure
documentContent: z.string(),
}),
)
.handler(async ({ input }) => {
.handler(async ({ input, ctx }) => {
if (!env.COPILOT_WORKSPACE_API_KEY) {
throw new BadRequestError('COPILOT_WORKSPACE_API_KEY is not set')
}
Expand All @@ -43,6 +43,7 @@ export const generateSuggestedEvaluationsAction = authProcedure
}

const sdk = await createSdk({
workspace: ctx.workspace,
apiKey: env.COPILOT_WORKSPACE_API_KEY,
projectId: env.COPILOT_PROJECT_ID,
}).then((r) => r.unwrap())
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/actions/procedures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import {
ProjectsRepository,
} from '@latitude-data/core/repositories'
import * as Sentry from '@sentry/nextjs'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { getCurrentUserOrError } from '$/services/auth/getCurrentUser'
import { z } from 'zod'
import { createServerActionProcedure } from 'zsa'

export const errorHandlingProcedure = createServerActionProcedure()
.onError(async (error) => {
try {
const data = await getCurrentUser()
const data = await getCurrentUserOrError()

Sentry.captureException(error, {
user: {
Expand All @@ -30,7 +30,7 @@ export const authProcedure = createServerActionProcedure(
errorHandlingProcedure,
).handler(async () => {
try {
const data = await getCurrentUser()
const data = await getCurrentUserOrError()

return {
session: data.session!,
Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/actions/sdk/addMessagesAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
type StreamChainResponse,
} from '@latitude-data/sdk'
import { createSdk } from '$/app/(private)/_lib/createSdk'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { getCurrentUserOrError } from '$/services/auth/getCurrentUser'
import { createStreamableValue, StreamableValue } from 'ai/rsc'

type AddMessagesActionProps = {
Expand All @@ -27,7 +27,7 @@ export async function addMessagesAction({
documentLogUuid,
messages,
}: AddMessagesActionProps) {
const { workspace, user } = await getCurrentUser()
const { workspace, user } = await getCurrentUserOrError()

publisher.publishLater({
type: 'chatMessageRequested',
Expand All @@ -40,6 +40,7 @@ export async function addMessagesAction({
})

const sdk = await createSdk({
workspace,
__internal: { source: LogSources.Playground },
}).then((r) => r.unwrap())
const stream = createStreamableValue<
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/actions/sdk/generateDatasetPreviewAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BadRequestError } from '@latitude-data/core/lib/errors'
import { env } from '@latitude-data/env'
import { ChainEventDto } from '@latitude-data/sdk'
import { createSdk } from '$/app/(private)/_lib/createSdk'
import { getCurrentUserOrError } from '$/services/auth/getCurrentUser'
import { createStreamableValue } from 'ai/rsc'

type RunDocumentActionProps = {
Expand Down Expand Up @@ -32,7 +33,9 @@ export async function generateDatasetPreviewAction({
throw new BadRequestError('DATASET_GENERATOR_WORKSPACE_APIKEY is not set')
}

const { workspace } = await getCurrentUserOrError()
const sdk = await createSdk({
workspace,
apiKey: env.DATASET_GENERATOR_WORKSPACE_APIKEY,
projectId: env.DATASET_GENERATOR_PROJECT_ID,
__internal: { source: LogSources.Playground },
Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/actions/sdk/runDocumentAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { LogSources, StreamEventTypes } from '@latitude-data/core/browser'
import { publisher } from '@latitude-data/core/events/publisher'
import { Latitude, type ChainEventDto } from '@latitude-data/sdk'
import { createSdk } from '$/app/(private)/_lib/createSdk'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { getCurrentUserOrError } from '$/services/auth/getCurrentUser'
import { createStreamableValue, StreamableValue } from 'ai/rsc'

type RunDocumentActionProps = {
Expand All @@ -27,7 +27,7 @@ export async function runDocumentAction({
commitUuid,
parameters,
}: RunDocumentActionProps) {
const { workspace, user } = await getCurrentUser()
const { workspace, user } = await getCurrentUserOrError()

publisher.publishLater({
type: 'documentRunRequested',
Expand All @@ -42,6 +42,7 @@ export async function runDocumentAction({
})

const sdk = await createSdk({
workspace,
projectId,
__internal: { source: LogSources.Playground },
}).then((r) => r.unwrap())
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/(admin)/backoffice/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default async function AdminLayout({
if (!data.session) redirect(ROUTES.root)

const { user, workspace } = await getCurrentUser()
if (!user.admin) redirect(ROUTES.root)
if (!user?.admin) redirect(ROUTES.root)

return (
<SessionProvider currentUser={user} workspace={workspace}>
Expand Down
12 changes: 6 additions & 6 deletions apps/web/src/app/(private)/_lib/createSdk.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { LogSources } from '@latitude-data/core/browser'
import { LogSources, Workspace } from '@latitude-data/core/browser'
import { compactObject } from '@latitude-data/core/lib/compactObject'
import { NotFoundError } from '@latitude-data/core/lib/errors'
import { Result } from '@latitude-data/core/lib/Result'
import { LatitudeApiKeysRepository } from '@latitude-data/core/repositories'
import { env } from '@latitude-data/env'
import { Latitude } from '@latitude-data/sdk'
import { getCurrentUser } from '$/services/auth/getCurrentUser'

// NOTE: this would be a great candidate for a cache function with redis
async function getLatitudeApiKey() {
const { workspace } = await getCurrentUser()
async function getLatitudeApiKey(workspace: Workspace) {
const repo = new LatitudeApiKeysRepository(workspace.id)
const firstApiKey = await repo.findFirst().then((r) => r.unwrap())

Expand All @@ -23,16 +21,18 @@ async function getLatitudeApiKey() {
}

export async function createSdk({
workspace,
projectId,
apiKey,
__internal,
}: {
workspace: Workspace
projectId?: number
apiKey?: string
__internal?: { source: LogSources }
} = {}) {
}) {
if (!apiKey) {
const result = await getLatitudeApiKey()
const result = await getLatitudeApiKey(workspace)
if (result.error) return result

apiKey = result.value.token
Expand Down
6 changes: 0 additions & 6 deletions apps/web/src/app/(private)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ import {
} from '@latitude-data/web-ui'
import { AppTabs } from '$/app/(private)/AppTabs'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { getSession } from '$/services/auth/getSession'
import { ROUTES } from '$/services/routes'
import Link from 'next/link'
import { redirect } from 'next/navigation'

import { getActiveProjectsCached } from '../_data-access'
import { ProjectsTable } from './_components/ProjectsTable'
Expand All @@ -20,12 +18,8 @@ export default async function DashboardLayout({
}: Readonly<{
children: ReactNode
}>) {
const data = await getSession()
if (!data.session) return redirect(ROUTES.auth.login)

const { workspace } = await getCurrentUser()
const projects = await getActiveProjectsCached({ workspaceId: workspace.id })

return (
<Container>
<AppTabs />
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/app/(private)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export default async function PrivateLayout({
if (!data.session) return redirect(ROUTES.auth.login)

const { workspace, user } = await getCurrentUser()
if (!user) return redirect(ROUTES.auth.login)

const supportIdentity = createSupportUserIdentity(user)

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default function WorkspaceApiKeys() {
</TableCell>
<TableCell>
<Tooltip
asChild
trigger={
<Button
variant='ghost'
Expand Down
18 changes: 17 additions & 1 deletion apps/web/src/data-access/users.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { database } from '@latitude-data/core/client'
import { SessionData, unsafelyGetUser } from '@latitude-data/core/data-access'
import {
SessionData,
unsafelyFindWorkspacesFromUser,
unsafelyGetUser,
} from '@latitude-data/core/data-access'
import { NotFoundError } from '@latitude-data/core/lib/errors'
import { Result } from '@latitude-data/core/lib/Result'
import { PromisedResult } from '@latitude-data/core/lib/Transaction'
Expand Down Expand Up @@ -72,3 +76,15 @@ export async function getCurrentUserFromDB({
return Result.error(err as Error)
}
}

export async function getUnsafelyCurrentUserFromDb({
userId,
}: {
userId: string | undefined
}) {
const user = await unsafelyGetUser(userId)
const workspaces = await unsafelyFindWorkspacesFromUser(userId)
const workspace = workspaces[0]

return { user, workspace }
}
4 changes: 2 additions & 2 deletions apps/web/src/middlewares/authHandler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { getCurrentUserOrError } from '$/services/auth/getCurrentUser'
import { NextRequest, NextResponse } from 'next/server'

export function authHandler(handler: any) {
return async (req: NextRequest, { ...rest }) => {
let user, workspace
try {
const { user: uzer, workspace: workzpace } = await getCurrentUser()
const { user: uzer, workspace: workzpace } = await getCurrentUserOrError()
user = uzer
workspace = workzpace
} catch (error) {
Expand Down
31 changes: 21 additions & 10 deletions apps/web/src/services/auth/getCurrentUser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { cache } from 'react'

import { User, Workspace } from '@latitude-data/core/browser'
import { getCurrentUserFromDB } from '$/data-access'
import {
getCurrentUserFromDB,
getUnsafelyCurrentUserFromDb,
} from '$/data-access'
import { Session } from 'lucia'

import { getSession } from './getSession'
Expand All @@ -11,25 +14,33 @@ export type SessionData = {
user: User
workspace: Workspace
}

/**
* This method is used in places where session has been already checked
* An example of it are inner `app/(private)` routes. Session was check in main
* layout. We don't want to throw a not found error if session is not present
*/
export const getCurrentUser = cache(async () => {
const sessionData = await getSession()
const result = await getCurrentUserFromDB({ userId: sessionData?.user?.id })
const { user, workspace } = result.unwrap()
const { user, workspace } = await getUnsafelyCurrentUserFromDb({
userId: sessionData?.user?.id,
})

return {
session: sessionData.session!,
user,
workspace,
user: user!,
workspace: workspace!,
}
})

export const getSafeCurrentUser = cache(async () => {
/**
* This method is used in places where session has to be check
* If something is not present it will throw an error
*/
export const getCurrentUserOrError = cache(async () => {
const sessionData = await getSession()
const result = await getCurrentUserFromDB({ userId: sessionData?.user?.id })

if (result.error) return null

const { user, workspace } = result.value
const { user, workspace } = result.unwrap()

return {
session: sessionData.session!,
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/data-access/workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ export async function unsafelyFindWorkspace(id: number, db = database) {
}

export async function unsafelyFindWorkspacesFromUser(
userId: string,
userId: string | undefined,
db = database,
) {
if (!userId) return []

return await db
.select(workspacesDtoColumns)
.from(workspaces)
Expand Down

0 comments on commit 2ad5785

Please sign in to comment.