@shivasankaran18/stackd 1.6.0 → 1.8.0

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.
Files changed (63) hide show
  1. package/dist/apps/cli/src/cli.js +217 -0
  2. package/dist/apps/cli/src/commands/create.js +148 -0
  3. package/dist/apps/cli/src/scripts/Auth/jwt.js +78 -0
  4. package/dist/apps/cli/src/scripts/Auth/nextAuth.js +131 -0
  5. package/dist/apps/cli/src/scripts/Auth/passport.js +218 -0
  6. package/dist/apps/cli/src/scripts/backend/django.js +20 -0
  7. package/dist/apps/cli/src/scripts/backend/expressjs.js +58 -0
  8. package/dist/apps/cli/src/scripts/backend/expressts.js +83 -0
  9. package/dist/apps/cli/src/scripts/frontend/angularjs.js +1 -0
  10. package/dist/apps/cli/src/scripts/frontend/angularts.js +22 -0
  11. package/dist/apps/cli/src/scripts/frontend/nextjs.js +62 -0
  12. package/dist/apps/cli/src/scripts/frontend/reactjs.js +31 -0
  13. package/dist/apps/cli/src/scripts/frontend/reactts.js +30 -0
  14. package/dist/apps/cli/src/scripts/frontend/vuejs.js +37 -0
  15. package/dist/apps/cli/src/scripts/frontend/vuets.js +43 -0
  16. package/dist/apps/cli/src/scripts/orms/drizzleSetup.js +85 -0
  17. package/dist/apps/cli/src/scripts/orms/mongoSetup.js +53 -0
  18. package/dist/apps/cli/src/scripts/orms/prismaSetup.js +12 -0
  19. package/dist/apps/cli/src/scripts/ui/shadcn.js +207 -0
  20. package/dist/apps/cli/src/scripts/ui/tailwindcss.js +102 -0
  21. package/dist/apps/web/app/api/auth/[...nextauth]/route.js +5 -0
  22. package/dist/apps/web/app/api/scaffold/route.js +251 -0
  23. package/dist/apps/web/app/home/page.js +19 -0
  24. package/dist/apps/web/app/layout.js +19 -0
  25. package/dist/apps/web/app/page.js +1 -0
  26. package/dist/apps/web/app/providers.js +7 -0
  27. package/dist/apps/web/app/scaffold/page.js +326 -0
  28. package/dist/apps/web/components/Sidebar.js +54 -0
  29. package/dist/apps/web/components/theme-provider.js +6 -0
  30. package/dist/apps/web/components/ui/button.js +32 -0
  31. package/dist/apps/web/components/ui/card.js +15 -0
  32. package/dist/apps/web/components/ui/dropdown-menu.js +54 -0
  33. package/dist/apps/web/components/ui/input.js +7 -0
  34. package/dist/apps/web/components/ui/label.js +9 -0
  35. package/dist/apps/web/components/ui/scroll-area.js +19 -0
  36. package/dist/apps/web/components/ui/sonner.js +15 -0
  37. package/dist/apps/web/components/ui/steps.js +14 -0
  38. package/dist/apps/web/components/ui/switch.js +9 -0
  39. package/dist/apps/web/lib/auth.js +32 -0
  40. package/dist/apps/web/lib/redis.js +9 -0
  41. package/dist/apps/web/lib/utils.js +5 -0
  42. package/dist/packages/scripts/Auth/jwt.js +78 -0
  43. package/dist/packages/scripts/Auth/nextAuth.js +131 -0
  44. package/dist/packages/scripts/Auth/passport.js +218 -0
  45. package/dist/packages/scripts/backend/django.js +20 -0
  46. package/dist/packages/scripts/backend/expressjs.js +58 -0
  47. package/dist/packages/scripts/backend/expressts.js +83 -0
  48. package/dist/packages/scripts/frontend/angularjs.js +1 -0
  49. package/dist/packages/scripts/frontend/angularts.js +22 -0
  50. package/dist/packages/scripts/frontend/nextjs.js +62 -0
  51. package/dist/packages/scripts/frontend/reactjs.js +31 -0
  52. package/dist/packages/scripts/frontend/reactts.js +30 -0
  53. package/dist/packages/scripts/frontend/vuejs.js +37 -0
  54. package/dist/packages/scripts/frontend/vuets.js +43 -0
  55. package/dist/packages/scripts/orms/drizzleSetup.js +85 -0
  56. package/dist/packages/scripts/orms/mongoSetup.js +53 -0
  57. package/dist/packages/scripts/orms/prismaSetup.js +12 -0
  58. package/dist/packages/scripts/ui/shadcn.js +207 -0
  59. package/dist/packages/scripts/ui/tailwindcss.js +102 -0
  60. package/dist/stackd.js +114 -0
  61. package/dist/tsconfig.tsbuildinfo +1 -0
  62. package/package.json +3 -3
  63. package/stackd.ts +0 -0
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import inquirer from 'inquirer';
4
+ import chalk from 'chalk';
5
+ import { createProject } from './commands/create.js';
6
+ const showBanner = () => {
7
+ console.log(chalk.cyan(`
8
+ ██████╗████████╗ █████╗ ██████╗██╗ ██╗'██████╗
9
+ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝██╔══██╗
10
+ ╚█████╗ ██║ ███████║██║ █████═╝ ██║ ██║
11
+ ╚═══██╗ ██║ ██╔══██║██║ ██╔═██╗ ██║ ██║
12
+ ██████╔╝ ██║ ██║ ██║╚██████╗██║ ██╗██████╔╝
13
+ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═════╝
14
+ `));
15
+ console.log(chalk.yellow.bold(' 🚀 Full Stack Project Generator\n'));
16
+ };
17
+ const createBorder = () => {
18
+ const border = '='.repeat(60);
19
+ return chalk.cyan(border);
20
+ };
21
+ const CHOICES = {
22
+ EXPRESS_TS: 'Express + TypeScript',
23
+ EXPRESS_JS: 'Express (JavaScript)',
24
+ DJANGO: 'Django',
25
+ REACT_TS: 'React + TypeScript',
26
+ REACT_JS: 'React (JavaScript)',
27
+ VUE_TS: 'Vue + TypeScript',
28
+ VUE_JS: 'Vue (JavaScript)',
29
+ DJANGO_TEMPLATES: 'Django Templates',
30
+ NONE: 'None',
31
+ SKIP: 'Skip',
32
+ POSTGRESQL: 'PostgreSQL',
33
+ MONGODB: 'MongoDB',
34
+ PRISMA: 'Prisma',
35
+ DRIZZLE: 'Drizzle',
36
+ MONGOOSE: 'Mongoose',
37
+ JWT: 'JWT',
38
+ NEXTAUTH: 'NextAuth',
39
+ PASSPORT: 'Passport'
40
+ };
41
+ program
42
+ .command('run ')
43
+ .description('Create a new full-stack project')
44
+ .action(async () => {
45
+ // showBanner();
46
+ // console.log(createBorder());
47
+ // console.log(chalk.bgCyan.white.bold('\n 💫 Let\'s create something awesome! \n'));
48
+ // console.log(createBorder() + '\n');
49
+ const projectSettings = await inquirer.prompt([
50
+ {
51
+ type: 'input',
52
+ name: 'projectPath',
53
+ message: chalk.magenta.bold('📁 Where do you want to create the project?'),
54
+ },
55
+ {
56
+ type: 'input',
57
+ name: 'projectName',
58
+ message: chalk.magenta.bold('💫 Enter the project name:'),
59
+ },
60
+ {
61
+ type: 'number',
62
+ name: 'frontendPort',
63
+ message: chalk.blue.bold('🌐 Enter frontend port:'),
64
+ default: 3000,
65
+ },
66
+ {
67
+ type: 'number',
68
+ name: 'backendPort',
69
+ message: chalk.green.bold('⚙️ Enter backend port:'),
70
+ default: 3001,
71
+ }
72
+ ]);
73
+ const frontendChoice = await inquirer.prompt([
74
+ {
75
+ type: 'list',
76
+ name: 'frontend',
77
+ message: chalk.yellow.bold('🎨 Choose a frontend framework:'),
78
+ choices: [
79
+ chalk.blue(CHOICES.REACT_TS),
80
+ chalk.blue(CHOICES.REACT_JS),
81
+ chalk.green(CHOICES.VUE_TS),
82
+ chalk.green(CHOICES.VUE_JS),
83
+ chalk.green(CHOICES.DJANGO_TEMPLATES),
84
+ CHOICES.SKIP
85
+ ],
86
+ default: CHOICES.SKIP,
87
+ }
88
+ ]);
89
+ const backendChoice = await inquirer.prompt([
90
+ {
91
+ type: 'list',
92
+ name: 'backend',
93
+ message: chalk.cyan.bold('🛠️ Choose a backend framework:'),
94
+ choices: (answers) => {
95
+ if (frontendChoice.frontend === chalk.green(CHOICES.DJANGO_TEMPLATES)) {
96
+ return [chalk.green(CHOICES.DJANGO)];
97
+ }
98
+ return [
99
+ chalk.blue(CHOICES.EXPRESS_TS),
100
+ chalk.blue(CHOICES.EXPRESS_JS),
101
+ chalk.green(CHOICES.DJANGO),
102
+ CHOICES.SKIP
103
+ ];
104
+ },
105
+ default: chalk.blue(CHOICES.EXPRESS_TS),
106
+ }
107
+ ]);
108
+ const databaseChoice = await inquirer.prompt([
109
+ {
110
+ type: 'list',
111
+ name: 'database',
112
+ message: chalk.magenta.bold('🗄️ Choose a database:'),
113
+ choices: [
114
+ chalk.blue(CHOICES.POSTGRESQL),
115
+ chalk.green(CHOICES.MONGODB),
116
+ CHOICES.SKIP
117
+ ],
118
+ default: CHOICES.SKIP,
119
+ }
120
+ ]);
121
+ let ormChoice = { orm: CHOICES.SKIP };
122
+ if (databaseChoice.database !== CHOICES.SKIP) {
123
+ ormChoice = await inquirer.prompt([
124
+ {
125
+ type: 'list',
126
+ name: 'orm',
127
+ message: chalk.yellow.bold('🔗 Choose an ORM:'),
128
+ choices: () => {
129
+ const cleanDatabase = databaseChoice.database.replace(/\u001b\[\d+m/g, '').trim();
130
+ return cleanDatabase === CHOICES.POSTGRESQL
131
+ ? [chalk.magenta(CHOICES.PRISMA), chalk.cyan(CHOICES.DRIZZLE), CHOICES.SKIP]
132
+ : [chalk.green(CHOICES.MONGOOSE), CHOICES.SKIP];
133
+ },
134
+ default: CHOICES.SKIP,
135
+ }
136
+ ]);
137
+ }
138
+ // Step 6: Authentication Selection
139
+ const authChoice = await inquirer.prompt([
140
+ {
141
+ type: 'list',
142
+ name: 'auth',
143
+ message: chalk.cyan.bold('🔐 Choose an authentication method:'),
144
+ choices: [
145
+ chalk.yellow(CHOICES.JWT),
146
+ chalk.blue(CHOICES.NEXTAUTH),
147
+ chalk.green(CHOICES.PASSPORT),
148
+ CHOICES.SKIP
149
+ ],
150
+ default: CHOICES.SKIP,
151
+ }
152
+ ]);
153
+ let dbUrlChoice = { dbUrl: '' };
154
+ if (databaseChoice.database !== CHOICES.SKIP) {
155
+ dbUrlChoice = await inquirer.prompt([
156
+ {
157
+ type: 'input',
158
+ name: 'dbUrl',
159
+ message: chalk.green.bold('🔌 Enter database connection URL:'),
160
+ }
161
+ ]);
162
+ }
163
+ const answers = {
164
+ ...projectSettings,
165
+ ...frontendChoice,
166
+ ...backendChoice,
167
+ ...databaseChoice,
168
+ ...ormChoice,
169
+ ...authChoice,
170
+ ...dbUrlChoice,
171
+ };
172
+ const cleanAnswers = Object.entries(answers).reduce((acc, [key, value]) => {
173
+ if (typeof value === 'string') {
174
+ const cleanValue = value.replace(/\u001b\[\d+m/g, '').trim();
175
+ // @ts-ignore
176
+ acc[key] = cleanValue;
177
+ }
178
+ else {
179
+ // @ts-ignore
180
+ acc[key] = value;
181
+ }
182
+ return acc;
183
+ }, {});
184
+ // @ts-ignore
185
+ if (cleanAnswers.database !== 'Skip' && cleanAnswers.orm !== 'Skip') {
186
+ // @ts-ignore
187
+ if (cleanAnswers.database === 'MongoDB' && cleanAnswers.orm !== 'Mongoose') {
188
+ console.log('\n' + createBorder());
189
+ console.error(chalk.bgRed.white.bold(" ❌ Error: MongoDB supports only Mongoose ORM. "));
190
+ console.log(createBorder());
191
+ process.exit(1);
192
+ }
193
+ // @ts-ignore
194
+ if (cleanAnswers.database === 'PostgreSQL' && !['Prisma', 'Drizzle'].includes(cleanAnswers.orm)) {
195
+ console.log('\n' + createBorder());
196
+ console.error(chalk.bgRed.white.bold(" ❌ Error: PostgreSQL supports only Prisma or Drizzle ORM. "));
197
+ console.log(createBorder());
198
+ process.exit(1);
199
+ }
200
+ }
201
+ // @ts-ignore
202
+ if (cleanAnswers.frontend === 'Django Templates') {
203
+ // @ts-ignore
204
+ cleanAnswers.backend = 'Django';
205
+ }
206
+ // @ts-ignore
207
+ if (cleanAnswers.backend === 'Django') {
208
+ // @ts-ignore
209
+ cleanAnswers.frontend = 'Django Templates';
210
+ }
211
+ console.log('\n' + createBorder());
212
+ console.log(chalk.bgGreen.black.bold("\n 📦 Creating your project... \n"));
213
+ console.log(createBorder() + '\n');
214
+ // @ts-ignore
215
+ await createProject(projectSettings.projectName, cleanAnswers);
216
+ });
217
+ program.parse(process.argv);
@@ -0,0 +1,148 @@
1
+ import { join } from 'path';
2
+ import { createReactTS } from '../scripts/frontend/reactts.js';
3
+ import { createReactJS } from '../scripts/frontend/reactjs.js';
4
+ import { createVueTS } from '../scripts/frontend/vuets.js';
5
+ import { createVueJS } from '../scripts/frontend/vuejs.js';
6
+ import { createExpressTS } from '../scripts/backend/expressts.js';
7
+ import { createExpressJS } from '../scripts/backend/expressjs.js';
8
+ import { installDjangoDependencies } from '../scripts/backend/django.js';
9
+ import { setupPrisma } from '../scripts/orms/prismaSetup.js';
10
+ import { setupDrizzle } from '../scripts/orms/drizzleSetup.js';
11
+ import { setupMongoose } from '../scripts/orms/mongoSetup.js';
12
+ import { setupPassport } from '../scripts/Auth/passport.js';
13
+ import { jwtAuthts } from '../scripts/Auth/jwt.js';
14
+ import chalk from 'chalk';
15
+ import ora from 'ora';
16
+ import { mkdir } from 'fs/promises';
17
+ const emitLog = (message) => {
18
+ console.log(`[Emit Logs]: ${message}`);
19
+ };
20
+ export async function createProject(projectName, options) {
21
+ const spinner = ora('Creating project...').start();
22
+ console.log(options);
23
+ console.log(projectName);
24
+ try {
25
+ const projectDir = join(options.projectPath, projectName);
26
+ const config = {
27
+ projectName,
28
+ projectPath: options.projectPath,
29
+ frontendPort: options.frontendPort,
30
+ backendPort: options.backendPort,
31
+ dbUrl: options.dbUrl,
32
+ };
33
+ // Create project directory
34
+ await mkdir(projectDir, { recursive: true });
35
+ // Frontend setup
36
+ spinner.text = 'Setting up frontend...';
37
+ switch (options.frontend) {
38
+ case 'React + TypeScript':
39
+ await createReactTS(config, projectDir, emitLog);
40
+ break;
41
+ case 'React (JavaScript)':
42
+ await createReactJS(config, projectDir, emitLog);
43
+ break;
44
+ case 'Vue + TypeScript':
45
+ await createVueTS(config, projectDir, emitLog);
46
+ break;
47
+ case 'Vue (JavaScript)':
48
+ await createVueJS(config, projectDir, emitLog);
49
+ break;
50
+ case 'Django Templates':
51
+ // Django templates will be handled with backend setup
52
+ break;
53
+ case 'Skip':
54
+ emitLog('Skipping frontend setup');
55
+ break;
56
+ default:
57
+ emitLog('Unknown frontend choice');
58
+ break;
59
+ }
60
+ // Backend setup
61
+ spinner.text = 'Setting up backend...';
62
+ switch (options.backend) {
63
+ case 'Express + TypeScript':
64
+ await createExpressTS(config, projectDir, emitLog);
65
+ break;
66
+ case 'Express (JavaScript)':
67
+ await createExpressJS(config, projectDir, emitLog);
68
+ break;
69
+ case 'Django':
70
+ await installDjangoDependencies(projectDir);
71
+ break;
72
+ case 'Skip':
73
+ emitLog('Skipping backend setup');
74
+ break;
75
+ default:
76
+ emitLog('Unknown backend choice');
77
+ break;
78
+ }
79
+ // Database & ORM setup
80
+ if (options.database !== 'Skip') {
81
+ spinner.text = 'Setting up database and ORM...';
82
+ // Set up ORM based on database choice
83
+ if (options.orm !== 'Skip') {
84
+ switch (options.orm) {
85
+ case 'Prisma':
86
+ await setupPrisma(config, projectDir, emitLog);
87
+ break;
88
+ case 'Drizzle':
89
+ await setupDrizzle(config, projectDir, emitLog);
90
+ break;
91
+ case 'Mongoose':
92
+ await setupMongoose(config, projectDir, emitLog);
93
+ break;
94
+ default:
95
+ emitLog('Unknown ORM choice');
96
+ break;
97
+ }
98
+ }
99
+ }
100
+ // Authentication setup
101
+ if (options.auth !== 'Skip') {
102
+ spinner.text = 'Setting up authentication...';
103
+ switch (options.auth) {
104
+ case 'JWT':
105
+ await jwtAuthts(config, projectDir, emitLog);
106
+ break;
107
+ // case 'NextAuth':
108
+ // await setupNextAuth(config, projectDir,emitLog);
109
+ // break;
110
+ case 'Passport':
111
+ await setupPassport(config, projectDir, emitLog);
112
+ break;
113
+ default:
114
+ emitLog('Unknown authentication choice');
115
+ break;
116
+ }
117
+ }
118
+ spinner.succeed(chalk.green('Project created successfully!'));
119
+ console.log('\nTo get started:');
120
+ console.log(chalk.cyan(` cd ${projectName}`));
121
+ // Add specific instructions based on choices
122
+ if (options.frontend !== 'Skip' || options.backend !== 'Skip') {
123
+ // console.log(chalk.cyan(' npm install'));
124
+ if (options.frontend === 'Django Templates' || options.backend === 'Django') {
125
+ console.log(chalk.cyan(' python manage.py runserver'));
126
+ }
127
+ else {
128
+ console.log(chalk.cyan(' npm run dev'));
129
+ }
130
+ }
131
+ // Add database specific instructions
132
+ if (options.database !== 'Skip') {
133
+ console.log(chalk.yellow('\nDatabase Setup:'));
134
+ if (options.orm === 'Prisma') {
135
+ console.log(chalk.cyan(' npx prisma generate'));
136
+ console.log(chalk.cyan(' npx prisma migrate dev'));
137
+ }
138
+ else if (options.orm === 'Mongoose') {
139
+ console.log(chalk.cyan(' Make sure MongoDB is running'));
140
+ }
141
+ }
142
+ }
143
+ catch (error) {
144
+ spinner.fail(chalk.red('Failed to create project'));
145
+ console.error(error);
146
+ process.exit(1);
147
+ }
148
+ }
@@ -0,0 +1,78 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import fs from 'fs';
4
+ export async function jwtAuthts(config, projectDir, emitLog) {
5
+ emitLog('Installing jsonwebtoken...');
6
+ const packageJsonPath = join(projectDir, 'backend', 'package.json');
7
+ const jsonData = await JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
8
+ emitLog('Updating package.json...');
9
+ jsonData.dependencies = jsonData.dependencies || {};
10
+ jsonData.dependencies["jsonwebtoken"] = "^9.0.2";
11
+ fs.writeFileSync(packageJsonPath, JSON.stringify(jsonData, null, 2), 'utf8');
12
+ emitLog('Writing jwt.ts...');
13
+ const jwtAuthFile = `
14
+ const jwt = require('jsonwebtoken');
15
+ export const authenticateToken = (req:any, res:any, next:any) => {
16
+ const token = req.header('Authorization')?.split(' ')[1];
17
+ if (!token) return res.status(401).json({ error: 'Access Denied' });
18
+
19
+ jwt.verify(token, process.env.JWT_SECRET, (err:any, user:any) => {
20
+ if (err) return res.status(403).json({ error: 'Invalid Token' });
21
+ req.user = user;
22
+ next();
23
+ });
24
+ };
25
+ `;
26
+ emitLog('Writing middleware.ts...');
27
+ const middlewareDir = join(projectDir, 'backend', 'src', 'middleware');
28
+ await mkdir(middlewareDir, { recursive: true });
29
+ emitLog('Writing middleware.ts...');
30
+ await writeFile(join(middlewareDir, 'middleware.ts'), jwtAuthFile, 'utf8');
31
+ emitLog('✅ JWT authentication setup completed successfully!');
32
+ }
33
+ export async function jwtAuthdjango(config, projectDir, emitLog) {
34
+ emitLog('Installing jsonwebtoken...');
35
+ const settingsPath = join(projectDir, 'backend', 'core', 'settings.py');
36
+ emitLog('Updating settings.py...');
37
+ try {
38
+ let settingsContent = fs.readFileSync(settingsPath, 'utf8');
39
+ const restFrameworkSettings = `
40
+
41
+ REST_FRAMEWORK = {
42
+ 'DEFAULT_AUTHENTICATION_CLASSES': [
43
+ 'rest_framework_simplejwt.authentication.JWTAuthentication',
44
+ ],
45
+ }
46
+ `;
47
+ const installedAppsIndex = settingsContent.indexOf('INSTALLED_APPS');
48
+ const insertPosition = settingsContent.indexOf(']', installedAppsIndex) + 1;
49
+ settingsContent =
50
+ settingsContent.slice(0, insertPosition) +
51
+ restFrameworkSettings +
52
+ settingsContent.slice(insertPosition);
53
+ fs.writeFileSync(settingsPath, settingsContent, 'utf8');
54
+ emitLog('Writing urls.py...');
55
+ let urlsContent = fs.readFileSync(`${projectDir}/backend/core/urls.py`, 'utf8');
56
+ const newUrlsContent = `from django.contrib import admin
57
+ from django.urls import path, include
58
+ from rest_framework_simplejwt import views as jwt_views
59
+
60
+ urlpatterns = [
61
+ path('admin/', admin.site.urls),
62
+ path('api/token/',
63
+ jwt_views.TokenObtainPairView.as_view(),
64
+ name='token_obtain_pair'),
65
+ path('api/token/refresh/',
66
+ jwt_views.TokenRefreshView.as_view(),
67
+ name='token_refresh'),
68
+ path('', include('main.urls')),
69
+ ]
70
+ `;
71
+ fs.writeFileSync(`${projectDir}/backend/core/urls.py`, newUrlsContent, 'utf8');
72
+ emitLog('✅ JWT authentication setup completed successfully!');
73
+ }
74
+ catch (error) {
75
+ console.error('Error configuring Django settings:', error);
76
+ throw error;
77
+ }
78
+ }
@@ -0,0 +1,131 @@
1
+ import { join } from 'node:path';
2
+ import { mkdir, writeFile } from 'node:fs/promises';
3
+ import 'dotenv/config';
4
+ export async function setupNextAuth(config, projectDir, emitLog) {
5
+ try {
6
+ const authDir = join(projectDir, 'frontend', 'src', 'app', 'api', 'auth');
7
+ await mkdir(authDir, { recursive: true });
8
+ emitLog('Creating auth directory...');
9
+ const routeCode = `
10
+ import NextAuth from "next-auth";
11
+ import { AuthOptions } from "next-auth";
12
+ import GithubProvider from "next-auth/providers/github";
13
+ import GoogleProvider from "next-auth/providers/google";
14
+ import CredentialsProvider from "next-auth/providers/credentials";
15
+ export const authOptions: AuthOptions = {
16
+ providers: [
17
+ GithubProvider({
18
+ clientId: process.env.GITHUB_ID!,
19
+ clientSecret: process.env.GITHUB_SECRET!,
20
+ }),
21
+ GoogleProvider({
22
+ clientId: process.env.GOOGLE_CLIENT_ID!,
23
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
24
+ }),
25
+ CredentialsProvider({
26
+ name: 'Credentials',
27
+ credentials: {
28
+ email: { label: "Email", type: "email" },
29
+ password: { label: "Password", type: "password" }
30
+ },
31
+ async authorize(credentials) {
32
+ // Add your credentials logic here
33
+ if (!credentials?.email || !credentials?.password) return null;
34
+
35
+ try {
36
+ // Example user verification
37
+ const user = { id: "1", email: credentials.email, name: "User" };
38
+ return user;
39
+ } catch (error) {
40
+ return null;
41
+ }
42
+ }
43
+ }),
44
+ ],
45
+ pages: {
46
+ signIn: '/auth/signin',
47
+ signOut: '/auth/signout',
48
+ error: '/auth/error',
49
+ },
50
+ callbacks: {
51
+ async jwt({ token, user }) {
52
+ if (user) {
53
+ token.id = user.id;
54
+ }
55
+ return token;
56
+ },
57
+ async session({ session, token }) {
58
+ if (session.user) {
59
+ (session.user as any).id = token.id;
60
+ }
61
+ return session;
62
+ },
63
+ },
64
+ session: {
65
+ strategy: "jwt",
66
+ },
67
+ secret: process.env.NEXTAUTH_SECRET,
68
+ };
69
+
70
+ const handler = NextAuth(authOptions);
71
+ export { handler as GET, handler as POST };
72
+ `;
73
+ await writeFile(join(authDir, 'route.ts'), routeCode.trim() + '\n');
74
+ const utilsDir = join(projectDir, 'frontend', 'src', 'utils');
75
+ await mkdir(utilsDir, { recursive: true });
76
+ emitLog('Creating utils directory...');
77
+ const utilsCode = `
78
+ import { getServerSession } from "next-auth/next";
79
+ import { authOptions } from "@/app/api/auth/route";
80
+
81
+ export async function getSession() {
82
+ return await getServerSession(authOptions);
83
+ }
84
+
85
+ export async function getCurrentUser() {
86
+ const session = await getSession();
87
+ return session?.user;
88
+ }
89
+
90
+ export async function isAuthenticated() {
91
+ const session = await getSession();
92
+ return !!session;
93
+ }
94
+ `;
95
+ emitLog('Writing utils.ts...');
96
+ await writeFile(join(utilsDir, 'auth.ts'), utilsCode.trim() + '\n');
97
+ const envContent = `
98
+ # NextAuth Configuration
99
+ NEXTAUTH_SECRET=your-secret-key-here
100
+ NEXTAUTH_URL=http://localhost:${config.frontendPort}
101
+
102
+ # OAuth Providers
103
+ GITHUB_ID=your-github-id
104
+ GITHUB_SECRET=your-github-secret
105
+
106
+ GOOGLE_CLIENT_ID=your-google-client-id
107
+ GOOGLE_CLIENT_SECRET=your-google-client-secret
108
+ `;
109
+ emitLog('Writing .env file...');
110
+ await writeFile(join(projectDir, 'frontend', '.env'), envContent.trim() + '\n');
111
+ emitLog('Writing AuthProvider.tsx...');
112
+ const providerCode = `
113
+ 'use client';
114
+
115
+ import { SessionProvider } from "next-auth/react";
116
+
117
+ export function AuthProvider({ children }: { children: React.ReactNode }) {
118
+ return <SessionProvider>{children}</SessionProvider>;
119
+ }
120
+ `;
121
+ emitLog('Creating components directory...');
122
+ const providersDir = join(projectDir, 'frontend', 'src', 'components', 'auth');
123
+ await mkdir(providersDir, { recursive: true });
124
+ await writeFile(join(providersDir, 'AuthProvider.tsx'), providerCode.trim() + '\n');
125
+ emitLog('✅ NextAuth setup completed successfully!');
126
+ }
127
+ catch (error) {
128
+ emitLog(`❌ Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
129
+ throw error;
130
+ }
131
+ }