@ifecodes/backend-template 1.1.7 → 1.1.9

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 (37) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +22 -4
  3. package/bin/cli.js +162 -42
  4. package/bin/lib/readme-generator.js +118 -124
  5. package/bin/lib/service-setup.js +23 -18
  6. package/package.json +1 -1
  7. package/template/base/js/.env.example +5 -5
  8. package/template/base/js/.husky/pre-commit +1 -7
  9. package/template/base/js/src/app.js +3 -0
  10. package/template/base/js/src/config/db.js +8 -8
  11. package/template/base/js/src/config/env.js +14 -14
  12. package/template/base/js/src/config/index.js +7 -7
  13. package/template/base/js/src/middlewares/error-handler.middleware.js +19 -0
  14. package/template/base/js/src/middlewares/index.js +11 -9
  15. package/template/base/js/src/middlewares/method-not-allowed.middleware.js +13 -13
  16. package/template/base/js/src/middlewares/not-found.middleware.js +10 -10
  17. package/template/base/js/src/middlewares/root.middleware.js +16 -16
  18. package/template/base/js/src/modules/v1/health/health.controller.js +21 -21
  19. package/template/base/js/src/modules/v1/health/health.route.js +9 -9
  20. package/template/base/js/src/modules/v1/health/index.js +5 -5
  21. package/template/base/js/src/modules/v1/index.js +8 -8
  22. package/template/base/js/src/routes.js +16 -16
  23. package/template/base/js/src/utils/http-error.js +74 -53
  24. package/template/base/js/src/utils/index.js +8 -2
  25. package/template/base/ts/package.json +1 -1
  26. package/template/base/ts/src/app.ts +3 -0
  27. package/template/base/ts/src/middlewares/error-handler.middleware.ts +23 -0
  28. package/template/base/ts/src/middlewares/index.ts +1 -0
  29. package/template/base/ts/src/middlewares/method-not-allowed.middleware.ts +2 -0
  30. package/template/base/ts/src/utils/http-error.ts +18 -0
  31. package/template/base/ts/src/utils/index.ts +3 -0
  32. package/template/base/ts/tsconfig.json +1 -2
  33. package/template/features/auth/models/index.ts +1 -1
  34. package/template/features/auth/models/user.model.ts +28 -28
  35. package/template/features/auth/modules/index.ts +1 -1
  36. package/template/features/auth/utils/jwt.ts +15 -15
  37. package/template/gateway/js/inject.js +8 -6
