Eneza API Middleware
The API uses 14 middleware functions for authentication, authorization, rate limiting, and request processing.
Authentication Middleware
JWT Token Verification (utils/jwtUtils.ts)
Verifies JWT tokens for authenticated endpoints.
typescript
// Token verification middleware
export function verifyToken(req: CustomRequest, res: Response, next: NextFunction) {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ message: 'No token provided' });
}
try {
const decoded = jwt.verify(token, JWT_SECRET) as JwtPayload;
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ message: 'Invalid or expired token' });
}
}
// Admin check middleware
export function isAdmin(req: CustomRequest, res: Response, next: NextFunction) {
if (!req.user?.isAdmin) {
return res.status(403).json({ message: 'Admin access required' });
}
next();
}
// Extended request type
interface CustomRequest extends Request {
user?: {
_id: string;
phoneNumber?: string;
email?: string;
isAdmin?: boolean;
role?: string;
permissions?: string[];
};
}Admin Authentication (adminAuthMiddleware.ts)
Specialized middleware for admin routes.
typescript
export const adminAuthMiddleware = async (
req: Request,
res: Response,
next: NextFunction
) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Authentication required' });
}
try {
const decoded = jwt.verify(token, JWT_SECRET) as AdminJwtPayload;
// Verify admin still exists and is active
const admin = await prisma.adminUser.findUnique({
where: { id: decoded.adminId }
});
if (!admin || admin.status !== 'active') {
return res.status(401).json({ error: 'Admin account not active' });
}
// Attach admin to request
req.admin = {
id: admin.id,
email: admin.email,
role: admin.role,
permissions: admin.permissions
};
// Update last login
await prisma.adminUser.update({
where: { id: admin.id },
data: { lastLoginAt: new Date() }
});
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
};Permission Checking (adminMiddleware.ts)
Role-based access control for admin routes.
typescript
// Check if admin has specific permission
export const requirePermission = (permission: string) => {
return (req: Request, res: Response, next: NextFunction) => {
const admin = req.admin;
if (!admin) {
return res.status(401).json({ error: 'Authentication required' });
}
// Super admin has all permissions
if (admin.role === 'super_admin') {
return next();
}
// Check specific permission
if (!admin.permissions.includes(permission)) {
return res.status(403).json({
error: 'Permission denied',
required: permission
});
}
next();
};
};
// Available permissions
const PERMISSIONS = {
// Users
'users:read': 'View user details',
'users:edit': 'Edit user profiles',
'users:ban': 'Ban/unban users',
// Ads
'ads:read': 'View ad details',
'ads:moderate': 'Approve/reject ads',
'ads:edit': 'Edit ad details',
'ads:delete': 'Delete ads',
// Screenshots
'screenshots:review': 'Review screenshot queue',
'screenshots:approve': 'Approve screenshots',
'screenshots:reject': 'Reject screenshots',
// Finance
'finance:read': 'View financial data',
'finance:process': 'Process withdrawals',
// Settings
'settings:read': 'View settings',
'settings:edit': 'Edit settings',
// System
'system:read': 'View system health',
'system:admin': 'Manage admin users'
};Geo Extraction Middleware
Basic Geo Extraction (geoExtractMiddleware.ts)
Extracts location from Cloudflare headers.
typescript
export function extractGeoData(req: GeoRequest, res: Response, next: NextFunction) {
// Extract IP address (Cloudflare → standard headers → fallback)
const ipAddress =
(req.headers['cf-connecting-ip'] as string) ||
(req.headers['x-forwarded-for'] as string)?.split(',')[0]?.trim() ||
(req.headers['x-real-ip'] as string) ||
req.ip ||
'unknown';
// Extract country code (Cloudflare header)
const country = ((req.headers['cf-ipcountry'] as string) || 'XX').toUpperCase();
// Extract optional geo data
const city = req.headers['cf-ipcity'] as string;
const asn = parseInt(req.headers['cf-ipasn'] as string, 10);
const asOrganization = req.headers['cf-ipasorganization'] as string;
// Attach to request
req.geo = {
ipAddress,
country,
city,
asn: isNaN(asn) ? undefined : asn,
asOrganization
};
next();
}Enhanced Geo Extraction
Uses external IP geolocation API when Cloudflare returns "XX".
typescript
export async function extractGeoDataEnhanced(
req: GeoRequest,
res: Response,
next: NextFunction
) {
// First, extract basic geo data
extractGeoData(req, res, () => {});
// If country is unknown, use external service
if (req.geo?.country === 'XX' && req.geo.ipAddress !== 'unknown') {
try {
const geoData = await ipGeolocationService.lookup(req.geo.ipAddress);
if (geoData) {
req.geo = {
...req.geo,
country: geoData.countryCode || 'XX',
city: geoData.city,
region: geoData.region
};
}
} catch (error) {
logger.warn('Enhanced geo lookup failed:', error);
}
}
next();
}Security Middleware
API Key Middleware (apiKeyMiddleware.ts)
Validates API keys for public endpoints.
typescript
export const verifyApiKey = (req: Request, res: Response, next: NextFunction) => {
const apiKey = req.header('X-API-Key');
if (!apiKey) {
return res.status(401).json({ error: 'API key required' });
}
// Verify against stored key
const validKey = process.env.DASHBOARD_API_KEY;
if (apiKey !== validKey) {
return res.status(403).json({ error: 'Invalid API key' });
}
next();
};Job Authentication (jobAuthMiddleware.ts)
Authenticates Cloud Scheduler jobs.
typescript
export const verifyJobSecret = (req: Request, res: Response, next: NextFunction) => {
const jobSecret = req.header('X-Job-Secret');
if (!jobSecret || jobSecret !== process.env.JOB_SECRET) {
logger.warn('Unauthorized job request attempt');
return res.status(403).json({ error: 'Unauthorized' });
}
next();
};Pub/Sub Authentication (pubsubAuth.ts)
Verifies Pub/Sub push subscription messages.
typescript
export const verifyPubSubToken = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const token = req.query.token as string;
if (!token) {
return res.status(403).json({ error: 'No token provided' });
}
// Verify token matches configured secret
if (token !== process.env.PUBSUB_VERIFICATION_TOKEN) {
return res.status(403).json({ error: 'Invalid token' });
}
// Parse Pub/Sub message
const message = req.body.message;
if (!message || !message.data) {
return res.status(400).json({ error: 'Invalid Pub/Sub message' });
}
// Decode base64 data
const data = JSON.parse(Buffer.from(message.data, 'base64').toString());
req.pubsubData = data;
next();
} catch (error) {
logger.error('Pub/Sub auth error:', error);
return res.status(400).json({ error: 'Invalid message format' });
}
};Kill Switch (killSwitchMiddleware.ts)
Emergency shutdown capability.
typescript
export const verifyKillSwitch = (req: Request, res: Response, next: NextFunction) => {
const killCode = req.body.killCode;
if (!killCode || killCode !== process.env.KILL_SWITCH_CODE) {
return res.status(403).json({ error: 'Invalid kill code' });
}
next();
};Rate Limiting
Rate Limit Middleware (rateLimitMiddleware.ts)
Protects endpoints from abuse.
typescript
import rateLimit from 'express-rate-limit';
// OTP request limiter
export const otpRateLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 3, // 3 requests per minute
message: { error: 'Too many OTP requests, please try again later' },
keyGenerator: (req) => req.body.phoneNumber || req.ip,
skipSuccessfulRequests: false
});
// General API limiter
export const apiRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per 15 minutes
message: { error: 'Too many requests, please try again later' },
standardHeaders: true,
legacyHeaders: false
});
// Screenshot upload limiter
export const screenshotRateLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 10, // 10 screenshots per hour
message: { error: 'Screenshot upload limit reached' },
keyGenerator: (req) => (req as CustomRequest).user?._id || req.ip
});Request Logging
Request Logger (requestLogger.ts)
Logs all incoming requests.
typescript
const requestLogger = (req: Request, res: Response, next: NextFunction) => {
const start = Date.now();
// Log request
logger.info(`→ ${req.method} ${req.originalUrl}`, {
ip: req.ip,
userAgent: req.get('user-agent'),
contentLength: req.get('content-length')
});
// Log response on finish
res.on('finish', () => {
const duration = Date.now() - start;
const logLevel = res.statusCode >= 400 ? 'warn' : 'info';
logger[logLevel](`← ${req.method} ${req.originalUrl} ${res.statusCode}`, {
duration: `${duration}ms`,
contentLength: res.get('content-length')
});
});
next();
};
export default requestLogger;Input Validation
Validator Middleware (validatorMiddleware.ts)
Validates request body/params using Joi or similar.
typescript
import Joi from 'joi';
export const validate = (schema: Joi.ObjectSchema) => {
return (req: Request, res: Response, next: NextFunction) => {
const { error, value } = schema.validate(req.body, {
abortEarly: false,
stripUnknown: true
});
if (error) {
const errors = error.details.map(d => ({
field: d.path.join('.'),
message: d.message
}));
return res.status(400).json({
error: 'Validation failed',
details: errors
});
}
req.body = value;
next();
};
};
// Example schemas
export const createAdSchema = Joi.object({
title: Joi.string().required().max(255),
video: Joi.string().uri().required(),
categoryId: Joi.string().uuid().required(),
budgetUsd: Joi.number().positive().required(),
demographics: Joi.object({
ageMin: Joi.number().min(13).max(100).required(),
ageMax: Joi.number().min(13).max(100).required(),
gender: Joi.string().valid('male', 'female', 'both').default('both'),
countryId: Joi.string().uuid(),
cityIds: Joi.array().items(Joi.string().uuid())
})
});Dashboard Authentication
Dashboard Auth (dashboardAuth.ts)
API key authentication for CEO dashboard.
typescript
export const dashboardAuth = (req: Request, res: Response, next: NextFunction) => {
const apiKey = req.header('X-API-Key');
if (!apiKey) {
return res.status(401).json({
error: 'API key required',
hint: 'Include X-API-Key header'
});
}
// Verify against dashboard API key from Secret Manager
const validKey = process.env.DASHBOARD_API_KEY;
if (apiKey !== validKey) {
logger.warn('Invalid dashboard API key attempt', {
ip: req.ip,
providedKey: apiKey.substring(0, 8) + '...'
});
return res.status(403).json({ error: 'Invalid API key' });
}
next();
};Middleware Application Order
typescript
// In app.ts - order matters!
// 1. CORS (before everything)
app.use(cors(corsOptions));
// 2. Stripe webhooks (needs raw body - before bodyParser)
app.use('/stripe', stripeRoutes);
app.use('/api/campaign-payments', campaignPaymentRoutes);
app.use('/api/deposits', depositRoutes);
app.use('/api/stripe-billing', stripeBillingRoutes);
// 3. Body parsing
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
// 4. File upload
app.use(fileUpload({
limits: { fileSize: 200 * 1024 * 1024 },
useTempFiles: true,
tempFileDir: '/tmp/'
}));
// 5. Request logging
app.use(requestLogger);
// 6. Routes (each with own auth middleware)
app.use('/auth', authRoutes); // No auth required
app.use('/users', verifyToken, userRoutes); // User JWT required
app.use('/admin', adminAuthMiddleware, adminRoutes); // Admin JWT required