Working Correctly but added logs
This commit is contained in:
parent
4121b99775
commit
8e6f6d32d4
|
|
@ -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
|
||||
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue