diff --git a/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/auth/urls-and-callbacks/page-client.tsx b/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/auth/urls-and-callbacks/page-client.tsx index bc7a0ebe1..24f1d173f 100644 --- a/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/auth/urls-and-callbacks/page-client.tsx +++ b/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/auth/urls-and-callbacks/page-client.tsx @@ -1,6 +1,6 @@ "use client"; -import { IconButton, List, ListItem, ListDivider, Input, FormControl, FormLabel } from "@mui/joy"; +import { IconButton, List, ListItem, ListDivider, Input, FormControl, FormLabel, Checkbox, Typography, Box } from "@mui/joy"; import React, { use, useState } from "react"; import { Paragraph } from "@/components/paragraph"; import { Icon } from "@/components/icon"; @@ -9,6 +9,7 @@ import { AsyncButton } from "@/components/async-button"; import { useStrictMemo } from "@stackframe/stack-shared/src/hooks/use-strict-memo"; import { SimpleCard } from "@/components/simple-card"; import { useAdminApp } from "../../useAdminInterface"; +import { SmartSwitch } from "@/components/smart-switch"; export default function UrlsAndCallbacksClient() { const stackAdminApp = useAdminApp(); @@ -32,47 +33,59 @@ export default function UrlsAndCallbacksClient() { return ( <> - - Domains and Handler - + + + { + await stackAdminApp.updateProject({ + config: { + allowLocalhost: event.target.checked, + }, + }); + setInvalidationCounter(invalidationCounter + 1); + }} + > + Allow all localhost callbacks for development + + - - - Please put in a list of your domains that you control and run the handler on. - + {domains.size >= 0 || ( + + {[...domains].map(({ domain }, i) => ( + + {i !== 0 && } + setDeleteDialogDomain(domain)} + > + + + } + > + {domain} + + + ))} + + )} - - {[...domains].map(({ domain }, i) => ( - - {i !== 0 && } - setDeleteDialogDomain(domain)} - > - - - } - > - {domain} - - - ))} - setAddNewDialogOpen(true)} - variant="plain" + variant="soft" color="neutral" + sx={{ mt: 3 }} > - Add new prefix + Add new domain diff --git a/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/settings/environment/page-client.tsx b/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/settings/environment/page-client.tsx index b15fcb538..8d56d727b 100644 --- a/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/settings/environment/page-client.tsx +++ b/packages/stack-server/src/app/(main)/(protected)/projects/[projectId]/settings/environment/page-client.tsx @@ -56,7 +56,7 @@ export default function EnvironmentClient() { }} > - Enable production mode + Enable production mode diff --git a/packages/stack-server/src/app/api/v1/projects/[projectId]/route.tsx b/packages/stack-server/src/app/api/v1/projects/[projectId]/route.tsx index 0d54db0fd..0053e0b9e 100644 --- a/packages/stack-server/src/app/api/v1/projects/[projectId]/route.tsx +++ b/packages/stack-server/src/app/api/v1/projects/[projectId]/route.tsx @@ -34,6 +34,7 @@ const putOrGetSchema = yup.object({ }) ).optional(), credentialEnabled: yup.boolean().optional(), + allowLocalhost: yup.boolean().optional(), }).optional(), }).nullable(), }); @@ -47,7 +48,7 @@ const handler = smartRouteHandler(async (req: NextRequest, options: { params: { "x-stack-admin-access-token": adminAccessToken, }, body, - } = await parseRequest(req, putOrGetSchema); + } = await parseRequest(req, putOrGetSchema); const { showDisabledOauth, ...update } = body ?? {}; @@ -59,6 +60,8 @@ const handler = smartRouteHandler(async (req: NextRequest, options: { params: { isProductionMode: update.isProductionMode, config: update.config && { domains: update.config.domains, + allowLocalhost: update.config.allowLocalhost, + credentialEnabled: update.config.credentialEnabled, oauthProviders: update.config.oauthProviders && update.config.oauthProviders.map((provider) => { if (sharedProviders.includes(provider.type as SharedProvider)) { return { @@ -86,7 +89,6 @@ const handler = smartRouteHandler(async (req: NextRequest, options: { params: { throw new StatusError(StatusError.BadRequest, "Invalid oauth provider type"); } }), - credentialEnabled: update.config.credentialEnabled, }, }; diff --git a/packages/stack-server/src/lib/projects.tsx b/packages/stack-server/src/lib/projects.tsx index 66fb46185..c1acdee05 100644 --- a/packages/stack-server/src/lib/projects.tsx +++ b/packages/stack-server/src/lib/projects.tsx @@ -358,14 +358,22 @@ export async function updateProject( } } + // Update credentialEnabled if (options.config?.credentialEnabled !== undefined) { - // Update credentialEnabled transaction.push(prismaClient.projectConfig.update({ where: { id: project.config.id }, data: { credentialEnabled: options.config.credentialEnabled }, })); } + // Update allowLocalhost + if (options.config?.allowLocalhost !== undefined) { + transaction.push(prismaClient.projectConfig.update({ + where: { id: project.config.id }, + data: { allowLocalhost: options.config.allowLocalhost }, + })); + } + if (options.isProductionMode !== undefined) { // Update production mode transaction.push(prismaClient.project.update({ diff --git a/packages/stack-shared/src/interface/adminInterface.ts b/packages/stack-shared/src/interface/adminInterface.ts index 5d67b1e20..0bf34f67e 100644 --- a/packages/stack-shared/src/interface/adminInterface.ts +++ b/packages/stack-shared/src/interface/adminInterface.ts @@ -39,6 +39,7 @@ export type ProjectUpdateOptions = Readonly<{ }[], oauthProviders?: OauthProviderUpdateOptions[], credentialEnabled?: boolean, + allowLocalhost?: boolean, }, }> @@ -131,7 +132,7 @@ export class StackAdminInterface extends StackServerInterface { headers: { "content-type": "application/json", }, - body: JSON.stringify(options || {}), + body: JSON.stringify(options ?? {}), }, null, );