@@ -1,10 +1,10 @@
1
- const notFound = (req, res) => {
2
- res.status(404).json({
3
- status: "error",
4
- message: `Route ${req.originalUrl} not found`,
5
- });
6
- };
7
-
8
- module.exports = {
9
- notFound,
10
- };
1
+ const notFound = (req, res) => {
2
+ res.status(404).json({
3
+ status: "error",
4
+ message: `Route ${req.originalUrl} not found`,
5
+ });
6
+ };
7
+
8
+ module.exports = {
9
+ notFound,
10
+ };
@@ -1,16 +1,16 @@
1
- const rootHandler = (_, res) => {
2
- res.json({
3
- name: "/*__PROJECT_NAME__*/",
4
- type: "/*__PROJECT_TYPE__*/",
5
- version: "1.0.0",
6
- status: "running",
7
- endpoints: {
8
- health: "/api/v1/health",
9
- /*__AUTH_ENDPOINT__*/
10
- },
11
- });
12
- };
13
-
14
- module.exports = {
15
- rootHandler,
16
- };
1
+ const rootHandler = (_, res) => {
2
+ res.json({
3
+ name: "/*__PROJECT_NAME__*/",
4
+ type: "/*__PROJECT_TYPE__*/",
5
+ version: "1.0.0",
6
+ status: "running",
7
+ endpoints: {
8
+ health: "/api/v1/health",
9
+ /*__AUTH_ENDPOINT__*/
10
+ },
11
+ });
12
+ };
13
+
14
+ module.exports = {
15
+ rootHandler,
16
+ };
@@ -1,21 +1,21 @@
1
- const { logger } = require("../../../utils");
2
-
3
- const healthCheck = async (_, res) => {
4
- logger.info("Health", "healthy");
5
-
6
- return res.status(200).json({
7
- status: "healthy",
8
- uptime: process.uptime(),
9
- timestamp: new Date().toISOString(),
10
- services: {
11
- memory: {
12
- rss: process.memoryUsage().rss,
13
- heapUsed: process.memoryUsage().heapUsed,
14
- },
15
- },
16
- });
17
- };
18
-
19
- module.exports = {
20
- healthCheck
21
- };
1
+ const { logger } = require("../../../utils");
2
+
3
+ const healthCheck = async (_, res) => {
4
+ logger.info("Health", "healthy");
5
+
6
+ return res.status(200).json({
7
+ status: "healthy",
8
+ uptime: process.uptime(),
9
+ timestamp: new Date().toISOString(),
10
+ services: {
11
+ memory: {
12
+ rss: process.memoryUsage().rss,
13
+ heapUsed: process.memoryUsage().heapUsed,
14
+ },
15
+ },
16
+ });
17
+ };
18
+
19
+ module.exports = {
20
+ healthCheck
21
+ };
@@ -1,9 +1,9 @@
1
- const { Router } = require("express");
2
- const { healthCheck } = require("./health.controller");
3
- const { methodNotAllowedHandler } = require("../../../middlewares");
4
-
5
- const router = Router();
6
- router.use(methodNotAllowedHandler(["GET"]));
7
- router.get("/", healthCheck);
8
-
9
- module.exports = router;
1
+ const { Router } = require("express");
2
+ const { healthCheck } = require("./health.controller");
3
+ const { methodNotAllowedHandler } = require("../../../middlewares");
4
+
5
+ const router = Router();
6
+ router.use(methodNotAllowedHandler(["GET"]));
7
+ router.get("/", healthCheck);
8
+
9
+ module.exports = router;
@@ -1,5 +1,5 @@
1
- const healthRoutes = require("./health.route");
2
-
3
- module.exports = {
4
- healthRoutes,
5
- };
1
+ const healthRoutes = require("./health.route");
2
+
3
+ module.exports = {
4
+ healthRoutes,
5
+ };
@@ -1,8 +1,8 @@
1
- const { Router } = require("express");
2
- const { healthRoutes } = require("./health");
3
-
4
- const router = Router();
5
-
6
- router.use("/health", healthRoutes);
7
-
8
- module.exports = router;
1
+ const { Router } = require("express");
2
+ const { healthRoutes } = require("./health");
3
+
4
+ const router = Router();
5
+
6
+ router.use("/health", healthRoutes);
7
+
8
+ module.exports = router;
@@ -1,16 +1,16 @@
1
- const { Router } = require("express");
2
- const modulesRouter = require("./modules");
3
- const { notFound, rootHandler } = require("./middlewares");
4
-
5
- const router = Router();
6
-
7
- // Root endpoint
8
- router.get("/", rootHandler);
9
-
10
- router.use("/api", modulesRouter);
11
-
12
- // 404 handler - must be last
13
- router.use(notFound);
14
-
15
- module.exports = router;
16
-
1
+ const { Router } = require("express");
2
+ const modulesRouter = require("./modules");
3
+ const { notFound, rootHandler } = require("./middlewares");
4
+
5
+ const router = Router();
6
+
7
+ // Root endpoint
8
+ router.get("/", rootHandler);
9
+
10
+ router.use("/api", modulesRouter);
11
+
12
+ // 404 handler - must be last
13
+ router.use(notFound);
14
+
15
+ module.exports = router;
16
+
@@ -1,53 +1,74 @@
1
- class HttpError extends Error {
2
- constructor(status, message) {
3
- super(message);
4
- this.status = status;
5
- Object.setPrototypeOf(this, new.target.prototype);
6
- }
7
- }
8
-
9
- class BadRequestError extends HttpError {
10
- constructor(message = "Bad Request") {
11
- super(400, message);
12
- }
13
- }
14
-
15
- class UnauthorizedError extends HttpError {
16
- constructor(message = "Unauthorized") {
17
- super(401, message);
18
- }
19
- }
20
-
21
- class ForbiddenError extends HttpError {
22
- constructor(message = "Forbidden") {
23
- super(403, message);
24
- }
25
- }
26
-
27
- class NotFoundError extends HttpError {
28
- constructor(message = "Not Found") {
29
- super(404, message);
30
- }
31
- }
32
-
33
- class ConflictError extends HttpError {
34
- constructor(message = "Conflict") {
35
- super(409, message);
36
- }
37
- }
38
-
39
- class InternalServerError extends HttpError {
40
- constructor(message = "Internal Server Error") {
41
- super(500, message);
42
- }
43
- }
44
-
45
- module.exports = {
46
- HttpError,
47
- BadRequestError,
48
- UnauthorizedError,
49
- ForbiddenError,
50
- NotFoundError,
51
- ConflictError,
52
- InternalServerError,
53
- };
1
+ class HttpError extends Error {
2
+ constructor(status, message) {
3
+ super(message);
4
+ this.status = status;
5
+ Object.setPrototypeOf(this, new.target.prototype);
6
+ }
7
+ }
8
+
9
+ class BadRequestError extends HttpError {
10
+ constructor(message = "Bad Request") {
11
+ super(400, message);
12
+ }
13
+ }
14
+
15
+ class UnprocessableEntityError extends HttpError {
16
+ constructor(message = "Unprocessable Entity") {
17
+ super(422, message);
18
+ }
19
+ }
20
+
21
+ class UnauthorizedError extends HttpError {
22
+ constructor(message = "Unauthorized") {
23
+ super(401, message);
24
+ }
25
+ }
26
+
27
+ class ForbiddenError extends HttpError {
28
+ constructor(message = "Forbidden") {
29
+ super(403, message);
30
+ }
31
+ }
32
+
33
+ class NotFoundError extends HttpError {
34
+ constructor(message = "Not Found") {
35
+ super(404, message);
36
+ }
37
+ }
38
+
39
+ class ConflictError extends HttpError {
40
+ constructor(message = "Conflict") {
41
+ super(409, message);
42
+ }
43
+ }
44
+
45
+ class TooManyRequestsError extends HttpError {
46
+ constructor(message = "Too Many Requests") {
47
+ super(429, message);
48
+ }
49
+ }
50
+
51
+ class BadGatewayError extends HttpError {
52
+ constructor(message = "Bad Gateway") {
53
+ super(502, message);
54
+ }
55
+ }
56
+
57
+ class InternalServerError extends HttpError {
58
+ constructor(message = "Internal Server Error") {
59
+ super(500, message);
60
+ }
61
+ }
62
+
63
+ module.exports = {
64
+ HttpError,
65
+ BadRequestError,
66
+ UnprocessableEntityError,
67
+ UnauthorizedError,
68
+ ForbiddenError,
69
+ NotFoundError,
70
+ ConflictError,
71
+ TooManyRequestsError,
72
+ BadGatewayError,
73
+ InternalServerError,
74
+ };
@@ -1,22 +1,28 @@
1
1
  const {
2
2
  HttpError,
3
3
  BadRequestError,
4
+ UnprocessableEntityError,
4
5
  UnauthorizedError,
5
6
  ForbiddenError,
6
7
  NotFoundError,
7
8
  ConflictError,
9
+ TooManyRequestsError,
10
+ BadGatewayError,
8
11
  InternalServerError,
9
- } = require("./http-error");
12
+ } = require('./http-error');
10
13
 
