@ifecodes/backend-template 1.1.3 → 1.1.6
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/README.md +39 -18
- package/bin/cli.js +602 -101
- package/bin/lib/microservice-config.js +68 -14
- package/bin/lib/prompts.js +25 -6
- package/bin/lib/readme-generator.js +119 -29
- package/bin/lib/service-setup.js +401 -132
- package/package.json +2 -2
- package/template/base/js/.eslintrc.json +13 -0
- package/template/base/js/.prettierrc +7 -0
- package/template/base/js/eslint.config.js +31 -0
- package/template/base/js/package.json +28 -0
- package/template/base/js/src/app.js +15 -0
- package/template/base/js/src/config/db.js +8 -0
- package/template/base/js/src/config/env.js +14 -0
- package/template/base/js/src/config/index.js +7 -0
- package/template/base/js/src/middlewares/index.js +9 -0
- package/template/base/js/src/middlewares/method-not-allowed.middleware.js +13 -0
- package/template/base/js/src/middlewares/not-found.middleware.js +10 -0
- package/template/base/js/src/middlewares/root.middleware.js +16 -0
- package/template/base/js/src/modules/index.js +8 -0
- package/template/base/js/src/modules/v1/health/health.controller.js +21 -0
- package/template/base/js/src/modules/v1/health/health.route.js +9 -0
- package/template/base/js/src/modules/v1/health/index.js +5 -0
- package/template/base/js/src/modules/v1/index.js +8 -0
- package/template/base/js/src/routes.js +16 -0
- package/template/base/js/src/server.js +18 -0
- package/template/base/js/src/utils/http-error.js +53 -0
- package/template/base/js/src/utils/index.js +22 -0
- package/template/base/js/src/utils/logger.js +67 -0
- package/template/base/ts/.env.example +5 -0
- package/template/base/ts/.husky/pre-commit +13 -0
- package/template/base/ts/.prettierignore +47 -0
- package/template/base/ts/gitignore +31 -0
- package/template/base/ts/src/app.ts +15 -0
- package/template/base/{src → ts/src}/config/env.ts +10 -10
- package/template/base/{src → ts/src}/middlewares/index.ts +3 -3
- package/template/base/{src → ts/src}/middlewares/method-not-allowed.middleware.ts +16 -17
- package/template/base/{src → ts/src}/middlewares/root.middleware.ts +2 -2
- package/template/base/{src → ts/src}/modules/v1/health/health.controller.ts +18 -18
- package/template/base/{src → ts/src}/modules/v1/health/health.route.ts +9 -9
- package/template/base/{src → ts/src}/modules/v1/health/index.ts +1 -1
- package/template/base/{src → ts/src}/modules/v1/index.ts +8 -8
- package/template/base/{src → ts/src}/routes.ts +15 -15
- package/template/base/{src → ts/src}/utils/http-error.ts +45 -45
- package/template/base/{src → ts/src}/utils/logger.ts +23 -0
- package/template/features/auth/argon2/inject.js +29 -4
- package/template/features/auth/base/inject.js +108 -26
- package/template/features/auth/bcrypt/inject.js +25 -5
- package/template/features/auth/models/user.model.js +24 -0
- package/template/features/auth/modules/auth.controller.js +21 -0
- package/template/features/auth/modules/auth.routes.js +10 -0
- package/template/features/auth/modules/auth.service.js +29 -0
- package/template/features/auth/modules/index.js +1 -0
- package/template/features/auth/utils/jwt.js +12 -0
- package/template/features/cors/inject.js +4 -1
- package/template/features/helmet/inject.js +4 -1
- package/template/features/morgan/inject.js +4 -1
- package/template/features/rate-limit/inject.js +4 -1
- package/template/gateway/js/app.js +42 -0
- package/template/gateway/js/inject.js +31 -0
- package/template/gateway/js/server.js +19 -0
- package/template/gateway/ts/app.ts +43 -0
- package/template/gateway/ts/inject.js +32 -0
- package/template/gateway/{server.ts → ts/server.ts} +3 -3
- package/template/microservice/docker/.dockerignore +10 -0
- package/template/microservice/docker/Dockerfile +2 -1
- package/template/microservice/docker/docker-compose.yml +0 -1
- package/bin/lib/ts-to-js.js +0 -342
- package/template/base/src/app.ts +0 -8
- package/template/gateway/app.ts +0 -26
- package/template/gateway/inject.js +0 -27
- /package/template/base/{.env.example → js/.env.example} +0 -0
- /package/template/base/{.husky → js/.husky}/pre-commit +0 -0
- /package/template/base/{.prettierignore → js/.prettierignore} +0 -0
- /package/template/base/{gitignore → js/gitignore} +0 -0
- /package/template/base/{.eslintrc.json → ts/.eslintrc.json} +0 -0
- /package/template/base/{.prettierrc → ts/.prettierrc} +0 -0
- /package/template/base/{eslint.config.js → ts/eslint.config.js} +0 -0
- /package/template/base/{package.json → ts/package.json} +0 -0
- /package/template/base/{src → ts/src}/config/db.ts +0 -0
- /package/template/base/{src → ts/src}/config/index.ts +0 -0
- /package/template/base/{src → ts/src}/middlewares/not-found.middleware.ts +0 -0
- /package/template/base/{src → ts/src}/modules/index.ts +0 -0
- /package/template/base/{src → ts/src}/server.ts +0 -0
- /package/template/base/{src → ts/src}/utils/index.ts +0 -0
- /package/template/base/{tsconfig.json → ts/tsconfig.json} +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { Request, Response } from "express";
|
|
2
|
-
import { logger } from "@/utils";
|
|
3
|
-
|
|
4
|
-
export const healthCheck = async (_: Request, res: Response) => {
|
|
5
|
-
logger.info("Health", "healthy");
|
|
6
|
-
|
|
7
|
-
return res.status(200).json({
|
|
8
|
-
status: "healthy",
|
|
9
|
-
uptime: process.uptime(),
|
|
10
|
-
timestamp: new Date().toISOString(),
|
|
11
|
-
services: {
|
|
12
|
-
memory: {
|
|
13
|
-
rss: process.memoryUsage().rss,
|
|
14
|
-
heapUsed: process.memoryUsage().heapUsed,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
};
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import { logger } from "@/utils";
|
|
3
|
+
|
|
4
|
+
export const healthCheck = async (_: Request, res: Response) => {
|
|
5
|
+
logger.info("Health", "healthy");
|
|
6
|
+
|
|
7
|
+
return res.status(200).json({
|
|
8
|
+
status: "healthy",
|
|
9
|
+
uptime: process.uptime(),
|
|
10
|
+
timestamp: new Date().toISOString(),
|
|
11
|
+
services: {
|
|
12
|
+
memory: {
|
|
13
|
+
rss: process.memoryUsage().rss,
|
|
14
|
+
heapUsed: process.memoryUsage().heapUsed,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Router } from "express";
|
|
2
|
-
import { healthCheck } from "./health.controller";
|
|
3
|
-
import { methodNotAllowedHandler } from "@/middlewares";
|
|
4
|
-
|
|
5
|
-
const router = Router();
|
|
6
|
-
router.use(methodNotAllowedHandler(["GET"]));
|
|
7
|
-
router.get("/", healthCheck);
|
|
8
|
-
|
|
9
|
-
export default router;
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { healthCheck } from "./health.controller";
|
|
3
|
+
import { methodNotAllowedHandler } from "@/middlewares";
|
|
4
|
+
|
|
5
|
+
const router = Router();
|
|
6
|
+
router.use(methodNotAllowedHandler(["GET"]));
|
|
7
|
+
router.get("/", healthCheck);
|
|
8
|
+
|
|
9
|
+
export default router;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as healthRoutes } from "./health.route";
|
|
1
|
+
export { default as healthRoutes } from "./health.route";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Router } from "express";
|
|
2
|
-
import { healthRoutes } from "./health";
|
|
3
|
-
|
|
4
|
-
const router = Router();
|
|
5
|
-
|
|
6
|
-
router.use("/health", healthRoutes);
|
|
7
|
-
|
|
8
|
-
export default router;
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { healthRoutes } from "./health";
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
|
|
6
|
+
router.use("/health", healthRoutes);
|
|
7
|
+
|
|
8
|
+
export default router;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { Router } from "express";
|
|
2
|
-
import modulesRouter from "./modules";
|
|
3
|
-
import { notFound, rootHandler } from "./middlewares";
|
|
4
|
-
|
|
5
|
-
const router = Router();
|
|
6
|
-
|
|
7
|
-
// Root endpoint
|
|
8
|
-
router.get("/", rootHandler);
|
|
9
|
-
|
|
10
|
-
router.use("/", modulesRouter);
|
|
11
|
-
|
|
12
|
-
// 404 handler - must be last
|
|
13
|
-
router.use(notFound);
|
|
14
|
-
|
|
15
|
-
export default router;
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import modulesRouter from "./modules";
|
|
3
|
+
import { notFound, rootHandler } from "./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
|
+
export default router;
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
export class HttpError extends Error {
|
|
2
|
-
public status: number;
|
|
3
|
-
|
|
4
|
-
constructor(status: number, message: string) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.status = status;
|
|
7
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class BadRequestError extends HttpError {
|
|
12
|
-
constructor(message = "Bad Request") {
|
|
13
|
-
super(400, message);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export class UnauthorizedError extends HttpError {
|
|
18
|
-
constructor(message = "Unauthorized") {
|
|
19
|
-
super(401, message);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class ForbiddenError extends HttpError {
|
|
24
|
-
constructor(message = "Forbidden") {
|
|
25
|
-
super(403, message);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class NotFoundError extends HttpError {
|
|
30
|
-
constructor(message = "Not Found") {
|
|
31
|
-
super(404, message);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export class ConflictError extends HttpError {
|
|
36
|
-
constructor(message = "Conflict") {
|
|
37
|
-
super(409, message);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export class InternalServerError extends HttpError {
|
|
42
|
-
constructor(message = "Internal Server Error") {
|
|
43
|
-
super(500, message);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
1
|
+
export class HttpError extends Error {
|
|
2
|
+
public status: number;
|
|
3
|
+
|
|
4
|
+
constructor(status: number, message: string) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.status = status;
|
|
7
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class BadRequestError extends HttpError {
|
|
12
|
+
constructor(message = "Bad Request") {
|
|
13
|
+
super(400, message);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class UnauthorizedError extends HttpError {
|
|
18
|
+
constructor(message = "Unauthorized") {
|
|
19
|
+
super(401, message);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class ForbiddenError extends HttpError {
|
|
24
|
+
constructor(message = "Forbidden") {
|
|
25
|
+
super(403, message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class NotFoundError extends HttpError {
|
|
30
|
+
constructor(message = "Not Found") {
|
|
31
|
+
super(404, message);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class ConflictError extends HttpError {
|
|
36
|
+
constructor(message = "Conflict") {
|
|
37
|
+
super(409, message);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class InternalServerError extends HttpError {
|
|
42
|
+
constructor(message = "Internal Server Error") {
|
|
43
|
+
super(500, message);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -20,6 +20,29 @@ function getEnvironment() {
|
|
|
20
20
|
: ENV.NODE_ENV;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
console.log(
|
|
24
|
+
format("logger", colors.blue),
|
|
25
|
+
`Logger initialized for ${getEnvironment()} environment.`,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const requiredKeys = ENV && Object.keys(ENV).length ? Object.keys(ENV) : [];
|
|
29
|
+
|
|
30
|
+
const missing = requiredKeys.filter(
|
|
31
|
+
(k) => ENV == null || (ENV as Record<string, string | undefined>)[k] === undefined || (ENV as Record<string, string | undefined>)[k] === "",
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (missing.length === requiredKeys.length) {
|
|
35
|
+
console.warn(
|
|
36
|
+
format("logger", colors.yellow),
|
|
37
|
+
"ENV values missing — make sure to set up your environment variables correctly.",
|
|
38
|
+
);
|
|
39
|
+
} else if (missing.length > 0) {
|
|
40
|
+
console.warn(
|
|
41
|
+
format("logger", colors.yellow),
|
|
42
|
+
`Missing ENV keys: ${missing.join(", ")}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
23
46
|
const logger = {
|
|
24
47
|
log(tag: string, ...args: unknown[]) {
|
|
25
48
|
if (getEnvironment() !== "development") return;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export const deps = ["argon2"];
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
"
|
|
5
|
-
|
|
3
|
+
export const getFiles = (language = "typescript") => {
|
|
4
|
+
const ext = language === "javascript" ? ".js" : ".ts";
|
|
5
|
+
|
|
6
|
+
const tsContent = `import argon2 from "argon2";
|
|
6
7
|
|
|
7
8
|
const HASH_OPTIONS = {
|
|
8
9
|
type: argon2.argon2id,
|
|
@@ -21,5 +22,29 @@ export const verifyPassword = async (
|
|
|
21
22
|
): Promise<boolean> => {
|
|
22
23
|
return argon2.verify(hash, password);
|
|
23
24
|
};
|
|
24
|
-
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const jsContent = `const argon2 = require("argon2");
|
|
28
|
+
|
|
29
|
+
const HASH_OPTIONS = {
|
|
30
|
+
type: argon2.argon2id,
|
|
31
|
+
memoryCost: 2 ** 16,
|
|
32
|
+
timeCost: 3,
|
|
33
|
+
parallelism: 1,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
async function hashPassword(password) {
|
|
37
|
+
return argon2.hash(password, HASH_OPTIONS);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function verifyPassword(hash, password) {
|
|
41
|
+
return argon2.verify(hash, password);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { hashPassword, verifyPassword };
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
[`src/utils/hash${ext}`]: language === "javascript" ? jsContent : tsContent,
|
|
49
|
+
};
|
|
25
50
|
};
|
|
@@ -9,45 +9,78 @@ const modulesDir = path.join(__dirname, "../modules");
|
|
|
9
9
|
const modelsDir = path.join(__dirname, "../models");
|
|
10
10
|
const utilsDir = path.join(__dirname, "../utils");
|
|
11
11
|
|
|
12
|
-
const readDirFiles = (dir, prefix = "") => {
|
|
12
|
+
const readDirFiles = (dir, prefix = "", language = "typescript") => {
|
|
13
13
|
const files = {};
|
|
14
14
|
const items = fs.readdirSync(dir);
|
|
15
|
-
|
|
15
|
+
const ext = language === "javascript" ? ".js" : ".ts";
|
|
16
|
+
|
|
16
17
|
for (const item of items) {
|
|
17
18
|
const fullPath = path.join(dir, item);
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const isDir = fs.statSync(fullPath).isDirectory();
|
|
20
|
+
const desiredName = item.replace(/\.ts$|\.js$/, ext);
|
|
21
|
+
const chosenPath = isDir
|
|
22
|
+
? fullPath
|
|
23
|
+
: fs.existsSync(path.join(dir, desiredName))
|
|
24
|
+
? path.join(dir, desiredName)
|
|
25
|
+
: fullPath;
|
|
26
|
+
|
|
27
|
+
const relativePath = path.join(prefix, path.basename(desiredName));
|
|
28
|
+
|
|
29
|
+
if (isDir) {
|
|
30
|
+
Object.assign(files, readDirFiles(chosenPath, relativePath, language));
|
|
22
31
|
} else {
|
|
23
|
-
|
|
32
|
+
const content = fs.readFileSync(chosenPath, "utf8");
|
|
33
|
+
files[relativePath] = content;
|
|
24
34
|
}
|
|
25
35
|
}
|
|
26
|
-
|
|
36
|
+
|
|
27
37
|
return files;
|
|
28
38
|
};
|
|
29
39
|
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const utilFiles = readDirFiles(utilsDir, "src/utils");
|
|
33
|
-
|
|
34
|
-
// Add MongoDB-enabled db.ts
|
|
35
|
-
const dbContent = `import mongoose from "mongoose";
|
|
40
|
+
const getDbContent = (language) => {
|
|
41
|
+
const tsContent = `import mongoose from "mongoose";
|
|
36
42
|
import { ENV } from "./env";
|
|
37
43
|
import { logger } from "@/utils";
|
|
38
44
|
|
|
39
45
|
export const connectDB = async () => {
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
try {
|
|
47
|
+
await mongoose.connect(ENV.MONGO_URI);
|
|
48
|
+
logger.log("db", "MongoDB connected");
|
|
49
|
+
} catch (error) {
|
|
50
|
+
logger.error("db", "MongoDB connection failed", error);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
42
53
|
};
|
|
43
54
|
`;
|
|
44
55
|
|
|
45
|
-
|
|
46
|
-
const
|
|
56
|
+
if (language === "javascript") {
|
|
57
|
+
return `const mongoose = require("mongoose");
|
|
58
|
+
const { ENV } = require("./env");
|
|
59
|
+
const { logger } = require("../utils");
|
|
60
|
+
|
|
61
|
+
async function connectDB() {
|
|
62
|
+
try {
|
|
63
|
+
await mongoose.connect(ENV.MONGO_URI);
|
|
64
|
+
logger.log("db", "MongoDB connected");
|
|
65
|
+
} catch (error) {
|
|
66
|
+
logger.error("db", "MongoDB connection failed", error);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { connectDB };
|
|
72
|
+
`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return tsContent;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const getHealthControllerContent = (language) => {
|
|
79
|
+
const tsContent = `import { Request, Response } from "express";
|
|
47
80
|
import mongoose from "mongoose";
|
|
48
|
-
|
|
81
|
+
const { logger } = require("../../../utils");
|
|
49
82
|
|
|
50
|
-
export const healthCheck = async (
|
|
83
|
+
export const healthCheck = async (_req: Request, res: Response) => {
|
|
51
84
|
const mongoState = mongoose.connection.readyState;
|
|
52
85
|
const healthy = mongoState === 1;
|
|
53
86
|
|
|
@@ -72,16 +105,65 @@ export const healthCheck = async (_: Request, res: Response) => {
|
|
|
72
105
|
};
|
|
73
106
|
`;
|
|
74
107
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
108
|
+
if (language === "javascript") {
|
|
109
|
+
return `const mongoose = require("mongoose");
|
|
110
|
+
const { logger } = require("../../../utils");
|
|
111
|
+
|
|
112
|
+
async function healthCheck(_req, res) {
|
|
113
|
+
const mongoState = mongoose.connection.readyState;
|
|
114
|
+
const healthy = mongoState === 1;
|
|
115
|
+
|
|
116
|
+
const failed = [];
|
|
117
|
+
if (mongoState !== 1) failed.push("mongodb");
|
|
118
|
+
|
|
119
|
+
logger.info("Health", healthy ? "healthy" : "unhealthy");
|
|
120
|
+
|
|
121
|
+
return res.status(healthy ? 200 : 503).json({
|
|
122
|
+
status: healthy ? "healthy" : "unhealthy",
|
|
123
|
+
uptime: process.uptime(),
|
|
124
|
+
timestamp: new Date().toISOString(),
|
|
125
|
+
services: {
|
|
126
|
+
mongodb: mongoState === 1 ? "connected" : "disconnected",
|
|
127
|
+
memory: {
|
|
128
|
+
rss: process.memoryUsage().rss,
|
|
129
|
+
heapUsed: process.memoryUsage().heapUsed,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
failed,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = { healthCheck };
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return tsContent;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const getFiles = (language = "typescript") => {
|
|
144
|
+
const ext = language === "javascript" ? ".js" : ".ts";
|
|
145
|
+
const moduleFiles = readDirFiles(modulesDir, "src/modules/v1/auth", language);
|
|
146
|
+
const modelFiles = readDirFiles(modelsDir, "src/models", language);
|
|
147
|
+
const utilFiles = readDirFiles(utilsDir, "src/utils", language);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
...moduleFiles,
|
|
151
|
+
...modelFiles,
|
|
152
|
+
...utilFiles,
|
|
153
|
+
[`src/config/db${ext}`]: getDbContent(language),
|
|
154
|
+
[`src/modules/v1/health/health.controller${ext}`]: getHealthControllerContent(language),
|
|
155
|
+
};
|
|
81
156
|
};
|
|
82
157
|
|
|
158
|
+
// For backward compatibility
|
|
159
|
+
export const files = getFiles("typescript");
|
|
160
|
+
|
|
83
161
|
export const imports = `import { authRoutes } from "./auth";`;
|
|
84
162
|
|
|
163
|
+
export const getImports = (language) => language === "javascript"
|
|
164
|
+
? `const { authRoutes } = require("./auth");`
|
|
165
|
+
: `import { authRoutes } from "./auth";`;
|
|
166
|
+
|
|
85
167
|
export const middleware = `router.use("/auth", authRoutes);`;
|
|
86
168
|
|
|
87
169
|
export const deps = ["jsonwebtoken", "mongoose"];
|
|
@@ -2,12 +2,14 @@ export const deps = ["bcrypt"];
|
|
|
2
2
|
|
|
3
3
|
export const devDeps = ["@types/bcrypt"];
|
|
4
4
|
|
|
5
|
-
export const
|
|
6
|
-
"
|
|
7
|
-
|
|
5
|
+
export const getFiles = (language = "typescript") => {
|
|
6
|
+
const ext = language === "javascript" ? ".js" : ".ts";
|
|
7
|
+
|
|
8
|
+
const tsContent = `import bcrypt from "bcrypt";
|
|
8
9
|
|
|
9
10
|
export const hashPassword = async (password: string): Promise<string> => {
|
|
10
|
-
|
|
11
|
+
const salt = await bcrypt.genSalt(10);
|
|
12
|
+
return bcrypt.hash(password, salt);
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
export const verifyPassword = async (
|
|
@@ -16,5 +18,23 @@ export const verifyPassword = async (
|
|
|
16
18
|
): Promise<boolean> => {
|
|
17
19
|
return bcrypt.compare(password, hash);
|
|
18
20
|
};
|
|
19
|
-
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const jsContent = `const bcrypt = require("bcrypt");
|
|
24
|
+
|
|
25
|
+
async function hashPassword(password) {
|
|
26
|
+
const salt = await bcrypt.genSalt(10);
|
|
27
|
+
return bcrypt.hash(password, salt);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function verifyPassword(hash, password) {
|
|
31
|
+
return bcrypt.compare(password, hash);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { hashPassword, verifyPassword };
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
[`src/utils/hash${ext}`]: language === "javascript" ? jsContent : tsContent,
|
|
39
|
+
};
|
|
20
40
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const mongoose = require("mongoose");
|
|
2
|
+
|
|
3
|
+
const userSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
email: {
|
|
6
|
+
type: String,
|
|
7
|
+
required: true,
|
|
8
|
+
unique: true,
|
|
9
|
+
},
|
|
10
|
+
password: {
|
|
11
|
+
type: String,
|
|
12
|
+
required: true,
|
|
13
|
+
},
|
|
14
|
+
fullName: {
|
|
15
|
+
type: String,
|
|
16
|
+
required: true,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{ timestamps: true },
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const UserModel = mongoose.model("User", userSchema);
|
|
23
|
+
|
|
24
|
+
module.exports = { UserModel };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { loginService, registerService } = require("./auth.service");
|
|
2
|
+
|
|
3
|
+
async function login(req, res, next) {
|
|
4
|
+
try {
|
|
5
|
+
const token = await loginService(req.body);
|
|
6
|
+
return res.status(200).json({ token });
|
|
7
|
+
} catch (err) {
|
|
8
|
+
return next(err);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function register(req, res, next) {
|
|
13
|
+
try {
|
|
14
|
+
const token = await registerService(req.body);
|
|
15
|
+
return res.status(201).json({ token });
|
|
16
|
+
} catch (err) {
|
|
17
|
+
return next(err);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { login, register };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const { Router } = require("express");
|
|
2
|
+
const { register, login } = require("./auth.controller");
|
|
3
|
+
const { methodNotAllowedHandler } = require("../../../middlewares");
|
|
4
|
+
|
|
5
|
+
const router = Router();
|
|
6
|
+
router.use(methodNotAllowedHandler(["POST"]));
|
|
7
|
+
router.post("/login", login);
|
|
8
|
+
router.post("/register", register);
|
|
9
|
+
|
|
10
|
+
module.exports = router;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const { generateToken } = require("../../../utils/jwt");
|
|
2
|
+
const { UserModel } = require("../../../models/user.model");
|
|
3
|
+
const { hashPassword, verifyPassword } = require("../../../utils/hash");
|
|
4
|
+
const {
|
|
5
|
+
UnauthorizedError,
|
|
6
|
+
ConflictError,
|
|
7
|
+
} = require("../../../utils/http-error");
|
|
8
|
+
|
|
9
|
+
async function loginService({ email, password }) {
|
|
10
|
+
const user = await UserModel.findOne({ email }).exec();
|
|
11
|
+
if (!user) throw new UnauthorizedError("User not found");
|
|
12
|
+
|
|
13
|
+
const match = await verifyPassword(user.password, password);
|
|
14
|
+
if (!match) throw new UnauthorizedError("Invalid credentials");
|
|
15
|
+
|
|
16
|
+
return generateToken({ email: user.email });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function registerService({ email, password, fullName }) {
|
|
20
|
+
const exists = await UserModel.findOne({ email }).exec();
|
|
21
|
+
if (exists) throw new ConflictError("Email already registered");
|
|
22
|
+
|
|
23
|
+
const hashed = await hashPassword(password);
|
|
24
|
+
const user = await UserModel.create({ email, password: hashed, fullName });
|
|
25
|
+
|
|
26
|
+
return generateToken({ email: user.email });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { loginService, registerService };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = { authRoutes: require("./auth.routes") };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const jwt = require("jsonwebtoken");
|
|
2
|
+
const { ENV } = require("../config");
|
|
3
|
+
|
|
4
|
+
async function generateToken(payload) {
|
|
5
|
+
return jwt.sign(payload, ENV.JWT_SECRET, { expiresIn: "7d" });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function verifyToken(token) {
|
|
9
|
+
return jwt.verify(token, ENV.JWT_SECRET);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = { generateToken, verifyToken };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export const deps = ["cors"];
|
|
2
2
|
export const devDeps = ["@types/cors"];
|
|
3
|
-
export const
|
|
3
|
+
export const getImports = (language) => language === "javascript"
|
|
4
|
+
? `const cors = require("cors");\nconst { ENV } = require("./config");`
|
|
5
|
+
: `import cors from "cors";\nimport { ENV } from "@/config";`;
|
|
6
|
+
export const imports = getImports("typescript");
|
|
4
7
|
export const middleware = `
|
|
5
8
|
const corsOptions = {
|
|
6
9
|
origin: ENV.ALLOWED_ORIGIN,
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export const deps = ["helmet"];
|
|
2
|
-
export const
|
|
2
|
+
export const getImports = (language) => language === "javascript"
|
|
3
|
+
? `const helmet = require("helmet");`
|
|
4
|
+
: `import helmet from "helmet";`;
|
|
5
|
+
export const imports = getImports("typescript");
|
|
3
6
|
export const middleware = `app.use(helmet());`;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const deps = ["morgan"];
|
|
2
2
|
export const devDeps = ["@types/morgan"];
|
|
3
|
-
export const
|
|
3
|
+
export const getImports = (language) => language === "javascript"
|
|
4
|
+
? `const morgan = require("morgan");`
|
|
5
|
+
: `import morgan from "morgan";`;
|
|
6
|
+
export const imports = getImports("typescript");
|
|
4
7
|
export const middleware = `app.use(morgan("dev"));`;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export const deps = ["express-rate-limit"];
|
|
2
|
-
export const
|
|
2
|
+
export const getImports = (language) => language === "javascript"
|
|
3
|
+
? `const rateLimit = require("express-rate-limit");`
|
|
4
|
+
: `import rateLimit from "express-rate-limit";`;
|
|
5
|
+
export const imports = getImports("typescript");
|
|
3
6
|
export const middleware = `app.use(rateLimit({ windowMs: 15*60*1000, max: 100 }));`;
|