diff --git a/apps/sim/app/(auth)/signup/page.tsx b/apps/sim/app/(auth)/signup/page.tsx index c469074e30f..c415382ef2b 100644 --- a/apps/sim/app/(auth)/signup/page.tsx +++ b/apps/sim/app/(auth)/signup/page.tsx @@ -1,3 +1,4 @@ +import { env, isTruthy } from '@/lib/env' import { getOAuthProviderStatus } from '../components/oauth-provider-checker' import SignupForm from './signup-form' @@ -7,6 +8,10 @@ export const dynamic = 'force-dynamic' export default async function SignupPage() { const { githubAvailable, googleAvailable, isProduction } = await getOAuthProviderStatus() + if (isTruthy(env.DISABLE_REGISTRATION)) { + return
Registration is disabled, please contact your admin.
+ } + return ( { - logger.info('Notification store state:', { addNotification: !!addNotification }) + logger.info('Notification store state:', { + addNotification: !!addNotification, + }) }, [addNotification]) useEffect(() => { @@ -154,7 +157,10 @@ export function useVerification({ // Set both state variables to ensure the error shows setIsInvalidOtp(true) setErrorMessage(message) - logger.info('Error state after API error:', { isInvalidOtp: true, errorMessage: message }) + logger.info('Error state after API error:', { + isInvalidOtp: true, + errorMessage: message, + }) // Clear the OTP input on invalid code setOtp('') } @@ -173,7 +179,10 @@ export function useVerification({ // Set both state variables to ensure the error shows setIsInvalidOtp(true) setErrorMessage(message) - logger.info('Error state after caught error:', { isInvalidOtp: true, errorMessage: message }) + logger.info('Error state after caught error:', { + isInvalidOtp: true, + errorMessage: message, + }) // Clear the OTP input on error setOtp('') @@ -218,7 +227,7 @@ export function useVerification({ logger.info('Auto-verifying user', { email: storedEmail }) } - const isDevOrDocker = !isProduction || process.env.DOCKER_BUILD === 'true' + const isDevOrDocker = !isProduction || isTruthy(env.DOCKER_BUILD) // Auto-verify and redirect in development/docker environments if (isDevOrDocker || !hasResendKey) { diff --git a/apps/sim/lib/auth.ts b/apps/sim/lib/auth.ts index 6b0be3a33cf..8031024697e 100644 --- a/apps/sim/lib/auth.ts +++ b/apps/sim/lib/auth.ts @@ -2,7 +2,7 @@ import { stripe } from '@better-auth/stripe' import { betterAuth } from 'better-auth' import { drizzleAdapter } from 'better-auth/adapters/drizzle' import { nextCookies } from 'better-auth/next-js' -import { emailOTP, genericOAuth, organization } from 'better-auth/plugins' +import { createAuthMiddleware, emailOTP, genericOAuth, organization } from 'better-auth/plugins' import { and, eq } from 'drizzle-orm' import { headers } from 'next/headers' import { Resend } from 'resend' @@ -16,7 +16,7 @@ import { import { createLogger } from '@/lib/logs/console-logger' import { db } from '@/db' import * as schema from '@/db/schema' -import { env } from './env' +import { env, isTruthy } from './env' import { getEmailDomain } from './urls/utils' const logger = createLogger('Auth') @@ -93,10 +93,15 @@ export const auth = betterAuth({ }, } } - logger.info('No organizations found for user', { userId: session.userId }) + logger.info('No organizations found for user', { + userId: session.userId, + }) return { data: session } } catch (error) { - logger.error('Error setting active organization', { error, userId: session.userId }) + logger.error('Error setting active organization', { + error, + userId: session.userId, + }) return { data: session } } }, @@ -158,6 +163,14 @@ export const auth = betterAuth({ } }, }, + hooks: { + before: createAuthMiddleware(async (ctx) => { + if (ctx.path.startsWith('/sign-up') && isTruthy(env.DISABLE_REGISTRATION)) + throw new Error('Registration is disabled, please contact your admin.') + + return + }), + }, plugins: [ nextCookies(), emailOTP({ @@ -469,7 +482,9 @@ export const auth = betterAuth({ userId = decodedToken.sub } } catch (e) { - logger.warn('Failed to decode Supabase ID token', { error: e }) + logger.warn('Failed to decode Supabase ID token', { + error: e, + }) } } diff --git a/apps/sim/lib/env.ts b/apps/sim/lib/env.ts index 2212dac5e39..91b7195fca6 100644 --- a/apps/sim/lib/env.ts +++ b/apps/sim/lib/env.ts @@ -11,6 +11,7 @@ export const env = createEnv({ DATABASE_URL: z.string().url(), BETTER_AUTH_URL: z.string().url(), BETTER_AUTH_SECRET: z.string().min(32), + DISABLE_REGISTRATION: z.boolean().optional(), ENCRYPTION_KEY: z.string().min(32), POSTGRES_URL: z.string().url().optional(), @@ -110,3 +111,7 @@ export const env = createEnv({ NEXT_PUBLIC_GOOGLE_PROJECT_NUMBER: getEnv('NEXT_PUBLIC_GOOGLE_PROJECT_NUMBER'), }, }) + +// Needing this utility because t3-env is returning string for boolean values. +export const isTruthy = (value: string | boolean | number | undefined) => + typeof value === 'string' ? value === 'true' || value === '1' : Boolean(value) diff --git a/apps/sim/next.config.ts b/apps/sim/next.config.ts index ec1968ce8c5..8c39a1f4493 100644 --- a/apps/sim/next.config.ts +++ b/apps/sim/next.config.ts @@ -1,7 +1,7 @@ import path from 'path' import { withSentryConfig } from '@sentry/nextjs' import type { NextConfig } from 'next' -import { env } from './lib/env' +import { env, isTruthy } from './lib/env' const nextConfig: NextConfig = { devIndicators: false, @@ -13,12 +13,12 @@ const nextConfig: NextConfig = { ], }, typescript: { - ignoreBuildErrors: env.DOCKER_BUILD, + ignoreBuildErrors: isTruthy(env.DOCKER_BUILD), }, eslint: { - ignoreDuringBuilds: env.DOCKER_BUILD, + ignoreDuringBuilds: isTruthy(env.DOCKER_BUILD), }, - output: env.DOCKER_BUILD ? 'standalone' : undefined, + output: isTruthy(env.DOCKER_BUILD) ? 'standalone' : undefined, turbopack: { resolveExtensions: ['.tsx', '.ts', '.jsx', '.js', '.mjs', '.json'], }, @@ -99,7 +99,10 @@ const nextConfig: NextConfig = { source: '/api/workflows/:id/execute', headers: [ { key: 'Access-Control-Allow-Origin', value: '*' }, - { key: 'Access-Control-Allow-Methods', value: 'GET,POST,OPTIONS,PUT' }, + { + key: 'Access-Control-Allow-Methods', + value: 'GET,POST,OPTIONS,PUT', + }, { key: 'Access-Control-Allow-Headers', value: