@govish/shared-services
v1.6.0
Published
Govish shared services package - officer, device, station, penal code, station-duty block, and rank leave days cache services; API key service; audit logging; authentication middleware with support for API key + officer combined authentication
Downloads
56
Maintainers
Readme
Govish Shared Services Package
This package contains shared services, middleware, and utilities that can be used across multiple microservices.
Quick Start
import {
AuditService,
createAuthenticateDeviceOrOfficer,
SharedServicesDependencies
} from '@govish/shared-services';
const dependencies: SharedServicesDependencies = {
redisClient,
logger,
kafkaBrokers: process.env.KAFKA_BROKER || 'localhost:9092',
auditTopic: 'audit-log',
enableDebugLogs: false, // Set to true for verbose logging
};
const auditService = new AuditService(dependencies);
const authenticateDeviceOrOfficer = createAuthenticateDeviceOrOfficer(dependencies);Services Included
- OfficerCacheService: Redis-based caching service for officers with indexing and efficient queries
- PenalCodeCacheService: Redis-based caching service for penal codes
- DeviceCacheService: Redis-based caching service for devices with indexing
- StationCacheService: Redis-based caching service for police stations (JSON list, set of IDs, and per-station hashes)
- StationDutyBlockCacheService: Read-only cache for station-duty blocks and sectors — fetch only (
getBlocks,getBlocksByStationId,getBlock,getSectors,getSector) - RankLeaveDaysCacheService: Read-only cache for rank leave days (
getLeaveDays,getLeaveDay) - ApiKeyService: Service for managing and validating API keys with Redis caching
- AuditService: Service for logging audit events to Kafka with comprehensive event tracking
- authenticateDeviceOrOfficer: Express middleware for authenticating devices, officers, or microservices via API keys
Utilities Included
- createRollingCache: List/item rolling cache (
buildKey,get,set,getOrSet,invalidate,invalidateByPrefix,recordAccess) with day-based TTL fromCACHE_DAYS_LIST/CACHE_DAYS_ITEM; enable withROLLING_CACHE_ENABLED=true - createSafeRedisGet: Factory function to create a safe Redis GET operation with automatic reconnection
- createSafeRedisSet: Factory function to create a safe Redis SET operation with automatic reconnection
- createSafeRedisUtils: Factory function that creates both safeRedisGet and safeRedisSet functions
Installation
This is a local package. To use it in your project:
cd packages/ob-shared-services
npm install
npm run buildUsage
Initialization
All services require dependencies to be injected. Create a factory file in your project:
import {
OfficerCacheService,
PenalCodeCacheService,
DeviceCacheService,
ApiKeyService,
createAuthenticateDeviceOrOfficer,
SharedServicesDependencies
} from '@govish/shared-services';
import { redisClient } from './app';
import { logger } from './utils/logger';
// ... other dependencies
const dependencies: SharedServicesDependencies = {
redisClient,
logger,
authHost: process.env.AUTH_HOST,
authPort: process.env.AUTH_PORT,
serverName: process.env.SERVER_NAME,
auditTopic: process.env.AUDIT_TOPIC || 'audit-log',
enableDebugLogs: process.env.AUDIT_DEBUG_LOGS === 'true',
jwtConfig,
// Option 1: Use Kafka brokers (recommended - audit service manages its own connection)
kafkaBrokers: process.env.KAFKA_BROKER || process.env.KAFKA_BROKERS,
kafkaClientId: process.env.KAFKA_CLIENT_ID || 'audit-service',
// Option 2: Use existing producer (backward compatibility)
// kafkaProducer: producer,
// isProducerConnected: () => kafkaProducerService?.getIsConnected() || false,
...createSafeRedisUtils(redisClient), // Spreads safeRedisGet and safeRedisSet
};
// Initialize services
export const officerCacheService = new OfficerCacheService(dependencies);
export const penalCodeCacheService = new PenalCodeCacheService(dependencies);
export const deviceCacheService = new DeviceCacheService(dependencies);
export const apiKeyService = new ApiKeyService(dependencies);
export const auditService = new AuditService(dependencies);
// Create middleware
export const authenticateDeviceOrOfficer = createAuthenticateDeviceOrOfficer(dependencies);
// Export Redis utilities (optional - can also use createSafeRedisUtils directly)
export const { safeRedisGet, safeRedisSet } = createSafeRedisUtils(redisClient);Using Services
import {
officerCacheService,
penalCodeCacheService,
deviceCacheService,
apiKeyService,
auditService,
safeRedisGet,
safeRedisSet
} from './services/sharedServicesFactory';
// Get officer by ID
const officer = await officerCacheService.getOfficerById(123);
// Get officers by station
const officers = await officerCacheService.getOfficersByStation(1);
// Store officer
await officerCacheService.storeOfficer(officerData);
// Get penal codes
const penalCodes = await penalCodeCacheService.getPenalCodes();
// Validate API key
const apiKey = await apiKeyService.validateApiKey('ak_xxx');
// Log audit event
await auditService.logAuditEvent(req, {
event_type: AuditEventType.ENDPOINT_ACCESSED
});
// Log audit event with custom keys
await auditService.logAuditEvent(req, {
event_type: AuditEventType.ENDPOINT_ACCESSED,
custom_field: 'custom_value',
metadata: {
action: 'special_action',
reason: 'user_requested'
}
});
// Use Redis utilities
const value = await safeRedisGet('some-key');
await safeRedisSet('some-key', 'some-value');Using Middleware
import { authenticateDeviceOrOfficer } from './services/sharedServicesFactory';
import { Router } from 'express';
const router = Router();
router.get('/api/v1/officers', authenticateDeviceOrOfficer, getOfficers);Dependencies
The package requires the following dependencies to be provided:
Required:
redisClient: Redis client instancelogger: Winston logger instance
Optional:
authHost: Auth service host (defaults toprocess.env.AUTH_HOST)authPort: Auth service port (defaults toprocess.env.AUTH_PORT)serverName: Server/microservice name (defaults toprocess.env.SERVER_NAME)auditTopic: Kafka topic for audit logs (defaults toprocess.env.AUDIT_TOPICor'audit-log')jwtConfig: JWT configuration object (for authentication middleware)enableDebugLogs: Enable verbose debug logging (defaults tofalse, can be set viaAUDIT_DEBUG_LOGS=true)- Kafka Configuration (choose one):
kafkaBrokers: Kafka broker URLs as string or array (e.g.,'localhost:9092'or['broker1:9092', 'broker2:9092']) - Recommended: Audit service manages its own connectionkafkaClientId: Kafka client ID (defaults to'audit-service')- OR (for backward compatibility):
kafkaProducer: Existing Kafka producer instanceisProducerConnected: Function that returns boolean indicating Kafka connection status
safeRedisGet: Safe Redis get function (created viacreateSafeRedisGet)safeRedisSet: Safe Redis set function (created viacreateSafeRedisSet)
Audit Service
Features
Automatic Event Type Detection
The audit service automatically determines event types based on request path and method:
- Login events:
LOGIN_SUCCESS,LOGIN_FAILED,LOGIN_NOT_PERMITTED - Officer events:
OFFICER_RETRIEVED,OFFICERS_LISTED - Device events:
DEVICE_RETRIEVED,DEVICES_LISTED - Arrest events:
ARREST_CREATED,ARREST_UPDATED,ARREST_DELETED,ARREST_RETRIEVED,ARRESTS_LISTED - General:
ENDPOINT_ACCESSED,UNAUTHORIZED_ACCESS,FORBIDDEN_ACCESS
Custom Keys Support
You can add custom keys to audit events - they will be included in the Kafka message:
await auditService.logAuditEvent(req, {
custom_field: 'custom_value',
metadata: { action: 'special_action' },
any_other_key: 'any_value'
});IP Address Detection
The audit service automatically detects client IP addresses from multiple sources (in order of priority):
x-forwarded-for(most common behind proxies/load balancers)cf-connecting-ip(Cloudflare)x-real-ip(nginx)true-client-ip(Cloudflare Enterprise)x-client-ipreq.ip(requires trust proxy)req.socket.remoteAddressreq.connection.remoteAddress
Kafka Connection Management
The audit service can manage its own Kafka connection when kafkaBrokers is provided:
- Creates its own Kafka client and producer
- Handles connection lifecycle automatically
- Connects on first use (lazy connection)
- Gracefully handles connection failures
- Falls back silently if Kafka is unavailable (doesn't break requests)
User Information Tracking
Automatically extracts and includes:
- Officer information (id, name, service_number, email)
- Device information (id, device_id)
- Microservice information (for API key authentication)
- Authentication type (officer, device, both, api_key, microservice)
Usage Examples
import { auditService, AuditEventType } from './services/sharedServicesFactory';
// Basic usage - event type determined automatically
await auditService.logAuditEvent(req, {});
// Explicit event type
await auditService.logAuditEvent(req, {
event_type: AuditEventType.ARREST_CREATED
});
// With custom keys
await auditService.logAuditEvent(req, {
event_type: AuditEventType.ARREST_CREATED,
custom_action: 'manual_review',
review_reason: 'suspicious_pattern'
});
// With response information
await auditService.logAuditEvent(req, {
response_status: 200,
duration_ms: 150
});
// Log endpoint access (helper method)
await auditService.logEndpointAccess(req);
// Log with response status (helper method)
const startTime = Date.now();
// ... handle request ...
await auditService.logEndpointAccessWithResponse(req, res, startTime);Environment Variables
Audit Service Configuration
# Kafka brokers (required for internal connection)
KAFKA_BROKER=localhost:9092
# or
KAFKA_BROKERS=broker1:9092,broker2:9092
# Kafka client ID (optional)
KAFKA_CLIENT_ID=audit-service
# Audit topic (optional, defaults to 'audit-log')
AUDIT_TOPIC=audit-log
# Enable debug logs (optional, defaults to false)
AUDIT_DEBUG_LOGS=trueOther Configuration
# Auth service (for device/officer service fallback)
AUTH_HOST=localhost
AUTH_PORT=3000
# Server name
SERVER_NAME=ob-mainEnvironment Modes
The package respects the NODE_ENV environment variable to control logging behavior:
Development Mode (NODE_ENV=development or NODE_ENV=dev)
- All console logs are shown (debug, info, warnings, errors)
- Detailed error information is displayed
- Debug logging is enabled
- Full stack traces are shown
Production Mode (NODE_ENV=production or NODE_ENV=prod)
- Console logs are suppressed (only errors with minimal info)
- Only warnings and errors are logged via Winston logger
- Debug and info logs are hidden
- Reduced logging for better performance
Usage
The package provides utility functions for conditional logging:
import { devLog, devError, devWarn, devDebug, isDevelopmentMode } from '@govish/shared-services';
// Only logs in development mode
devLog('This will only show in dev mode');
devDebug('Debug information');
devWarn('Warning message');
// Errors always log, but with less detail in production
devError('Error occurred', errorDetails);
// Check mode programmatically
if (isDevelopmentMode()) {
// Development-only code
}Logger Configuration
The Winston logger automatically adjusts based on environment:
- Development: Logs all levels (debug, info, warn, error) to console and files
- Production: Only logs warnings and errors to files (console disabled unless
LOG_TO_CONSOLE=true)
To enable console logging in production, set:
LOG_TO_CONSOLE=trueDebug Logging Control
The audit service has its own debug logging control via enableDebugLogs:
// Enable all debug logs (console.log, console.error for audit service)
const dependencies: SharedServicesDependencies = {
// ...
enableDebugLogs: true, // or set AUDIT_DEBUG_LOGS=true
};When enableDebugLogs is false (default):
- No
[AUDIT KAFKA]debug logs - No
[AUDIT]user access logs - Only errors are logged via Winston logger
When enableDebugLogs is true:
- All
[AUDIT KAFKA]debug logs are shown [AUDIT]user access logs are shown- Detailed Kafka connection and send logs
Building
npm run buildThis will compile TypeScript to JavaScript in the dist/ directory.