crypt-express-app 1.0.0 → 1.3.1

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.

Potentially problematic release.


This version of crypt-express-app might be problematic. Click here for more details.

@@ -1,88 +1,104 @@
1
- // generate-utils.ts
2
1
  import * as fs from "fs";
3
2
  import * as path from "path";
4
- const basePath = path.join(__dirname, "..");
5
- if (!fs.existsSync(basePath)) {
6
- fs.mkdirSync(basePath, { recursive: true });
7
- }
8
- /* ================= redis.ts ================= */
9
- const appContent = `
3
+ export function generateApp(projectDir) {
4
+ const basePath = projectDir;
5
+ const srcPath = path.join(basePath, "src");
6
+ // ensure src directory exists
7
+ if (!fs.existsSync(srcPath)) {
8
+ fs.mkdirSync(srcPath, { recursive: true });
9
+ }
10
+ /* ================= app.ts ================= */
11
+ const routeContent = `
12
+ import express, { Router } from 'express';
13
+ import swaggerUi from "swagger-ui-express";
14
+ import { swaggerSpec } from "./utils/swagger";
15
+
16
+ const router: Router = express.Router();
17
+
18
+ router.get("/", (req, res) => {
19
+ res.send(req.t("WELCOME"));
20
+ });
21
+
22
+ router.use(
23
+ "/api/docs",
24
+ swaggerUi.serve,
25
+ swaggerUi.setup(swaggerSpec, {
26
+ swaggerOptions: {
27
+ persistAuthorization: true,
28
+ },
29
+ })
30
+ );
31
+
32
+ export default router;
33
+ `;
34
+ fs.writeFileSync(path.join(srcPath, "routes.ts"), routeContent.trim());
35
+ /* ================= app.ts ================= */
36
+ const appContent = `
10
37
  import express, { type Express } from "express";
11
38
  import cors from "cors";
12
39
  import morgan from "morgan";
13
40
  import { json, urlencoded } from "express";
14
- import i18next from 'i18next';
15
- import Backend from 'i18next-fs-backend';
16
- import middleware from 'i18next-http-middleware';
17
- import path from 'path';
41
+ import i18next from "i18next";
42
+ import Backend from "i18next-fs-backend";
43
+ import middleware from "i18next-http-middleware";
44
+ import path from "path";
18
45
  import { errorMiddleware } from "./middlewares/error.middleware";
19
46
  import { loggerMiddleware } from "./middlewares/logger.middleware";
20
47
  import swaggerUi from "swagger-ui-express";
21
48
  import { swaggerSpec } from "./utils/swagger";
49
+ import routes from "./routes";
22
50
 
23
51
  export const app: Express = express();
24
52
 
25
- // i18n
26
53
  i18next
27
- .use(Backend)
28
- .use(middleware.LanguageDetector)
29
- .init({
30
- fallbackLng: 'en',
31
- preload: ['en', 'bn'],
32
- backend: {
33
- loadPath: path.join(__dirname, 'locales/{{lng}}/translation.json')
34
- },
35
- detection: {
36
- order: ['querystring', 'cookie', 'header'],
37
- caches: ['cookie']
38
- }
39
- });
54
+ .use(Backend)
55
+ .use(middleware.LanguageDetector)
56
+ .init({
57
+ fallbackLng: "en",
58
+ preload: ["en", "bn"],
59
+ backend: {
60
+ loadPath: path.join(__dirname, "locales/{{lng}}/translation.json"),
61
+ },
62
+ detection: {
63
+ order: ["querystring", "cookie", "header"],
64
+ caches: ["cookie"],
65
+ },
66
+ });
40
67
 
41
- // Middlewares
42
68
  app.use(cors());
43
69
  app.use(json());
44
70
  app.use(urlencoded({ extended: true }));
45
71
  app.use(morgan("dev"));
46
72
  app.use(middleware.handle(i18next));
47
-
48
- // Logger middleware should come first
49
73
  app.use(loggerMiddleware);
50
-
51
- // Routes
52
- app.get('/', (req, res) => {
53
- res.send(req.t('WELCOME')); // automatically returns text in detected language
54
- });
55
- app.use("/api/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
56
- swaggerOptions: {
57
- persistAuthorization: true, // ✅ এটা রাখে token reload-এও
58
- },
59
- }));
60
-
61
- // Error handler
74
+ app.use(routes);
62
75
  app.use(errorMiddleware);
63
76
 
64
77
  export default app;
