fix(automate): redirect github auth flow to workspace if possible (#3632)

* fix(automate): redirect to workspace after auth if possible

* fix(automate): include session in github auth flow

* chore(automate): update props after merge

* fix(automate): pick => fragment

* fix(automate): use fragments correctly
This commit is contained in:
Chuck Driesler
2024-12-06 08:24:23 +00:00
committed by GitHub
parent 4e6f199882
commit 02be49f71f
14 changed files with 90 additions and 46 deletions
@@ -61,8 +61,12 @@ import {
useUpdateAutomateFunction
} from '~/lib/automate/composables/management'
import { useMutationLoading } from '@vue/apollo-composable'
import type { AutomateFunctionCreateDialogDoneStep_AutomateFunctionFragment } from '~~/lib/common/generated/gql/graphql'
import type {
AutomateFunctionCreateDialogDoneStep_AutomateFunctionFragment,
AutomateFunctionCreateDialog_WorkspaceFragment
} from '~~/lib/common/generated/gql/graphql'
import { useMixpanel } from '~/lib/core/composables/mp'
import { graphql } from '~/lib/common/generated/gql'
enum FunctionCreateSteps {
Authorize,
@@ -73,11 +77,19 @@ enum FunctionCreateSteps {
type DetailsFormValues = FunctionDetailsFormValues
graphql(`
fragment AutomateFunctionCreateDialog_Workspace on Workspace {
id
name
slug
}
`)
const props = defineProps<{
isAuthorized: boolean
templates: CreatableFunctionTemplate[]
githubOrgs: string[]
workspaceId?: string
workspace?: AutomateFunctionCreateDialog_WorkspaceFragment
}>()
const open = defineModel<boolean>('open', { required: true })
@@ -114,19 +126,19 @@ const onDetailsSubmit = handleDetailsSubmit(async (values) => {
templateId: selectedTemplate.value.id,
name: values.name,
/* eslint-disable-next-line camelcase */
workspace_id: props.workspaceId
workspace_id: props.workspace?.id
})
createdFunction.value = res
step.value++
if (!props.workspaceId) {
if (!props.workspace?.id) {
return
}
await updateFunction({
input: {
id: res.id,
workspaceIds: [props.workspaceId]
workspaceIds: [props.workspace.id]
}
})
})
@@ -184,7 +196,10 @@ const title = computed(() => {
})
const authorizeGithubUrl = computed(() => {
const redirectUrl = new URL(automateGithubAppAuthorizationRoute, apiBaseUrl)
const redirectUrl = new URL(
automateGithubAppAuthorizationRoute(props.workspace?.slug),
apiBaseUrl
)
return redirectUrl.toString()
})
@@ -18,12 +18,20 @@ import { difference, differenceBy } from 'lodash-es'
import { useForm } from 'vee-validate'
import { useUpdateAutomateFunction } from '~/lib/automate/composables/management'
import type { FunctionDetailsFormValues } from '~/lib/automate/helpers/functions'
import type { Workspace } from '~/lib/common/generated/gql/graphql'
import { graphql } from '~/lib/common/generated/gql'
import type { AutomateFunctionEditDialog_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
graphql(`
fragment AutomateFunctionEditDialog_Workspace on Workspace {
id
name
}
`)
const props = defineProps<{
model: FunctionDetailsFormValues
fnId: string
workspaces?: Pick<Workspace, 'id' | 'name'>[]
workspaces?: AutomateFunctionEditDialog_WorkspaceFragment[]
}>()
const open = defineModel<boolean>('open', { required: true })
const { handleSubmit, setValues } = useForm<FunctionDetailsFormValues>()
@@ -109,19 +109,11 @@
<script setup lang="ts">
import { ValidationHelpers } from '@speckle/ui-components'
import { isArray } from 'lodash-es'
import { graphql } from '~/lib/common/generated/gql'
import type { Workspace } from '~/lib/common/generated/gql/graphql'
graphql(`
fragment AutomateFunctionCreateDialog_Workspace on Workspace {
id
name
}
`)
import type { AutomateFunctionCreateDialog_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
defineProps<{
githubOrgs?: string[]
workspaces?: Pick<Workspace, 'id' | 'name'>[]
workspaces?: AutomateFunctionCreateDialog_WorkspaceFragment[]
}>()
const avatarEditMode = ref(false)
@@ -35,7 +35,7 @@
:is-authorized="!!activeUser?.automateInfo.hasAutomateGithubApp"
:github-orgs="activeUser?.automateInfo.availableGithubOrgs || []"
:templates="availableTemplates"
:workspace-id="workspace?.id"
:workspace="workspace"
/>
</div>
</template>
@@ -45,8 +45,8 @@ import { Roles, type Nullable, type Optional } from '@speckle/shared'
import { useDebouncedTextInput } from '@speckle/ui-components'
import { graphql } from '~/lib/common/generated/gql'
import type {
AutomateFunctionsPageHeader_QueryFragment,
Workspace
AutomateFunctionCreateDialog_WorkspaceFragment,
AutomateFunctionsPageHeader_QueryFragment
} from '~/lib/common/generated/gql/graphql'
import { workspaceFunctionsRoute, workspaceRoute } from '~/lib/common/helpers/route'
import { useMixpanel } from '~/lib/core/composables/mp'
@@ -74,7 +74,7 @@ graphql(`
const props = defineProps<{
activeUser: Optional<AutomateFunctionsPageHeader_QueryFragment['activeUser']>
serverInfo: Optional<AutomateFunctionsPageHeader_QueryFragment['serverInfo']>
workspace?: Pick<Workspace, 'id' | 'slug' | 'name'>
workspace?: AutomateFunctionCreateDialog_WorkspaceFragment
}>()
const search = defineModel<string>('search')
@@ -2,7 +2,7 @@
<div class="flex flex-col gap-y-4 md:gap-y-6">
<ProjectPageAutomationsHeader
v-model:search="search"
:workspace-slug="workspaceSlug"
:workspace-slug="workspace?.slug"
:show-empty-state="shouldShowEmptyState"
:creation-disabled-message="disableCreateAutomationMessage"
@new-automation="onNewAutomation"
@@ -13,7 +13,7 @@
<template v-else>
<ProjectPageAutomationsEmptyState
v-if="shouldShowEmptyState"
:workspace-slug="workspaceSlug"
:workspace-slug="workspace?.slug"
:hidden-actions="hiddenActions"
:disabled-actions="disabledActions"
@new-automation="onNewAutomation"
@@ -37,15 +37,15 @@
</template>
</template>
<AutomateAutomationCreateDialog
v-if="workspaceId"
v-if="workspace?.id"
v-model:open="showNewAutomationDialog"
:workspace-id="workspaceId"
:workspace-id="workspace?.id"
:preselected-project="project"
:preselected-function="newAutomationTargetFn"
/>
<AutomateFunctionCreateDialog
v-model:open="showNewFunctionDialog"
:workspace-id="workspaceId"
:workspace="workspace"
:is-authorized="isGithubAppConfigured"
:github-orgs="githubOrgs"
:templates="availableFunctionTemplates"
@@ -83,8 +83,7 @@ const { result, loading } = useQuery(
})
)
const workspaceId = computed(() => result.value?.project?.workspace?.id)
const workspaceSlug = computed(() => result.value?.project?.workspace?.slug)
const workspace = computed(() => result.value?.project?.workspace ?? undefined)
const workspaceFunctionCount = computed(
() => result.value?.project.workspace?.automateFunctions.totalCount ?? 0
@@ -27,7 +27,8 @@ const documents = {
"\n fragment AutomateAutomationCreateDialogFunctionParametersStep_AutomateFunction on AutomateFunction {\n id\n releases(limit: 1) {\n items {\n id\n inputSchema\n }\n }\n }\n": types.AutomateAutomationCreateDialogFunctionParametersStep_AutomateFunctionFragmentDoc,
"\n query AutomationCreateDialogFunctionsSearch(\n $workspaceId: String!\n $search: String\n $cursor: String = null\n ) {\n workspace(id: $workspaceId) {\n automateFunctions(limit: 20, filter: { search: $search }, cursor: $cursor) {\n cursor\n totalCount\n items {\n id\n ...AutomateAutomationCreateDialog_AutomateFunction\n }\n }\n }\n }\n": types.AutomationCreateDialogFunctionsSearchDocument,
"\n fragment AutomationsFunctionsCard_AutomateFunction on AutomateFunction {\n id\n name\n isFeatured\n description\n logo\n repo {\n id\n url\n owner\n name\n }\n }\n": types.AutomationsFunctionsCard_AutomateFunctionFragmentDoc,
"\n fragment AutomateFunctionCreateDialog_Workspace on Workspace {\n id\n name\n }\n": types.AutomateFunctionCreateDialog_WorkspaceFragmentDoc,
"\n fragment AutomateFunctionCreateDialog_Workspace on Workspace {\n id\n name\n slug\n }\n": types.AutomateFunctionCreateDialog_WorkspaceFragmentDoc,
"\n fragment AutomateFunctionEditDialog_Workspace on Workspace {\n id\n name\n }\n": types.AutomateFunctionEditDialog_WorkspaceFragmentDoc,
"\n fragment AutomateFunctionCreateDialogDoneStep_AutomateFunction on AutomateFunction {\n id\n repo {\n id\n url\n owner\n name\n }\n ...AutomationsFunctionsCard_AutomateFunction\n }\n": types.AutomateFunctionCreateDialogDoneStep_AutomateFunctionFragmentDoc,
"\n fragment AutomateFunctionCreateDialogTemplateStep_AutomateFunctionTemplate on AutomateFunctionTemplate {\n id\n title\n logo\n url\n }\n": types.AutomateFunctionCreateDialogTemplateStep_AutomateFunctionTemplateFragmentDoc,
"\n fragment AutomateFunctionPageHeader_Function on AutomateFunction {\n id\n name\n logo\n repo {\n id\n url\n owner\n name\n }\n releases(limit: 1) {\n totalCount\n }\n workspaceIds\n }\n": types.AutomateFunctionPageHeader_FunctionFragmentDoc,
@@ -258,7 +259,7 @@ const documents = {
"\n query ProjectModelVersions(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n id\n ...ProjectModelPageVersionsPagination\n }\n }\n": types.ProjectModelVersionsDocument,
"\n query ProjectModelsPage($projectId: String!) {\n project(id: $projectId) {\n id\n ...ProjectModelsPageHeader_Project\n ...ProjectModelsPageResults_Project\n }\n }\n": types.ProjectModelsPageDocument,
"\n query ProjectDiscussionsPage($projectId: String!) {\n project(id: $projectId) {\n id\n ...ProjectDiscussionsPageHeader_Project\n ...ProjectDiscussionsPageResults_Project\n }\n }\n": types.ProjectDiscussionsPageDocument,
"\n query ProjectAutomationsTab($projectId: String!) {\n project(id: $projectId) {\n id\n role\n models(limit: 1) {\n items {\n id\n }\n }\n automations(filter: null, cursor: null, limit: 5) {\n totalCount\n items {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n cursor\n }\n workspace {\n id\n slug\n automateFunctions(limit: 0) {\n totalCount\n }\n }\n ...FormSelectProjects_Project\n }\n ...AutomateFunctionsPageHeader_Query\n }\n": types.ProjectAutomationsTabDocument,
"\n query ProjectAutomationsTab($projectId: String!) {\n project(id: $projectId) {\n id\n role\n models(limit: 1) {\n items {\n id\n }\n }\n automations(filter: null, cursor: null, limit: 5) {\n totalCount\n items {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n cursor\n }\n workspace {\n id\n automateFunctions(limit: 0) {\n totalCount\n }\n ...AutomateFunctionCreateDialog_Workspace\n }\n ...FormSelectProjects_Project\n }\n ...AutomateFunctionsPageHeader_Query\n }\n": types.ProjectAutomationsTabDocument,
"\n query ProjectAutomationsTabAutomationsPagination(\n $projectId: String!\n $search: String = null\n $cursor: String = null\n ) {\n project(id: $projectId) {\n id\n automations(filter: $search, cursor: $cursor, limit: 5) {\n totalCount\n cursor\n items {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n }\n }\n }\n": types.ProjectAutomationsTabAutomationsPaginationDocument,
"\n query ProjectAutomationPage($projectId: String!, $automationId: String!) {\n project(id: $projectId) {\n id\n ...ProjectPageAutomationPage_Project\n automation(id: $automationId) {\n id\n ...ProjectPageAutomationPage_Automation\n }\n }\n }\n": types.ProjectAutomationPageDocument,
"\n query ProjectAutomationPagePaginatedRuns(\n $projectId: String!\n $automationId: String!\n $cursor: String = null\n ) {\n project(id: $projectId) {\n id\n automation(id: $automationId) {\n id\n runs(cursor: $cursor, limit: 10) {\n totalCount\n cursor\n items {\n id\n ...AutomationRunDetails\n }\n }\n }\n }\n }\n": types.ProjectAutomationPagePaginatedRunsDocument,
@@ -372,7 +373,7 @@ const documents = {
"\n query AutoAcceptableWorkspaceInvite(\n $token: String!\n $workspaceId: String!\n $options: WorkspaceInviteLookupOptions\n ) {\n workspaceInvite(token: $token, workspaceId: $workspaceId, options: $options) {\n id\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n }\n": types.AutoAcceptableWorkspaceInviteDocument,
"\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n project(id: $projectId) {\n comment(id: $commentId) {\n id\n ...LinkableComment\n }\n }\n }\n": types.ResolveCommentLinkDocument,
"\n fragment AutomateFunctionPage_AutomateFunction on AutomateFunction {\n id\n name\n description\n logo\n supportedSourceApps\n tags\n ...AutomateFunctionPageHeader_Function\n ...AutomateFunctionPageInfo_AutomateFunction\n ...AutomateAutomationCreateDialog_AutomateFunction\n creator {\n id\n }\n }\n": types.AutomateFunctionPage_AutomateFunctionFragmentDoc,
"\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n activeUser {\n workspaces {\n items {\n ...AutomateFunctionCreateDialog_Workspace\n }\n }\n }\n }\n": types.AutomateFunctionPageDocument,
"\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n activeUser {\n workspaces {\n items {\n ...AutomateFunctionCreateDialog_Workspace\n ...AutomateFunctionEditDialog_Workspace\n }\n }\n }\n }\n": types.AutomateFunctionPageDocument,
"\n query AutomateFunctionsPage($search: String, $cursor: String = null) {\n ...AutomateFunctionsPageItems_Query\n ...AutomateFunctionsPageHeader_Query\n }\n": types.AutomateFunctionsPageDocument,
"\n fragment ProjectPageProject on Project {\n id\n createdAt\n modelCount: models(limit: 0) {\n totalCount\n }\n commentThreadCount: commentThreads(limit: 0) {\n totalCount\n }\n workspace {\n id\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...ProjectsMoveToWorkspaceDialog_Project\n }\n": types.ProjectPageProjectFragmentDoc,
"\n fragment ProjectPageAutomationPage_Automation on Automation {\n id\n ...ProjectPageAutomationHeader_Automation\n ...ProjectPageAutomationFunctions_Automation\n ...ProjectPageAutomationRuns_Automation\n }\n": types.ProjectPageAutomationPage_AutomationFragmentDoc,
@@ -449,7 +450,11 @@ export function graphql(source: "\n fragment AutomationsFunctionsCard_AutomateF
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment AutomateFunctionCreateDialog_Workspace on Workspace {\n id\n name\n }\n"): (typeof documents)["\n fragment AutomateFunctionCreateDialog_Workspace on Workspace {\n id\n name\n }\n"];
export function graphql(source: "\n fragment AutomateFunctionCreateDialog_Workspace on Workspace {\n id\n name\n slug\n }\n"): (typeof documents)["\n fragment AutomateFunctionCreateDialog_Workspace on Workspace {\n id\n name\n slug\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment AutomateFunctionEditDialog_Workspace on Workspace {\n id\n name\n }\n"): (typeof documents)["\n fragment AutomateFunctionEditDialog_Workspace on Workspace {\n id\n name\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1373,7 +1378,7 @@ export function graphql(source: "\n query ProjectDiscussionsPage($projectId: St
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query ProjectAutomationsTab($projectId: String!) {\n project(id: $projectId) {\n id\n role\n models(limit: 1) {\n items {\n id\n }\n }\n automations(filter: null, cursor: null, limit: 5) {\n totalCount\n items {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n cursor\n }\n workspace {\n id\n slug\n automateFunctions(limit: 0) {\n totalCount\n }\n }\n ...FormSelectProjects_Project\n }\n ...AutomateFunctionsPageHeader_Query\n }\n"): (typeof documents)["\n query ProjectAutomationsTab($projectId: String!) {\n project(id: $projectId) {\n id\n role\n models(limit: 1) {\n items {\n id\n }\n }\n automations(filter: null, cursor: null, limit: 5) {\n totalCount\n items {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n cursor\n }\n workspace {\n id\n slug\n automateFunctions(limit: 0) {\n totalCount\n }\n }\n ...FormSelectProjects_Project\n }\n ...AutomateFunctionsPageHeader_Query\n }\n"];
export function graphql(source: "\n query ProjectAutomationsTab($projectId: String!) {\n project(id: $projectId) {\n id\n role\n models(limit: 1) {\n items {\n id\n }\n }\n automations(filter: null, cursor: null, limit: 5) {\n totalCount\n items {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n cursor\n }\n workspace {\n id\n automateFunctions(limit: 0) {\n totalCount\n }\n ...AutomateFunctionCreateDialog_Workspace\n }\n ...FormSelectProjects_Project\n }\n ...AutomateFunctionsPageHeader_Query\n }\n"): (typeof documents)["\n query ProjectAutomationsTab($projectId: String!) {\n project(id: $projectId) {\n id\n role\n models(limit: 1) {\n items {\n id\n }\n }\n automations(filter: null, cursor: null, limit: 5) {\n totalCount\n items {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n cursor\n }\n workspace {\n id\n automateFunctions(limit: 0) {\n totalCount\n }\n ...AutomateFunctionCreateDialog_Workspace\n }\n ...FormSelectProjects_Project\n }\n ...AutomateFunctionsPageHeader_Query\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1829,7 +1834,7 @@ export function graphql(source: "\n fragment AutomateFunctionPage_AutomateFunct
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n activeUser {\n workspaces {\n items {\n ...AutomateFunctionCreateDialog_Workspace\n }\n }\n }\n }\n"): (typeof documents)["\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n activeUser {\n workspaces {\n items {\n ...AutomateFunctionCreateDialog_Workspace\n }\n }\n }\n }\n"];
export function graphql(source: "\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n activeUser {\n workspaces {\n items {\n ...AutomateFunctionCreateDialog_Workspace\n ...AutomateFunctionEditDialog_Workspace\n }\n }\n }\n }\n"): (typeof documents)["\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n activeUser {\n workspaces {\n items {\n ...AutomateFunctionCreateDialog_Workspace\n ...AutomateFunctionEditDialog_Workspace\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
File diff suppressed because one or more lines are too long
@@ -62,7 +62,11 @@ export const projectWebhooksRoute = (projectId: string) =>
export const threadRedirectRoute = (projectId: string, threadId: string) =>
`/projects/${projectId}/threads/${threadId}`
export const automateGithubAppAuthorizationRoute = '/api/automate/auth/githubapp'
export const automateGithubAppAuthorizationRoute = (workspaceSlug?: string) => {
return `/api/automate/auth/githubapp${
workspaceSlug ? `?workspaceSlug=${workspaceSlug}` : ''
}`
}
export const automationFunctionsRoute = '/functions'
@@ -253,10 +253,10 @@ export const projectAutomationsTabQuery = graphql(`
}
workspace {
id
slug
automateFunctions(limit: 0) {
totalCount
}
...AutomateFunctionCreateDialog_Workspace
}
...FormSelectProjects_Project
}
@@ -62,6 +62,7 @@ const pageQuery = graphql(`
workspaces {
items {
...AutomateFunctionCreateDialog_Workspace
...AutomateFunctionEditDialog_Workspace
}
}
}
@@ -11,10 +11,14 @@ import { authMiddlewareCreator } from '@/modules/shared/middleware'
import { getRolesFactory } from '@/modules/shared/repositories/roles'
import { Roles, Scopes } from '@speckle/shared'
import { Application } from 'express'
import { validateRequest } from 'zod-express'
import { z } from 'zod'
import { sessionMiddlewareFactory } from '@/modules/auth/middleware'
export default (app: Application) => {
app.get(
'/api/automate/auth/githubapp',
sessionMiddlewareFactory(),
corsMiddleware(),
authMiddlewareCreator([
validateServerRoleBuilderFactory({
@@ -22,7 +26,14 @@ export default (app: Application) => {
})({ requiredRole: Roles.Server.Guest }),
validateScope({ requiredScope: Scopes.AutomateFunctions.Write })
]),
validateRequest({
query: z.object({
workspaceSlug: z.string().optional()
})
}),
async (req, res) => {
req.session.workspaceSlug = req.query.workspaceSlug
const startAuth = startAutomateFunctionCreatorAuthFactory({
createStoredAuthCode: createStoredAuthCodeFactory({
redis: getGenericRedis()
@@ -34,6 +45,7 @@ export default (app: Application) => {
app.get(
'/api/automate/ghAuthComplete',
sessionMiddlewareFactory(),
corsMiddleware(),
authMiddlewareCreator([
validateServerRoleBuilderFactory({
@@ -46,6 +46,7 @@ import { getFunctionsMarketplaceUrl } from '@/modules/core/helpers/routeHelper'
import { automateLogger } from '@/logging/logging'
import { CreateStoredAuthCode } from '@/modules/automate/domain/operations'
import { GetUser } from '@/modules/core/domain/users/operations'
import { noop } from 'lodash'
const mapGqlTemplateIdToExecEngineTemplateId = (
id: AutomateFunctionTemplateLanguage
@@ -272,9 +273,11 @@ export const handleAutomateFunctionCreatorAuthCallbackFactory =
} = req.query as Record<string, string>
const isSuccess = ghAuth === 'success'
const redirectUrl = getFunctionsMarketplaceUrl()
const redirectUrl = getFunctionsMarketplaceUrl(req.session.workspaceSlug)
redirectUrl.searchParams.set('ghAuth', isSuccess ? 'success' : ghAuth)
redirectUrl.searchParams.set('ghAuthDesc', isSuccess ? '' : ghAuthDesc)
req.session?.destroy?.(noop)
return res.redirect(redirectUrl.toString())
}
@@ -60,6 +60,7 @@ export function buildAbsoluteFrontendUrlFromPath(route: string): string {
return new URL(route, getFrontendOrigin()).toString()
}
export function getFunctionsMarketplaceUrl() {
return new URL('/functions', getFrontendOrigin())
export function getFunctionsMarketplaceUrl(workspaceSlug?: string) {
const path = workspaceSlug ? `/workspaces/${workspaceSlug}/functions` : '/functions'
return new URL(path, getFrontendOrigin())
}
@@ -15,6 +15,7 @@ import { omit } from 'lodash'
declare module 'express-session' {
interface SessionData {
workspaceId?: string
workspaceSlug?: string
ssoNonce?: string
ssoState?: SsoSessionState
oidcProvider?: OidcProvider