create-backlist 2.0.1 → 4.0.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/bin/index.js +23 -6
- package/package.json +1 -1
- package/src/generators/node.js +86 -70
- package/src/templates/node-ts-express/partials/Auth.controller.ts.ejs +89 -0
- package/src/templates/node-ts-express/partials/Auth.middleware.ts.ejs +27 -0
- package/src/templates/node-ts-express/partials/Auth.routes.ts.ejs +15 -0
- package/src/templates/node-ts-express/partials/Controller.ts.ejs +7 -0
- package/src/templates/node-ts-express/partials/Seeder.ts.ejs +83 -0
- package/src/templates/node-ts-express/partials/routes.ts.ejs +16 -12
package/bin/index.js
CHANGED
|
@@ -34,15 +34,32 @@ async function main() {
|
|
|
34
34
|
],
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
type: 'input',
|
|
38
|
+
name: 'srcPath',
|
|
39
|
+
message: 'Enter the path to your frontend `src` directory:',
|
|
40
|
+
default: 'src',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'confirm',
|
|
44
|
+
name: 'addAuth',
|
|
45
|
+
message: 'Do you want to add basic JWT authentication? (generates a User model, login/register routes)',
|
|
46
|
+
default: true,
|
|
47
|
+
when: (answers) => answers.stack === 'node-ts-express'
|
|
48
|
+
},
|
|
49
|
+
// --- NEW QUESTION FOR V4.0 ---
|
|
50
|
+
{
|
|
51
|
+
type: 'confirm',
|
|
52
|
+
name: 'addSeeder',
|
|
53
|
+
message: 'Do you want to add a database seeder with sample user data?',
|
|
54
|
+
default: true,
|
|
55
|
+
// Only ask this if Node.js is selected AND authentication is being added
|
|
56
|
+
when: (answers) => answers.stack === 'node-ts-express' && answers.addAuth
|
|
41
57
|
}
|
|
42
58
|
]);
|
|
43
59
|
|
|
60
|
+
// Pass all answers to the options object
|
|
44
61
|
const options = {
|
|
45
|
-
...answers,
|
|
62
|
+
...answers,
|
|
46
63
|
projectDir: path.resolve(process.cwd(), answers.projectName),
|
|
47
64
|
frontendSrcDir: path.resolve(process.cwd(), answers.srcPath),
|
|
48
65
|
};
|
|
@@ -53,7 +70,7 @@ async function main() {
|
|
|
53
70
|
// --- Dispatcher Logic ---
|
|
54
71
|
switch (options.stack) {
|
|
55
72
|
case 'node-ts-express':
|
|
56
|
-
await generateNodeProject(options);
|
|
73
|
+
await generateNodeProject(options); // Pass the entire options object
|
|
57
74
|
break;
|
|
58
75
|
|
|
59
76
|
case 'dotnet-webapi':
|
package/package.json
CHANGED
package/src/generators/node.js
CHANGED
|
@@ -6,7 +6,8 @@ const { analyzeFrontend } = require('../analyzer');
|
|
|
6
6
|
const { renderAndWrite, getTemplatePath } = require('./template');
|
|
7
7
|
|
|
8
8
|
async function generateNodeProject(options) {
|
|
9
|
-
|
|
9
|
+
// v4.0: Destructure the new 'addSeeder' option
|
|
10
|
+
const { projectDir, projectName, frontendSrcDir, addAuth, addSeeder } = options;
|
|
10
11
|
|
|
11
12
|
try {
|
|
12
13
|
// --- Step 1: Analyze Frontend to get Endpoints and Schema Info ---
|
|
@@ -21,107 +22,122 @@ async function generateNodeProject(options) {
|
|
|
21
22
|
// --- Step 2: Identify which Database Models to Generate ---
|
|
22
23
|
const modelsToGenerate = new Map();
|
|
23
24
|
endpoints.forEach(ep => {
|
|
24
|
-
// If an endpoint has schemaFields and a valid controllerName, add it to our map.
|
|
25
25
|
if (ep.schemaFields && ep.controllerName !== 'Default' && !modelsToGenerate.has(ep.controllerName)) {
|
|
26
26
|
modelsToGenerate.set(ep.controllerName, ep.schemaFields);
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
if (addAuth && !modelsToGenerate.has('User')) {
|
|
31
|
+
console.log(chalk.yellow(' -> Authentication requires a "User" model. Creating a default one.'));
|
|
32
|
+
modelsToGenerate.set('User', { name: 'String', email: 'String', password: 'String' });
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
// --- Step 3: Scaffold Base Project Structure & Files ---
|
|
31
36
|
console.log(chalk.blue(' -> Scaffolding Node.js (Express + TS) project...'));
|
|
32
|
-
|
|
33
|
-
// Create the main source directory
|
|
34
37
|
const destSrcDir = path.join(projectDir, 'src');
|
|
35
38
|
await fs.ensureDir(destSrcDir);
|
|
36
|
-
|
|
37
|
-
// Copy static base files
|
|
38
39
|
await fs.copy(getTemplatePath('node-ts-express/base/server.ts'), path.join(destSrcDir, 'server.ts'));
|
|
39
40
|
await fs.copy(getTemplatePath('node-ts-express/base/tsconfig.json'), path.join(projectDir, 'tsconfig.json'));
|
|
40
41
|
|
|
41
|
-
// --- Step 4:
|
|
42
|
-
|
|
43
|
-
// Prepare package.json content (in memory)
|
|
42
|
+
// --- Step 4: Prepare and Write package.json with all conditional dependencies ---
|
|
44
43
|
const packageJsonContent = JSON.parse(
|
|
45
44
|
await ejs.renderFile(getTemplatePath('node-ts-express/partials/package.json.ejs'), { projectName })
|
|
46
45
|
);
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
if (modelsToGenerate.size > 0 || addAuth) {
|
|
48
|
+
packageJsonContent.dependencies['mongoose'] = '^7.5.0';
|
|
49
|
+
}
|
|
50
|
+
if (addAuth) {
|
|
51
|
+
packageJsonContent.dependencies['jsonwebtoken'] = '^9.0.2';
|
|
52
|
+
packageJsonContent.dependencies['bcryptjs'] = '^2.4.3';
|
|
53
|
+
packageJsonContent.devDependencies['@types/jsonwebtoken'] = '^9.0.2';
|
|
54
|
+
packageJsonContent.devDependencies['@types/bcryptjs'] = '^2.4.2';
|
|
55
|
+
}
|
|
56
|
+
// v4.0: Add seeder dependencies and scripts
|
|
57
|
+
if (addSeeder) {
|
|
58
|
+
packageJsonContent.devDependencies['@faker-js/faker'] = '^8.2.0';
|
|
59
|
+
// We also need chalk for the seeder script's console logs
|
|
60
|
+
packageJsonContent.dependencies['chalk'] = '^4.1.2';
|
|
61
|
+
packageJsonContent.scripts['seed'] = 'ts-node scripts/seeder.ts';
|
|
62
|
+
packageJsonContent.scripts['destroy'] = 'ts-node scripts/seeder.ts -d';
|
|
52
63
|
}
|
|
53
|
-
|
|
54
|
-
// Write the final package.json to the disk
|
|
55
64
|
await fs.writeJson(path.join(projectDir, 'package.json'), packageJsonContent, { spaces: 2 });
|
|
56
65
|
|
|
57
|
-
//
|
|
66
|
+
// --- Step 5 & 6: Generate Models, Controllers, and Auth boilerplate ---
|
|
58
67
|
if (modelsToGenerate.size > 0) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
console.log(chalk.blue(' -> Generating database models and controllers...'));
|
|
69
|
+
await fs.ensureDir(path.join(destSrcDir, 'models'));
|
|
70
|
+
await fs.ensureDir(path.join(destSrcDir, 'controllers'));
|
|
71
|
+
for (let [modelName, schema] of modelsToGenerate.entries()) {
|
|
72
|
+
if (addAuth && modelName === 'User') {
|
|
73
|
+
schema = { name: 'String', email: 'String', password: 'String', ...schema };
|
|
74
|
+
}
|
|
75
|
+
await renderAndWrite(getTemplatePath('node-ts-express/partials/Model.ts.ejs'), path.join(destSrcDir, 'models', `${modelName}.model.ts`), { modelName, schema });
|
|
76
|
+
await renderAndWrite(getTemplatePath('node-ts-express/partials/Controller.ts.ejs'), path.join(destSrcDir, 'controllers', `${modelName}.controller.ts`), { modelName });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (addAuth) {
|
|
80
|
+
console.log(chalk.blue(' -> Generating authentication boilerplate...'));
|
|
81
|
+
await fs.ensureDir(path.join(destSrcDir, 'routes'));
|
|
82
|
+
await fs.ensureDir(path.join(destSrcDir, 'middleware'));
|
|
83
|
+
await renderAndWrite(getTemplatePath('node-ts-express/partials/Auth.controller.ts.ejs'), path.join(destSrcDir, 'controllers', 'Auth.controller.ts'), {});
|
|
84
|
+
await renderAndWrite(getTemplatePath('node-ts-express/partials/Auth.routes.ts.ejs'), path.join(destSrcDir, 'routes', 'Auth.routes.ts'), {});
|
|
85
|
+
await renderAndWrite(getTemplatePath('node-ts-express/partials/Auth.middleware.ts.ejs'), path.join(destSrcDir, 'middleware', 'Auth.middleware.ts'), {});
|
|
62
86
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
path.join(destSrcDir, 'controllers', `${modelName}.controller.ts`),
|
|
74
|
-
{ modelName }
|
|
75
|
-
);
|
|
76
|
-
}
|
|
87
|
+
const userModelPath = path.join(destSrcDir, 'models', 'User.model.ts');
|
|
88
|
+
if (await fs.pathExists(userModelPath)) {
|
|
89
|
+
let userModelContent = await fs.readFile(userModelPath, 'utf-8');
|
|
90
|
+
if (!userModelContent.includes('bcryptjs')) {
|
|
91
|
+
userModelContent = userModelContent.replace(`import mongoose, { Schema, Document } from 'mongoose';`, `import mongoose, { Schema, Document } from 'mongoose';\nimport bcrypt from 'bcryptjs';`);
|
|
92
|
+
const preSaveHook = `\n// Hash password before saving\nUserSchema.pre('save', async function(next) {\n if (!this.isModified('password')) {\n return next();\n }\n const salt = await bcrypt.genSalt(10);\n this.password = await bcrypt.hash(this.password, salt);\n next();\n});\n`;
|
|
93
|
+
userModelContent = userModelContent.replace(`// Create and export the Model`, `${preSaveHook}\n// Create and export the Model`);
|
|
94
|
+
await fs.writeFile(userModelPath, userModelContent);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
77
97
|
}
|
|
78
98
|
|
|
79
|
-
// --- Step
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
// --- Step 7 (v4.0): Generate Seeder Script ---
|
|
100
|
+
if (addSeeder) {
|
|
101
|
+
console.log(chalk.blue(' -> Generating database seeder script...'));
|
|
102
|
+
await fs.ensureDir(path.join(projectDir, 'scripts'));
|
|
103
|
+
await renderAndWrite(
|
|
104
|
+
getTemplatePath('node-ts-express/partials/Seeder.ts.ejs'),
|
|
105
|
+
path.join(projectDir, 'scripts', 'seeder.ts'),
|
|
106
|
+
{ projectName }
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- Step 8: Generate the Main Route File ---
|
|
111
|
+
console.log(chalk.gray(' -> Generating dynamic API routes...'));
|
|
112
|
+
await renderAndWrite(getTemplatePath('node-ts-express/partials/routes.ts.ejs'), path.join(destSrcDir, 'routes.ts'), { endpoints, addAuth });
|
|
90
113
|
|
|
114
|
+
// --- Step 9: Inject Logic into Main Server File ---
|
|
115
|
+
let serverFileContent = await fs.readFile(path.join(destSrcDir, 'server.ts'), 'utf-8');
|
|
91
116
|
let dbConnectionCode = '';
|
|
92
|
-
if (modelsToGenerate.size > 0) {
|
|
93
|
-
dbConnectionCode =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.then(() => console.log('MongoDB Connected...'))
|
|
99
|
-
.catch(err => console.error('MongoDB Connection Error:', err));
|
|
100
|
-
// -------------------------
|
|
101
|
-
`;
|
|
117
|
+
if (modelsToGenerate.size > 0 || addAuth) {
|
|
118
|
+
dbConnectionCode = `\n// --- Database Connection ---\nimport mongoose from 'mongoose';\nconst MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/${projectName}';\nmongoose.connect(MONGO_URI)\n .then(() => console.log('MongoDB Connected...'))\n .catch(err => console.error('MongoDB Connection Error:', err));\n// -------------------------\n`;
|
|
119
|
+
}
|
|
120
|
+
let authRoutesInjector = '';
|
|
121
|
+
if (addAuth) {
|
|
122
|
+
authRoutesInjector = `import authRoutes from './routes/Auth.routes';\napp.use('/api/auth', authRoutes);\n\n`;
|
|
102
123
|
}
|
|
103
|
-
|
|
104
|
-
// Inject DB connection code after dotenv.config() and route loader
|
|
105
124
|
serverFileContent = serverFileContent
|
|
106
|
-
.replace("dotenv.config();", `dotenv.config()
|
|
107
|
-
.replace('// INJECT:ROUTES',
|
|
108
|
-
|
|
109
|
-
await fs.writeFile(serverDestPath, serverFileContent);
|
|
125
|
+
.replace("dotenv.config();", `dotenv.config();${dbConnectionCode}`)
|
|
126
|
+
.replace('// INJECT:ROUTES', `${authRoutesInjector}import apiRoutes from './routes';\napp.use('/api', apiRoutes);`);
|
|
127
|
+
await fs.writeFile(path.join(destSrcDir, 'server.ts'), serverFileContent);
|
|
110
128
|
|
|
111
|
-
// --- Step
|
|
112
|
-
console.log(chalk.magenta(' -> Installing dependencies
|
|
129
|
+
// --- Step 10: Install All Dependencies ---
|
|
130
|
+
console.log(chalk.magenta(' -> Installing all dependencies... This might take a moment.'));
|
|
113
131
|
await execa('npm', ['install'], { cwd: projectDir });
|
|
114
132
|
|
|
115
|
-
// --- Step
|
|
116
|
-
await renderAndWrite(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
133
|
+
// --- Step 11: Generate Final Files (README, .env.example) ---
|
|
134
|
+
await renderAndWrite(getTemplatePath('node-ts-express/partials/README.md.ejs'), path.join(projectDir, 'README.md'), { projectName });
|
|
135
|
+
if (addAuth) {
|
|
136
|
+
const envExampleContent = `PORT=8000\nMONGO_URI=mongodb://127.0.0.1:27017/${projectName}\nJWT_SECRET=your_super_secret_jwt_key_123`;
|
|
137
|
+
await fs.writeFile(path.join(projectDir, '.env.example'), envExampleContent);
|
|
138
|
+
}
|
|
121
139
|
|
|
122
140
|
} catch (error) {
|
|
123
|
-
// Re-throw the error so it can be caught by the main CLI handler in index.js
|
|
124
|
-
// This allows for centralized error message display and cleanup.
|
|
125
141
|
throw error;
|
|
126
142
|
}
|
|
127
143
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Auto-generated by create-backlist v3.0 on <%= new Date().toISOString() %>
|
|
2
|
+
import { Request, Response } from 'express';
|
|
3
|
+
import bcrypt from 'bcryptjs';
|
|
4
|
+
import jwt from 'jsonwebtoken';
|
|
5
|
+
import User, { IUser } from '../models/User.model'; // We assume the model is named 'User'
|
|
6
|
+
|
|
7
|
+
// @desc Register a new user
|
|
8
|
+
export const registerUser = async (req: Request, res: Response) => {
|
|
9
|
+
const { name, email, password } = req.body;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Check if user already exists
|
|
13
|
+
let user = await User.findOne({ email });
|
|
14
|
+
if (user) {
|
|
15
|
+
return res.status(400).json({ message: 'User already exists' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Create a new user instance
|
|
19
|
+
user = new User({
|
|
20
|
+
name,
|
|
21
|
+
email,
|
|
22
|
+
password, // Password will be hashed by the pre-save hook in the model
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Save the user to the database
|
|
26
|
+
await user.save();
|
|
27
|
+
|
|
28
|
+
// Create JWT Payload
|
|
29
|
+
const payload = {
|
|
30
|
+
user: {
|
|
31
|
+
id: user.id,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Sign the token
|
|
36
|
+
jwt.sign(
|
|
37
|
+
payload,
|
|
38
|
+
process.env.JWT_SECRET as string,
|
|
39
|
+
{ expiresIn: '5h' }, // Token expires in 5 hours
|
|
40
|
+
(err, token) => {
|
|
41
|
+
if (err) throw err;
|
|
42
|
+
res.status(201).json({ token });
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(error);
|
|
47
|
+
res.status(500).send('Server Error');
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// @desc Authenticate user & get token (Login)
|
|
52
|
+
export const loginUser = async (req: Request, res: Response) => {
|
|
53
|
+
const { email, password } = req.body;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
// Check if user exists
|
|
57
|
+
const user = await User.findOne({ email });
|
|
58
|
+
if (!user) {
|
|
59
|
+
return res.status(400).json({ message: 'Invalid Credentials' });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Compare entered password with stored hashed password
|
|
63
|
+
const isMatch = await bcrypt.compare(password, user.password);
|
|
64
|
+
if (!isMatch) {
|
|
65
|
+
return res.status(400).json({ message: 'Invalid Credentials' });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Create JWT Payload
|
|
69
|
+
const payload = {
|
|
70
|
+
user: {
|
|
71
|
+
id: user.id,
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Sign the token
|
|
76
|
+
jwt.sign(
|
|
77
|
+
payload,
|
|
78
|
+
process.env.JWT_SECRET as string,
|
|
79
|
+
{ expiresIn: '5h' },
|
|
80
|
+
(err, token) => {
|
|
81
|
+
if (err) throw err;
|
|
82
|
+
res.json({ token });
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(error);
|
|
87
|
+
res.status(500).send('Server Error');
|
|
88
|
+
}
|
|
89
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Auto-generated by create-backlist v3.0 on <%= new Date().toISOString() %>
|
|
2
|
+
import { Request, Response, NextFunction } from 'express';
|
|
3
|
+
import jwt from 'jsonwebtoken';
|
|
4
|
+
|
|
5
|
+
// Extend the default Request interface to include our 'user' property
|
|
6
|
+
interface AuthRequest extends Request {
|
|
7
|
+
user?: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const protect = (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
11
|
+
// Get token from header
|
|
12
|
+
const token = req.header('x-auth-token');
|
|
13
|
+
|
|
14
|
+
// Check if not token
|
|
15
|
+
if (!token) {
|
|
16
|
+
return res.status(401).json({ message: 'No token, authorization denied' });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Verify token
|
|
20
|
+
try {
|
|
21
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET as string);
|
|
22
|
+
req.user = decoded.user;
|
|
23
|
+
next();
|
|
24
|
+
} catch (err) {
|
|
25
|
+
res.status(401).json({ message: 'Token is not valid' });
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Auto-generated by create-backlist v3.0 on <%= new Date().toISOString() %>
|
|
2
|
+
import { Router } from 'express';
|
|
3
|
+
import { registerUser, loginUser } from '../controllers/Auth.controller';
|
|
4
|
+
|
|
5
|
+
const router = Router();
|
|
6
|
+
|
|
7
|
+
// @route POST /api/auth/register
|
|
8
|
+
// @desc Register a new user
|
|
9
|
+
router.post('/register', registerUser);
|
|
10
|
+
|
|
11
|
+
// @route POST /api/auth/login
|
|
12
|
+
// @desc Authenticate user and get token
|
|
13
|
+
router.post('/login', loginUser);
|
|
14
|
+
|
|
15
|
+
export default router;
|
|
@@ -9,6 +9,9 @@ export const create<%= modelName %> = async (req: Request, res: Response) => {
|
|
|
9
9
|
await newDoc.save();
|
|
10
10
|
res.status(201).json(newDoc);
|
|
11
11
|
} catch (error) {
|
|
12
|
+
if (error.name === 'ValidationError') {
|
|
13
|
+
return res.status(400).json({ message: 'Validation Error', errors: error.errors });
|
|
14
|
+
}
|
|
12
15
|
res.status(500).json({ message: 'Error creating document', error });
|
|
13
16
|
}
|
|
14
17
|
};
|
|
@@ -41,6 +44,9 @@ export const update<%= modelName %>ById = async (req: Request, res: Response) =>
|
|
|
41
44
|
if (!doc) return res.status(404).json({ message: 'Document not found' });
|
|
42
45
|
res.status(200).json(doc);
|
|
43
46
|
} catch (error) {
|
|
47
|
+
if (error.name === 'ValidationError') {
|
|
48
|
+
return res.status(400).json({ message: 'Validation Error', errors: error.errors });
|
|
49
|
+
}
|
|
44
50
|
res.status(500).json({ message: 'Error updating document', error });
|
|
45
51
|
}
|
|
46
52
|
};
|
|
@@ -50,6 +56,7 @@ export const delete<%= modelName %>ById = async (req: Request, res: Response) =>
|
|
|
50
56
|
try {
|
|
51
57
|
const doc = await <%= modelName %>.findByIdAndDelete(req.params.id);
|
|
52
58
|
if (!doc) return res.status(404).json({ message: 'Document not found' });
|
|
59
|
+
// For DELETE, it's common to return a success message or just a 204 No Content status.
|
|
53
60
|
res.status(200).json({ message: 'Document deleted successfully' });
|
|
54
61
|
} catch (error) {
|
|
55
62
|
res.status(500).json({ message: 'Error deleting document', error });
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Auto-generated by create-backlist v4.0 on <%= new Date().toISOString() %>
|
|
2
|
+
import mongoose from 'mongoose';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import { faker } from '@faker-js/faker';
|
|
5
|
+
import chalk from 'chalk'; // For colorful console logs
|
|
6
|
+
|
|
7
|
+
// Load env vars
|
|
8
|
+
dotenv.config();
|
|
9
|
+
|
|
10
|
+
// We assume a User model exists for seeding.
|
|
11
|
+
// The path is relative to the generated 'backend' project root.
|
|
12
|
+
import User from '../src/models/User.model';
|
|
13
|
+
|
|
14
|
+
// --- Connect to DB ---
|
|
15
|
+
const connectDB = async () => {
|
|
16
|
+
try {
|
|
17
|
+
const MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/<%= projectName %>';
|
|
18
|
+
if (!MONGO_URI) {
|
|
19
|
+
throw new Error('MONGO_URI is not defined in your .env file');
|
|
20
|
+
}
|
|
21
|
+
await mongoose.connect(MONGO_URI);
|
|
22
|
+
console.log(chalk.green('MongoDB Connected for Seeder...'));
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.error(chalk.red(`Seeder DB Connection Error: ${err.message}`));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// --- Import Data ---
|
|
30
|
+
const importData = async () => {
|
|
31
|
+
try {
|
|
32
|
+
// Clear existing data
|
|
33
|
+
await User.deleteMany();
|
|
34
|
+
|
|
35
|
+
const sampleUsers = [];
|
|
36
|
+
const userCount = 10; // Number of sample users to create
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < userCount; i++) {
|
|
39
|
+
sampleUsers.push({
|
|
40
|
+
name: faker.person.fullName(),
|
|
41
|
+
email: faker.internet.email().toLowerCase(),
|
|
42
|
+
password: 'password123', // All sample users will have the same password for easy testing
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await User.insertMany(sampleUsers);
|
|
47
|
+
|
|
48
|
+
console.log(chalk.green.bold('✅ Data Imported Successfully!'));
|
|
49
|
+
process.exit();
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(chalk.red(`Error with data import: ${error.message}`));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// --- Destroy Data ---
|
|
57
|
+
const destroyData = async () => {
|
|
58
|
+
try {
|
|
59
|
+
await User.deleteMany();
|
|
60
|
+
// If you have other models, you can add them here for destruction
|
|
61
|
+
// e.g., await Product.deleteMany();
|
|
62
|
+
|
|
63
|
+
console.log(chalk.red.bold('🔥 Data Destroyed Successfully!'));
|
|
64
|
+
process.exit();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(chalk.red(`Error with data destruction: ${error.message}`));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// --- CLI Logic to run the seeder ---
|
|
72
|
+
const runSeeder = async () => {
|
|
73
|
+
await connectDB();
|
|
74
|
+
|
|
75
|
+
// process.argv[2] will be '-d' if the script is run with `npm run destroy`
|
|
76
|
+
if (process.argv[2] === '-d') {
|
|
77
|
+
await destroyData();
|
|
78
|
+
} else {
|
|
79
|
+
await importData();
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
runSeeder();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Auto-generated by create-backlist on <%= new Date().toISOString() %>
|
|
1
|
+
// Auto-generated by create-backlist v3.0 on <%= new Date().toISOString() %>
|
|
2
2
|
import { Router, Request, Response } from 'express';
|
|
3
3
|
<%# Create a unique set of controller names from the endpoints array %>
|
|
4
4
|
<% const controllersToImport = new Set(endpoints.map(ep => ep.controllerName).filter(name => name !== 'Default')); %>
|
|
@@ -8,6 +8,11 @@ import { Router, Request, Response } from 'express';
|
|
|
8
8
|
import * as <%= controller %>Controller from '../controllers/<%= controller %>.controller';
|
|
9
9
|
<% } %>
|
|
10
10
|
|
|
11
|
+
<%# Import the protect middleware only if authentication is enabled %>
|
|
12
|
+
<% if (addAuth) { %>
|
|
13
|
+
import { protect } from '../middleware/Auth.middleware';
|
|
14
|
+
<% } %>
|
|
15
|
+
|
|
11
16
|
const router = Router();
|
|
12
17
|
|
|
13
18
|
<%# Loop through each endpoint found by the analyzer %>
|
|
@@ -19,41 +24,40 @@ const router = Router();
|
|
|
19
24
|
let handlerFunction;
|
|
20
25
|
|
|
21
26
|
// --- LOGIC TO MAP ENDPOINT TO A CRUD CONTROLLER FUNCTION ---
|
|
22
|
-
// This logic assumes a standard RESTful API structure.
|
|
23
|
-
|
|
24
27
|
if (controllerName !== 'Default') {
|
|
25
28
|
if (endpoint.method === 'POST' && !expressPath.includes(':')) {
|
|
26
|
-
// e.g., POST /users -> create a new user
|
|
27
29
|
handlerFunction = `${controllerName}Controller.create${controllerName}`;
|
|
28
30
|
} else if (endpoint.method === 'GET' && !expressPath.includes(':')) {
|
|
29
|
-
// e.g., GET /users -> get all users
|
|
30
31
|
handlerFunction = `${controllerName}Controller.getAll${controllerName}s`;
|
|
31
32
|
} else if (endpoint.method === 'GET' && expressPath.includes(':')) {
|
|
32
|
-
// e.g., GET /users/:id -> get a single user by ID
|
|
33
33
|
handlerFunction = `${controllerName}Controller.get${controllerName}ById`;
|
|
34
34
|
} else if (endpoint.method === 'PUT' && expressPath.includes(':')) {
|
|
35
|
-
// e.g., PUT /users/:id -> update a user by ID
|
|
36
35
|
handlerFunction = `${controllerName}Controller.update${controllerName}ById`;
|
|
37
36
|
} else if (endpoint.method === 'DELETE' && expressPath.includes(':')) {
|
|
38
|
-
// e.g., DELETE /users/:id -> delete a user by ID
|
|
39
37
|
handlerFunction = `${controllerName}Controller.delete${controllerName}ById`;
|
|
40
38
|
}
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
// If no specific CRUD function matches,
|
|
44
|
-
// create a simple placeholder function.
|
|
41
|
+
// If no specific CRUD function matches, create a placeholder handler.
|
|
45
42
|
if (!handlerFunction) {
|
|
46
43
|
handlerFunction = `(req: Request, res: Response) => {
|
|
47
|
-
// TODO: Implement logic for this custom endpoint
|
|
48
44
|
res.status(501).json({ message: 'Handler not implemented for <%= endpoint.method %> <%= expressPath %>' });
|
|
49
45
|
}`;
|
|
50
46
|
}
|
|
47
|
+
|
|
48
|
+
// --- V3.0 AUTH LOGIC: Decide if the route should be protected ---
|
|
49
|
+
// We protect all routes that modify data (POST, PUT, DELETE) if auth is enabled.
|
|
50
|
+
// We leave GET routes public by default. This is a common pattern.
|
|
51
|
+
const middleware = (addAuth && (endpoint.method === 'POST' || endpoint.method === 'PUT' || endpoint.method === 'DELETE'))
|
|
52
|
+
? 'protect, '
|
|
53
|
+
: '';
|
|
51
54
|
%>
|
|
52
55
|
/**
|
|
53
56
|
* Route for <%= endpoint.method.toUpperCase() %> <%= endpoint.path %>
|
|
54
57
|
* Mapped to: <%- handlerFunction.includes('=>') ? 'Inline Handler' : handlerFunction %>
|
|
58
|
+
* Protected: <%= middleware ? 'Yes' : 'No' %>
|
|
55
59
|
*/
|
|
56
|
-
router.<%= endpoint.method.toLowerCase() %>('<%- expressPath %>', <%- handlerFunction %>);
|
|
60
|
+
router.<%= endpoint.method.toLowerCase() %>('<%- expressPath %>', <%- middleware %><%- handlerFunction %>);
|
|
57
61
|
|
|
58
62
|
<% }); %>
|
|
59
63
|
|