65
78
  `;
66
- fs.writeFileSync(path.join(basePath, "src", "app.ts"), appContent);
67
- /* ================= redis.ts ================= */
68
- const serverContent = `
79
+ fs.writeFileSync(path.join(srcPath, "app.ts"), appContent.trim());
80
+ /* ================= server.ts ================= */
81
+ const serverContent = `
69
82
  import app from "./src/app";
70
83
  import bootstrapSystem from "./src/utils/bootstrap";
71
84
  import { connectRedis } from "./src/utils/redis";
72
85
 
86
+ const port = process.env.PORT ?? 3000
87
+
73
88
  async function startServer() {
74
- try{
75
- await bootstrapSystem(); // 🔥 important
76
- await connectRedis();
89
+ try {
90
+ await bootstrapSystem();
91
+ await connectRedis();
77
92
 
78
- app.listen(5010, () => {
79
- console.log("🚀 Server running at http://localhost:3000");
80
- console.log("📘 Swagger docs at http://localhost:3000/api/docs");
81
- });
82
- }catch(error){
83
- console.log("Error.........\\n\\n")
84
- }
93
+ app.listen(port, () => {
94
+ console.log(\`🚀 Server running at http://localhost:\${port}\`);
95
+ console.log(\`📘 Swagger docs at http://localhost:\${port}/api/docs\`);
96
+ });
97
+ } catch (error) {
98
+ console.error("Error.........", error);
99
+ }
85
100
  }
101
+
86
102
  process.on("uncaughtException", (err) => {
87
103
  console.error("🔥 Uncaught Exception:", err);
88
104
  });
@@ -90,7 +106,9 @@ process.on("uncaughtException", (err) => {
90
106
  process.on("unhandledRejection", (reason) => {
91
107
  console.error("🔥 Unhandled Rejection:", reason);
92
108
  });
109
+
93
110
  startServer();
94
111
  `;
95
- fs.writeFileSync(path.join(basePath, "server.ts"), serverContent);
96
- console.log("✅ App files generated successfully inside src");
112
+ fs.writeFileSync(path.join(basePath, "server.ts"), serverContent.trim());
113
+ console.log("✅ App files generated successfully inside src");
114
+ }
@@ -1,29 +1,25 @@
1
- // generate-locales.ts
2
1
  import * as fs from "fs";
3
2
  import * as path from "path";
4
- const basePath = path.join(__dirname, "..", "src", "locales");
5
- const bnPath = path.join(basePath, "bn");
6
- const enPath = path.join(basePath, "en");
7
- // ফোল্ডার তৈরি
8
- [basePath, bnPath, enPath].forEach((dir) => {
9
- if (!fs.existsSync(dir)) {
10
- fs.mkdirSync(dir, { recursive: true });
11
- }
12
- });
13
- // বাংলা translation
14
- const bnContent = `
15
- {
16
- "WELCOME": "আমাদের এপিতে স্বাগতম",
17
- "USER_NOT_FOUND": "ব্যবহারকারী পাওয়া যায়নি"
3
+ export function generateLocales(projectDir) {
4
+ const localesPath = path.join(projectDir, "src", "locales");
5
+ const bnPath = path.join(localesPath, "bn");
6
+ const enPath = path.join(localesPath, "en");
7
+ // create directories if not exist
8
+ [localesPath, bnPath, enPath].forEach((dir) => {
9
+ if (!fs.existsSync(dir)) {
10
+ fs.mkdirSync(dir, { recursive: true });
11
+ }
12
+ });
13
+ // translations
14
+ const bnContent = {
15
+ WELCOME: "আমাদের এপিতে স্বাগতম",
16
+ USER_NOT_FOUND: "ব্যবহারকারী পাওয়া যায়নি",
17
+ };
18
+ const enContent = {
19
+ WELCOME: "Welcome to our API",
20
+ USER_NOT_FOUND: "User not found",
21
+ };
22
+ fs.writeFileSync(path.join(bnPath, "translation.json"), JSON.stringify(bnContent, null, 2));
23
+ fs.writeFileSync(path.join(enPath, "translation.json"), JSON.stringify(enContent, null, 2));
24
+ console.log("Locales generated successfully inside src/locales");
18
25
  }
19
- `;
20
- // ইংরেজি translation
21
- const enContent = `
22
- {
23
- "WELCOME": "Welcome to our API",
24
- "USER_NOT_FOUND": "User not found"
25
- }
26
- `;
27
- fs.writeFileSync(path.join(bnPath, "translation.json"), bnContent.trim());
28
- fs.writeFileSync(path.join(enPath, "translation.json"), enContent.trim());
29
- console.log("Locales generated successfully inside src/locales");
@@ -1,24 +1,25 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
- const projectRoot = process.cwd();
4
- const basePath = path.join(projectRoot, "src", "middlewares");
5
- if (!fs.existsSync(basePath)) {
6
- fs.mkdirSync(basePath, { recursive: true });
7
- }
8
- /* ================= AUTHORIZATION ================= */
9
- const authorizationContent = `
3
+ export function generateMiddlewares(projectDir) {
4
+ const basePath = path.join(projectDir, "src", "middlewares");
5
+ if (!fs.existsSync(basePath)) {
6
+ fs.mkdirSync(basePath, { recursive: true });
7
+ }
8
+ /* ================= AUTHORIZATION ================= */
9
+ const authorizationContent = `
10
10
  import { Request, Response, NextFunction } from "express";
