UNPKG

google-oauth-cli-generator

Version:

CLI tool to quickly set up Google OAuth authentication for hackathons and projects

400 lines (364 loc) 10.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateReactTemplate = generateReactTemplate; const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); async function generateReactTemplate(data) { const { projectPath } = data; const frontendPath = path_1.default.join(projectPath, 'frontend'); // Create frontend directory structure await fs_extra_1.default.ensureDir(path_1.default.join(frontendPath, 'src', 'components')); await fs_extra_1.default.ensureDir(path_1.default.join(frontendPath, 'public')); // Generate package.json const packageJson = { name: `${data.config.projectName}-frontend`, version: '1.0.0', private: true, dependencies: { 'react': '^18.2.0', 'react-dom': '^18.2.0', 'axios': '^1.5.0', 'react-router-dom': '^6.15.0' }, devDependencies: { '@types/react': '^18.2.22', '@types/react-dom': '^18.2.7', '@vitejs/plugin-react': '^4.0.4', 'typescript': '^5.2.2', 'vite': '^4.4.9' }, scripts: { 'dev': 'vite', 'build': 'tsc && vite build', 'preview': 'vite preview' } }; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'package.json'), JSON.stringify(packageJson, null, 2)); // Generate vite.config.ts const viteConfig = `import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], server: { port: 3000, proxy: { '/api': { target: 'http://localhost:5000', changeOrigin: true } } } })`; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'vite.config.ts'), viteConfig); // Generate tsconfig.json const tsConfig = { compilerOptions: { target: 'ES2020', useDefineForClassFields: true, lib: ['ES2020', 'DOM', 'DOM.Iterable'], module: 'ESNext', skipLibCheck: true, moduleResolution: 'bundler', allowImportingTsExtensions: true, resolveJsonModule: true, isolatedModules: true, noEmit: true, jsx: 'react-jsx', strict: true, noUnusedLocals: true, noUnusedParameters: true, noFallthroughCasesInSwitch: true }, include: ['src'], references: [{ path: './tsconfig.node.json' }] }; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2)); // Generate index.html const indexHtml = `<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>${data.config.projectName}</title> </head> <body> <div id="root"></div> <script type="module" src="/src/main.tsx"></script> </body> </html>`; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'index.html'), indexHtml); // Generate main.tsx const mainTsx = `import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.tsx' import './index.css' ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <App /> </React.StrictMode>, )`; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'src', 'main.tsx'), mainTsx); // Generate App.tsx const appTsx = `import { useState, useEffect } from 'react' import LoginButton from './components/LoginButton' import UserProfile from './components/UserProfile' import './App.css' interface User { id: string; name: string; email: string; picture: string; } function App() { const [user, setUser] = useState<User | null>(null); const [loading, setLoading] = useState(true); useEffect(() => { checkAuthStatus(); }, []); const checkAuthStatus = async () => { try { const response = await fetch('/api/auth/user', { credentials: 'include' }); if (response.ok) { const userData = await response.json(); setUser(userData); } } catch (error) { console.error('Auth check failed:', error); } finally { setLoading(false); } }; const handleLogin = () => { window.location.href = '/api/auth/google'; }; const handleLogout = async () => { try { await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' }); setUser(null); } catch (error) { console.error('Logout failed:', error); } }; if (loading) { return ( <div className="app"> <div className="loading">Loading...</div> </div> ); } return ( <div className="app"> <header className="app-header"> <h1>🚀 ${data.config.projectName}</h1> <p>Google OAuth Authentication Demo</p> </header> <main className="app-main"> {user ? ( <UserProfile user={user} onLogout={handleLogout} /> ) : ( <LoginButton onLogin={handleLogin} /> )} </main> </div> ) } export default App`; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'src', 'App.tsx'), appTsx); // Generate LoginButton component const loginButtonTsx = `interface LoginButtonProps { onLogin: () => void; } function LoginButton({ onLogin }: LoginButtonProps) { return ( <div className="login-container"> <h2>Welcome! Please sign in</h2> <button className="google-login-btn" onClick={onLogin} > <svg className="google-icon" viewBox="0 0 24 24"> <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/> <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/> <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/> <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/> </svg> Sign in with Google </button> </div> ); } export default LoginButton`; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'src', 'components', 'LoginButton.tsx'), loginButtonTsx); // Generate UserProfile component const userProfileTsx = `interface User { id: string; name: string; email: string; picture: string; } interface UserProfileProps { user: User; onLogout: () => void; } function UserProfile({ user, onLogout }: UserProfileProps) { return ( <div className="profile-container"> <div className="profile-card"> <img src={user.picture} alt={user.name} className="profile-picture" /> <h2>Welcome, {user.name}!</h2> <p className="profile-email">{user.email}</p> <button className="logout-btn" onClick={onLogout} > Sign Out </button> </div> </div> ); } export default UserProfile`; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'src', 'components', 'UserProfile.tsx'), userProfileTsx); // Generate CSS files const appCss = `#root { max-width: 1280px; margin: 0 auto; padding: 2rem; text-align: center; } .app { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; } .app-header h1 { font-size: 3rem; margin-bottom: 0.5rem; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } .app-header p { font-size: 1.2rem; opacity: 0.9; margin-bottom: 2rem; } .loading { font-size: 1.5rem; animation: pulse 1.5s ease-in-out infinite alternate; } @keyframes pulse { from { opacity: 0.6; } to { opacity: 1; } } .login-container { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border-radius: 20px; padding: 3rem; box-shadow: 0 8px 32px rgba(0,0,0,0.1); border: 1px solid rgba(255, 255, 255, 0.2); } .login-container h2 { margin-bottom: 2rem; font-size: 1.8rem; } .google-login-btn { display: flex; align-items: center; justify-content: center; gap: 12px; background: white; color: #333; border: none; border-radius: 12px; padding: 16px 32px; font-size: 16px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 12px rgba(0,0,0,0.15); } .google-login-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.2); } .google-icon { width: 20px; height: 20px; } .profile-container { display: flex; justify-content: center; align-items: center; } .profile-card { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border-radius: 20px; padding: 3rem; box-shadow: 0 8px 32px rgba(0,0,0,0.1); border: 1px solid rgba(255, 255, 255, 0.2); text-align: center; } .profile-picture { width: 120px; height: 120px; border-radius: 50%; margin-bottom: 1.5rem; border: 4px solid rgba(255, 255, 255, 0.3); box-shadow: 0 4px 12px rgba(0,0,0,0.2); } .profile-card h2 { margin-bottom: 0.5rem; font-size: 2rem; } .profile-email { opacity: 0.8; margin-bottom: 2rem; font-size: 1.1rem; } .logout-btn { background: rgba(255, 255, 255, 0.2); color: white; border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 12px; padding: 12px 24px; font-size: 16px; cursor: pointer; transition: all 0.3s ease; } .logout-btn:hover { background: rgba(255, 255, 255, 0.3); transform: translateY(-2px); }`; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'src', 'App.css'), appCss); const indexCss = `body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } * { box-sizing: border-box; }`; await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'src', 'index.css'), indexCss); } //# sourceMappingURL=react-template.js.map