UNPKG

@bitcoin-os/bapps

Version:

Create new Bitcoin apps with Next.js and Bitcoin OS integration

410 lines (344 loc) • 12.6 kB
#!/usr/bin/env node const { Command } = require('commander'); const inquirer = require('inquirer'); const fs = require('fs-extra'); const path = require('path'); const chalk = require('chalk'); const { execSync } = require('child_process'); const program = new Command(); const TEMPLATES = { basic: 'Basic Bitcoin App', writer: 'Document Editor (like Bitcoin Writer)', drive: 'File Storage (like Bitcoin Drive)', email: 'Email Client (like Bitcoin Email)', music: 'Media Player (like Bitcoin Music)', calendar: 'Calendar App (like Bitcoin Calendar)', exchange: 'Trading Platform (like Bitcoin Exchange)', wallet: 'Wallet Interface (like Bitcoin Wallet)', custom: 'Custom App (minimal setup)' }; const COLORS = { 'text-orange-500': '#f97316', 'text-yellow-500': '#eab308', 'text-red-500': '#ef4444', 'text-purple-500': '#a855f7', 'text-fuchsia-500': '#d946ef', 'text-green-500': '#22c55e', 'text-blue-500': '#3b82f6', 'text-sky-400': '#38bdf8', 'text-cyan-500': '#06b6d4', 'text-pink-500': '#ec4899' }; program .name('create-bapp') .description('Create a new Bitcoin app with Next.js and Bitcoin OS integration') .version('1.0.0') .argument('[app-name]', 'Name of the app to create') .option('-t, --template <template>', 'Template to use') .option('-y, --yes', 'Skip prompts and use defaults') .action(async (appName, options) => { try { console.log(chalk.bitcoin('₿'), chalk.bold.magenta('Bitcoin OS App Creator')); console.log(chalk.gray('Creating a new Bitcoin app with full OS integration\n')); let answers = {}; if (!appName || !options.yes) { const questions = []; if (!appName) { questions.push({ type: 'input', name: 'appName', message: 'What is your app name?', default: appName || 'my-bitcoin-app', validate: (input) => { if (!input.trim()) return 'App name is required'; if (!/^[a-z0-9-]+$/.test(input)) return 'App name must be lowercase with hyphens only'; return true; } }); } if (!options.template) { questions.push({ type: 'list', name: 'template', message: 'Choose a template:', choices: Object.entries(TEMPLATES).map(([key, value]) => ({ name: value, value: key })) }); } questions.push( { type: 'input', name: 'displayName', message: 'Display name for your app:', default: (answers) => { const name = answers.appName || appName; return name.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1) ).join(' '); } }, { type: 'list', name: 'colorScheme', message: 'Choose a color scheme:', choices: Object.entries(COLORS).map(([key, value]) => ({ name: `${key} (${value})`, value: key })) }, { type: 'input', name: 'description', message: 'App description:', default: 'A Bitcoin app built with Bitcoin OS' }, { type: 'confirm', name: 'includeAuth', message: 'Include HandCash authentication?', default: true }, { type: 'confirm', name: 'includeBlockchain', message: 'Include blockchain integration?', default: true }, { type: 'confirm', name: 'includePayments', message: 'Include payment processing?', default: false } ); answers = await inquirer.prompt(questions); } const config = { appName: appName || answers.appName, template: options.template || answers.template || 'basic', displayName: answers.displayName || appName || 'My Bitcoin App', colorScheme: answers.colorScheme || 'text-orange-500', description: answers.description || 'A Bitcoin app built with Bitcoin OS', includeAuth: options.yes ? true : answers.includeAuth, includeBlockchain: options.yes ? true : answers.includeBlockchain, includePayments: options.yes ? false : answers.includePayments }; await createApp(config); } catch (error) { console.error(chalk.red('Error creating app:'), error.message); process.exit(1); } }); async function createApp(config) { const { appName, template, displayName, colorScheme, description, includeAuth, includeBlockchain, includePayments } = config; console.log(chalk.cyan(`\nCreating ${displayName}...`)); const appPath = path.resolve(appName); if (await fs.pathExists(appPath)) { throw new Error(`Directory ${appName} already exists`); } // Create Next.js app console.log(chalk.yellow('šŸ“¦ Creating Next.js app...')); execSync(`npx create-next-app@latest ${appName} --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"`, { stdio: 'inherit' }); // Install Bitcoin OS dependencies console.log(chalk.yellow('šŸ”§ Installing Bitcoin OS dependencies...')); process.chdir(appPath); const dependencies = [ '@bitcoin-os/bridge@latest', 'lucide-react@latest' ]; if (includeAuth) { dependencies.push('@handcash/handcash-connect@latest'); } if (includeBlockchain) { dependencies.push('bsv@latest'); } if (includePayments) { dependencies.push('@moneybutton/api-client@latest'); } execSync(`npm install ${dependencies.join(' ')}`, { stdio: 'inherit' }); // Generate app files await generateAppFiles(config, appPath); console.log(chalk.green('\nāœ… Bitcoin app created successfully!')); console.log(chalk.cyan('\nNext steps:')); console.log(chalk.white(` cd ${appName}`)); console.log(chalk.white(' npm run dev')); console.log(chalk.gray('\nYour app will be available at http://localhost:3000')); console.log(chalk.gray(`\nApp includes:`)); console.log(chalk.gray(` • Full Bitcoin OS integration (taskbar, dock, sidebar)`)); console.log(chalk.gray(` • ${template} template`)); if (includeAuth) console.log(chalk.gray(` • HandCash authentication`)); if (includeBlockchain) console.log(chalk.gray(` • Blockchain integration`)); if (includePayments) console.log(chalk.gray(` • Payment processing`)); } async function generateAppFiles(config, appPath) { const { appName, template, displayName, colorScheme, description, includeAuth } = config; // Generate layout.tsx const layoutContent = `import type { Metadata } from 'next' import { Inter } from 'next/font/google' import { BitcoinOSProvider } from '@bitcoin-os/bridge' import './globals.css' const inter = Inter({ subsets: ['latin'] }) export const metadata: Metadata = { title: '${displayName}', description: '${description}', icons: { icon: '/favicon.ico', }, } export default function RootLayout({ children, }: { children: React.ReactNode }) { const bitcoinOSConfig = { context: { appName: '${displayName}', exchangeUrl: 'https://${appName}-exchange.vercel.app', // Your custom exchange branding: { name: '${displayName}', color: '${colorScheme.includes('text-') ? colorScheme.replace('text-', '') : colorScheme}' } }, showDevSidebar: true, showDock: true, showPocBar: true, customStyles: \` .bitcoin-symbol { color: ${COLORS[colorScheme] || '#f97316'} !important; } \` } return ( <html lang="en"> <body className={inter.className}> <BitcoinOSProvider config={bitcoinOSConfig}> {children} </BitcoinOSProvider> </body> </html> ) }`; await fs.writeFile(path.join(appPath, 'src/app/layout.tsx'), layoutContent); // Generate main page const pageContent = `'use client' import { useState${includeAuth ? ', useEffect' : ''} } from 'react' ${includeAuth ? `import { HandCashConnect } from '@handcash/handcash-connect'` : ''} export default function Home() { ${includeAuth ? ` const [handcashConnect, setHandcashConnect] = useState<HandCashConnect | null>(null) const [user, setUser] = useState<any>(null) useEffect(() => { const hcc = new HandCashConnect({ appId: process.env.NEXT_PUBLIC_HANDCASH_APP_ID || 'your-app-id', appSecret: process.env.NEXT_PUBLIC_HANDCASH_APP_SECRET || 'your-app-secret' }) setHandcashConnect(hcc) }, []) const handleAuth = async () => { if (handcashConnect) { try { const authToken = await handcashConnect.requestPermissions() const account = handcashConnect.getAccountFromAuthToken(authToken) const profile = await account.profile.getCurrentProfile() setUser(profile) } catch (error) { console.error('Auth error:', error) } } }` : ''} return ( <main className="flex min-h-screen flex-col items-center justify-center p-24"> <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm"> <h1 className="text-4xl font-bold text-center mb-8" style={{ color: '${COLORS[colorScheme] || '#f97316'}' }}> ₿ ${displayName} </h1> <div className="text-center mb-8"> <p className="text-lg text-gray-600 dark:text-gray-300"> ${description} </p> </div> ${includeAuth ? ` <div className="text-center"> {user ? ( <div className="bg-green-100 dark:bg-green-900 p-4 rounded-lg"> <p className="text-green-800 dark:text-green-200"> Welcome, {user.displayName || user.handle}! </p> </div> ) : ( <button onClick={handleAuth} className="bg-orange-500 hover:bg-orange-600 text-white font-bold py-2 px-4 rounded" > Connect with HandCash </button> )} </div>` : ` <div className="text-center"> <p className="text-gray-500 dark:text-gray-400"> Start building your Bitcoin app here! </p> </div>`} </div> </main> ) }`; await fs.writeFile(path.join(appPath, 'src/app/page.tsx'), pageContent); // Update package.json with app info const packageJsonPath = path.join(appPath, 'package.json'); const packageJson = await fs.readJson(packageJsonPath); packageJson.name = appName; packageJson.description = description; packageJson.keywords = [ ...packageJson.keywords || [], 'bitcoin', 'bitcoin-os', 'bapp' ]; await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); // Create .env.local template if auth is included if (includeAuth) { const envContent = `# HandCash Configuration NEXT_PUBLIC_HANDCASH_APP_ID=your-app-id NEXT_PUBLIC_HANDCASH_APP_SECRET=your-app-secret # Get your credentials from https://dashboard.handcash.io `; await fs.writeFile(path.join(appPath, '.env.local.example'), envContent); } // Create README const readmeContent = `# ${displayName} ${description} Built with [Bitcoin OS](https://bitcoin-os.vercel.app) and Next.js. ## Features - šŸŽØ Full Bitcoin OS integration (taskbar, dock, sidebar) - ⚔ Next.js 14 with App Router - šŸ’Ž TypeScript support - šŸŽ­ Tailwind CSS styling ${includeAuth ? '- šŸ” HandCash authentication' : ''} ${config.includeBlockchain ? '- ā›“ļø Bitcoin SV blockchain integration' : ''} ${config.includePayments ? '- šŸ’° Payment processing' : ''} ## Getting Started \`\`\`bash npm run dev \`\`\` Open [http://localhost:3000](http://localhost:3000) to view your app. ${includeAuth ? `## Setup 1. Copy \`.env.local.example\` to \`.env.local\` 2. Get your HandCash credentials from [dashboard.handcash.io](https://dashboard.handcash.io) 3. Update the environment variables ` : ''}## Learn More - [Bitcoin OS Documentation](https://bitcoin-os.vercel.app/docs) - [Next.js Documentation](https://nextjs.org/docs) - [HandCash Connect](https://docs.handcash.io/docs/handcash-connect) ## Deploy Deploy easily with [Vercel](https://vercel.com/new) or any Next.js hosting platform. --- Built with ā¤ļø by [The Bitcoin Corporation](https://thebitcoincorporation.com) `; await fs.writeFile(path.join(appPath, 'README.md'), readmeContent); } program.parse();