@shivasankaran18/stackd 1.3.0 → 1.5.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.
- package/dist/apps/cli/src/cli.js +217 -0
- package/dist/apps/cli/src/commands/create.js +148 -0
- package/dist/apps/cli/src/scripts/Auth/jwt.js +78 -0
- package/dist/apps/cli/src/scripts/Auth/nextAuth.js +131 -0
- package/dist/apps/cli/src/scripts/Auth/passport.js +218 -0
- package/dist/apps/cli/src/scripts/backend/django.js +20 -0
- package/dist/apps/cli/src/scripts/backend/expressjs.js +58 -0
- package/dist/apps/cli/src/scripts/backend/expressts.js +83 -0
- package/dist/apps/cli/src/scripts/frontend/angularjs.js +1 -0
- package/dist/apps/cli/src/scripts/frontend/angularts.js +22 -0
- package/dist/apps/cli/src/scripts/frontend/nextjs.js +62 -0
- package/dist/apps/cli/src/scripts/frontend/reactjs.js +31 -0
- package/dist/apps/cli/src/scripts/frontend/reactts.js +30 -0
- package/dist/apps/cli/src/scripts/frontend/vuejs.js +37 -0
- package/dist/apps/cli/src/scripts/frontend/vuets.js +43 -0
- package/dist/apps/cli/src/scripts/orms/drizzleSetup.js +85 -0
- package/dist/apps/cli/src/scripts/orms/mongoSetup.js +53 -0
- package/dist/apps/cli/src/scripts/orms/prismaSetup.js +12 -0
- package/dist/apps/cli/src/scripts/ui/shadcn.js +207 -0
- package/dist/apps/cli/src/scripts/ui/tailwindcss.js +102 -0
- package/dist/apps/web/app/api/auth/[...nextauth]/route.js +5 -0
- package/dist/apps/web/app/api/scaffold/route.js +251 -0
- package/dist/apps/web/app/home/page.js +19 -0
- package/dist/apps/web/app/layout.js +19 -0
- package/dist/apps/web/app/page.js +1 -0
- package/dist/apps/web/app/providers.js +7 -0
- package/dist/apps/web/app/scaffold/page.js +326 -0
- package/dist/apps/web/components/Sidebar.js +54 -0
- package/dist/apps/web/components/theme-provider.js +6 -0
- package/dist/apps/web/components/ui/button.js +32 -0
- package/dist/apps/web/components/ui/card.js +15 -0
- package/dist/apps/web/components/ui/dropdown-menu.js +54 -0
- package/dist/apps/web/components/ui/input.js +7 -0
- package/dist/apps/web/components/ui/label.js +9 -0
- package/dist/apps/web/components/ui/scroll-area.js +19 -0
- package/dist/apps/web/components/ui/sonner.js +15 -0
- package/dist/apps/web/components/ui/steps.js +14 -0
- package/dist/apps/web/components/ui/switch.js +9 -0
- package/dist/apps/web/lib/auth.js +32 -0
- package/dist/apps/web/lib/redis.js +9 -0
- package/dist/apps/web/lib/utils.js +5 -0
- package/dist/packages/scripts/Auth/jwt.js +78 -0
- package/dist/packages/scripts/Auth/nextAuth.js +131 -0
- package/dist/packages/scripts/Auth/passport.js +218 -0
- package/dist/packages/scripts/backend/django.js +20 -0
- package/dist/packages/scripts/backend/expressjs.js +58 -0
- package/dist/packages/scripts/backend/expressts.js +83 -0
- package/dist/packages/scripts/frontend/angularjs.js +1 -0
- package/dist/packages/scripts/frontend/angularts.js +22 -0
- package/dist/packages/scripts/frontend/nextjs.js +62 -0
- package/dist/packages/scripts/frontend/reactjs.js +31 -0
- package/dist/packages/scripts/frontend/reactts.js +30 -0
- package/dist/packages/scripts/frontend/vuejs.js +37 -0
- package/dist/packages/scripts/frontend/vuets.js +43 -0
- package/dist/packages/scripts/orms/drizzleSetup.js +85 -0
- package/dist/packages/scripts/orms/mongoSetup.js +53 -0
- package/dist/packages/scripts/orms/prismaSetup.js +12 -0
- package/dist/packages/scripts/ui/shadcn.js +207 -0
- package/dist/packages/scripts/ui/tailwindcss.js +102 -0
- package/dist/packages/ui/src/button.js +10 -0
- package/dist/packages/ui/src/card.js +11 -0
- package/dist/packages/ui/src/code.js +6 -0
- package/dist/packages/ui/turbo/generators/config.js +30 -0
- package/dist/stackd.js +114 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
@@ -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
|
+
}
|