forgestack-os-cli 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,6 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generateAuth = generateAuth;
7
- const path_1 = __importDefault(require("path"));
8
- const fs_extra_1 = __importDefault(require("fs-extra"));
9
- async function generateAuth(config, frontendDir, backendDir) {
1
+ import path from 'path';
2
+ import fs from 'fs-extra';
3
+ export async function generateAuth(config, frontendDir, backendDir) {
10
4
  switch (config.auth) {
11
5
  case 'jwt':
12
6
  await generateJWTAuth(config, frontendDir, backendDir);
@@ -26,11 +20,62 @@ async function generateAuth(config, frontendDir, backendDir) {
26
20
  default:
27
21
  throw new Error(`Unsupported auth: ${config.auth}`);
28
22
  }
23
+ // Ensure frontend pages exist for all auth types
24
+ await generateFrontendAuthPages(config, frontendDir);
29
25
  }
30
- async function generateJWTAuth(config, frontendDir, backendDir) {
31
- // Backend: Auth routes
32
- const authRoutes = `import { Router } from 'express';
33
- import bcrypt from 'bcrypt';
26
+ async function generateJWTAuth(config, _frontendDir, backendDir) {
27
+ // Backend: Auth logic
28
+ if (config.backend === 'express') {
29
+ const routesDir = path.join(backendDir, 'src', 'routes');
30
+ await fs.ensureDir(routesDir);
31
+ await fs.writeFile(path.join(routesDir, 'auth.ts'), getExpressAuthRoutes(config));
32
+ }
33
+ else if (config.backend === 'fastify') {
34
+ const routesDir = path.join(backendDir, 'src', 'routes');
35
+ await fs.ensureDir(routesDir);
36
+ await fs.writeFile(path.join(routesDir, 'auth.ts'), getFastifyAuthRoutes(config));
37
+ }
38
+ else if (config.backend === 'nestjs') {
39
+ await generateNestJSAuth(config, backendDir);
40
+ }
41
+ // Isolation: Add .eslintrc.json to prevent parent config inheritance
42
+ const eslintConfig = {
43
+ root: true,
44
+ extends: ["eslint:recommended"],
45
+ env: { node: true, es2021: true }
46
+ };
47
+ await fs.writeJSON(path.join(backendDir, '.eslintrc.json'), eslintConfig, { spaces: 2 });
48
+ }
49
+ async function generateFrontendAuthPages(config, frontendDir) {
50
+ const isNext = config.frontend === 'nextjs';
51
+ const frontendLibDir = path.join(frontendDir, isNext ? 'app/lib' : 'src/lib');
52
+ await fs.ensureDir(frontendLibDir);
53
+ // Always generate auth context file for JWT-based auth
54
+ await fs.writeFile(path.join(frontendLibDir, 'auth.tsx'), getAuthContextCode());
55
+ if (isNext) {
56
+ const nextEslint = {
57
+ root: true,
58
+ extends: ["next/core-web-vitals"]
59
+ };
60
+ await fs.writeJSON(path.join(frontendDir, '.eslintrc.json'), nextEslint, { spaces: 2 });
61
+ const authAppDir = path.join(frontendDir, 'app');
62
+ await fs.ensureDir(path.join(authAppDir, 'login'));
63
+ await fs.ensureDir(path.join(authAppDir, 'dashboard'));
64
+ await fs.writeFile(path.join(authAppDir, 'login', 'page.tsx'), getLoginPageCode(config));
65
+ await fs.writeFile(path.join(authAppDir, 'dashboard', 'page.tsx'), getDashboardPageCode(config));
66
+ await fs.writeFile(path.join(authAppDir, 'page.tsx'), getHomePageCode(config));
67
+ }
68
+ else {
69
+ const pagesDir = path.join(frontendDir, 'src', 'pages');
70
+ await fs.ensureDir(pagesDir);
71
+ await fs.writeFile(path.join(pagesDir, 'LoginPage.tsx'), getLoginPageCode(config));
72
+ await fs.writeFile(path.join(pagesDir, 'HomePage.tsx'), getHomePageCode(config));
73
+ await fs.writeFile(path.join(pagesDir, 'DashboardPage.tsx'), getDashboardPageCode(config));
74
+ }
75
+ }
76
+ function getExpressAuthRoutes(config) {
77
+ return `import { Router } from 'express';
78
+ import bcrypt from 'bcryptjs';
34
79
  import jwt from 'jsonwebtoken';
35
80
  import { z } from 'zod';
36
81
  ${config.database === 'mongodb' ? "import User from '../models/User';" : "import prisma from '../lib/prisma';"}
@@ -41,128 +86,108 @@ const registerSchema = z.object({
41
86
  email: z.string().email(),
42
87
  password: z.string().min(8),
43
88
  name: z.string().optional(),
44
- ${config.multiTenant ? 'tenantId: z.string(),' : ''}
45
- });
46
-
47
- const loginSchema = z.object({
48
- email: z.string().email(),
49
- password: z.string(),
50
89
  });
51
90
 
52
91
  router.post('/register', async (req, res, next) => {
53
92
  try {
54
93
  const data = registerSchema.parse(req.body);
55
-
56
94
  const hashedPassword = await bcrypt.hash(data.password, 10);
57
-
58
- ${config.database === 'mongodb' ? `
59
- const user = await User.create({
60
- email: data.email,
61
- password: hashedPassword,
62
- name: data.name,
63
- ${config.multiTenant ? 'tenantId: data.tenantId,' : ''}
64
- });
65
- ` : `
66
- const user = await prisma.user.create({
67
- data: {
68
- email: data.email,
69
- password: hashedPassword,
70
- name: data.name,
71
- ${config.multiTenant ? 'tenantId: data.tenantId,' : ''}
72
- },
73
- });
74
- `}
75
-
76
- const token = jwt.sign(
77
- {
78
- userId: user.id,
79
- email: user.email,
80
- ${config.multiTenant ? 'tenantId: user.tenantId,' : ''}
81
- },
82
- process.env.JWT_SECRET!,
83
- { expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
84
- );
85
-
95
+ const user = ${config.database === 'mongodb' ? `await User.create({ email: data.email, password: hashedPassword, name: data.name })` : `await prisma.user.create({ data: { email: data.email, password: hashedPassword, name: data.name } })`};
96
+ const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET || '${config.jwtSecret}', { expiresIn: '7d' });
86
97
  res.json({ token, user: { id: user.id, email: user.email, name: user.name } });
87
- } catch (error) {
88
- next(error);
89
- }
98
+ } catch (error) { next(error); }
90
99
  });
91
100
 
92
101
  router.post('/login', async (req, res, next) => {
93
102
  try {
94
- const data = loginSchema.parse(req.body);
95
-
96
- ${config.database === 'mongodb' ? `
97
- const user = await User.findOne({ email: data.email });
98
- ` : `
99
- const user = await prisma.user.findUnique({
100
- where: { email: data.email },
101
- });
102
- `}
103
-
104
- if (!user || !user.password) {
105
- return res.status(401).json({ error: 'Invalid credentials' });
106
- }
107
-
108
- const validPassword = await bcrypt.compare(data.password, user.password);
109
-
110
- if (!validPassword) {
111
- return res.status(401).json({ error: 'Invalid credentials' });
112
- }
113
-
114
- const token = jwt.sign(
115
- {
116
- userId: user.id,
117
- email: user.email,
118
- ${config.multiTenant ? 'tenantId: user.tenantId,' : ''}
119
- },
120
- process.env.JWT_SECRET!,
121
- { expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
122
- );
123
-
103
+ const { email, password } = req.body;
104
+ const user = ${config.database === 'mongodb' ? `await User.findOne({ email })` : `await prisma.user.findUnique({ where: { email } })`};
105
+ if (!user || !(await bcrypt.compare(password, user.password))) return res.status(401).json({ error: 'Invalid credentials' });
106
+ const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET || '${config.jwtSecret}', { expiresIn: '7d' });
124
107
  res.json({ token, user: { id: user.id, email: user.email, name: user.name } });
125
- } catch (error) {
126
- next(error);
127
- }
108
+ } catch (error) { next(error); }
128
109
  });
129
110
 
130
111
  export default router;
131
112
  `;
132
- const routesDir = path_1.default.join(backendDir, 'src', 'routes');
133
- await fs_extra_1.default.writeFile(path_1.default.join(routesDir, 'auth.ts'), authRoutes);
134
- // Frontend: Auth context
135
- const authContext = `import React, { createContext, useContext, useState, useEffect } from 'react';
136
- import api from '../lib/api';
113
+ }
114
+ function getFastifyAuthRoutes(_config) {
115
+ return `import { FastifyInstance } from 'fastify';
116
+ import bcrypt from 'bcrypt';
137
117
 
138
- interface User {
139
- id: string;
140
- email: string;
118
+ export default async function authRoutes(fastify: FastifyInstance) {
119
+ fastify.post('/register', async (request, reply) => {
120
+ reply.send({ success: true });
121
+ });
122
+ fastify.post('/login', async (request, reply) => {
123
+ reply.send({ token: '...' });
124
+ });
125
+ }
126
+ `;
127
+ }
128
+ async function generateNestJSAuth(_config, backendDir) {
129
+ const authDir = path.join(backendDir, 'src', 'auth');
130
+ await fs.ensureDir(authDir);
131
+ await fs.ensureDir(path.join(authDir, 'dto'));
132
+ const controller = `import { Controller, Post, Body } from '@nestjs/common';
133
+ import { AuthService } from './auth.service';
134
+ import { RegisterDto, LoginDto } from './dto/auth.dto';
135
+
136
+ @Controller('auth')
137
+ export class AuthController {
138
+ constructor(private authService: AuthService) {}
139
+ @Post('register')
140
+ async register(@Body() dto: RegisterDto) { return this.authService.register(dto); }
141
+ @Post('login')
142
+ async login(@Body() dto: LoginDto) { return this.authService.login(dto); }
143
+ }
144
+ `;
145
+ await fs.writeFile(path.join(authDir, 'auth.controller.ts'), controller);
146
+ const dto = `import { IsEmail, IsString, MinLength } from 'class-validator';
147
+ export class RegisterDto {
148
+ @IsEmail()
149
+ email!: string;
150
+ @IsString()
151
+ @MinLength(8)
152
+ password!: string;
153
+ @IsString()
141
154
  name?: string;
142
155
  }
143
-
144
- interface AuthContextType {
145
- user: User | null;
146
- login: (email: string, password: string) => Promise<void>;
147
- register: (email: string, password: string, name?: string) => Promise<void>;
148
- logout: () => void;
149
- isLoading: boolean;
156
+ export class LoginDto {
157
+ @IsEmail()
158
+ email!: string;
159
+ @IsString()
160
+ password!: string;
150
161
  }
162
+ `;
163
+ await fs.writeFile(path.join(authDir, 'dto/auth.dto.ts'), dto);
164
+ const service = `import { Injectable } from '@nestjs/common';
165
+ import { JwtService } from '@nestjs/jwt';
166
+
167
+ @Injectable()
168
+ export class AuthService {
169
+ constructor(private jwtService: JwtService) {}
170
+ async register(dto: any) { return { success: true }; }
171
+ async login(user: any) { return { token: this.jwtService.sign({ id: '1' }) }; }
172
+ }
173
+ `;
174
+ await fs.writeFile(path.join(authDir, 'auth.service.ts'), service);
175
+ }
176
+ function getAuthContextCode() {
177
+ return `'use client';
178
+ import React, { createContext, useContext, useState, useEffect } from 'react';
179
+ import api from '../lib/api';
151
180
 
152
- const AuthContext = createContext<AuthContextType | undefined>(undefined);
181
+ const AuthContext = createContext<any>(undefined);
153
182
 
154
183
  export function AuthProvider({ children }: { children: React.ReactNode }) {
155
- const [user, setUser] = useState<User | null>(null);
184
+ const [user, setUser] = useState(null);
156
185
  const [isLoading, setIsLoading] = useState(true);
157
186
 
158
187
  useEffect(() => {
159
188
  const token = localStorage.getItem('token');
160
189
  if (token) {
161
- // Verify token and get user
162
- api.get('/auth/me')
163
- .then(res => setUser(res.data.user))
164
- .catch(() => localStorage.removeItem('token'))
165
- .finally(() => setIsLoading(false));
190
+ api.get('/auth/me').then(res => setUser(res.data.user)).finally(() => setIsLoading(false));
166
191
  } else {
167
192
  setIsLoading(false);
168
193
  }
@@ -174,296 +199,86 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
174
199
  setUser(res.data.user);
175
200
  };
176
201
 
177
- const register = async (email: string, password: string, name?: string) => {
178
- const res = await api.post('/auth/register', { email, password, name });
179
- localStorage.setItem('token', res.data.token);
180
- setUser(res.data.user);
181
- };
182
-
183
202
  const logout = () => {
184
203
  localStorage.removeItem('token');
185
204
  setUser(null);
186
205
  };
187
206
 
188
- return (
189
- <AuthContext.Provider value={{ user, login, register, logout, isLoading }}>
190
- {children}
191
- </AuthContext.Provider>
192
- );
207
+ return <AuthContext.Provider value={{ user, login, logout, isLoading }}>{children}</AuthContext.Provider>;
193
208
  }
194
209
 
195
- export function useAuth() {
196
- const context = useContext(AuthContext);
197
- if (!context) {
198
- throw new Error('useAuth must be used within AuthProvider');
199
- }
200
- return context;
201
- }
210
+ export const useAuth = () => useContext(AuthContext);
202
211
  `;
203
- const frontendLibDir = path_1.default.join(frontendDir, config.frontend === 'nextjs' ? 'lib' : 'src/lib');
204
- // For Next.js, generateNextJS already handles auth pages and middleware.
205
- // We only need the backend routes and potentially the lib/auth.tsx if we
206
- // want a context, but Next.js template currently uses direct api calls.
207
- // To restore reliability, we skip frontend JWT auth files for Next.js for now.
208
- if (config.frontend === 'nextjs') {
209
- return;
210
- }
211
- await fs_extra_1.default.ensureDir(frontendLibDir);
212
- await fs_extra_1.default.writeFile(path_1.default.join(frontendLibDir, 'auth.tsx'), authContext);
213
- // Frontend: Login page
214
- const loginPage = `import { useState } from 'react';
215
- import { useNavigate } from 'react-router-dom';
216
- import { useAuth } from '../lib/auth';
212
+ }
213
+ function getLoginPageCode(config) {
214
+ const isNext = config.frontend === 'nextjs';
215
+ const authPath = isNext ? '../lib/auth' : '../lib/auth';
216
+ return `${isNext ? "'use client';" : ""}
217
+ import { useState } from 'react';
218
+ ${isNext ? "import { useRouter } from 'next/navigation';" : "import { useNavigate } from 'react-router-dom';"}
219
+ import { useAuth } from '${authPath}';
217
220
 
218
221
  export default function LoginPage() {
219
222
  const [email, setEmail] = useState('');
220
223
  const [password, setPassword] = useState('');
221
- const [error, setError] = useState('');
222
224
  const { login } = useAuth();
223
- const navigate = useNavigate();
225
+ ${isNext ? "const router = useRouter();" : "const navigate = useNavigate();"}
224
226
 
225
- const handleSubmit = async (e: React.FormEvent) => {
227
+ const handleSubmit = async (e: any) => {
226
228
  e.preventDefault();
227
- try {
228
- await login(email, password);
229
- navigate('/dashboard');
230
- } catch (err: any) {
231
- setError(err.response?.data?.error || 'Login failed');
232
- }
229
+ await login(email, password);
230
+ ${isNext ? "router.push('/dashboard');" : "navigate('/dashboard');"}
233
231
  };
234
232
 
235
233
  return (
236
234
  <div className="min-h-screen flex items-center justify-center bg-gray-900">
237
- <div className="bg-gray-800 p-8 rounded-lg shadow-lg w-full max-w-md">
238
- <h1 className="text-3xl font-bold text-white mb-6">Login</h1>
239
- {error && <div className="bg-red-500 text-white p-3 rounded mb-4">{error}</div>}
240
- <form onSubmit={handleSubmit}>
241
- <div className="mb-4">
242
- <label className="block text-gray-300 mb-2">Email</label>
243
- <input
244
- type="email"
245
- value={email}
246
- onChange={(e) => setEmail(e.target.value)}
247
- className="w-full p-3 rounded bg-gray-700 text-white"
248
- required
249
- />
250
- </div>
251
- <div className="mb-6">
252
- <label className="block text-gray-300 mb-2">Password</label>
253
- <input
254
- type="password"
255
- value={password}
256
- onChange={(e) => setPassword(e.target.value)}
257
- className="w-full p-3 rounded bg-gray-700 text-white"
258
- required
259
- />
260
- </div>
261
- <button
262
- type="submit"
263
- className="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 rounded"
264
- >
265
- Login
266
- </button>
267
- </form>
268
- </div>
235
+ <form onSubmit={handleSubmit} className="bg-gray-800 p-8 rounded shadow-lg">
236
+ <input type="email" value={email} onChange={e => setEmail(e.target.value)} className="block w-full mb-4 p-2 bg-gray-700 text-white" />
237
+ <input type="password" value={password} onChange={e => setPassword(e.target.value)} className="block w-full mb-4 p-2 bg-gray-700 text-white" />
238
+ <button type="submit" className="w-full bg-blue-600 p-2 text-white">Login</button>
239
+ </form>
269
240
  </div>
270
241
  );
271
242
  }
272
243
  `;
273
- const pagesDir = path_1.default.join(frontendDir, 'src', 'pages');
274
- await fs_extra_1.default.writeFile(path_1.default.join(pagesDir, 'LoginPage.tsx'), loginPage);
275
- // Frontend: Home page
276
- const homePage = `export default function HomePage() {
244
+ }
245
+ function getHomePageCode(config) {
246
+ return `export default function HomePage() {
277
247
  return (
278
- <div className="min-h-screen bg-gray-900 text-white">
279
- <div className="container mx-auto px-4 py-16">
280
- <h1 className="text-5xl font-bold mb-4">${config.projectName}</h1>
281
- <p className="text-xl text-gray-400 mb-8">
282
- Generated by ForgeStack OS
283
- </p>
284
- <div className="space-x-4">
285
- <a
286
- href="/login"
287
- className="inline-block bg-blue-600 hover:bg-blue-700 px-6 py-3 rounded font-semibold"
288
- >
289
- Get Started
290
- </a>
291
- </div>
292
- </div>
248
+ <div className="min-h-screen bg-gray-900 text-white flex items-center justify-center">
249
+ <h1 className="text-4xl">${config.projectName}</h1>
293
250
  </div>
294
251
  );
295
252
  }
296
253
  `;
297
- await fs_extra_1.default.writeFile(path_1.default.join(pagesDir, 'HomePage.tsx'), homePage);
298
- // Frontend: Dashboard page
299
- const dashboardPage = `import { useAuth } from '../lib/auth';
300
- import { useNavigate } from 'react-router-dom';
301
- import { useEffect } from 'react';
302
-
254
+ }
255
+ function getDashboardPageCode(config) {
256
+ const isNext = config.frontend === 'nextjs';
257
+ const authPath = isNext ? '../lib/auth' : '../lib/auth';
258
+ return `${isNext ? "'use client';" : ""}
259
+ import { useAuth } from '${authPath}';
303
260
  export default function DashboardPage() {
304
- const { user, logout, isLoading } = useAuth();
305
- const navigate = useNavigate();
306
-
307
- useEffect(() => {
308
- if (!isLoading && !user) {
309
- navigate('/login');
310
- }
311
- }, [user, isLoading, navigate]);
312
-
313
- if (isLoading) {
314
- return <div className="min-h-screen bg-gray-900 flex items-center justify-center">
315
- <div className="text-white">Loading...</div>
316
- </div>;
317
- }
318
-
261
+ const { user, logout } = useAuth();
319
262
  return (
320
- <div className="min-h-screen bg-gray-900 text-white">
321
- <nav className="bg-gray-800 p-4">
322
- <div className="container mx-auto flex justify-between items-center">
323
- <h1 className="text-2xl font-bold">${config.projectName}</h1>
324
- <button
325
- onClick={logout}
326
- className="bg-red-600 hover:bg-red-700 px-4 py-2 rounded"
327
- >
328
- Logout
329
- </button>
330
- </div>
331
- </nav>
332
- <div className="container mx-auto px-4 py-16">
333
- <h2 className="text-3xl font-bold mb-4">Welcome, {user?.name || user?.email}!</h2>
334
- <p className="text-gray-400">You're logged in to your dashboard.</p>
335
- </div>
263
+ <div className="min-h-screen bg-gray-900 text-white p-8">
264
+ <h1>Dashboard for ${config.projectName}</h1>
265
+ <p>Welcome, {user?.email}</p>
266
+ <button onClick={logout} className="bg-red-600 p-2 rounded">Logout</button>
336
267
  </div>
337
268
  );
338
269
  }
339
270
  `;
340
- await fs_extra_1.default.writeFile(path_1.default.join(pagesDir, 'DashboardPage.tsx'), dashboardPage);
341
271
  }
342
272
  async function generateClerkAuth(config, frontendDir, _backendDir) {
343
- const isNextJS = config.frontend === 'nextjs';
344
- const clerkSetup = `# Clerk Authentication Setup
345
-
346
- 1. Create a Clerk account at https://clerk.com
347
- 2. Create a new application
348
- 3. Copy your API keys to .env
349
- 4. For ${isNextJS ? 'Next.js' : 'React'}, the components are already configured.
350
-
351
- Components used:
352
- - LoginPage: Uses Clerk's SignIn component
353
- - Dashboard: Uses Clerk's UserButton
354
- - Layout: ${isNextJS ? 'Configured with ClerkProvider' : 'Needs ClerkProvider in main.tsx'}
355
- `;
356
- await fs_extra_1.default.writeFile(path_1.default.join(frontendDir, 'CLERK_SETUP.md'), clerkSetup);
357
- if (isNextJS) {
358
- // Next.js specific logic (mostly handled in generateNextJS)
359
- const middleware = `import { authMiddleware } from '@clerk/nextjs';
360
-
361
- export default authMiddleware({
362
- publicRoutes: ['/'],
363
- });
364
-
365
- export const config = {
366
- matcher: ['/((?!.+\\\\.[\\\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
367
- };
368
- `;
369
- await fs_extra_1.default.writeFile(path_1.default.join(frontendDir, 'middleware.ts'), middleware);
370
- }
371
- else {
372
- // React+Vite specific logic
373
- const hook = `import { useAuth, useUser, SignInButton, SignOutButton } from "@clerk/clerk-react";
374
-
375
- export function AuthStatus() {
376
- const { isSignedIn, user } = useUser();
377
- if (isSignedIn) return <div>Hello {user.fullName} <SignOutButton /></div>;
378
- return <SignInButton />;
379
- }
380
- `;
381
- await fs_extra_1.default.ensureDir(path_1.default.join(frontendDir, 'src', 'components'));
382
- await fs_extra_1.default.writeFile(path_1.default.join(frontendDir, 'src', 'components', 'AuthStatus.tsx'), hook);
383
- }
273
+ await fs.writeFile(path.join(frontendDir, 'CLERK_SETUP.md'), '# Clerk Setup');
384
274
  }
385
275
  async function generateSupabaseAuth(config, frontendDir, _backendDir) {
386
- const isNextJS = config.frontend === 'nextjs';
387
- const supabaseSetup = `# Supabase Authentication Setup
388
-
389
- 1. Create a Supabase project at https://supabase.com
390
- 2. Copy your project URL and anon key to .env
391
- 3. Configure auth providers in Supabase dashboard
392
- 4. For ${isNextJS ? 'Next.js' : 'React'}, the clients are already configured in src/lib/supabase.ts.
393
- `;
394
- await fs_extra_1.default.writeFile(path_1.default.join(frontendDir, 'SUPABASE_SETUP.md'), supabaseSetup);
395
- const supabaseClient = isNextJS
396
- ? `import { createBrowserClient } from '@supabase/ssr'
397
-
398
- export function createClient() {
399
- return createBrowserClient(
400
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
401
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
402
- )
403
- }`
404
- : `import { createClient } from '@supabase/supabase-js'
405
-
406
- export const supabase = createClient(
407
- process.env.VITE_SUPABASE_URL!,
408
- process.env.VITE_SUPABASE_ANON_KEY!
409
- )`;
410
- const libDir = path_1.default.join(frontendDir, isNextJS ? 'lib' : 'src/lib');
411
- await fs_extra_1.default.ensureDir(libDir);
412
- await fs_extra_1.default.writeFile(path_1.default.join(libDir, 'supabase.ts'), supabaseClient);
276
+ await fs.writeFile(path.join(frontendDir, 'SUPABASE_SETUP.md'), '# Supabase Setup');
413
277
  }
414
278
  async function generateAuthJS(config, frontendDir, _backendDir) {
415
- const isNextJS = config.frontend === 'nextjs';
416
- const authJSSetup = `# Auth.js (NextAuth) Setup
417
-
418
- 1. Configure providers in ${isNextJS ? 'app/api/auth/[...nextauth]/route.ts' : 'src/lib/auth.ts'}
419
- 2. Add provider credentials to .env
420
- 3. See https://authjs.dev for setup guides.
421
- `;
422
- await fs_extra_1.default.writeFile(path_1.default.join(frontendDir, 'AUTHJS_SETUP.md'), authJSSetup);
423
- if (isNextJS) {
424
- const authRouteDir = path_1.default.join(frontendDir, 'app/api/auth/[...nextauth]');
425
- await fs_extra_1.default.ensureDir(authRouteDir);
426
- const authRoute = `import NextAuth from "next-auth"
427
- import GithubProvider from "next-auth/providers/github"
428
-
429
- export const authOptions = {
430
- providers: [
431
- GithubProvider({
432
- clientId: process.env.GITHUB_ID!,
433
- clientSecret: process.env.GITHUB_SECRET!,
434
- }),
435
- ],
436
- }
437
-
438
- const handler = NextAuth(authOptions)
439
- export { handler as GET, handler as POST }
440
- `;
441
- await fs_extra_1.default.writeFile(path_1.default.join(authRouteDir, 'route.ts'), authRoute);
442
- }
279
+ await fs.writeFile(path.join(frontendDir, 'AUTHJS_SETUP.md'), '# Auth.js Setup');
443
280
  }
444
281
  async function generateFirebaseAuth(config, frontendDir, _backendDir) {
445
- const isNextJS = config.frontend === 'nextjs';
446
- const firebaseSetup = `# Firebase Auth Setup
447
-
448
- 1. Create a Firebase project at https://firebase.google.com
449
- 2. Enable authentication methods
450
- 3. Copy your project config to .env
451
- `;
452
- await fs_extra_1.default.writeFile(path_1.default.join(frontendDir, 'FIREBASE_SETUP.md'), firebaseSetup);
453
- const firebaseConfig = `import { initializeApp } from "firebase/app";
454
- import { getAuth } from "firebase/auth";
455
-
456
- const firebaseConfig = {
457
- apiKey: process.env.${isNextJS ? 'NEXT_PUBLIC_' : 'VITE_'}FIREBASE_API_KEY,
458
- authDomain: process.env.${isNextJS ? 'NEXT_PUBLIC_' : 'VITE_'}FIREBASE_AUTH_DOMAIN,
459
- projectId: process.env.${isNextJS ? 'NEXT_PUBLIC_' : 'VITE_'}FIREBASE_PROJECT_ID,
460
- };
461
-
462
- const app = initializeApp(firebaseConfig);
463
- export const auth = getAuth(app);
464
- `;
465
- const libDir = path_1.default.join(frontendDir, isNextJS ? 'lib' : 'src/lib');
466
- await fs_extra_1.default.ensureDir(libDir);
467
- await fs_extra_1.default.writeFile(path_1.default.join(libDir, 'firebase.ts'), firebaseConfig);
282
+ await fs.writeFile(path.join(frontendDir, 'FIREBASE_SETUP.md'), '# Firebase Setup');
468
283
  }
469
284
  //# sourceMappingURL=auth.js.map