diff --git a/docker-compose.yml b/docker-compose.yml index e9abd84006..98c2aa9c7e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,6 @@ services: - POSTGRES_URL=postgresql://postgres:postgres@db:5432/simstudio - BETTER_AUTH_URL=http://localhost:3000 - NEXT_PUBLIC_APP_URL=http://localhost:3000 - - NEXT_PUBLIC_DISABLE_AUTH=true depends_on: - db command: npm run dev diff --git a/sim/.env.example b/sim/.env.example index b24e11651a..e04f9e65ce 100644 --- a/sim/.env.example +++ b/sim/.env.example @@ -4,7 +4,6 @@ DATABASE_URL="postgresql://postgres:password@localhost:5432/postgres" # Authentication (Required) BETTER_AUTH_SECRET=your_secret_key # Use `openssl rand -hex 32` to generate, or visit https://www.better-auth.com/docs/installation BETTER_AUTH_URL=http://localhost:3000 -# NEXT_PUBLIC_DISABLE_AUTH=true # Uncomment to disable authentication for local development # Email Provider (Optional) # RESEND_API_KEY= # Uncomment and add your key from https://resend.com to send actual emails @@ -14,4 +13,4 @@ BETTER_AUTH_URL=http://localhost:3000 NEXT_PUBLIC_APP_URL=http://localhost:3000 # StackBlitz (Webcontainer) API Key (Optional, for handling sandboxed code execution for functions/custom-tools) -WEBCONTAINER_CLIENT_ID=your_webcontainer_client_id \ No newline at end of file +# WEBCONTAINER_CLIENT_ID= # Uncomment and add your key from https://stackblitz.com/docs/webcontainer-api#webcontainer-client-id \ No newline at end of file diff --git a/sim/app/lib/auth.ts b/sim/app/lib/auth.ts index cf7871e4c5..c1a8ed595c 100644 --- a/sim/app/lib/auth.ts +++ b/sim/app/lib/auth.ts @@ -17,7 +17,10 @@ const resend = ? new Resend(process.env.RESEND_API_KEY) : { emails: { - send: async (...args: any[]) => logger.info('Email sent:', args), + send: async (...args: any[]) => { + logger.info('Email would have been sent in production. Details:', args) + return { id: 'local-dev-mode' } + }, }, } @@ -91,6 +94,17 @@ export const auth = betterAuth({ throw new Error('Email is required') } + // In development with no RESEND_API_KEY, log verification code + if (!process.env.RESEND_API_KEY || process.env.RESEND_API_KEY.trim() === '') { + logger.info('🔑 VERIFICATION CODE FOR LOGIN/SIGNUP', { + email: data.email, + otp: data.otp, + type: data.type, + }) + return + } + + // In production, send an actual email const result = await resend.emails.send({ from: 'Sim Studio ', to: data.email, @@ -111,7 +125,6 @@ export const auth = betterAuth({ logger.error('Error sending verification code:', { error, email: data.email, - otp: data.otp, }) throw error } diff --git a/sim/app/scripts/build-standalone.js b/sim/app/scripts/build-standalone.js deleted file mode 100755 index c2b1359e93..0000000000 --- a/sim/app/scripts/build-standalone.js +++ /dev/null @@ -1,281 +0,0 @@ -#!/usr/bin/env node - -/** - * Build Standalone App Script - * - * This script builds a standalone version of Sim Studio that can be run without a database. - * It creates a tarball that includes: - * 1. A pre-built Next.js application - * 2. A simple Express server to serve the application - * 3. Configuration to use browser localStorage for data persistence - */ - -const fs = require('fs') -const path = require('path') -const { execSync } = require('child_process') -const tar = require('tar') -const crypto = require('crypto') - -// Configuration -const ROOT_DIR = path.resolve(__dirname, '..') -const STANDALONE_DIR = path.join(ROOT_DIR, 'packages/simstudio/standalone') -const STANDALONE_PACKAGE_JSON = path.join(STANDALONE_DIR, 'package.json') -const STANDALONE_SERVER_JS = path.join(STANDALONE_DIR, 'server.js') -const OUTPUT_TARBALL = path.join(ROOT_DIR, 'sim-standalone.tar.gz') - -// Ensure the standalone directory exists -if (!fs.existsSync(STANDALONE_DIR)) { - fs.mkdirSync(STANDALONE_DIR, { recursive: true }) -} - -// Clean the standalone directory first -if (fs.existsSync(STANDALONE_DIR)) { - fs.rmSync(STANDALONE_DIR, { recursive: true, force: true }) -} -fs.mkdirSync(STANDALONE_DIR, { recursive: true }) - -// Build the Next.js app with local storage mode -console.log('Building Next.js app in standalone mode...') -try { - // Instead of using static export, we'll build a regular Next.js app - // and then copy the necessary files to run it with a simple Express server - execSync('npm run build', { - cwd: ROOT_DIR, - stdio: 'inherit', - env: { - ...process.env, - USE_LOCAL_STORAGE: 'true', - NEXT_PUBLIC_USE_LOCAL_STORAGE: 'true', - DISABLE_DB_SYNC: 'true', - NODE_ENV: 'production', - }, - }) -} catch (error) { - console.error('Failed to build Next.js app:', error) - console.log('Continuing with the standalone build process...') -} - -// Copy the built app to the standalone directory -console.log('Copying built app to standalone directory...') -try { - // Copy the .next directory - if (fs.existsSync(path.join(ROOT_DIR, '.next'))) { - execSync(`cp -r ${path.join(ROOT_DIR, '.next')} ${path.join(STANDALONE_DIR, '.next')}`) - } - - // Copy the public directory - if (fs.existsSync(path.join(ROOT_DIR, 'public'))) { - execSync(`cp -r ${path.join(ROOT_DIR, 'public')} ${path.join(STANDALONE_DIR, 'public')}`) - } - - // Copy necessary files for standalone operation - if (fs.existsSync(path.join(ROOT_DIR, 'next.config.ts'))) { - execSync( - `cp ${path.join(ROOT_DIR, 'next.config.ts')} ${path.join(STANDALONE_DIR, 'next.config.ts')}` - ) - } -} catch (error) { - console.error('Failed to copy built app:', error) - process.exit(1) -} - -// Create a package.json for the standalone app -console.log('Creating package.json for standalone app...') -const packageJson = { - name: 'sim-studio-standalone', - version: '0.1.0', - private: true, - scripts: { - start: 'node server.js', - }, - dependencies: { - express: '^4.18.2', - next: '^15.2.0', - react: '^18.2.0', - 'react-dom': '^18.2.0', - compression: '^1.7.4', - 'serve-favicon': '^2.5.0', - typescript: '^5.7.3', - }, -} - -fs.writeFileSync(STANDALONE_PACKAGE_JSON, JSON.stringify(packageJson, null, 2)) - -// Create a simple Express server to serve the static files -console.log('Creating server.js for standalone app...') -const serverJs = ` -const express = require('express'); -const next = require('next'); -const path = require('path'); -const fs = require('fs'); -const compression = require('compression'); -const favicon = require('serve-favicon'); -const crypto = require('crypto'); - -// Register TypeScript compiler -require('typescript'); - -// Set environment variables for standalone mode -process.env.USE_LOCAL_STORAGE = 'true'; -process.env.NEXT_PUBLIC_USE_LOCAL_STORAGE = 'true'; -process.env.DISABLE_DB_SYNC = 'true'; -process.env.DISABLE_AUTH = 'true'; -process.env.NEXT_PUBLIC_DISABLE_AUTH = 'true'; - -const port = process.env.SIM_STUDIO_PORT || 3000; -const dev = false; // Always run in production mode - -// Initialize Next.js -const app = next({ dev, dir: __dirname }); -const handle = app.getRequestHandler(); - -app.prepare().then(() => { - const server = express(); - - // Enable compression - server.use(compression()); - - // Serve favicon - if (fs.existsSync(path.join(__dirname, 'public', 'favicon.ico'))) { - server.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); - } - - // Middleware to set environment variables for each request - server.use((req, res, next) => { - // These will be available to the API routes - process.env.USE_LOCAL_STORAGE = 'true'; - process.env.NEXT_PUBLIC_USE_LOCAL_STORAGE = 'true'; - process.env.DISABLE_DB_SYNC = 'true'; - process.env.DISABLE_AUTH = 'true'; - process.env.NEXT_PUBLIC_DISABLE_AUTH = 'true'; - next(); - }); - - // Redirect root path to a new workflow - server.get('/', (req, res) => { - // Generate a UUID for the new workflow - const uuid = crypto.randomUUID(); - console.log(\`Redirecting to new workflow: \${uuid}\`); - - // Use 302 redirect to ensure it's not cached and is followed - return res.redirect(302, \`/w/\${uuid}\`); - }); - - // Handle all other requests with Next.js - server.all('*', (req, res) => { - return handle(req, res); - }); - - // Start the server - server.listen(port, (err) => { - if (err) throw err; - console.log(\`> Sim Studio standalone server ready on http://localhost:\${port}\`); - console.log('> Running in local storage mode - all data will be stored in the browser'); - console.log('> Authentication is disabled - anyone can access the app'); - console.log('> Root path (/) will redirect to a new workflow automatically'); - }); -}); -` - -fs.writeFileSync(STANDALONE_SERVER_JS, serverJs) - -// Skip creating .env file for the standalone app -console.log('Skipping .env file creation for standalone app...') - -// Create a tsconfig.json for the standalone app -console.log('Creating tsconfig.json for standalone app...') -const tsconfigJson = { - compilerOptions: { - target: 'es5', - lib: ['dom', 'dom.iterable', 'esnext'], - allowJs: true, - skipLibCheck: true, - strict: true, - forceConsistentCasingInFileNames: true, - noEmit: true, - esModuleInterop: true, - module: 'esnext', - moduleResolution: 'node', - resolveJsonModule: true, - isolatedModules: true, - jsx: 'preserve', - incremental: true, - plugins: [ - { - name: 'next', - }, - ], - paths: { - '@/*': ['./*'], - }, - }, - include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'], - exclude: ['node_modules'], -} - -fs.writeFileSync(path.join(STANDALONE_DIR, 'tsconfig.json'), JSON.stringify(tsconfigJson, null, 2)) - -// Create a README.md for the standalone app -console.log('Creating README.md for standalone app...') -const readmeFile = `# Sim Studio Standalone - -This is a standalone version of Sim Studio that runs without a database. All data is stored in your browser's localStorage. - -## Getting Started - -1. Install dependencies: - \`\`\` - npm install - \`\`\` - -2. Start the server: - \`\`\` - npm start - \`\`\` - -3. Open your browser to http://localhost:3000 - -## Features - -- **Full Functionality**: All features of Sim Studio are available, including API routes -- **No Authentication Required**: Just start building workflows right away -- **Local Storage**: All your workflows and settings are stored in your browser's localStorage -- **Drag and Drop Interface**: Easily create and connect workflow blocks -- **Real-time Execution**: Test your workflows as you build them - -## Environment Variables - -You can customize the app by setting these environment variables: - -- \`SIM_STUDIO_PORT\`: Change the port (default: 3000) - -## How It Works - -When you start the app, it will: -1. Automatically redirect you to a new workflow with a unique ID -2. Allow you to drag and drop blocks to build your workflow -3. Save all your work to localStorage in your browser -4. Let you execute workflows directly in the browser - -No database or authentication is required! -` - -fs.writeFileSync(path.join(STANDALONE_DIR, 'README.md'), readmeFile) - -// Create the tarball -console.log('Creating tarball...') -try { - // Remove any .env files before creating the tarball - console.log('Removing any .env files from the standalone directory...') - execSync('find ' + STANDALONE_DIR + ' -name ".env*" -type f -delete') - - // Create the tarball - execSync( - `tar -czf ${OUTPUT_TARBALL} -C ${path.dirname(STANDALONE_DIR)} ${path.basename(STANDALONE_DIR)}` - ) - console.log(`Standalone app built and packaged to ${OUTPUT_TARBALL}`) - console.log('You can now upload this file to GitHub releases') -} catch (error) { - console.error('Failed to create tarball:', error) - process.exit(1) -} diff --git a/sim/middleware.ts b/sim/middleware.ts index e53086be6e..293cddf297 100644 --- a/sim/middleware.ts +++ b/sim/middleware.ts @@ -7,11 +7,6 @@ export async function middleware(request: NextRequest) { return NextResponse.redirect(new URL('/w/1', request.url)) } - // Skip auth check if DISABLE_AUTH is set (for standalone mode) - if (process.env.DISABLE_AUTH === 'true' || process.env.NEXT_PUBLIC_DISABLE_AUTH === 'true') { - return NextResponse.next() - } - const sessionCookie = getSessionCookie(request) if (!sessionCookie) { return NextResponse.redirect(new URL('/login', request.url))