Working Correctly but added logs

This commit is contained in:
Chandresh Kerkar 2025-12-21 12:03:51 +05:30
parent 4121b99775
commit 8e6f6d32d4
6 changed files with 559 additions and 12 deletions

294
API_FLOW_DOCUMENTATION.md Normal file
View File

@ -0,0 +1,294 @@
# API Request Flow Documentation
## GET `/users/:userId` Request Flow
### Complete Request Journey
```
Mobile App (Android/iOS)
GET http://10.0.2.2:3200/users/:userId
Headers: Authorization: Bearer <JWT_TOKEN>
┌─────────────────────────────────────────────────────────────┐
│ Backend Server (Port 3200) │
│ │
│ 1. Express App Layer │
│ ├─ CORS middleware │
│ ├─ express.json() │
│ │ │
│ 2. Global Middleware Chain │
│ ├─ requestContext │
│ │ └─ Extracts: IP, User-Agent, Request-ID │
│ │ │
│ ├─ auditLoggerMiddleware │
│ │ └─ Attaches audit logger to req.auditLogger │
│ │ │
│ 3. Route Matching │
│ └─ /users → userRoutes │
│ │
│ 4. Route-Level Middleware (userRoutes.js) │
│ ├─ jwtAuthenticate │
│ │ ├─ Extracts token from Authorization header │
│ │ ├─ Calls Auth Service API: │
│ │ │ POST http://auth-service:3000/auth/validate-token
│ │ │ Body: { token: <JWT_TOKEN> } │
│ │ ├─ Auth Service validates: signature, expiry, etc. │
│ │ └─ Sets req.user = { userId, role, ... } │
│ │ │
│ ├─ rateLimiterRead │
│ │ └─ Checks rate limits (Redis-based) │
│ │ │
│ ├─ requireUserOrAdmin (coarseAuthorize) │
│ │ └─ Verifies role is USER or ADMIN │
│ │ │
│ └─ fineAuthorize (for GET /:id only) │
│ └─ Verifies user can read this specific user │
│ (user can read own profile OR admin can read any)│
│ │
│ 5. Route Handler │
│ └─ GET /:id handler │
│ ├─ Extracts userId from req.params.id │
│ ├─ Database Query (via queryHelper) │
│ │ SELECT * FROM users WHERE id = :userId │
│ └─ Returns user data │
└─────────────────────────────────────────────────────────────┘
Response: { id, name, phone_number, avatar_url, ... }
Mobile App
```
### Services & Dependencies
1. **Backend Server** (Port 3200)
- Main API server
- Handles business logic
2. **Auth Service** (Port 3000)
- JWT token validation endpoint: `/auth/validate-token`
- Called by `jwtAuthenticate` middleware
- Returns user payload if token is valid
3. **Database** (PostgreSQL)
- Stores user data
- Queried via queryHelper
4. **Redis** (Optional)
- Used for rate limiting
- Falls back gracefully if unavailable
---
## Implementation Pattern for All APIs
### Standard Middleware Chain Pattern
All API routes should follow this consistent pattern:
```javascript
// routes/exampleRoutes.js
import express from "express";
import jwtAuthenticate from "../middleware/jwtAuthenticate.js";
import { rateLimiterRead, rateLimiterWrite } from "../middleware/rateLimiter.js";
import createCoarseAuthorize from "../middleware/coarseAuthorize.js";
import { fineAuthorize } from "../middleware/fineAuthorize.js";
const router = express.Router();
// 1. Apply authentication to ALL routes
router.use(jwtAuthenticate);
// 2. Apply rate limiting (read for GET, write for POST/PUT/DELETE)
router.use(rateLimiterRead); // Default: read rate limiter
// 3. Apply coarse-grained authorization (role-based)
const requireUserOrAdmin = createCoarseAuthorize(['USER', 'ADMIN']);
router.use(requireUserOrAdmin);
// 4. Define routes with fine-grained authorization where needed
router.get("/", async (req, res) => {
// No fine authorization - all authenticated users can list
});
router.get("/:id",
fineAuthorize({
action: 'read',
resource: 'resource_type', // e.g., 'user', 'order', 'listing'
getResourceOwnerId: (req) => req.params.id,
}),
async (req, res) => {
// Handler logic
}
);
router.post("/",
rateLimiterWrite, // Override default rate limiter for write operations
async (req, res) => {
// Handler logic
}
);
router.put("/:id",
rateLimiterWrite,
fineAuthorize({
action: 'write',
resource: 'resource_type',
getResourceOwnerId: (req) => req.params.id,
}),
async (req, res) => {
// Handler logic
}
);
```
### Middleware Execution Order
```
Request
[Global Middleware - server.js]
1. requestContext
2. auditLoggerMiddleware
3. CORS
4. express.json()
[Route Matching]
[Route-Level Middleware - routes/*.js]
1. jwtAuthenticate → Validates JWT, sets req.user
2. rateLimiterRead/Write → Rate limiting
3. coarseAuthorize → Role-based access (USER/ADMIN)
4. fineAuthorize (optional) → Resource-level permissions
[Route Handler]
Response
```
### Quick Reference: Route Setup Checklist
For each new API route file:
- [ ] Import required middleware
- [ ] Apply `jwtAuthenticate` to all routes (router.use)
- [ ] Apply appropriate rate limiter (router.use or per-route)
- [ ] Apply coarse authorization (router.use)
- [ ] Add fine authorization for resource-specific routes
- [ ] Add audit logging in handlers (req.auditLogger)
- [ ] Handle errors appropriately
### Example: New Resource Route
```javascript
// routes/productsRoutes.js
import express from "express";
import jwtAuthenticate from "../middleware/jwtAuthenticate.js";
import { rateLimiterRead, rateLimiterWrite } from "../middleware/rateLimiter.js";
import createCoarseAuthorize from "../middleware/coarseAuthorize.js";
import { fineAuthorize } from "../middleware/fineAuthorize.js";
import { select, insert, update } from "../db/queryHelper/index.js";
const router = express.Router();
// Authentication & Authorization
router.use(jwtAuthenticate);
router.use(rateLimiterRead);
router.use(createCoarseAuthorize(['USER', 'ADMIN']));
// GET /products - List all (public listings, no fine auth needed)
router.get("/", async (req, res) => {
try {
const products = await select({ table: 'products', where: { deleted: false } });
res.json(products);
} catch (err) {
res.status(500).json({ error: "Internal server error" });
}
});
// GET /products/:id - Get specific product
router.get("/:id",
fineAuthorize({
action: 'read',
resource: 'product',
getResourceOwnerId: (req) => req.params.id,
}),
async (req, res) => {
try {
const product = await select({
table: 'products',
where: { id: req.params.id, deleted: false },
limit: 1,
});
if (product.length === 0) return res.status(404).json({ error: "Not found" });
res.json(product[0]);
} catch (err) {
res.status(500).json({ error: "Internal server error" });
}
}
);
// POST /products - Create product (requires write rate limiter)
router.post("/",
rateLimiterWrite,
async (req, res) => {
try {
const product = await insert({
table: 'products',
data: req.body,
returning: '*',
});
res.status(201).json(product);
} catch (err) {
res.status(500).json({ error: "Internal server error" });
}
}
);
// PUT /products/:id - Update product
router.put("/:id",
rateLimiterWrite,
fineAuthorize({
action: 'write',
resource: 'product',
getResourceOwnerId: (req) => req.params.id,
}),
async (req, res) => {
try {
const updated = await update({
table: 'products',
data: req.body,
where: { id: req.params.id, deleted: false },
returning: '*',
});
if (updated.length === 0) return res.status(404).json({ error: "Not found" });
res.json(updated[0]);
} catch (err) {
res.status(500).json({ error: "Internal server error" });
}
}
);
export default router;
```
---
## Summary
**All API requests follow this pattern:**
1. Mobile App sends request with JWT token
2. Backend receives request → Global middleware → Route middleware → Handler
3. JWT validated via Auth Service
4. Rate limiting applied
5. Authorization checks (coarse + fine)
6. Business logic executes
7. Response returned to app
**Key Principles:**
- JWT authentication is mandatory for all protected routes
- Rate limiting prevents abuse
- Coarse authorization checks roles (USER/ADMIN)
- Fine authorization checks resource-level permissions
- Audit logging tracks all operations

View File

@ -97,22 +97,39 @@ function normalizeRole(role) {
function createCoarseAuthorize(allowedRoles = null) {
return function coarseAuthorize(req, res, next) {
try {
console.log(`[Coarse Auth] Starting authorization check for ${req.method} ${req.path}`);
// User should be set by jwtAuthenticate middleware
if (!req.user || !req.user.userId) {
console.log(`[Coarse Auth] ❌ FAILED: No user found in request. req.user:`, req.user);
return res.status(401).json({
error: 'Unauthorized',
message: 'Authentication required',
});
}
console.log(`[Coarse Auth] User found:`, {
userId: req.user.userId,
role: req.user.role,
userType: req.user.userType,
});
const userRole = normalizeRole(req.user.role);
// Determine required roles for this route
const requiredRoles = allowedRoles || getRequiredRoles(req.path);
const normalizedRequiredRoles = requiredRoles.map(normalizeRole);
console.log(`[Coarse Auth] Role check:`, {
userRole,
requiredRoles: normalizedRequiredRoles,
routePath: req.path,
});
// Check if user's role is in the allowed roles list
if (!normalizedRequiredRoles.includes(userRole)) {
console.log(`[Coarse Auth] ❌ FAILED: User role "${userRole}" not in required roles:`, normalizedRequiredRoles);
// Log authorization failure
if (req.auditLogger) {
req.auditLogger.log({
@ -133,9 +150,10 @@ function createCoarseAuthorize(allowedRoles = null) {
});
}
console.log(`[Coarse Auth] ✅ Authorization passed: User role "${userRole}" is allowed`);
next();
} catch (err) {
console.error('Authorization error:', err);
console.error('[Coarse Auth] ❌ Authorization error:', err);
return res.status(500).json({
error: 'Internal server error',
message: 'Authorization check failed',

View File

@ -154,9 +154,21 @@ function authorizeLocationAction({ user, action, resourceOwnerId }) {
export function fineAuthorize({ action, resource, getResourceOwnerId, getResourceData }) {
return async function(req, res, next) {
try {
console.log(`[Fine Auth] Starting fine-grained authorization for ${req.method} ${req.path}`);
console.log(`[Fine Auth] Action: ${action}, Resource: ${resource}`);
console.log(`[Fine Auth] User:`, req.user ? {
userId: req.user.userId,
role: req.user.role,
} : 'NOT SET');
const resourceOwnerId = getResourceOwnerId ? await getResourceOwnerId(req) : null;
const resourceData = getResourceData ? await getResourceData(req) : {};
console.log(`[Fine Auth] Resource details:`, {
resourceOwnerId,
resourceData: Object.keys(resourceData),
});
const result = authorizeAction({
user: req.user,
action,
@ -165,11 +177,18 @@ export function fineAuthorize({ action, resource, getResourceOwnerId, getResourc
resourceData,
});
console.log(`[Fine Auth] Authorization result:`, {
authorized: result.authorized,
reason: result.reason || 'N/A',
});
if (!result.authorized) {
console.log(`[Fine Auth] ❌ FAILED: ${result.reason || 'Access denied'}`);
// Log authorization failure
if (req.auditLogger) {
req.auditLogger.log({
userId: req.user.userId,
userId: req.user?.userId,
action: 'fine_authorization_failed',
route: req.path,
status: 'forbidden',
@ -188,9 +207,10 @@ export function fineAuthorize({ action, resource, getResourceOwnerId, getResourc
});
}
console.log(`[Fine Auth] ✅ Authorization passed`);
next();
} catch (err) {
console.error('Fine authorization error:', err);
console.error('[Fine Auth] ❌ Fine authorization error:', err);
return res.status(500).json({
error: 'Internal server error',
message: 'Authorization check failed',

View File

@ -23,6 +23,7 @@ const AUTH_SERVICE_TIMEOUT = parseInt(process.env.AUTH_SERVICE_TIMEOUT || '5000'
*/
async function validateTokenViaAuthService(token) {
try {
console.log(`[JWT Auth] Calling auth service to validate token...`);
const response = await axios.post(
`${AUTH_SERVICE_URL}/auth/validate-token`,
{ token },
@ -34,12 +35,16 @@ async function validateTokenViaAuthService(token) {
}
);
console.log(`[JWT Auth] Auth service responded with status: ${response.status}`);
if (response.data.valid === true) {
console.log(`[JWT Auth] Auth service confirmed token is valid`);
return {
valid: true,
payload: response.data.payload,
};
} else {
console.log(`[JWT Auth] Auth service reported token as invalid:`, response.data.error);
return {
valid: false,
error: response.data.error || 'Token validation failed'
@ -49,21 +54,21 @@ async function validateTokenViaAuthService(token) {
// Handle different error types
if (err.response) {
// Auth service returned an error response
console.error('Auth service validation error:', err.response.status, err.response.data);
console.error(`[JWT Auth] ❌ Auth service validation error:`, err.response.status, err.response.data);
return {
valid: false,
error: err.response.data?.error || 'Token validation failed'
};
} else if (err.request) {
// Request was made but no response received
console.error('Auth service unavailable:', err.message);
console.error(`[JWT Auth] ❌ Auth service unavailable:`, err.message);
return {
valid: false,
error: 'Authentication service unavailable'
};
} else {
// Error setting up request
console.error('Error calling auth service:', err.message);
console.error(`[JWT Auth] ❌ Error calling auth service:`, err.message);
return {
valid: false,
error: 'Failed to validate token'
@ -78,21 +83,34 @@ async function validateTokenViaAuthService(token) {
* Calls auth service to validate token and authorize the request
*/
async function jwtAuthenticate(req, res, next) {
console.log(`[JWT Auth] Starting authentication check for ${req.method} ${req.path}`);
console.log(`[JWT Auth] Headers received:`, {
authorization: req.headers.authorization ? 'Bearer <token>' : 'MISSING',
'content-type': req.headers['content-type'],
'user-agent': req.headers['user-agent']?.substring(0, 50),
});
// Extract token from Authorization header
const authHeader = req.headers.authorization || '';
const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null;
if (!token) {
console.log(`[JWT Auth] ❌ FAILED: No token found in Authorization header`);
console.log(`[JWT Auth] Auth header value: "${authHeader}"`);
return res.status(401).json({
error: 'Unauthorized',
message: 'Missing Authorization header. Expected format: Authorization: Bearer <token>'
});
}
console.log(`[JWT Auth] Token found (length: ${token.length}), validating via auth service...`);
console.log(`[JWT Auth] Auth service URL: ${AUTH_SERVICE_URL}/auth/validate-token`);
// Validate token via auth service API
const validationResult = await validateTokenViaAuthService(token);
if (!validationResult.valid) {
console.log(`[JWT Auth] ❌ FAILED: Token validation failed - ${validationResult.error}`);
// Log failed authentication attempt
if (req.auditLogger) {
req.auditLogger.logFailure('authenticate', validationResult.error || 'Token validation failed');
@ -106,6 +124,14 @@ async function jwtAuthenticate(req, res, next) {
}
const payload = validationResult.payload;
console.log(`[JWT Auth] ✅ Token validated successfully`);
console.log(`[JWT Auth] Token payload:`, {
userId: payload.sub,
role: payload.role,
userType: payload.user_type,
phoneNumber: payload.phone_number ? '***' : null,
tokenVersion: payload.token_version,
});
// Extract user information from auth service response
req.user = {
@ -119,6 +145,12 @@ async function jwtAuthenticate(req, res, next) {
tenantId: payload.tenant_id || null,
};
console.log(`[JWT Auth] ✅ User authenticated:`, {
userId: req.user.userId,
role: req.user.role,
userType: req.user.userType,
});
// Log successful authentication
if (req.auditLogger) {
req.auditLogger.logSuccess('authenticate', { userId: req.user.userId });

177
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"axios": "^1.7.9",
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"express": "^5.1.0",
@ -16,6 +17,7 @@
"knex": "^3.1.0",
"node-cron": "^4.2.1",
"pg": "^8.16.3",
"redis": "^4.7.0",
"socket.io": "^4.8.1"
}
},
@ -330,6 +332,65 @@
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
"optional": true
},
"node_modules/@redis/bloom": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/client": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
"license": "MIT",
"dependencies": {
"cluster-key-slot": "1.1.2",
"generic-pool": "3.9.0",
"yallist": "4.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/@redis/graph": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/json": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
"integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/search": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz",
"integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/time-series": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz",
"integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
@ -567,8 +628,55 @@
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"optional": true
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/axios/node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/axios/node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
@ -682,6 +790,15 @@
"node": ">=12"
}
},
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -710,7 +827,6 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"optional": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
@ -795,7 +911,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"optional": true,
"engines": {
"node": ">=0.4.0"
}
@ -993,7 +1108,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"optional": true,
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
@ -1185,6 +1299,26 @@
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz",
@ -1293,6 +1427,15 @@
"node": ">=14"
}
},
"node_modules/generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -1450,7 +1593,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"optional": true,
"dependencies": {
"has-symbols": "^1.0.3"
},
@ -2221,6 +2363,12 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
@ -2283,6 +2431,23 @@
"node": ">= 10.13.0"
}
},
"node_modules/redis": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz",
"integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==",
"license": "MIT",
"workspaces": [
"./packages/*"
],
"dependencies": {
"@redis/bloom": "1.2.0",
"@redis/client": "1.6.1",
"@redis/graph": "1.1.1",
"@redis/json": "1.0.7",
"@redis/search": "1.2.0",
"@redis/time-series": "1.1.0"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",

View File

@ -17,6 +17,17 @@ router.use(requireUserOrAdmin);
// 1. CREATE User (Requires write rate limiter)
router.post("/", rateLimiterWrite, async (req, res) => {
console.log(`[User Route] POST /users - Request received`);
console.log(`[User Route] Authenticated user:`, req.user ? {
userId: req.user.userId,
role: req.user.role,
} : 'NOT AUTHENTICATED');
console.log(`[User Route] Request body:`, {
id: req.body?.id,
name: req.body?.name,
phone_number: req.body?.phone_number ? '***' : undefined,
});
try {
// Parse and extract user data from request body
const {
@ -128,6 +139,13 @@ router.get("/:id",
getResourceOwnerId: (req) => req.params.id,
}),
async (req, res) => {
console.log(`[User Route] GET /users/:id - Request received`);
console.log(`[User Route] User ID requested: ${req.params.id}`);
console.log(`[User Route] Authenticated user:`, req.user ? {
userId: req.user.userId,
role: req.user.role,
} : 'NOT AUTHENTICATED');
try {
const { id } = req.params;