neatnode 1.0.1 → 3.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.
Files changed (62) hide show
  1. package/README.md +107 -0
  2. package/bin/index.js +1 -1
  3. package/package.json +9 -1
  4. package/src/actions/createProject.js +26 -9
  5. package/src/actions/removeCRUD.js +49 -0
  6. package/src/cli.js +48 -8
  7. package/src/utils/copyTemplate.js +19 -10
  8. package/templates/express-basic/.env.example +3 -0
  9. package/templates/express-basic/package-lock.json +1505 -0
  10. package/templates/express-basic/package.json +21 -0
  11. package/templates/express-basic/server.js +11 -0
  12. package/templates/express-basic/src/app.js +45 -0
  13. package/templates/express-basic/src/config/db.config.js +12 -0
  14. package/templates/express-basic/src/config/env.config.js +11 -0
  15. package/templates/express-basic/src/controllers/todo.controller.js +90 -0
  16. package/templates/express-basic/src/middleware/notFound.middleware.js +5 -0
  17. package/templates/express-basic/src/models/todo.model.js +21 -0
  18. package/templates/express-basic/src/routes/index.route.js +10 -0
  19. package/templates/express-basic/src/routes/todo.route.js +15 -0
  20. package/templates/express-basic/src/utils/responseHandler.js +7 -0
  21. package/templates/express-rest-api/.env.example +7 -0
  22. package/templates/express-rest-api/package-lock.json +1977 -0
  23. package/templates/express-rest-api/package.json +30 -0
  24. package/templates/express-rest-api/server.js +14 -0
  25. package/templates/express-rest-api/src/app.js +55 -0
  26. package/templates/express-rest-api/src/config/db.config.js +14 -0
  27. package/templates/express-rest-api/src/config/env.config.js +13 -0
  28. package/templates/express-rest-api/src/config/logger.config.js +20 -0
  29. package/templates/express-rest-api/src/controllers/user.controller.js +35 -0
  30. package/templates/express-rest-api/src/middleware/auth.middleware.js +36 -0
  31. package/templates/express-rest-api/src/middleware/error.middleware.js +32 -0
  32. package/templates/express-rest-api/src/middleware/rateLimiter.js +13 -0
  33. package/templates/express-rest-api/src/middleware/validateRequest.middleware.js +15 -0
  34. package/templates/express-rest-api/src/models/user.model.js +31 -0
  35. package/templates/express-rest-api/src/routes/user.route.js +14 -0
  36. package/templates/express-rest-api/src/schemas/user.schema.js +43 -0
  37. package/templates/express-rest-api/src/services/user.service.js +54 -0
  38. package/templates/express-rest-api/src/utils/ApiError.js +17 -0
  39. package/templates/express-rest-api/src/utils/ApiResponse.js +9 -0
  40. package/templates/express-rest-api/src/utils/CatchAsync.js +5 -0
  41. package/templates/express-rest-api/src/utils/Token.js +16 -0
  42. package/templates/express-socket/.env.example +5 -0
  43. package/templates/express-socket/package-lock.json +2262 -0
  44. package/templates/express-socket/package.json +31 -0
  45. package/templates/express-socket/server.js +19 -0
  46. package/templates/express-socket/src/app.js +33 -0
  47. package/templates/express-socket/src/config/db.config.js +14 -0
  48. package/templates/express-socket/src/config/env.config.js +10 -0
  49. package/templates/express-socket/src/config/logger.js +20 -0
  50. package/templates/express-socket/src/config/socket.js +23 -0
  51. package/templates/express-socket/src/middleware/auth.middleware.js +36 -0
  52. package/templates/express-socket/src/middleware/error.middleware.js +31 -0
  53. package/templates/express-socket/src/middleware/rateLimiter.js +14 -0
  54. package/templates/express-socket/src/middleware/validateRequest.middleware.js +15 -0
  55. package/templates/express-socket/src/utils/ApiError.js +17 -0
  56. package/templates/express-socket/src/utils/ApiResponse.js +9 -0
  57. package/templates/express-socket/src/utils/CatchAsync.js +5 -0
  58. package/templates/express-socket/src/utils/Token.js +16 -0
  59. package/templates/express-basic/.env +0 -0
  60. package/templates/express-basic/app.js +0 -5
  61. package/templates/express-rest-api/index.js +0 -5
  62. package/templates/express-socket/index.js +0 -5
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "project-name",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "server.js",
6
+ "scripts": {
7
+ "start": "node server.js",
8
+ "dev": "nodemon server.js"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "type": "module",
14
+ "dependencies": {
15
+ "bcryptjs": "^3.0.2",
16
+ "cors": "^2.8.5",
17
+ "dotenv": "^17.2.3",
18
+ "express": "^5.1.0",
19
+ "express-rate-limit": "^8.2.1",
20
+ "helmet": "^8.1.0",
21
+ "joi": "^18.0.1",
22
+ "jsonwebtoken": "^9.0.2",
23
+ "mongoose": "^8.19.2",
24
+ "morgan": "^1.10.1",
25
+ "winston": "^3.18.3"
26
+ },
27
+ "devDependencies": {
28
+ "nodemon": "^3.1.10"
29
+ }
30
+ }
@@ -0,0 +1,14 @@
1
+ import app from "./src/app.js";
2
+ import { connectDB } from "./src/config/db.config.js";
3
+ import { config } from "./src/config/env.config.js";
4
+ import logger from "./src/config/logger.config.js";
5
+ const PORT = config.port || 3000;
6
+
7
+ // Connect to the database
8
+ connectDB();
9
+
10
+ // Start the server
11
+ app.listen(PORT, () => {
12
+ logger.info(`Server is running on port ${PORT}`);
13
+ });
14
+
@@ -0,0 +1,55 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import morgan from 'morgan';
4
+
5
+ // ROUTE_IMPORTS_START
6
+ import authRoutes from './routes/user.route.js'
7
+ // ROUTE_IMPORTS_END
8
+
9
+ import { errorHandler, notFound } from './middleware/error.middleware.js';
10
+ import { config } from './config/env.config.js';
11
+ import helmet from 'helmet';
12
+ import { rateLimiter } from './middleware/rateLimiter.js';
13
+
14
+
15
+
16
+
17
+ // instance
18
+ const app = express();
19
+
20
+ // middlewares
21
+ app.use(cors({
22
+ origin: config.allowedOrigins,
23
+ // { credentials: true } // uncomment this if you need to work with cookies
24
+ }));
25
+
26
+
27
+ app.use(express.json());
28
+ app.use(express.urlencoded({ extended: true }));
29
+ app.use(morgan('dev'));
30
+ app.use(helmet());
31
+
32
+ // routes
33
+
34
+ // ROUTE_USES_START
35
+ app.use("/api/auth", authRoutes);
36
+ // ROUTE_USES_END
37
+
38
+
39
+ // default route
40
+
41
+ app.get("/", (req, res) => {
42
+ res.status(200).json({ message: "API is running..." });
43
+ });
44
+
45
+ // health check route with rate limiting
46
+ app.get("/health", rateLimiter(20), (req, res) => {
47
+ res.status(200).json({ status: "OK", message: "ALL IS WELL😂..." });
48
+ });
49
+
50
+
51
+ // error handling middlewares
52
+ app.use(notFound);
53
+ app.use(errorHandler);
54
+
55
+ export default app;
@@ -0,0 +1,14 @@
1
+ import mongoose from "mongoose";
2
+ import { config } from "./env.config.js";
3
+ import logger from "./logger.config.js";
4
+
5
+ export const connectDB = async () => {
6
+ try {
7
+ await mongoose.connect(config.dbUri);
8
+ logger.info("✔ Database connected");
9
+
10
+ } catch (error) {
11
+ logger.error("✖ Database connection error:", error);
12
+ process.exit(1);
13
+ }
14
+ }
@@ -0,0 +1,13 @@
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+
4
+ export const config = {
5
+ jwtSecret: process.env.JWT_SECRET,
6
+ dbUri: process.env.MONGODB_URI,
7
+ port: process.env.PORT || 3000,
8
+ nodeEnv: process.env.NODE_ENV || "development",
9
+ allowedOrigins: [ process.env.CLIENT_URL,
10
+ "http://localhost:5137",
11
+ ],
12
+
13
+ }
@@ -0,0 +1,20 @@
1
+ import winston from "winston";
2
+
3
+ const { combine, timestamp, printf, colorize } = winston.format;
4
+
5
+ const logFormat = printf(({ level, message, timestamp }) => {
6
+ return `[${timestamp}] ${level}: ${message}`;
7
+ });
8
+
9
+ const logger = winston.createLogger({
10
+ level: "info",
11
+ format: combine(colorize(), timestamp({ format: "HH:mm:ss" }), logFormat),
12
+ transports: [
13
+ new winston.transports.Console(),
14
+ // new winston.transports.File({ filename: "logs/app.log" })
15
+ // To log to a file, uncomment the File transport above.
16
+ ],
17
+ });
18
+
19
+ export default logger;
20
+
@@ -0,0 +1,35 @@
1
+ import { createUserService, getUserService, loginUserService } from "../services/user.service.js";
2
+ import sendResponse from "../utils/ApiResponse.js";
3
+ import catchAsync from "../utils/catchAsync.js";
4
+
5
+ // Controller to create a new user
6
+ export const createUser = catchAsync(async (req, res, next) => {
7
+ const { name, email, password } = req.body;
8
+
9
+ // Create a new user
10
+ const user = await createUserService({ name, email, password });
11
+
12
+ // Send response
13
+ sendResponse(res, 201, "User created successfully", { user });
14
+ });
15
+
16
+ // Controller to login user
17
+ export const loginUser = catchAsync(async (req, res, next) => {
18
+ const { email, password } = req.body;
19
+
20
+ // Login user
21
+ const { user, token } = await loginUserService({ email, password });
22
+
23
+ // Send response
24
+ sendResponse(res, 200, "Login successful", { user, token });
25
+ });
26
+
27
+ // Controller to get user by logged in User ID
28
+ export const getUser = catchAsync(async (req, res, next) => {
29
+ const userId = req.user.userid;
30
+
31
+ const user = await getUserService(userId);
32
+ if (!user) return next(new ApiError(404, "User not found"));
33
+
34
+ sendResponse(res, 200, "User retrieved successfully", { user });
35
+ });
@@ -0,0 +1,36 @@
1
+ import { verifyAccessToken } from "../utils/Token.js";
2
+
3
+
4
+ // Middleware to protect routes
5
+ export const protect = (req, res, next) => {
6
+ const token = req.headers.authorization.split(' ')[1];
7
+
8
+ // Check for token
9
+ if (!token || !token.startsWith('Bearer ')) {
10
+ return res.status(401).json({ message: 'Unauthorized' });
11
+ }
12
+
13
+ // Verify token
14
+ const decoded = verifyAccessToken(token);
15
+
16
+ // Check if token is valid
17
+ if (!decoded) {
18
+ return res.status(401).json({ message: 'Unauthorized' });
19
+ }
20
+
21
+ // Attach token
22
+ req.user = decoded;
23
+ next();
24
+ };
25
+
26
+ // Middleware to restrict access based on user roles
27
+ export const restrictTo = (...roles) => {
28
+ return (req, res, next) => {
29
+ if (!roles.includes(req.user.role)) {
30
+ return res.status(403).json({ message: 'Forbidden' });
31
+ }
32
+ next();
33
+ };
34
+ };
35
+
36
+
@@ -0,0 +1,32 @@
1
+ import { config } from "../config/env.config.js";
2
+ import logger from "../config/logger.config.js";
3
+ import ApiError from "../utils/ApiError.js";
4
+
5
+
6
+ // Global error handling middleware
7
+ export const errorHandler = (err, req, res, next) => {
8
+ logger.error("Error 💥", err);
9
+
10
+ let { statusCode, message } = err;
11
+ if (!statusCode) statusCode = 500;
12
+
13
+ // Handle operational errors
14
+ if (err.isOperational) {
15
+ return res.status(err.statusCode).json({
16
+ success: false,
17
+ message: err.message,
18
+ });
19
+ }
20
+
21
+ // For unexpected errors (like coding bugs, db crashes, etc.)
22
+ res.status(statusCode).json({
23
+ success: false,
24
+ message,
25
+ });
26
+ };
27
+
28
+ // Handle invalid routes
29
+ export const notFound = (req, res, next) => {
30
+ const error = new ApiError(404, `Not Found - ${req.originalUrl}`);
31
+ next(error);
32
+ };
@@ -0,0 +1,13 @@
1
+ import rateLimit from "express-rate-limit";
2
+
3
+ export const rateLimiter = (max = 10) =>
4
+ rateLimit({
5
+ windowMs: 15 * 60 * 1000, // 15 minutes
6
+ max, // limit each IP to max requests per windowMs
7
+ message: {
8
+ success: false,
9
+ message: "Too many requests, please try again later."
10
+ },
11
+ standardHeaders: true, // return rate limit info in headers
12
+ legacyHeaders: false,
13
+ });
@@ -0,0 +1,15 @@
1
+ // Generic middleware to validate req.body against a Joi schema
2
+ const validate = (schema) => (req, res, next) => {
3
+ const { error } = schema.validate(req.body, { abortEarly: false });
4
+
5
+ if (error) {
6
+ return res.status(400).json({
7
+ success: false,
8
+ errors: error.details.map((err) => err.message),
9
+ });
10
+ }
11
+
12
+ next();
13
+ };
14
+
15
+ export default validate;
@@ -0,0 +1,31 @@
1
+ import mongoose from "mongoose";
2
+
3
+ const userSchema = new mongoose.Schema({
4
+ name: {
5
+ type: String,
6
+ required: true,
7
+ min: 3,
8
+ max: 30,
9
+ },
10
+ email: {
11
+ type: String,
12
+ required: true,
13
+ unique: true,
14
+ },
15
+ password: {
16
+ type: String,
17
+ required: true,
18
+ min: 6,
19
+ },
20
+ role: {
21
+ type: String,
22
+ enum: ['user', 'admin'],
23
+ default: 'user',
24
+ },
25
+ },{
26
+ timestamps: true,
27
+ });
28
+
29
+ const User = mongoose.model("user", userSchema);
30
+
31
+ export default User;
@@ -0,0 +1,14 @@
1
+ import express from "express";
2
+ import { getUser, createUser, loginUser } from "../controllers/user.controller.js";
3
+ import { createUserSchema, loginSchema } from "../schemas/user.schema.js";
4
+ import validate from "../middleware/validateRequest.middleware.js";
5
+ import { protect } from "../middleware/auth.middleware.js";
6
+
7
+ const router = express.Router();
8
+
9
+ router.get("/me", protect, getUser);
10
+ router.post("/create", validate(createUserSchema), createUser);
11
+ router.post("/login", validate(loginSchema), loginUser);
12
+
13
+
14
+ export default router;
@@ -0,0 +1,43 @@
1
+ import joi from "joi";
2
+
3
+ // Schema for creating a new user
4
+ export const createUserSchema = joi.object({
5
+ name: joi.string().min(3).max(30).required().messages({
6
+ 'string.min': 'Name should have a minimum length of 3',
7
+ 'string.max': 'Name should have a maximum length of 30',
8
+ 'any.required': 'Name is required'
9
+ }),
10
+ email: joi.string().email().required().messages({
11
+ 'string.email': 'Email must be a valid email',
12
+ 'any.required': 'Email is required'
13
+ }),
14
+ password: joi.string().min(6).required().messages({
15
+ 'string.min': 'Password should have a minimum length of 6',
16
+ 'any.required': 'Password is required'
17
+ }),
18
+ });
19
+
20
+ // Schema for updating an existing user
21
+ export const updateUserSchema = joi.object({
22
+ name: joi.string().min(3).max(30).messages({
23
+ 'string.min': 'Name should have a minimum length of 3',
24
+ 'string.max': 'Name should have a maximum length of 30',
25
+ }),
26
+ email: joi.string().email().messages({
27
+ 'string.email': 'Email must be a valid email',
28
+ }),
29
+ password: joi.string().min(6).messages({
30
+ 'string.min': 'Password should have a minimum length of 6',
31
+ }),
32
+ });
33
+
34
+ // Schema for user login
35
+ export const loginSchema = joi.object({
36
+ email: joi.string().email().required().messages({
37
+ 'string.email': 'Email must be a valid email',
38
+ 'any.required': 'Email is required'
39
+ }),
40
+ password: joi.string().required().messages({
41
+ 'any.required': 'Password is required'
42
+ }),
43
+ });
@@ -0,0 +1,54 @@
1
+ import bcrypt from "bcryptjs";
2
+ import User from "../models/user.model.js";
3
+ import ApiError from "../utils/ApiError.js";
4
+ import { generateToken } from "../utils/Token.js";
5
+
6
+ export const createUserService = async ({ name, email, password }) => {
7
+ // Check if user exists
8
+ const existing = await User.findOne({ email });
9
+ if (existing) throw new ApiError(409, "User already exists");
10
+
11
+ // Hash password
12
+ const salt = await bcrypt.genSalt(10);
13
+ const hashedPassword = await bcrypt.hash(password, salt);
14
+
15
+ // Create user
16
+ const user = await User.create({
17
+ name,
18
+ email,
19
+ password: hashedPassword,
20
+ });
21
+
22
+ const userObj = user.toObject(); // Convert Mongoose document to plain object
23
+ delete userObj.password; // Remove password field
24
+ delete userObj.__v;
25
+ delete userObj.createdAt;
26
+ delete userObj.updatedAt;
27
+
28
+ return userObj;
29
+ };
30
+
31
+ export const loginUserService = async ({ email, password }) => {
32
+ // Find user by email
33
+ const user = await User.findOne({ email });
34
+ if (!user) throw new ApiError(404, "Invalid Email or password");
35
+
36
+ // Check password
37
+ const isMatch = await bcrypt.compare(password, user.password);
38
+ if (!isMatch) throw new ApiError(401, "Invalid email or Password"); // notice the capitals, can be used for testing else keep it consistent
39
+
40
+ // Generate JWT token
41
+ const token = generateToken({userid: user._id, role: user.role}, '7h');
42
+
43
+ const userObj = user.toObject(); // Convert Mongoose document to plain object
44
+ delete userObj.password; // Remove password field
45
+ delete userObj.__v;
46
+ delete userObj.createdAt;
47
+ delete userObj.updatedAt;
48
+
49
+ return { user: userObj, token };
50
+ };
51
+
52
+ export const getUserService = async (userId) => {
53
+ return await User.findById(userId).select("-password -__v -createdAt -updatedAt");
54
+ }
@@ -0,0 +1,17 @@
1
+ class ApiError extends Error {
2
+ constructor(statusCode, message, errors = [], stack = "") {
3
+ super(message);
4
+ this.statusCode = statusCode;
5
+ this.errors = errors;
6
+ this.isOperational = true;
7
+
8
+
9
+ if (stack) {
10
+ this.stack = stack;
11
+ } else {
12
+ Error.captureStackTrace(this, this.constructor);
13
+ }
14
+ }
15
+ }
16
+
17
+ export default ApiError;
@@ -0,0 +1,9 @@
1
+ const sendResponse = (res, statusCode, message, data = {}) => {
2
+ res.status(statusCode).json({
3
+ success: statusCode < 400,
4
+ message,
5
+ ...data
6
+ });
7
+ };
8
+
9
+ export default sendResponse;
@@ -0,0 +1,5 @@
1
+ const catchAsync = (fn)=>(req, res, next)=>{
2
+ fn(req, res, next).catch(next);
3
+ }
4
+
5
+ export default catchAsync;
@@ -0,0 +1,16 @@
1
+ import jwt from "jsonwebtoken"
2
+ import { config } from "../config/env.config.js";
3
+
4
+ // Generate JWT token
5
+ export const generateToken = (payload, expiresIn = '1h') => {
6
+ return jwt.sign(payload, config.jwtSecret, { expiresIn });
7
+ }
8
+
9
+ // Verify JWT token
10
+ export const verifyAccessToken = (token) => {
11
+ try {
12
+ return jwt.verify(token, config.jwtSecret);
13
+ } catch (error) {
14
+ return null;
15
+ }
16
+ };
@@ -0,0 +1,5 @@
1
+ PORT=3000
2
+ MONGODB_URI=mongodb://localhost:27017/mydatabase
3
+ JWT_SECRET=your_jwt_secret_key
4
+ NODE_ENV=development
5
+