11
- const logger = require("./logger");
14
+ const logger = require('./logger');
12
15
 
13
16
  module.exports = {
14
17
  HttpError,
15
18
  BadRequestError,
19
+ UnprocessableEntityError,
16
20
  UnauthorizedError,
17
21
  ForbiddenError,
18
22
  NotFoundError,
19
23
  ConflictError,
24
+ TooManyRequestsError,
25
+ BadGatewayError,
20
26
  InternalServerError,
21
27
  logger,
22
28
  };
@@ -4,7 +4,7 @@
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "dev": "ts-node-dev --respawn --transpile-only -r tsconfig-paths/register src/server.ts",
7
- "start": "node dist/server.js",
7
+ "start": "node -r module-alias/register dist/server.js",
8
8
  "build": "tsc",
9
9
  "lint": "eslint \"src/**/*.{ts,tsx}\"",
10
10
  "format": "prettier --write \"src/**/*.{ts,json}\"",
@@ -1,5 +1,6 @@
1
1
  import express from "express";
2
2
  import router from "./routes";
3
+ import { errorHandler } from "@/middlewares";
3
4
  /*__IMPORTS__*/
4
5
 
5
6
  const app = express();
@@ -12,4 +13,6 @@ app.use(express.json());
12
13
  // Connect routes
