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: