@synchjs/ewb 1.0.0 → 1.0.2
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 +76 -196
- package/dist/Components/ServeMemoryStore.d.ts +15 -1
- package/dist/Components/ServeMemoryStore.d.ts.map +1 -1
- package/dist/Components/ServeMemoryStore.js +210 -28
- package/dist/Components/Server.d.ts +12 -1
- package/dist/Components/Server.d.ts.map +1 -1
- package/dist/Components/Server.js +185 -42
- package/dist/Components/UserHandler.d.ts +21 -0
- package/dist/Components/UserHandler.d.ts.map +1 -0
- package/dist/Components/UserHandler.js +9 -0
- package/dist/Decorations/Authorized.d.ts +2 -5
- package/dist/Decorations/Authorized.d.ts.map +1 -1
- package/dist/Decorations/Authorized.js +6 -6
- package/dist/Decorations/Security.d.ts +0 -1
- package/dist/Decorations/Security.d.ts.map +1 -1
- package/dist/Decorations/Security.js +0 -3
- package/dist/Decorations/Serve.d.ts +1 -0
- package/dist/Decorations/Serve.d.ts.map +1 -1
- package/dist/Decorations/Serve.js +20 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +12 -9
|
@@ -9,10 +9,12 @@ import rateLimit from "express-rate-limit";
|
|
|
9
9
|
import Ajv from "ajv";
|
|
10
10
|
import addFormats from "ajv-formats";
|
|
11
11
|
import { MetadataStorage } from "../Decorations";
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
12
|
+
import { createServer } from "http";
|
|
13
|
+
import { Server as SocketServer } from "socket.io";
|
|
14
|
+
import { PUBLIC_METADATA_KEY, ROLES_METADATA_KEY, } from "../Decorations/Authorized";
|
|
14
15
|
import { SECURITY_METADATA_KEY, } from "../Decorations/Security";
|
|
15
16
|
import { TAILWIND_METADATA_KEY, } from "../Decorations/Tailwind";
|
|
17
|
+
import { SERVE_HTML_METADATA_KEY } from "../Decorations/Serve";
|
|
16
18
|
import { ServeMemoryStore } from "./ServeMemoryStore";
|
|
17
19
|
import boxen from "boxen";
|
|
18
20
|
import pc from "picocolors";
|
|
@@ -20,6 +22,7 @@ export class Server {
|
|
|
20
22
|
_app;
|
|
21
23
|
_port;
|
|
22
24
|
_controllersDir;
|
|
25
|
+
_viewsDir;
|
|
23
26
|
_id;
|
|
24
27
|
_enableSwagger;
|
|
25
28
|
_swaggerPath;
|
|
@@ -28,11 +31,15 @@ export class Server {
|
|
|
28
31
|
_ajv;
|
|
29
32
|
_securityHandlers;
|
|
30
33
|
_container;
|
|
34
|
+
_roleHandler;
|
|
35
|
+
_userHandler;
|
|
31
36
|
_serverInstance;
|
|
37
|
+
_io;
|
|
32
38
|
// Stats
|
|
33
39
|
_controllerCount = 0;
|
|
34
40
|
_routeCount = 0;
|
|
35
41
|
_tailwindEnabled = false;
|
|
42
|
+
_devMode = false;
|
|
36
43
|
constructor(options) {
|
|
37
44
|
this._port = options.port;
|
|
38
45
|
this._app = express();
|
|
@@ -42,28 +49,71 @@ export class Server {
|
|
|
42
49
|
this._enableLogging =
|
|
43
50
|
options.logging === undefined ? true : options.logging;
|
|
44
51
|
this._controllersDir = options.controllersDir || "controllers";
|
|
52
|
+
this._viewsDir = options.viewsDir;
|
|
45
53
|
this._securityHandlers = options.securityHandlers || {};
|
|
46
54
|
this._securitySchemes = options.securitySchemes;
|
|
47
55
|
this._container = options.container;
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
this._roleHandler = options.roleHandler;
|
|
57
|
+
// Initialize AJV with strict mode for better security
|
|
58
|
+
this._ajv = new Ajv({
|
|
59
|
+
allErrors: true,
|
|
60
|
+
strict: true,
|
|
61
|
+
removeAdditional: true, // Automatically remove properties not in schema
|
|
62
|
+
});
|
|
50
63
|
addFormats(this._ajv);
|
|
51
64
|
// Security Middleware
|
|
52
|
-
|
|
65
|
+
const helmetOptions = options.helmetOptions || {};
|
|
66
|
+
if (process.env.NODE_ENV !== "production") {
|
|
67
|
+
// Relax CSP for HMR in development
|
|
68
|
+
helmetOptions.contentSecurityPolicy = {
|
|
69
|
+
directives: {
|
|
70
|
+
defaultSrc: ["'self'"],
|
|
71
|
+
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'", "blob:"],
|
|
72
|
+
connectSrc: ["'self'", "ws:", "wss:", "http:", "https:"],
|
|
73
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
74
|
+
imgSrc: ["'self'", "data:", "blob:"],
|
|
75
|
+
frameSrc: ["'self'"],
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
this._app.use(helmet(helmetOptions));
|
|
53
80
|
this._app.use(cors(options.corsOptions));
|
|
54
81
|
this._app.use(rateLimit(options.rateLimitOptions || {
|
|
55
82
|
windowMs: 15 * 60 * 1000,
|
|
56
83
|
max: 100,
|
|
84
|
+
skip: (req) => ServeMemoryStore.instance.getAsset(req.path) !== undefined, // Don't rate limit static assets
|
|
57
85
|
}));
|
|
58
|
-
this._app.use(express.json());
|
|
86
|
+
this._app.use(express.json({ limit: "1mb" })); // Protection against large payloads
|
|
87
|
+
// Clear cache on startup
|
|
88
|
+
ServeMemoryStore.instance.clearCache();
|
|
89
|
+
this._devMode = process.env.NODE_ENV !== "production";
|
|
90
|
+
if (this._devMode) {
|
|
91
|
+
this.setupHmr();
|
|
92
|
+
}
|
|
59
93
|
global.servers.set(this._id, this._app);
|
|
60
94
|
}
|
|
95
|
+
setupHmr() {
|
|
96
|
+
ServeMemoryStore.instance.setDevMode(true);
|
|
97
|
+
ServeMemoryStore.instance.onRebuild((data) => {
|
|
98
|
+
if (data && data.html) {
|
|
99
|
+
this._io?.emit("rebuild", { html: data.html });
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
this._io?.emit("reload");
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
// No redundant global watcher here, we rely on ServeMemoryStore's specific watchers
|
|
106
|
+
// which rebuild before notifying.
|
|
107
|
+
}
|
|
61
108
|
log(message) {
|
|
62
109
|
if (this._enableLogging) {
|
|
63
110
|
console.log(message);
|
|
64
111
|
}
|
|
65
112
|
}
|
|
66
113
|
async init() {
|
|
114
|
+
if (this._userHandler) {
|
|
115
|
+
this.setupAuthRoutes();
|
|
116
|
+
}
|
|
67
117
|
await this.loadControllers();
|
|
68
118
|
if (this._enableSwagger) {
|
|
69
119
|
this.setupSwagger();
|
|
@@ -74,6 +124,11 @@ export class Server {
|
|
|
74
124
|
return next();
|
|
75
125
|
const asset = ServeMemoryStore.instance.getAsset(req.path);
|
|
76
126
|
if (asset) {
|
|
127
|
+
if (this._devMode) {
|
|
128
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
|
|
129
|
+
res.setHeader("Pragma", "no-cache");
|
|
130
|
+
res.setHeader("Expires", "0");
|
|
131
|
+
}
|
|
77
132
|
res.type(asset.type).send(Buffer.from(asset.content));
|
|
78
133
|
return;
|
|
79
134
|
}
|
|
@@ -81,13 +136,31 @@ export class Server {
|
|
|
81
136
|
});
|
|
82
137
|
// Global Error Handler
|
|
83
138
|
this._app.use((err, req, res, next) => {
|
|
84
|
-
|
|
139
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
140
|
+
if (!isProd) {
|
|
141
|
+
console.error(`[${this._id}] Error:`, err);
|
|
142
|
+
}
|
|
85
143
|
res.status(err.status || 500).json({
|
|
86
144
|
error: "Internal Server Error",
|
|
87
|
-
message:
|
|
145
|
+
message: isProd
|
|
146
|
+
? "An unexpected error occurred"
|
|
147
|
+
: err.message || "An unexpected error occurred",
|
|
148
|
+
// Mask stack trace in production
|
|
149
|
+
stack: isProd ? undefined : err.stack,
|
|
88
150
|
});
|
|
89
151
|
});
|
|
90
|
-
this._serverInstance = this._app
|
|
152
|
+
this._serverInstance = createServer(this._app);
|
|
153
|
+
if (this._devMode) {
|
|
154
|
+
this._io = new SocketServer(this._serverInstance, {
|
|
155
|
+
cors: {
|
|
156
|
+
origin: [/localhost/, /127\.0\.0\.1/], // Restrict HMR to local development origin
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
this._io.on("connection", (socket) => {
|
|
160
|
+
// Disconnected client log
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
this._serverInstance.listen(this._port, () => {
|
|
91
164
|
if (this._enableLogging) {
|
|
92
165
|
this.printStartupMessage();
|
|
93
166
|
}
|
|
@@ -103,7 +176,11 @@ export class Server {
|
|
|
103
176
|
`${pc.bold(pad("Controllers:"))}${this._controllerCount}`,
|
|
104
177
|
`${pc.bold(pad("Routes:"))}${this._routeCount}`,
|
|
105
178
|
`${pc.bold(pad("Tailwind:"))}${this._tailwindEnabled ? pc.blue("Enabled") : pc.dim("Disabled")}`,
|
|
179
|
+
`${pc.bold(pad("HMR:"))}${this._devMode ? pc.cyan("Active") : pc.dim("Inactive")}`,
|
|
106
180
|
];
|
|
181
|
+
if (this._viewsDir) {
|
|
182
|
+
lines.push(`${pc.bold(pad("Views:"))}${this._viewsDir}`);
|
|
183
|
+
}
|
|
107
184
|
if (this._enableSwagger) {
|
|
108
185
|
lines.push(`${pc.bold(pad("Swagger:"))}http://localhost:${this._port}${this._swaggerPath}`);
|
|
109
186
|
}
|
|
@@ -124,6 +201,40 @@ export class Server {
|
|
|
124
201
|
}
|
|
125
202
|
global.servers.delete(this._id);
|
|
126
203
|
}
|
|
204
|
+
setUserHandler(handler) {
|
|
205
|
+
this._userHandler = handler;
|
|
206
|
+
}
|
|
207
|
+
setupAuthRoutes() {
|
|
208
|
+
if (!this._userHandler)
|
|
209
|
+
return;
|
|
210
|
+
this._app.post("/auth/signin", async (req, res, next) => {
|
|
211
|
+
try {
|
|
212
|
+
const result = await this._userHandler.signin(req, res);
|
|
213
|
+
res.json(result);
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
next(err);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
this._app.post("/auth/signup", async (req, res, next) => {
|
|
220
|
+
try {
|
|
221
|
+
const result = await this._userHandler.signup(req, res);
|
|
222
|
+
res.json(result);
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
next(err);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
this._app.post("/auth/logout", async (req, res, next) => {
|
|
229
|
+
try {
|
|
230
|
+
const result = await this._userHandler.logout(req, res);
|
|
231
|
+
res.json(result);
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
next(err);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
127
238
|
async loadControllers() {
|
|
128
239
|
const absoluteControllersPath = path.resolve(process.cwd(), this._controllersDir);
|
|
129
240
|
if (!fs.existsSync(absoluteControllersPath)) {
|
|
@@ -151,28 +262,18 @@ export class Server {
|
|
|
151
262
|
const instance = this._container?.get
|
|
152
263
|
? this._container.get(controller.target)
|
|
153
264
|
: new controller.target();
|
|
154
|
-
// Check class level auth
|
|
155
|
-
const classAuthInfo = Reflect.getMetadata(AUTH_METADATA_KEY, controller.target);
|
|
156
265
|
for (const route of controller.routes) {
|
|
157
266
|
const fullPath = (controller.path + "/" + route.path).replace(/\/+/g, "/");
|
|
158
267
|
const middlewares = [];
|
|
159
|
-
// Check
|
|
160
|
-
const
|
|
268
|
+
// Check ROLES metadata
|
|
269
|
+
const classRoles = Reflect.getMetadata(ROLES_METADATA_KEY, controller.target);
|
|
270
|
+
const methodRoles = Reflect.getMetadata(ROLES_METADATA_KEY, controller.target.prototype, route.handlerName);
|
|
271
|
+
const requiredRoles = methodRoles || classRoles;
|
|
161
272
|
const isPublic = Reflect.getMetadata(PUBLIC_METADATA_KEY, controller.target.prototype, route.handlerName);
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
else if (methodAuthInfo) {
|
|
168
|
-
authSecret = methodAuthInfo.secret || process.env.JWT_SECRET;
|
|
169
|
-
}
|
|
170
|
-
else if (classAuthInfo) {
|
|
171
|
-
authSecret = classAuthInfo.secret || process.env.JWT_SECRET;
|
|
172
|
-
}
|
|
173
|
-
// 1. Auth Middleware (New Decorator Logic)
|
|
174
|
-
if (authSecret) {
|
|
175
|
-
middlewares.push(this.createJwtMiddleware(authSecret));
|
|
273
|
+
// 1. Auth & Role Middlewares
|
|
274
|
+
if (!isPublic) {
|
|
275
|
+
// If not public, we always run the role check (which might include authentication via UserHandler)
|
|
276
|
+
middlewares.push(this.createRoleMiddleware(requiredRoles || []));
|
|
176
277
|
// Inject Swagger security definition automatically
|
|
177
278
|
if (!route.swagger) {
|
|
178
279
|
route.swagger = {
|
|
@@ -192,7 +293,8 @@ export class Server {
|
|
|
192
293
|
route.swagger.security.push({ bearerAuth: [] });
|
|
193
294
|
}
|
|
194
295
|
}
|
|
195
|
-
//
|
|
296
|
+
// 1.1 Role Middlewares - now handled above for non-public routes
|
|
297
|
+
// 2. Generic Security Middleware (@Security, @ApiKey, etc)
|
|
196
298
|
const classGenericSecurity = Reflect.getMetadata(SECURITY_METADATA_KEY, controller.target) || [];
|
|
197
299
|
const methodGenericSecurity = Reflect.getMetadata(SECURITY_METADATA_KEY, controller.target.prototype, route.handlerName) || [];
|
|
198
300
|
let genericRequirements = [];
|
|
@@ -274,9 +376,17 @@ export class Server {
|
|
|
274
376
|
middlewares.push(...route.middlewares);
|
|
275
377
|
}
|
|
276
378
|
// 3. Route Handler
|
|
277
|
-
const handler = (req, res, next) => {
|
|
379
|
+
const handler = async (req, res, next) => {
|
|
278
380
|
try {
|
|
279
|
-
const
|
|
381
|
+
const user = this._userHandler
|
|
382
|
+
? await this._userHandler.authenticate(req, res)
|
|
383
|
+
: req.user;
|
|
384
|
+
const result = instance[route.handlerName]({
|
|
385
|
+
req,
|
|
386
|
+
res,
|
|
387
|
+
user,
|
|
388
|
+
next,
|
|
389
|
+
});
|
|
280
390
|
const handleResult = (val) => {
|
|
281
391
|
if (val !== undefined && !res.headersSent) {
|
|
282
392
|
if (typeof val === "string") {
|
|
@@ -300,25 +410,58 @@ export class Server {
|
|
|
300
410
|
};
|
|
301
411
|
this._app[route.method](fullPath, ...middlewares, handler);
|
|
302
412
|
this._routeCount++;
|
|
413
|
+
// Pre-build if @Serve is used
|
|
414
|
+
const htmlPath = Reflect.getMetadata(SERVE_HTML_METADATA_KEY, controller.target.prototype, route.handlerName);
|
|
415
|
+
if (htmlPath) {
|
|
416
|
+
// Silent pre-build
|
|
417
|
+
await ServeMemoryStore.instance.buildAndCache(htmlPath, tailwindOptions);
|
|
418
|
+
}
|
|
303
419
|
}
|
|
304
420
|
}
|
|
305
421
|
}
|
|
306
|
-
|
|
307
|
-
return (req, res, next) => {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
422
|
+
createRoleMiddleware(roles) {
|
|
423
|
+
return async (req, res, next) => {
|
|
424
|
+
const user = this._userHandler
|
|
425
|
+
? await this._userHandler.authenticate(req, res)
|
|
426
|
+
: req.user;
|
|
427
|
+
if (!user) {
|
|
428
|
+
return res.status(401).json({ error: "Unauthorized: No user found" });
|
|
429
|
+
}
|
|
430
|
+
// If no specific roles required, but it's not public, just authentication is enough
|
|
431
|
+
if (roles.length === 0) {
|
|
432
|
+
return next();
|
|
433
|
+
}
|
|
434
|
+
if (!this._roleHandler) {
|
|
435
|
+
// Default role check behavior
|
|
436
|
+
const userRoles = Array.isArray(user.roles)
|
|
437
|
+
? user.roles
|
|
438
|
+
: user.role
|
|
439
|
+
? [user.role]
|
|
440
|
+
: [];
|
|
441
|
+
const hasRole = roles.some((role) => userRoles.includes(role));
|
|
442
|
+
if (!hasRole) {
|
|
443
|
+
return res.status(403).json({
|
|
444
|
+
error: "Forbidden: You do not have the required permissions",
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
return next();
|
|
313
448
|
}
|
|
314
|
-
const token = authHeader.split(" ")[1];
|
|
315
449
|
try {
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
450
|
+
const result = await this._roleHandler(req, roles);
|
|
451
|
+
if (result) {
|
|
452
|
+
// Assuming 'result' is a boolean indicating success
|
|
453
|
+
next();
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
// The provided snippet seems to be for serving HTML, which is out of context for a role middleware.
|
|
457
|
+
// Applying the original logic for role failure.
|
|
458
|
+
res.status(403).json({
|
|
459
|
+
error: "Forbidden: You do not have the required permissions",
|
|
460
|
+
});
|
|
461
|
+
}
|
|
319
462
|
}
|
|
320
463
|
catch (err) {
|
|
321
|
-
|
|
464
|
+
next(err);
|
|
322
465
|
}
|
|
323
466
|
};
|
|
324
467
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
export declare abstract class UserHandler {
|
|
3
|
+
/**
|
|
4
|
+
* Optional: Logic to authenticate the request and return the user object.
|
|
5
|
+
* If this is not implemented, 'user' will be undefined in route parameters.
|
|
6
|
+
*/
|
|
7
|
+
authenticate(req: Request, res: Response): Promise<any | null>;
|
|
8
|
+
/**
|
|
9
|
+
* Sign in logic. Usually returns a token or user info.
|
|
10
|
+
*/
|
|
11
|
+
abstract signin(req: Request, res: Response): Promise<any> | any;
|
|
12
|
+
/**
|
|
13
|
+
* Sign up logic.
|
|
14
|
+
*/
|
|
15
|
+
abstract signup(req: Request, res: Response): Promise<any> | any;
|
|
16
|
+
/**
|
|
17
|
+
* Logout logic.
|
|
18
|
+
*/
|
|
19
|
+
abstract logout(req: Request, res: Response): Promise<any> | any;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=UserHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserHandler.d.ts","sourceRoot":"","sources":["../../src/Components/UserHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAE1D,8BAAsB,WAAW;IAC/B;;;OAGG;IACU,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAI3E;;OAEG;aACa,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG;IAEvE;;OAEG;aACa,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG;IAEvE;;OAEG;aACa,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG;CACxE"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
export declare const AUTH_METADATA_KEY = "auth:info";
|
|
2
1
|
export declare const PUBLIC_METADATA_KEY = "auth:public";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
export declare function BearerAuth(secret?: string): MethodDecorator & ClassDecorator;
|
|
2
|
+
export declare const ROLES_METADATA_KEY = "auth:roles";
|
|
3
|
+
export declare function Authorized(roles?: string[]): MethodDecorator & ClassDecorator;
|
|
7
4
|
export declare function Public(): MethodDecorator;
|
|
8
5
|
//# sourceMappingURL=Authorized.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Authorized.d.ts","sourceRoot":"","sources":["../../src/Decorations/Authorized.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"Authorized.d.ts","sourceRoot":"","sources":["../../src/Decorations/Authorized.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAE/C,wBAAgB,UAAU,CACxB,KAAK,GAAE,MAAM,EAAO,GACnB,eAAe,GAAG,cAAc,CAclC;AAED,wBAAgB,MAAM,IAAI,eAAe,CAQxC"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// Key to store auth metadata
|
|
2
|
-
export const AUTH_METADATA_KEY = "auth:info";
|
|
3
2
|
export const PUBLIC_METADATA_KEY = "auth:public";
|
|
4
|
-
export
|
|
3
|
+
export const ROLES_METADATA_KEY = "auth:roles";
|
|
4
|
+
export function Authorized(roles = []) {
|
|
5
5
|
return function (target, propertyKey, descriptor) {
|
|
6
|
-
// If used on a method
|
|
7
6
|
if (propertyKey) {
|
|
8
|
-
Reflect.defineMetadata(
|
|
7
|
+
Reflect.defineMetadata(ROLES_METADATA_KEY, roles, target, propertyKey);
|
|
8
|
+
// Explicitly mark as not public to prevent bypass if @Public() is also used
|
|
9
|
+
Reflect.defineMetadata(PUBLIC_METADATA_KEY, false, target, propertyKey);
|
|
9
10
|
}
|
|
10
11
|
else {
|
|
11
|
-
|
|
12
|
-
Reflect.defineMetadata(AUTH_METADATA_KEY, { secret }, target);
|
|
12
|
+
Reflect.defineMetadata(ROLES_METADATA_KEY, roles, target);
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
15
|
}
|
|
@@ -4,6 +4,5 @@ export interface SecurityRequirement {
|
|
|
4
4
|
[name: string]: string[];
|
|
5
5
|
}
|
|
6
6
|
export declare function Security(name: string, scopes?: string[]): MethodDecorator & ClassDecorator;
|
|
7
|
-
export declare function OAuth(scopes?: string[]): MethodDecorator & ClassDecorator;
|
|
8
7
|
export declare function ApiKey(name: string): MethodDecorator & ClassDecorator;
|
|
9
8
|
//# sourceMappingURL=Security.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Security.d.ts","sourceRoot":"","sources":["../../src/Decorations/Security.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,eAAO,MAAM,qBAAqB,eAAqB,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,MAAM,EAAO,GACpB,eAAe,GAAG,cAAc,CAyBlC;AAED,wBAAgB,
|
|
1
|
+
{"version":3,"file":"Security.d.ts","sourceRoot":"","sources":["../../src/Decorations/Security.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,eAAO,MAAM,qBAAqB,eAAqB,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,MAAM,EAAO,GACpB,eAAe,GAAG,cAAc,CAyBlC;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,cAAc,CAErE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Serve.d.ts","sourceRoot":"","sources":["../../src/Decorations/Serve.ts"],"names":[],"mappings":"AAIA,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"Serve.d.ts","sourceRoot":"","sources":["../../src/Decorations/Serve.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAwEvD"}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { ServeMemoryStore } from "../Components/ServeMemoryStore";
|
|
2
2
|
import { TAILWIND_METADATA_KEY } from "./Tailwind";
|
|
3
|
+
export const SERVE_HTML_METADATA_KEY = "serve:html";
|
|
3
4
|
export function Serve(htmlPath) {
|
|
4
5
|
return function (target, propertyKey, descriptor) {
|
|
6
|
+
// Store the HTML path in metadata for pre-building
|
|
7
|
+
Reflect.defineMetadata(SERVE_HTML_METADATA_KEY, htmlPath, target, propertyKey);
|
|
5
8
|
const originalMethod = descriptor.value;
|
|
6
|
-
descriptor.value = async function (req, res, next) {
|
|
9
|
+
descriptor.value = async function ({ req, res, user, next, }) {
|
|
7
10
|
try {
|
|
8
11
|
// Execute the original method
|
|
9
|
-
const result = await originalMethod.apply(this, [
|
|
12
|
+
const result = await originalMethod.apply(this, [
|
|
13
|
+
{ req, res, user, next },
|
|
14
|
+
]);
|
|
10
15
|
// Check if response has been sent or if result is not undefined/void
|
|
11
16
|
if (res.headersSent || result !== undefined) {
|
|
12
17
|
return result;
|
|
@@ -16,14 +21,25 @@ export function Serve(htmlPath) {
|
|
|
16
21
|
// If no response, build and serve the HTML
|
|
17
22
|
const html = await ServeMemoryStore.instance.buildAndCache(htmlPath, tailwindOptions);
|
|
18
23
|
if (html) {
|
|
24
|
+
const devMode = process.env.NODE_ENV === "development";
|
|
25
|
+
if (devMode) {
|
|
26
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
|
|
27
|
+
res.setHeader("Pragma", "no-cache");
|
|
28
|
+
res.setHeader("Expires", "0");
|
|
29
|
+
}
|
|
19
30
|
res.type("html").send(html);
|
|
20
31
|
}
|
|
21
|
-
else {
|
|
32
|
+
else if (next) {
|
|
22
33
|
next(); // No HTML found?
|
|
23
34
|
}
|
|
24
35
|
}
|
|
25
36
|
catch (error) {
|
|
26
|
-
next
|
|
37
|
+
if (next) {
|
|
38
|
+
next(error);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
27
43
|
}
|
|
28
44
|
};
|
|
29
45
|
return descriptor;
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,WAAW,CAAC;AACnB,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,WAAW,CAAC;AACnB,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synchjs/ewb",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -26,26 +26,29 @@
|
|
|
26
26
|
"example": "bun run --watch example/index.ts"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@types/cors": "^2.8.19",
|
|
30
|
+
"@types/express": "^5.0.6",
|
|
31
|
+
"@types/express-serve-static-core": "^5.1.1",
|
|
32
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
33
|
+
"@types/socket.io": "^3.0.2",
|
|
34
|
+
"@types/swagger-jsdoc": "^6.0.4",
|
|
35
|
+
"@types/swagger-ui-express": "^4.1.8",
|
|
29
36
|
"ajv": "^8.18.0",
|
|
30
37
|
"ajv-formats": "^3.0.1",
|
|
31
38
|
"boxen": "^8.0.1",
|
|
32
39
|
"bun-plugin-tailwind": "^0.1.2",
|
|
40
|
+
"cheerio": "^1.2.0",
|
|
33
41
|
"cors": "^2.8.6",
|
|
34
42
|
"express": "^5.2.1",
|
|
35
43
|
"express-rate-limit": "^8.2.1",
|
|
36
44
|
"helmet": "^8.1.0",
|
|
37
45
|
"jsonwebtoken": "^9.0.3",
|
|
46
|
+
"openapi-types": "^12.1.3",
|
|
38
47
|
"picocolors": "^1.1.1",
|
|
39
48
|
"reflect-metadata": "^0.2.2",
|
|
49
|
+
"socket.io": "^4.8.3",
|
|
40
50
|
"swagger-jsdoc": "^6.2.8",
|
|
41
51
|
"swagger-ui-express": "^5.0.1",
|
|
42
|
-
"tailwindcss": "^4.1.18"
|
|
43
|
-
"openapi-types": "^12.1.3",
|
|
44
|
-
"@types/express": "^5.0.6",
|
|
45
|
-
"@types/cors": "^2.8.19",
|
|
46
|
-
"@types/jsonwebtoken": "^9.0.10",
|
|
47
|
-
"@types/swagger-jsdoc": "^6.0.4",
|
|
48
|
-
"@types/swagger-ui-express": "^4.1.8",
|
|
49
|
-
"@types/express-serve-static-core": "^5.1.1"
|
|
52
|
+
"tailwindcss": "^4.1.18"
|
|
50
53
|
}
|
|
51
54
|
}
|