13
14
  app.use(router);
14
15
 
16
+ app.use(errorHandler);
17
+
15
18
  export default app;
@@ -0,0 +1,23 @@
1
+ import { NextFunction, Request, Response } from "express";
2
+ import { HttpError } from "@/utils";
3
+
4
+ // Centralized error handling middleware
5
+
6
+ export const errorHandler = (
7
+ err: unknown,
8
+ _: Request,
9
+ res: Response,
10
+ __: NextFunction,
11
+ ) => {
12
+ if (err instanceof HttpError) {
13
+ return res.status(err.status).json({
14
+ status: "error",
15
+ message: err.message,
16
+ });
17
+ }
18
+
19
+ return res.status(500).json({
20
+ status: "error",
21
+ message: "Internal Server Error",
22
+ });
23
+ };
@@ -1,3 +1,4 @@
1
1
  export { default as methodNotAllowedHandler } from "./method-not-allowed.middleware";
2
2
  export { notFound } from "./not-found.middleware";
3
3
  export { rootHandler } from "./root.middleware";
4
+ export { errorHandler } from "./error-handler.middleware";
@@ -1,5 +1,7 @@
1
1
  import { Request, Response, NextFunction } from "express";
2
2
 
3
+ // Middleware to handle 405 Method Not Allowed for unsupported HTTP methods on defined routes
4
+
3
5
  const methodNotAllowed =
