Skip to content
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

Avoid throwing not found error when not login in nextjs pages #494

Merged
merged 1 commit into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor Author

Choose a reason for hiding this comment

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

No need to fetch again workspace when is already fetched by the users of this function. Passing it by argument


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)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is already happening in the main layout. Is confusing having it here


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)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without session there is no user. But doesn't hurt this check

Copy link
Collaborator

Choose a reason for hiding this comment

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

this is why I mentioned to do the check in a middleware as redirects in pages always throw. Nextjs docs actually recommend doing any pre-render redirects in middleware (for the future)


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
Copy link
Collaborator

Choose a reason for hiding this comment

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

isn't that the default behaviour?

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 don't think. I had to add it. Not sure if default behaviour makes sense. I don't see recommended on radix docs
https://www.radix-ui.com/primitives/docs/guides/composition#composing-multiple-primitives

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 unsafelyGetCurrentUserFromDb({
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,
unsafelyGetCurrentUserFromDb,
} 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 unsafelyGetCurrentUserFromDb({
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
Loading