Skip to content

Instantly share code, notes, and snippets.

@MonteLogic
Created November 2, 2025 20:04
Show Gist options
  • Select an option

  • Save MonteLogic/2a0a90d4d2b77969cec57e9bc5cbd858 to your computer and use it in GitHub Desktop.

Select an option

Save MonteLogic/2a0a90d4d2b77969cec57e9bc5cbd858 to your computer and use it in GitHub Desktop.

Revisions

  1. MonteLogic created this gist Nov 2, 2025.
    129 changes: 129 additions & 0 deletions mobile-web-dev.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,129 @@

    // scripts/mobile-web-dev.js

    const { networkInterfaces } = require('os');
    const { spawn } = require('child_process');
    const net = require('net');

    // --- 1. Find the local IP address ---
    const nets = networkInterfaces();
    let localIp = 'localhost';

    // Iterate over network interfaces to find the non-internal IPv4 address
    for (const name of Object.keys(nets)) {
    for (const netInterface of nets[name]) {
    // Skip over internal (e.g. 127.0.0.1) and non-IPv4 addresses
    if (netInterface.family === 'IPv4' && !netInterface.internal) {
    localIp = netInterface.address;
    break;
    }
    }
    }

    // --- 2. Function to check if a port is available ---
    function isPortAvailable(port) {
    return new Promise((resolve) => {
    const server = net.createServer();

    server.once('error', (err) => {
    if (err.code === 'EADDRINUSE') {
    resolve(false); // Port is in use
    } else {
    resolve(false); // Other error, assume port not available
    }
    });

    server.once('listening', () => {
    server.once('close', () => {
    resolve(true); // Port is available
    });
    server.close();
    });

    server.listen(port);
    });
    }

    // --- 3. Find an available port starting from 3000 ---
    async function findAvailablePort(startPort = 3000, maxAttempts = 100) {
    console.log(`\nπŸ” Looking for an available port starting from ${startPort}...`);

    for (let port = startPort; port < startPort + maxAttempts; port++) {
    const available = await isPortAvailable(port);
    if (available) {
    console.log(`βœ… Found available port: ${port}\n`);
    return port;
    }
    process.stdout.write(` Port ${port} is in use, trying ${port + 1}...\r`);
    }

    throw new Error(`Could not find an available port after ${maxAttempts} attempts`);
    }

    // --- 4. Main function to start the server ---
    async function startServer() {
    try {
    const port = await findAvailablePort(3000);

    // Print the mobile URL
    console.log(`\nπŸš€ Server starting on port ${port}...`);
    console.log(`πŸ“± Access on your phone at: http://${localIp}:${port}\n`);
    console.log(`πŸ’» Or on your computer at: http://localhost:${port}\n`);

    // Security warning
    console.log(`\nπŸ”’ SECURITY WARNING:`);
    console.log(` This server is accessible to anyone on your local network (${localIp}:${port})`);
    console.log(` ⚠️ Risks:`);
    console.log(` - Dev server may expose stack traces and debug info`);
    console.log(` - Public API endpoints: /api/generate-basic-siddur, /blog, /`);
    console.log(` - Could be exploited for DoS (resource exhaustion)`);
    console.log(` βœ… Protections:`);
    console.log(` - Cannot directly hack your machine`);
    console.log(` - Clerk auth protects most routes`);
    console.log(` - Only accessible on local network (not internet)`);
    console.log(` πŸ’‘ Recommendations:`);
    console.log(` - Only use on trusted networks (home/work)`);
    console.log(` - Stop the server when not in use`);
    console.log(` - Don't use on public Wi-Fi\n`);

    console.log(`\n⚠️ IMPORTANT: For Clerk to work on your phone, add this URL to your Clerk dashboard:`);
    console.log(` - Go to https://dashboard.clerk.com β†’ Your App β†’ Settings β†’ Domains`);
    console.log(` - Add: ${localIp}:${port}`);
    console.log(` - Or add a wildcard pattern for your local network if needed\n`);

    // Set environment variables for Clerk to recognize the domain
    const env = {
    ...process.env,
    NEXT_PUBLIC_CLERK_FRONTEND_API: process.env.NEXT_PUBLIC_CLERK_FRONTEND_API || '',
    // Set the domain that Clerk will use for redirects
    CLERK_DOMAIN: `${localIp}:${port}`,
    };

    // Start the Next.js dev server
    const child = spawn('npx', ['next', 'dev', '-H', '0.0.0.0', '-p', port.toString()], {
    // Pipe the server's output (logs, errors, etc.) to this script's console
    stdio: 'inherit',
    env: env,
    });

    // Handle errors from the child process
    child.on('error', (err) => {
    console.error('Failed to start Next.js server:', err);
    process.exit(1);
    });

    // Handle process exit
    child.on('exit', (code) => {
    if (code !== 0) {
    console.error(`Next.js server exited with code ${code}`);
    process.exit(code);
    }
    });
    } catch (error) {
    console.error('Error starting server:', error.message);
    process.exit(1);
    }
    }

    // Start the server
    startServer();