11
- import { ResourceType, ActionsType, Resource } from "../utils/consts";
12
- import { cacheService } from "../utils/redis";
11
+ import { TypeResource, TypeAction, Resource } from "../utils/consts";
12
+ import cacheService from "../utils/redis";
13
13
 
14
14
  export function authorizationMiddleware(authorization: {
15
15
  resource: Resource;
16
- resourceType: ResourceType;
17
- actions: ActionsType[];
16
+ resourceType: TypeResource;
17
+ actions: TypeAction[];
18
18
  }) {
19
19
  return async (req: Request, res: Response, next: NextFunction) => {
20
20
  try {
21
21
  const userId = req?.user?.userId;
22
+ const isMasterRealmUser = req?.user?.isMasterRealmUser;
22
23
  if (!userId) {
23
24
  return res.status(401).json({ message: "Unauthorized" });
24
25
  }
@@ -28,15 +29,16 @@ export function authorizationMiddleware(authorization: {
28
29
  return res.status(403).json({ message: "Permissions not loaded" });
29
30
  }
30
31
 
31
- const profile = JSON.parse(cached as string);
32
+ const profile = JSON.parse(cached);
32
33
  const permissions: string[] = profile.permissions || [];
33
34
 
35
+ // check permission
34
36
  const allowed = permissions.some((perm) => {
35
37
  const [resName, action, type] = perm.split(":");
36
38
  return (
37
39
  resName === authorization.resource &&
38
40
  type === authorization.resourceType &&
39
- authorization.actions.includes(action as ActionsType)
41
+ authorization.actions.includes(action as TypeAction)
40
42
  );
41
43
  });
42
44
 
@@ -50,14 +52,13 @@ export function authorizationMiddleware(authorization: {
50
52
  return res.status(500).json({ message: "Authorization error" });
51
53
  }
52
54
  };
53
- };
55
+ }
54
56
  `;
55
- fs.writeFileSync(path.join(basePath, "authorization.middleware.ts"), authorizationContent.trim());
56
- /* ================= AUTH ================= */
57
- const authContent = `
57
+ fs.writeFileSync(path.join(basePath, "authorization.middleware.ts"), authorizationContent.trim());
58
+ /* ================= AUTH ================= */
59
+ const authContent = `
58
60
  import { Request, Response, NextFunction } from "express";
59
61
  import jwt from "jsonwebtoken";
60
- import prisma from "../../prisma/client";
61
62
  import { cacheService } from "../utils/redis";
62
63
 
63
64
  interface JwtPayload {
@@ -77,60 +78,54 @@ declare global {
77
78
  }
78
79
  }
79
80
 
81
+ /**
82
+ * @description Middleware to protect routes and verify JWT token
83
+ */
80
84
  export const authMiddleware = async (req: Request, res: Response, next: NextFunction) => {
81
- const authHeader = req.headers.authorization;
85
+ const authHeader = req.headers["authorization"];
82
86
  if (!authHeader) return res.status(401).json({ message: "Authorization header missing" });
83
87
 
84
- const [scheme, tokenRaw] = authHeader.split(" ");
85
- if (scheme !== "Bearer" || !tokenRaw) {
86
- return res.status(401).json({ message: "Invalid Authorization header format" });
87
- }
88
-
89
- const token = tokenRaw.replace(/^\"|\"$/g, "");
90
-
91
- const decodedPayload = jwt.decode(token) as JwtPayload | null;
92
- if (!decodedPayload || !decodedPayload.realmId) {
93
- return res.status(401).json({ message: "Invalid token: realmId missing" });
94
- }
95
-
96
- const realmId = decodedPayload.realmId;
97
-
98
- const secretKey = \`realm:\${realmId}:secret\`;
99
- const secret = await cacheService.get(secretKey);
100
- if (!secret) {
101
- return res.status(500).json({ message: "JWT secret not configured in Redis" });
102
- }
88
+ const token = authHeader.split(" ")[1]; // Bearer <token>
89
+ if (!token) return res.status(401).json({ message: "Token missing" });
103
90
 
104
91
  try {
105
- const decoded = jwt.verify(token, secret as string, {
106
- algorithms: ["HS256"],
107
- }) as JwtPayload & { userId: string; sessionId: string };
108
-
109
- const session = await prisma.userSession.findUnique({
110
- where: { userSessionId: decoded.sessionId },
111
- });
112
-
113
- if (!session) {
114
- return res.status(401).json({ message: "Session invalid or logged out" });
92
+ const decodedPayload = jwt.decode(token) as JwtPayload | null;
93
+ if (!decodedPayload || !decodedPayload.realmId) {
94
+ return res.status(401).json({ message: "Invalid token: realmId missing" });
115
95
  }
116
-
117
- req.user = decoded;
118
- next();
119
- } catch (err: any) {
120
- if (err.name === "TokenExpiredError") {
121
- return res.status(401).json({ message: "Token expired" });
96
+ const realmId = decodedPayload.realmId as string;
97
+
98
+ // 2. Redis থেকে secret load করা
99
+ const secretKey = \`realm:\${realmId}:secret\`;
100
+ const secret = await cacheService.get(secretKey) as string;
101
+ if (!secret) return res.status(500).json({ message: "JWT secret not configured in Redis" });
102
+
103
+ // const secret = JWT_SECRET;
104
+ const decoded = jwt.verify(token, secret, { algorithms: ["HS256"] }) as JwtPayload;
105
+
106
+ const isBlacklisted = await cacheService.get(\`blacklist:\${token}\`);
107
+ if (isBlacklisted) {
108
+ return res.status(401).json({ message: "Token revoked" });
122
109
  }
123
-
124
- return res.status(401).json({ message: "Invalid token" });
110
+ req.user = decoded; // attach payload to request
111
+ next();
112
+ } catch (err) {
113
+ return res.status(401).json({ message: "Invalid or expired token" });
125
114
  }
126
115
  };
116
+
127
117
  `;
128
- fs.writeFileSync(path.join(basePath, "auth.middleware.ts"), authContent.trim());
129
- /* ================= ERROR ================= */
130
- const errorContent = `
118
+ fs.writeFileSync(path.join(basePath, "auth.middleware.ts"), authContent.trim());
119
+ /* ================= ERROR ================= */
120
+ const errorContent = `
131
121
  import { Request, Response, NextFunction } from "express";
132
122
 
133
- export const errorMiddleware = (err: any, req: Request, res: Response, next: NextFunction) => {
123
+ export const errorMiddleware = (
124
+ err: any,
125
+ req: Request,
126
+ res: Response,
127
+ next: NextFunction
128
+ ) => {
134
129
  console.error(err);
135
130
 
136
131
  const status = err.status || 500;
@@ -144,22 +139,29 @@ export const errorMiddleware = (err: any, req: Request, res: Response, next: Nex
144
139
  });
145
140
  };
146
141
  `;
147
- fs.writeFileSync(path.join(basePath, "error.middleware.ts"), errorContent.trim());
148
- /* ================= LOGGER ================= */
149
- const loggerContent = `
142
+ fs.writeFileSync(path.join(basePath, "error.middleware.ts"), errorContent.trim());
143
+ /* ================= LOGGER ================= */
144
+ const loggerContent = `
150
145
  import { Request, Response, NextFunction } from "express";
151
146
  import logger from "../utils/logger";
152
147
 
153
- export const loggerMiddleware = (req: Request, res: Response, next: NextFunction) => {
148
+ export const loggerMiddleware = (
149
+ req: Request,
150
+ res: Response,
151
+ next: NextFunction
152
+ ) => {
154
153
  const start = Date.now();
155
154
 
156
155
  res.on("finish", () => {
157
156
  const duration = Date.now() - start;
158
- logger.info(\`[\${new Date().toISOString()}] \${req.method} \${req.originalUrl} \${res.statusCode} - \${duration}ms\`);
157
+ logger.info(
158
+ \`[\${new Date().toISOString()}] \${req.method} \${req.originalUrl} \${res.statusCode} - \${duration}ms\`
159
+ );
159
160
  });
160
161
 
161
162
  next();
162
163
  };
163
164
  `;
164
- fs.writeFileSync(path.join(basePath, "logger.middleware.ts"), loggerContent.trim());
165
- console.log(`✅ Middleware files generated successfully at ${basePath}`);
165
+ fs.writeFileSync(path.join(basePath, "logger.middleware.ts"), loggerContent.trim());
166
+ console.log(`✅ Middleware files generated successfully at ${basePath}`);
167
+ }