crypt-express-app 1.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.
- package/README.md +1 -0
- package/dist/index.js +20 -0
- package/dist/scripts/generate-app.js +96 -0
- package/dist/scripts/generate-locales.js +29 -0
- package/dist/scripts/generate-middleware.js +165 -0
- package/dist/scripts/generate-module.js +677 -0
- package/dist/scripts/generate-root-files.js +123 -0
- package/dist/scripts/generate-utils.js +191 -0
- package/dist/scripts/middleware.js +165 -0
- package/dist/scripts/setup-prisma.js +21 -0
- package/index.ts +29 -0
- package/package.json +23 -0
- package/scripts/generate-app.ts +104 -0
- package/scripts/generate-locales.ts +36 -0
- package/scripts/generate-middleware.ts +176 -0
- package/scripts/generate-module.ts +715 -0
- package/scripts/generate-root-files.ts +147 -0
- package/scripts/generate-utils.ts +208 -0
- package/scripts/setup-prisma.ts +26 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
|
|
4
|
+
const projectRoot = process.cwd();
|
|
5
|
+
const basePath = path.join(projectRoot, "src", "middlewares");
|
|
6
|
+
|
|
7
|
+
if (!fs.existsSync(basePath)) {
|
|
8
|
+
fs.mkdirSync(basePath, { recursive: true });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* ================= AUTHORIZATION ================= */
|
|
12
|
+
const authorizationContent = `
|
|
13
|
+
import { Request, Response, NextFunction } from "express";
|
|
14
|
+
import { ResourceType, ActionsType, Resource } from "../utils/consts";
|
|
15
|
+
import { cacheService } from "../utils/redis";
|
|
16
|
+
|
|
17
|
+
export function authorizationMiddleware(authorization: {
|
|
18
|
+
resource: Resource;
|
|
19
|
+
resourceType: ResourceType;
|
|
20
|
+
actions: ActionsType[];
|
|
21
|
+
}) {
|
|
22
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
23
|
+
try {
|
|
24
|
+
const userId = req?.user?.userId;
|
|
25
|
+
if (!userId) {
|
|
26
|
+
return res.status(401).json({ message: "Unauthorized" });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const cached = await cacheService.get(\`user:\${userId}:permissions\`);
|
|
30
|
+
if (!cached) {
|
|
31
|
+
return res.status(403).json({ message: "Permissions not loaded" });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const profile = JSON.parse(cached as string);
|
|
35
|
+
const permissions: string[] = profile.permissions || [];
|
|
36
|
+
|
|
37
|
+
const allowed = permissions.some((perm) => {
|
|
38
|
+
const [resName, action, type] = perm.split(":");
|
|
39
|
+
return (
|
|
40
|
+
resName === authorization.resource &&
|
|
41
|
+
type === authorization.resourceType &&
|
|
42
|
+
authorization.actions.includes(action as ActionsType)
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!allowed) {
|
|
47
|
+
return res.status(403).json({ message: "Access denied" });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
next();
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(error);
|
|
53
|
+
return res.status(500).json({ message: "Authorization error" });
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
fs.writeFileSync(path.join(basePath, "authorization.middleware.ts"), authorizationContent.trim());
|
|
60
|
+
|
|
61
|
+
/* ================= AUTH ================= */
|
|
62
|
+
const authContent = `
|
|
63
|
+
import { Request, Response, NextFunction } from "express";
|
|
64
|
+
import jwt from "jsonwebtoken";
|
|
65
|
+
import prisma from "../../prisma/client";
|
|
66
|
+
import { cacheService } from "../utils/redis";
|
|
67
|
+
|
|
68
|
+
interface JwtPayload {
|
|
69
|
+
realmId: string;
|
|
70
|
+
sessionId: string;
|
|
71
|
+
userId: string;
|
|
72
|
+
email: string;
|
|
73
|
+
clientIdInternal: string;
|
|
74
|
+
isMasterRealmUser: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
declare global {
|
|
78
|
+
namespace Express {
|
|
79
|
+
interface Request {
|
|
80
|
+
user?: JwtPayload;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const authMiddleware = async (req: Request, res: Response, next: NextFunction) => {
|
|
86
|
+
const authHeader = req.headers.authorization;
|
|
87
|
+
if (!authHeader) return res.status(401).json({ message: "Authorization header missing" });
|
|
88
|
+
|
|
89
|
+
const [scheme, tokenRaw] = authHeader.split(" ");
|
|
90
|
+
if (scheme !== "Bearer" || !tokenRaw) {
|
|
91
|
+
return res.status(401).json({ message: "Invalid Authorization header format" });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const token = tokenRaw.replace(/^\"|\"$/g, "");
|
|
95
|
+
|
|
96
|
+
const decodedPayload = jwt.decode(token) as JwtPayload | null;
|
|
97
|
+
if (!decodedPayload || !decodedPayload.realmId) {
|
|
98
|
+
return res.status(401).json({ message: "Invalid token: realmId missing" });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const realmId = decodedPayload.realmId;
|
|
102
|
+
|
|
103
|
+
const secretKey = \`realm:\${realmId}:secret\`;
|
|
104
|
+
const secret = await cacheService.get(secretKey);
|
|
105
|
+
if (!secret) {
|
|
106
|
+
return res.status(500).json({ message: "JWT secret not configured in Redis" });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const decoded = jwt.verify(token, secret as string, {
|
|
111
|
+
algorithms: ["HS256"],
|
|
112
|
+
}) as JwtPayload & { userId: string; sessionId: string };
|
|
113
|
+
|
|
114
|
+
const session = await prisma.userSession.findUnique({
|
|
115
|
+
where: { userSessionId: decoded.sessionId },
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (!session) {
|
|
119
|
+
return res.status(401).json({ message: "Session invalid or logged out" });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
req.user = decoded;
|
|
123
|
+
next();
|
|
124
|
+
} catch (err: any) {
|
|
125
|
+
if (err.name === "TokenExpiredError") {
|
|
126
|
+
return res.status(401).json({ message: "Token expired" });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return res.status(401).json({ message: "Invalid token" });
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
fs.writeFileSync(path.join(basePath, "auth.middleware.ts"), authContent.trim());
|
|
135
|
+
|
|
136
|
+
/* ================= ERROR ================= */
|
|
137
|
+
const errorContent = `
|
|
138
|
+
import { Request, Response, NextFunction } from "express";
|
|
139
|
+
|
|
140
|
+
export const errorMiddleware = (err: any, req: Request, res: Response, next: NextFunction) => {
|
|
141
|
+
console.error(err);
|
|
142
|
+
|
|
143
|
+
const status = err.status || 500;
|
|
144
|
+
const message = err.message || "Internal Server Error";
|
|
145
|
+
|
|
146
|
+
res.status(status).json({
|
|
147
|
+
success: false,
|
|
148
|
+
status,
|
|
149
|
+
message,
|
|
150
|
+
...(process.env.NODE_ENV === "development" && { stack: err.stack }),
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
`;
|
|
154
|
+
|
|
155
|
+
fs.writeFileSync(path.join(basePath, "error.middleware.ts"), errorContent.trim());
|
|
156
|
+
|
|
157
|
+
/* ================= LOGGER ================= */
|
|
158
|
+
const loggerContent = `
|
|
159
|
+
import { Request, Response, NextFunction } from "express";
|
|
160
|
+
import logger from "../utils/logger";
|
|
161
|
+
|
|
162
|
+
export const loggerMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
|
163
|
+
const start = Date.now();
|
|
164
|
+
|
|
165
|
+
res.on("finish", () => {
|
|
166
|
+
const duration = Date.now() - start;
|
|
167
|
+
logger.info(\`[\${new Date().toISOString()}] \${req.method} \${req.originalUrl} \${res.statusCode} - \${duration}ms\`);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
next();
|
|
171
|
+
};
|
|
172
|
+
`;
|
|
173
|
+
|
|
174
|
+
fs.writeFileSync(path.join(basePath, "logger.middleware.ts"), loggerContent.trim());
|
|
175
|
+
|
|
176
|
+
console.log(`✅ Middleware files generated successfully at ${basePath}`);
|