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.
- package/README.md +321 -1
- package/{scripts/generate-module.ts → dist/generate-module.js} +24 -81
- package/dist/index.js +118 -17
- package/dist/scripts/generate-app.js +73 -55
- package/dist/scripts/generate-locales.js +22 -26
- package/dist/scripts/generate-middleware.js +69 -67
- package/dist/scripts/generate-module.js +140 -78
- package/dist/scripts/generate-root-files.js +199 -63
- package/dist/scripts/generate-utils.js +811 -46
- package/dist/scripts/setup-prisma.js +14 -13
- package/package.json +10 -5
- package/index.ts +0 -29
- package/scripts/generate-app.ts +0 -104
- package/scripts/generate-locales.ts +0 -36
- package/scripts/generate-middleware.ts +0 -176
- package/scripts/generate-root-files.ts +0 -147
- package/scripts/generate-utils.ts +0 -208
- package/scripts/setup-prisma.ts +0 -26
- package/tsconfig.json +0 -10
|
@@ -1,88 +1,104 @@
|
|
|
1
|
-
// generate-utils.ts
|
|
2
1
|
import * as fs from "fs";
|
|
3
2
|
import * as path from "path";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
15
|
-
import Backend from
|
|
16
|
-
import middleware from
|
|
17
|
-
import path from
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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(
|
|
67
|
-
/* =================
|
|
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
|
-
|
|
76
|
-
|
|
89
|
+
try {
|
|
90
|
+
await bootstrapSystem();
|
|
91
|
+
await connectRedis();
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}catch(error){
|
|
83
|
-
|
|
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
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
fs.
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
4
|
-
const basePath = path.join(
|
|
5
|
-
if (!fs.existsSync(basePath)) {
|
|
6
|
-
|
|
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 {
|
|
12
|
-
import
|
|
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:
|
|
17
|
-
actions:
|
|
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
|
|
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
|
|
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
|
|
85
|
+
const authHeader = req.headers["authorization"];
|
|
82
86
|
if (!authHeader) return res.status(401).json({ message: "Authorization header missing" });
|
|
83
87
|
|
|
84
|
-
const
|
|
85
|
-
if (
|
|
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
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
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 = (
|
|
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 = (
|
|
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(
|
|
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
|
+
}
|