Files
speckle-server/packages/frontend-2/middleware/requireValidProject.ts
T
Kristaps Fabians Geikins a6287fc06d feat(fe2 & server): saved views foundation (list & view) + bits n bobs (#5163)
* init db migration

* WIP store view

* create service call

* WIP insertion

* insert sort of works

* moving code arounmd

* creation tests

* avoid duplicate entries

* fixes from main

* basic group retrieval works

* group filtering works

* WIP view listing

* filter by acl

* fixes + WIP single group retrieval

* wip pivot

* more pivot query fixes

* tests fixed after pivot

* views list tests

* fixing test command

* business plan only checks

* more tests for coverage

* .dts import fix

* cli fix

* anutha one

* auth policy tests for business plan access

* WIP saved views panel base

* BE listing adjustments

* WIP group rendering

* group render done

* WIP post create cache updates

* listing fine?

* my vs theirs

* auto open

* minor fixes

* click load omg

* nicely loading views

* type fix

* less spammy loading

* another type fix:

* more lint fix

* test fix

* codecov disable

* moar coverage

* fix sidebar flashin

* more test coverage

* more test cvoverage

* minor adfjustments

* adj

* saved view wipe fixes

* CSR viewer

* more improvements

* extra feature flag checks

* lint fix

* feature flags fix

* more test fixes
2025-08-05 11:52:50 +03:00

88 lines
2.8 KiB
TypeScript

import type { Optional } from '@speckle/shared'
import { useApolloClientFromNuxt } from '~/lib/common/composables/graphql'
import {
convertThrowIntoFetchResult,
errorsToAuthResult
} from '~/lib/common/helpers/graphql'
import { projectAccessCheckQuery } from '~/lib/projects/graphql/queries'
import { WorkspaceSsoErrorCodes } from '~/lib/workspaces/helpers/types'
import { useSetActiveWorkspace } from '~/lib/user/composables/activeWorkspace'
/**
* Used in project page to validate that project ID refers to a valid project and redirects to 404 if not
*/
export default defineNuxtRouteMiddleware(async (to, from) => {
const projectId = to.params.id as string
// Check if embed token is present in URL
const embedToken = to.query.embedToken as Optional<string>
// Skip middleware validation for embed tokens - let the auth system handle them
if (embedToken) {
return
}
const client = useApolloClientFromNuxt()
const { setActiveWorkspace } = useSetActiveWorkspace()
const { isLoggedIn } = useActiveUser()
const isWorkspacesEnabled = useIsWorkspacesEnabled()
const isInPlaceNavigation = checkIfIsInPlaceNavigation(to, from)
const { data, errors } = await client
.query({
query: projectAccessCheckQuery,
variables: { id: projectId },
context: {
skipLoggingErrors: true
},
fetchPolicy: isInPlaceNavigation ? 'cache-first' : 'network-only'
})
.catch(convertThrowIntoFetchResult)
// we may not even get to the authResult because of project() resolver errors, hence the mapping
// from errors to authResult
const authResult = data?.project.permissions.canRead || errorsToAuthResult({ errors })
if (!authResult.authorized) {
switch (authResult.code) {
case WorkspaceSsoErrorCodes.SESSION_MISSING_OR_EXPIRED: {
// Redirect to the SSO error page
const payload = authResult.payload as Optional<{
workspaceSlug: string
}>
const workspaceSlug = payload?.workspaceSlug
if (workspaceSlug) {
return navigateTo(`/workspaces/${workspaceSlug}/sso/session-error`)
}
}
// eslint-disable-next-line no-fallthrough
case 'FORBIDDEN':
return abortNavigation(
createError({
statusCode: 403,
message: authResult.message
})
)
case 'STREAM_NOT_FOUND':
return abortNavigation(
createError({
statusCode: 404,
message: authResult.message
})
)
default:
return abortNavigation(
createError({
statusCode: 500,
message: authResult.message
})
)
}
}
if (isLoggedIn.value && isWorkspacesEnabled.value && !isInPlaceNavigation) {
await setActiveWorkspace({ id: data?.project.workspaceId })
}
})