nexusbert commited on
Commit
aeffa8d
Β·
1 Parent(s): 497daee

commit all

Browse files
src/app.ts CHANGED
@@ -19,7 +19,9 @@ import adminRoutes from './routes/adminRoutes';
19
 
20
  const app: Application = express();
21
 
22
- app.set('trust proxy', true);
 
 
23
 
24
  app.use(helmet({
25
  contentSecurityPolicy: {
 
19
 
20
  const app: Application = express();
21
 
22
+ // Configure trust proxy securely for Hugging Face Spaces
23
+ // Only trust the first proxy (Hugging Face Spaces proxy)
24
+ app.set('trust proxy', 1);
25
 
26
  app.use(helmet({
27
  contentSecurityPolicy: {
src/middlewares/security.ts CHANGED
@@ -51,6 +51,17 @@ export const createStrictRateLimiter = (windowMs: number, max: number) => {
51
  message: 'Too many requests from this IP, please try again later.',
52
  standardHeaders: true,
53
  legacyHeaders: false,
 
 
 
 
 
 
 
 
 
 
 
54
  skip: (req) => {
55
  return process.env.NODE_ENV === 'development' && req.ip === '::1';
56
  },
 
51
  message: 'Too many requests from this IP, please try again later.',
52
  standardHeaders: true,
53
  legacyHeaders: false,
54
+ // Use a custom key generator that respects trust proxy setting
55
+ keyGenerator: (req) => {
56
+ // Trust proxy is set to 1, so we trust the first proxy
57
+ // X-Forwarded-For header format: "client, proxy1, proxy2"
58
+ const forwarded = req.headers['x-forwarded-for'];
59
+ if (forwarded && typeof forwarded === 'string') {
60
+ const ips = forwarded.split(',').map(ip => ip.trim());
61
+ return ips[0] || req.ip || 'unknown';
62
+ }
63
+ return req.ip || 'unknown';
64
+ },
65
  skip: (req) => {
66
  return process.env.NODE_ENV === 'development' && req.ip === '::1';
67
  },
src/routes/agentRoutes.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Router, Request } from 'express';
2
  import multer, { FileFilterCallback } from 'multer';
3
  import { AgentController } from '../controllers/agentController';
4
  import { authenticate, optionalAuth, requireAdmin } from '../middlewares/auth';
@@ -104,6 +104,73 @@ const avatarUpload = multer({
104
  // Public routes
105
  router.get('/', optionalAuth, agentController.listAgents.bind(agentController));
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  /**
108
  * @swagger
109
  * /agents/{id}:
@@ -126,10 +193,22 @@ router.get('/', optionalAuth, agentController.listAgents.bind(agentController));
126
  * application/json:
127
  * schema:
128
  * $ref: '#/components/schemas/Agent'
 
 
129
  * 404:
130
  * description: Agent not found
131
  */
132
- router.get('/:id', optionalAuth, agentController.getAgent.bind(agentController));
 
 
 
 
 
 
 
 
 
 
133
 
134
  /**
135
  * @swagger
@@ -270,6 +349,7 @@ router.post(
270
  router.put(
271
  '/:id',
272
  authenticate,
 
273
  avatarUpload.single('avatar'), // Accept avatar as single file
274
  agentController.updateAgent.bind(agentController)
275
  );
@@ -308,36 +388,7 @@ router.put(
308
  * 404:
309
  * description: Agent not found
310
  */
311
- router.delete('/:id', authenticate, agentController.deleteAgent.bind(agentController));
312
-
313
- /**
314
- * @swagger
315
- * /agents/my/list:
316
- * get:
317
- * summary: Get my agents (creator's agents)
318
- * tags: [Agents]
319
- * security:
320
- * - bearerAuth: []
321
- * parameters:
322
- * - in: query
323
- * name: status
324
- * schema:
325
- * type: string
326
- * enum: [pending, approved, rejected]
327
- * description: Filter by status
328
- * responses:
329
- * 200:
330
- * description: List of user's agents
331
- * content:
332
- * application/json:
333
- * schema:
334
- * type: array
335
- * items:
336
- * $ref: '#/components/schemas/Agent'
337
- * 401:
338
- * description: Unauthorized
339
- */
340
- router.get('/my/list', authenticate, agentController.getMyAgents.bind(agentController));
341
 
342
  /**
343
  * @swagger
@@ -373,6 +424,7 @@ router.get('/my/list', authenticate, agentController.getMyAgents.bind(agentContr
373
  router.patch(
374
  '/:id/delist',
375
  authenticate,
 
376
  async (req, res) => {
377
  try {
378
  const { id } = req.params;
@@ -436,6 +488,7 @@ router.patch(
436
  router.patch(
437
  '/:id/relist',
438
  authenticate,
 
439
  async (req, res) => {
440
  try {
441
  const { id } = req.params;
@@ -503,6 +556,7 @@ router.patch(
503
  '/:id/approve',
504
  authenticate,
505
  requireAdmin,
 
506
  async (req, res) => {
507
  try {
508
  const { id } = req.params;
@@ -561,6 +615,7 @@ router.patch(
561
  '/:id/reject',
562
  authenticate,
563
  requireAdmin,
 
564
  async (req, res) => {
565
  try {
566
  const { id } = req.params;
@@ -584,46 +639,4 @@ router.patch(
584
  }
585
  );
586
 
587
- /**
588
- * @swagger
589
- * /agents/admin/pending:
590
- * get:
591
- * summary: Get pending agents (admin only)
592
- * tags: [Agents]
593
- * security:
594
- * - bearerAuth: []
595
- * responses:
596
- * 200:
597
- * description: List of pending agents
598
- * content:
599
- * application/json:
600
- * schema:
601
- * type: array
602
- * items:
603
- * $ref: '#/components/schemas/Agent'
604
- * 401:
605
- * description: Unauthorized
606
- * 403:
607
- * description: Admin access required
608
- */
609
- router.get(
610
- '/admin/pending',
611
- authenticate,
612
- requireAdmin,
613
- async (req, res) => {
614
- try {
615
- const agentRepository = AppDataSource.getRepository(Agent);
616
- const agents = await agentRepository.find({
617
- where: { status: AgentStatus.PENDING },
618
- order: { createdAt: 'ASC' },
619
- });
620
-
621
- res.json(agents);
622
- } catch (error) {
623
- console.error('Get pending agents error:', error);
624
- res.status(500).json({ error: 'Failed to get pending agents' });
625
- }
626
- }
627
- );
628
-
629
  export default router;
 
1
+ import { Router, Request, Response, NextFunction } from 'express';
2
  import multer, { FileFilterCallback } from 'multer';
3
  import { AgentController } from '../controllers/agentController';
4
  import { authenticate, optionalAuth, requireAdmin } from '../middlewares/auth';
 
104
  // Public routes
105
  router.get('/', optionalAuth, agentController.listAgents.bind(agentController));
106
 
107
+ // Specific routes must come BEFORE /:id to avoid route conflicts
108
+ // These routes are matched first to prevent conflicts with /:id
109
+
110
+ /**
111
+ * @swagger
112
+ * /agents/my/list:
113
+ * get:
114
+ * summary: Get my agents (creator)
115
+ * tags: [Agents]
116
+ * security:
117
+ * - bearerAuth: []
118
+ * responses:
119
+ * 200:
120
+ * description: List of user's agents
121
+ * content:
122
+ * application/json:
123
+ * schema:
124
+ * type: array
125
+ * items:
126
+ * $ref: '#/components/schemas/Agent'
127
+ * 401:
128
+ * description: Unauthorized
129
+ */
130
+ router.get('/my/list', authenticate, agentController.getMyAgents.bind(agentController));
131
+
132
+ /**
133
+ * @swagger
134
+ * /agents/admin/pending:
135
+ * get:
136
+ * summary: Get pending agents (admin only)
137
+ * tags: [Agents]
138
+ * security:
139
+ * - bearerAuth: []
140
+ * responses:
141
+ * 200:
142
+ * description: List of pending agents
143
+ * content:
144
+ * application/json:
145
+ * schema:
146
+ * type: array
147
+ * items:
148
+ * $ref: '#/components/schemas/Agent'
149
+ * 401:
150
+ * description: Unauthorized
151
+ * 403:
152
+ * description: Admin access required
153
+ */
154
+ router.get(
155
+ '/admin/pending',
156
+ authenticate,
157
+ requireAdmin,
158
+ async (req, res) => {
159
+ try {
160
+ const agentRepository = AppDataSource.getRepository(Agent);
161
+ const agents = await agentRepository.find({
162
+ where: { status: AgentStatus.PENDING },
163
+ order: { createdAt: 'ASC' },
164
+ });
165
+
166
+ res.json(agents);
167
+ } catch (error) {
168
+ console.error('Get pending agents error:', error);
169
+ res.status(500).json({ error: 'Failed to get pending agents' });
170
+ }
171
+ }
172
+ );
173
+
174
  /**
175
  * @swagger
176
  * /agents/{id}:
 
193
  * application/json:
194
  * schema:
195
  * $ref: '#/components/schemas/Agent'
196
+ * 400:
197
+ * description: Invalid agent ID format
198
  * 404:
199
  * description: Agent not found
200
  */
201
+ // UUID validation middleware
202
+ const validateUUID = (req: Request, res: Response, next: NextFunction) => {
203
+ const { id } = req.params;
204
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
205
+ if (!uuidRegex.test(id)) {
206
+ return res.status(400).json({ error: 'Invalid agent ID format. Must be a valid UUID.' });
207
+ }
208
+ next();
209
+ };
210
+
211
+ router.get('/:id', optionalAuth, validateUUID, agentController.getAgent.bind(agentController));
212
 
213
  /**
214
  * @swagger
 
349
  router.put(
350
  '/:id',
351
  authenticate,
352
+ validateUUID,
353
  avatarUpload.single('avatar'), // Accept avatar as single file
354
  agentController.updateAgent.bind(agentController)
355
  );
 
388
  * 404:
389
  * description: Agent not found
390
  */
391
+ router.delete('/:id', authenticate, validateUUID, agentController.deleteAgent.bind(agentController));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
 
393
  /**
394
  * @swagger
 
424
  router.patch(
425
  '/:id/delist',
426
  authenticate,
427
+ validateUUID,
428
  async (req, res) => {
429
  try {
430
  const { id } = req.params;
 
488
  router.patch(
489
  '/:id/relist',
490
  authenticate,
491
+ validateUUID,
492
  async (req, res) => {
493
  try {
494
  const { id } = req.params;
 
556
  '/:id/approve',
557
  authenticate,
558
  requireAdmin,
559
+ validateUUID,
560
  async (req, res) => {
561
  try {
562
  const { id } = req.params;
 
615
  '/:id/reject',
616
  authenticate,
617
  requireAdmin,
618
+ validateUUID,
619
  async (req, res) => {
620
  try {
621
  const { id } = req.params;
 
639
  }
640
  );
641
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
642
  export default router;
zurri-mock-frontend ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit 4f5001f220128730d26e39e3faccf5f32dd8b734