4
6
  (allowedMethods: string[]) => (req: Request, res: Response, next: NextFunction) => {
5
7
  if (!allowedMethods.includes(req.method)) {
@@ -14,6 +14,12 @@ export class BadRequestError extends HttpError {
14
14
  }
15
15
  }
16
16
 
17
+ export class UnprocessableEntityError extends HttpError {
18
+ constructor(message = "Unprocessable Entity") {
19
+ super(422, message);
20
+ }
21
+ }
22
+
17
23
  export class UnauthorizedError extends HttpError {
18
24
  constructor(message = "Unauthorized") {
19
25
  super(401, message);
@@ -38,6 +44,18 @@ export class ConflictError extends HttpError {
38
44
  }
39
45
  }
40
46
 
47
+ export class TooManyRequestsError extends HttpError {
48
+ constructor(message = "Too Many Requests") {
49
+ super(429, message);
50
+ }
51
+ }
52
+
53
+ export class BadGatewayError extends HttpError {
54
+ constructor(message = "Bad Gateway") {
55
+ super(502, message);
56
+ }
57
+ }
58
+
41
59
  export class InternalServerError extends HttpError {
42
60
  constructor(message = "Internal Server Error") {
43
61
  super(500, message);
@@ -1,10 +1,13 @@
1
1
  export {
2
2
  HttpError,
3
3
  BadRequestError,
4
+ UnprocessableEntityError,
4
5
  UnauthorizedError,
5
6
  ForbiddenError,
6
7
  NotFoundError,
7
8
  ConflictError,
9
+ TooManyRequestsError,
10
+ BadGatewayError,
8
11
  InternalServerError,
9
12
  } from "./http-error";
10
13
 
@@ -11,9 +11,8 @@
11
11
  "esModuleInterop": true,
12
12
  "skipLibCheck": true,
13
13
 
14
- "baseUrl": "src",
15
14
  "paths": {
16
- "@/*": ["*"]
15
+ "@/*": ["./src/*"]
17
16
  },
18
17
  "types": ["node"]
19
18
  },
@@ -1 +1 @@
1
- export { UserModel, IUser } from "./user.model";
1
+ export { UserModel, IUser } from "./user.model";
@@ -1,28 +1,28 @@
1
- import { Schema, model, Document } from "mongoose";
2
-
3
- export interface IUser extends Document {
4
- email: string;
5
- password: string;
6
- fullName: string;
7
- }
8
-
9
- const userSchema = new Schema<IUser>(
10
- {
11
- email: {
12
- type: String,
13
- required: true,
14
- unique: true,
15
- },
16
- password: {
17
- type: String,
18
- required: true,
19
- },
20
- fullName: {
21
- type: String,
22
- required: true,
23
- },
24
- },
25
- { timestamps: true },
26
- );
27
-
28
- export const UserModel = model<IUser>("User", userSchema);
1
+ import { Schema, model, Document } from "mongoose";
2
+
3
+ export interface IUser extends Document {
4
+ email: string;
5
+ password: string;
6
+ fullName: string;
7
+ }
8
+
9
+ const userSchema = new Schema<IUser>(
10
+ {
11
+ email: {
12
+ type: String,
13
+ required: true,
14
+ unique: true,
15
+ },
16
+ password: {
17
+ type: String,
18
+ required: true,
19
+ },
20
+ fullName: {
21
+ type: String,
22
+ required: true,
23
+ },
24
+ },
25
+ { timestamps: true },
26
+ );
27
+
28
+ export const UserModel = model<IUser>("User", userSchema);
@@ -1 +1 @@
1
- export { default as authRoutes } from "./auth.routes";
1
+ export { default as authRoutes } from "./auth.routes";
@@ -1,15 +1,15 @@
1
- import jwt from "jsonwebtoken";
2
- import { ENV } from "@/config";
3
-
4
- // JwtPayload mirrors the global JwtPayload declared in `src/types/express.d.ts`.
5
- export type JwtPayload = {
6
- email?: string;
7
- iat?: number;
8
- exp?: number;
9
- };
10
-
11
- export const generateToken = (payload: JwtPayload) =>
12
- jwt.sign(payload, ENV.JWT_SECRET, { expiresIn: "7d" });
13
-
14
- export const verifyToken = (token: string): JwtPayload =>
15
- jwt.verify(token, ENV.JWT_SECRET) as JwtPayload;
1
+ import jwt from "jsonwebtoken";
2
+ import { ENV } from "@/config";
3
+
4
+ // JwtPayload mirrors the global JwtPayload declared in `src/types/express.d.ts`.
5
+ export type JwtPayload = {
6
+ email?: string;
7
+ iat?: number;
8
+ exp?: number;
9
+ };
10
+
11
+ export const generateToken = (payload: JwtPayload) =>
12
+ jwt.sign(payload, ENV.JWT_SECRET, { expiresIn: "7d" });
13
+
14
+ export const verifyToken = (token: string): JwtPayload =>
15
+ jwt.verify(token, ENV.JWT_SECRET) as JwtPayload;
@@ -2,20 +2,22 @@ export const gatewayDeps = ["http-proxy-middleware"];
2
2
 
3
3
  export const generateGatewayRoutes = (services, mode = "docker") => {
4
4
  const routes = services
5
- .filter(s => s !== "gateway")
5
+ .filter((s) => s !== "gateway")
6
6
  .map((service, index) => {
7
+ const servicePort = `${service.toUpperCase().replace(/-/g, "_")}_PORT`;
7
8
  const port = 4001 + index; // Host port mapping: gateway=4000, services start at 4001
8
- const routePath = service.replace("-service", "");
9
-
9
+
10
10
  // Docker: use container name with internal port 4000
11
11
  // Non-docker: use localhost with mapped host port
12
12
  const host = mode === "docker" ? service : "localhost";
13
- const targetPort = mode === "docker" ? 4000 : port;
14
-
13
+ // Build a placeholder that will render as ${SERVICE_PORT} in the generated code
14
+ const servicePortPlaceholder = "${" + servicePort + "}";
15
+
15
16
  return `
16
17
  // Proxy to ${service}
18
+ const ${servicePort} = ENV.${servicePort} || ${port}
17
19
  app.use("/api", createProxyMiddleware({
18
- target: "http://${host}:${targetPort}/api",
20
+ target: \`http://${host}:${servicePortPlaceholder}/api\`,
19
21
  changeOrigin: true,
20
22
  on: {
21
23
  error: (err, req, res) => {