Client Applications:
CPU: Dual-core 1.5GHz or equivalent
RAM: 4GB minimum, 8GB recommended
Storage: 500MB available space
Network: Broadband internet connection (10 Mbps+)
Mobile Applications:
OS: iOS 13+ or Android 8+
RAM: 2GB minimum
Storage: 200MB available space
Network: 4G LTE or WiFi recommended
Server Integration:
CPU: Quad-core 2.0GHz or equivalent
RAM: 8GB minimum, 16GB recommended
Storage: 50GB available space
Network: Dedicated internet connection (100 Mbps+)
Operating Systems:
- Windows 10/11 (64-bit)
- macOS 10.15+ (Catalina or later)
- Ubuntu 20.04+ LTS
- CentOS 8+ / RHEL 8+
Runtime Environments:
- Node.js 18.0+ (for JavaScript SDK)
- Python 3.8+ (for Python SDK)
- Java 11+ (for Java SDK)
- .NET 6.0+ (for C# SDK)
- Go 1.18+ (for Go SDK)
Web Browsers:
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
Bandwidth:
Minimum: 10 Mbps sustained
Recommended: 100 Mbps sustained
Peak: 1 Gbps for enterprise deployments
Latency:
Maximum: 500ms round-trip
Recommended: <100ms round-trip
Critical: <50ms for real-time features
Reliability:
Uptime: 99.9% availability required
Packet Loss: <0.1%
Jitter: <20ms
Required Ports:
HTTPS (443/TCP): API access and web interface
WSS (443/TCP): WebSocket connections
HTTP (80/TCP): Redirect to HTTPS only
IP Ranges:
Primary: 52.0.0.0/8 (AWS US-East)
Backup: 54.0.0.0/8 (AWS US-West)
CDN: 13.0.0.0/8 (CloudFront)
Domain Whitelist:
- api.mindpeeker.com
- cdn.mindpeeker.com
- ws.mindpeeker.com
- auth.mindpeeker.com
1. Navigate to Settings → API Keys
2. Click "Generate New Key"
3. Configure permissions and limits
4. Securely store the key
curl -X POST https://api.mindpeeker.com/v1/api-keys \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production API Key",
"permissions": [
{"resource": "sessions", "actions": ["create", "read"]},
{"resource": "analyses", "actions": ["read"]}
],
"rate_limit_per_minute": 1000,
"ip_whitelist": ["203.0.113.0/24"]
}'
// Token refresh implementation
class TokenManager {
private refreshToken: string;
private accessToken: string;
private tokenExpiry: Date;
async getValidToken(): Promise<string> {
if (!this.accessToken || this.isTokenExpired()) {
await this.refreshAccessToken();
}
return this.accessToken;
}
private async refreshAccessToken(): Promise<void> {
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: this.refreshToken })
});
const data = await response.json();
this.accessToken = data.token;
this.tokenExpiry = new Date(Date.now() + (data.expires_in * 1000));
}
private isTokenExpired(): boolean {
return new Date() >= this.tokenExpiry;
}
}
// Installation
npm install @mindpeeker/sdk
// Basic usage
import { MindPeekerClient } from '@mindpeeker/sdk';
const client = new MindPeekerClient({
apiKey: process.env.MINDPEEKER_API_KEY,
baseUrl: 'https://api.mindpeeker.com/v1',
timeout: 30000
});
// Create session
const session = await client.sessions.create({
cue: 'Describe the location of the missing artifact',
session_type: 'remote_viewing',
parameters: {
confidence_threshold: 0.8,
analysis_types: ['visual', 'spatial']
}
});
// Monitor progress
client.sessions.onProgress(session.id, (update) => {
console.log(`Progress: ${update.progress}%`);
console.log(`Status: ${update.status}`);
});
// Get results
const results = await client.sessions.getResults(session.id);
console.log('Analysis complete:', results);
pip install mindpeeker-sdk
from mindpeeker import MindPeekerClient
import asyncio
async def main():
client = MindPeekerClient(
api_key=os.getenv('MINDPEEKER_API_KEY'),
base_url='https://api.mindpeeker.com/v1'
)
# Create session
session = await client.sessions.create(
cue='Describe the location of the missing artifact',
session_type='remote_viewing',
parameters={
'confidence_threshold': 0.8,
'analysis_types': ['visual', 'spatial']
}
)
# Wait for completion
results = await client.sessions.wait_for_completion(
session.id,
timeout=1800
)
print(f"Analysis complete: {results}")
asyncio.run(main())
// Installation (Maven)
<dependency>
<groupId>com.mindpeeker</groupId>
<artifactId>mindpeeker-sdk</artifactId>
<version>1.5.0</version>
</dependency>
// Basic usage
import com.mindpeeker.client.MindPeekerClient;
import com.mindpeeker.model.*;
public class MindPeekerIntegration {
public static void main(String[] args) {
MindPeekerClient client = MindPeekerClient.builder()
.apiKey(System.getenv("MINDPEEKER_API_KEY"))
.baseUrl("https://api.mindpeeker.com/v1")
.timeout(Duration.ofSeconds(30))
.build();
// Create session
Session session = client.sessions().create(SessionCreateRequest.builder()
.cue("Describe the location of the missing artifact")
.sessionType(SessionType.REMOTE_VIEWING)
.parameter("confidence_threshold", 0.8)
.parameter("analysis_types", Arrays.asList("visual", "spatial"))
.build());
// Monitor progress
client.sessions().subscribeToUpdates(session.id, update -> {
System.out.println("Progress: " + update.getProgress() + "%");
System.out.println("Status: " + update.getStatus());
});
// Get results
SessionResults results = client.sessions().getResults(session.id);
System.out.println("Analysis complete: " + results);
}
}
import crypto from 'crypto';
import express from 'express';
const app = express();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
// Verify webhook signature
function verifyWebhookSignature(payload: string, signature: string): boolean {
const expectedSignature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
// Webhook endpoint
app.post('/webhooks/mindpeeker', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-mindpeeker-signature'] as string;
const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
try {
const event = JSON.parse(payload);
switch (event.type) {
case 'session.completed':
handleSessionCompleted(event.data);
break;
case 'session.failed':
handleSessionFailed(event.data);
break;
case 'analysis.ready':
handleAnalysisReady(event.data);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
async function handleSessionCompleted(data: any) {
console.log(`Session ${data.session_id} completed`);
// Process completed session
// Update database, notify users, etc.
}
async function handleSessionFailed(data: any) {
console.log(`Session ${data.session_id} failed: ${data.error}`);
// Handle failed session
// Notify administrators, retry if appropriate, etc.
}
async function handleAnalysisReady(data: any) {
console.log(`Analysis ${data.analysis_id} ready for session ${data.session_id}`);
// Process analysis results
// Store results, trigger notifications, etc.
}
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});
import hmac
import hashlib
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = os.getenv('WEBHOOK_SECRET')
def verify_webhook_signature(payload: str, signature: str) -> bool:
expected_signature = hmac.new(
WEBHOOK_SECRET.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
@app.route('/webhooks/mindpeeker', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-MindPeeker-Signature')
payload = request.get_data(as_text=True)
if not verify_webhook_signature(payload, signature):
return jsonify({'error': 'Invalid signature'}), 401
try:
event = json.loads(payload)
if event['type'] == 'session.completed':
handle_session_completed(event['data'])
elif event['type'] == 'session.failed':
handle_session_failed(event['data'])
elif event['type'] == 'analysis.ready':
handle_analysis_ready(event['data'])
else:
print(f"Unhandled event type: {event['type']}")
return jsonify({'received': True}), 200
except Exception as error:
print(f"Webhook processing error: {error}")
return jsonify({'error': 'Internal server error'}), 500
def handle_session_completed(data):
print(f"Session {data['session_id']} completed")
# Process completed session
def handle_session_failed(data):
print(f"Session {data['session_id']} failed: {data['error']}")
# Handle failed session
def handle_analysis_ready(data):
print(f"Analysis {data['analysis_id']} ready for session {data['session_id']}")
# Process analysis results
if __name__ == '__main__':
app.run(port=3000)
interface WebhookEvent {
id: string;
type: WebhookEventType;
data: any;
timestamp: string;
signature: string;
}
enum WebhookEventType {
SESSION_COMPLETED = 'session.completed',
SESSION_FAILED = 'session.failed',
SESSION_CANCELLED = 'session.cancelled',
ANALYSIS_READY = 'analysis.ready',
ANALYSIS_FAILED = 'analysis.failed',
USER_CREATED = 'user.created',
USER_UPDATED = 'user.updated',
BILLING_PAYMENT_SUCCEEDED = 'billing.payment_succeeded',
BILLING_PAYMENT_FAILED = 'billing.payment_failed'
}
class WebhookProcessor {
private eventHandlers: Map<WebhookEventType, EventHandler>;
private retryQueue: RetryQueue;
private deadLetterQueue: DeadLetterQueue;
constructor() {
this.eventHandlers = new Map();
this.retryQueue = new RetryQueue();
this.deadLetterQueue = new DeadLetterQueue();
this.setupHandlers();
}
private setupHandlers(): void {
this.eventHandlers.set(WebhookEventType.SESSION_COMPLETED, new SessionCompletedHandler());
this.eventHandlers.set(WebhookEventType.SESSION_FAILED, new SessionFailedHandler());
this.eventHandlers.set(WebhookEventType.ANALYSIS_READY, new AnalysisReadyHandler());
// ... other handlers
}
async processEvent(event: WebhookEvent): Promise<void> {
const handler = this.eventHandlers.get(event.type);
if (!handler) {
console.warn(`No handler for event type: ${event.type}`);
return;
}
try {
await handler.handle(event.data);
console.log(`Successfully processed event: ${event.id}`);
} catch (error) {
console.error(`Failed to process event ${event.id}:`, error);
await this.retryQueue.add(event, error);
}
}
}
interface EventHandler {
handle(data: any): Promise<void>;
}
class SessionCompletedHandler implements EventHandler {
async handle(data: any): Promise<void> {
// Update local database
await this.updateSessionStatus(data.session_id, 'completed');
// Process results
await this.processResults(data.session_id, data.results);
// Send notifications
await this.sendNotifications(data.user_id, data.session_id);
// Update analytics
await this.updateAnalytics(data);
}
private async updateSessionStatus(sessionId: string, status: string): Promise<void> {
// Database update logic
}
private async processResults(sessionId: string, results: any): Promise<void> {
// Results processing logic
}
private async sendNotifications(userId: string, sessionId: string): Promise<void> {
// Notification logic
}
private async updateAnalytics(data: any): Promise<void> {
// Analytics update logic
}
}
interface DataSyncConfig {
syncInterval: number; // Sync interval in milliseconds
batchSize: number; // Records per batch
maxRetries: number; // Maximum retry attempts
conflictResolution: ConflictResolutionStrategy;
}
enum ConflictResolutionStrategy {
LAST_WRITE_WINS = 'last_write_wins',
FIRST_WRITE_WINS = 'first_write_wins',
MANUAL_REVIEW = 'manual_review',
MERGE = 'merge'
}
class DataSynchronizer {
private config: DataSyncConfig;
private localDatabase: Database;
private mindPeekerClient: MindPeekerClient;
constructor(config: DataSyncConfig) {
this.config = config;
this.localDatabase = new Database();
this.mindPeekerClient = new MindPeekerClient();
}
async startSync(): Promise<void> {
setInterval(async () => {
try {
await this.syncSessions();
await this.syncAnalyses();
await this.syncUsers();
} catch (error) {
console.error('Sync error:', error);
}
}, this.config.syncInterval);
}
private async syncSessions(): Promise<void> {
const lastSyncTime = await this.getLastSyncTime('sessions');
const remoteSessions = await this.mindPeekerClient.sessions.list({
updated_since: lastSyncTime,
limit: this.config.batchSize
});
for (const session of remoteSessions.data) {
const localSession = await this.localDatabase.sessions.findById(session.id);
if (!localSession) {
await this.localDatabase.sessions.create(session);
} else if (this.hasConflict(localSession, session)) {
await this.resolveConflict(localSession, session);
} else if (this.needsUpdate(localSession, session)) {
await this.localDatabase.sessions.update(session.id, session);
}
}
await this.updateLastSyncTime('sessions');
}
private hasConflict(local: any, remote: any): boolean {
return local.updated_at !== remote.updated_at &&
local.version !== remote.version;
}
private async resolveConflict(local: any, remote: any): Promise<void> {
switch (this.config.conflictResolution) {
case ConflictResolutionStrategy.LAST_WRITE_WINS:
await this.localDatabase.sessions.update(remote.id, remote);
break;
case ConflictResolutionStrategy.MANUAL_REVIEW:
await this.queueForReview(local, remote);
break;
// ... other strategies
}
}
}
interface BackupConfig {
schedule: string; // Cron expression
retentionDays: number; // Days to retain backups
compressionEnabled: boolean; // Enable compression
encryptionEnabled: boolean; // Enable encryption
storageLocation: StorageLocation;
}
enum StorageLocation {
LOCAL = 'local',
S3 = 's3',
AZURE_BLOB = 'azure_blob',
GOOGLE_CLOUD = 'google_cloud'
}
class BackupManager {
private config: BackupConfig;
private encryptionKey: string;
constructor(config: BackupConfig, encryptionKey: string) {
this.config = config;
this.encryptionKey = encryptionKey;
}
async performBackup(): Promise<string> {
const timestamp = new Date().toISOString();
const backupId = `backup_${timestamp.replace(/[:.]/g, '-')}`;
try {
// Create backup
const backupData = await this.createBackup();
// Compress if enabled
const compressedData = this.config.compressionEnabled
? await this.compress(backupData)
: backupData;
// Encrypt if enabled
const encryptedData = this.config.encryptionEnabled
? await this.encrypt(compressedData)
: compressedData;
// Store backup
const backupUrl = await this.storeBackup(backupId, encryptedData);
// Update backup registry
await this.updateBackupRegistry(backupId, backupUrl, timestamp);
// Clean old backups
await this.cleanupOldBackups();
return backupId;
} catch (error) {
console.error('Backup failed:', error);
throw error;
}
}
private async createBackup(): Promise<any> {
// Export all relevant data
const users = await this.exportUsers();
const sessions = await this.exportSessions();
const analyses = await this.exportAnalyses();
const metadata = await this.exportMetadata();
return {
timestamp: new Date().toISOString(),
version: '1.0',
data: {
users,
sessions,
analyses,
metadata
}
};
}
private async storeBackup(backupId: string, data: Buffer): Promise<string> {
switch (this.config.storageLocation) {
case StorageLocation.S3:
return await this.storeToS3(backupId, data);
case StorageLocation.AZURE_BLOB:
return await this.storeToAzureBlob(backupId, data);
case StorageLocation.GOOGLE_CLOUD:
return await this.storeToGoogleCloud(backupId, data);
default:
return await this.storeLocally(backupId, data);
}
}
async restoreFromBackup(backupId: string): Promise<void> {
try {
// Retrieve backup
const backupData = await this.retrieveBackup(backupId);
// Decrypt if encrypted
const decryptedData = this.config.encryptionEnabled
? await this.decrypt(backupData)
: backupData;
// Decompress if compressed
const decompressedData = this.config.compressionEnabled
? await this.decompress(decryptedData)
: decryptedData;
// Parse backup data
const backup = JSON.parse(decompressedData.toString());
// Validate backup integrity
await this.validateBackup(backup);
// Restore data
await this.restoreUsers(backup.data.users);
await this.restoreSessions(backup.data.sessions);
await this.restoreAnalyses(backup.data.analyses);
await this.restoreMetadata(backup.data.metadata);
console.log(`Successfully restored from backup: ${backupId}`);
} catch (error) {
console.error('Restore failed:', error);
throw error;
}
}
}
import crypto from 'crypto';
class ClientEncryption {
private encryptionKey: Buffer;
constructor(keyBase64: string) {
this.encryptionKey = Buffer.from(keyBase64, 'base64');
}
encryptSensitiveData(data: string): string {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipher('aes-256-gcm', this.encryptionKey);
cipher.setAAD(Buffer.from('mindpeeker-data'));
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return JSON.stringify({
iv: iv.toString('hex'),
encrypted,
authTag: authTag.toString('hex')
});
}
decryptSensitiveData(encryptedData: string): string {
const { iv, encrypted, authTag } = JSON.parse(encryptedData);
const decipher = crypto.createDecipher('aes-256-gcm', this.encryptionKey);
decipher.setAAD(Buffer.from('mindpeeker-data'));
decipher.setAuthTag(Buffer.from(authTag, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
// Usage example
const encryption = new ClientEncryption(process.env.ENCRYPTION_KEY);
// Encrypt sensitive cue data before sending
const sensitiveCue = "Private investigation details about case #12345";
const encryptedCue = encryption.encryptSensitiveData(sensitiveCue);
// Send to API
await client.sessions.create({
cue: encryptedCue,
session_type: 'remote_viewing',
metadata: { encrypted: true }
});
interface Role {
id: string;
name: string;
permissions: Permission[];
conditions?: AccessCondition[];
}
interface Permission {
resource: string;
actions: string[];
conditions?: AccessCondition[];
}
interface AccessCondition {
field: string;
operator: 'eq' | 'ne' | 'in' | 'not_in' | 'gt' | 'lt';
value: any;
}
class AccessControlManager {
private roles: Map<string, Role>;
private userRoles: Map<string, string[]>;
constructor() {
this.roles = new Map();
this.userRoles = new Map();
this.loadRoles();
}
async checkPermission(
userId: string,
resource: string,
action: string,
context?: any
): Promise<boolean> {
const userRoleIds = this.userRoles.get(userId) || [];
for (const roleId of userRoleIds) {
const role = this.roles.get(roleId);
if (!role) continue;
for (const permission of role.permissions) {
if (this.matchesPermission(permission, resource, action, context)) {
return true;
}
}
}
return false;
}
private matchesPermission(
permission: Permission,
resource: string,
action: string,
context?: any
): boolean {
// Check resource match
if (!this.matchesResource(permission.resource, resource)) {
return false;
}
// Check action match
if (!permission.actions.includes(action) && !permission.actions.includes('*')) {
return false;
}
// Check conditions
if (permission.conditions && context) {
return this.evaluateConditions(permission.conditions, context);
}
return true;
}
private matchesResource(permissionResource: string, actualResource: string): boolean {
if (permissionResource === '*') return true;
if (permissionResource === actualResource) return true;
// Support wildcard patterns like 'sessions.*'
const pattern = permissionResource.replace('*', '.*');
const regex = new RegExp(`^${pattern}$`);
return regex.test(actualResource);
}
private evaluateConditions(conditions: AccessCondition[], context: any): boolean {
return conditions.every(condition => {
const contextValue = this.getNestedValue(context, condition.field);
switch (condition.operator) {
case 'eq':
return contextValue === condition.value;
case 'ne':
return contextValue !== condition.value;
case 'in':
return Array.isArray(condition.value) && condition.value.includes(contextValue);
case 'not_in':
return Array.isArray(condition.value) && !condition.value.includes(contextValue);
case 'gt':
return contextValue > condition.value;
case 'lt':
return contextValue < condition.value;
default:
return false;
}
});
}
private getNestedValue(obj: any, path: string): any {
return path.split('.').reduce((current, key) => current?.[key], obj);
}
}
// Middleware for API protection
function requirePermission(resource: string, action: string) {
return async (req: any, res: any, next: any) => {
const userId = req.user.id;
const accessControl = new AccessControlManager();
const hasPermission = await accessControl.checkPermission(
userId,
resource,
action,
{
sessionId: req.params.sessionId,
userId: req.params.userId,
organization: req.user.organization
}
);
if (!hasPermission) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// Usage in API routes
app.get('/sessions/:sessionId',
authenticateUser,
requirePermission('sessions', 'read'),
async (req, res) => {
// Session retrieval logic
}
);
interface CacheConfig {
levels: CacheLevel[];
defaultTTL: number;
maxSize: number;
evictionPolicy: EvictionPolicy;
}
enum CacheLevel {
MEMORY = 'memory',
REDIS = 'redis',
DATABASE = 'database'
}
enum EvictionPolicy {
LRU = 'lru',
LFU = 'lfu',
FIFO = 'fifo'
}
class MultiLevelCache {
private config: CacheConfig;
private memoryCache: Map<string, CacheEntry>;
private redisClient: Redis;
constructor(config: CacheConfig) {
this.config = config;
this.memoryCache = new Map();
this.redisClient = new Redis(process.env.REDIS_URL);
}
async get<T>(key: string): Promise<T | null> {
// Try memory cache first
const memoryEntry = this.memoryCache.get(key);
if (memoryEntry && !this.isExpired(memoryEntry)) {
return memoryEntry.value;
}
// Try Redis cache
try {
const redisValue = await this.redisClient.get(key);
if (redisValue) {
const parsed = JSON.parse(redisValue);
// Promote to memory cache
this.memoryCache.set(key, {
value: parsed,
timestamp: Date.now(),
ttl: this.config.defaultTTL
});
return parsed;
}
} catch (error) {
console.error('Redis cache error:', error);
}
return null;
}
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
const effectiveTTL = ttl || this.config.defaultTTL;
const entry: CacheEntry = {
value,
timestamp: Date.now(),
ttl: effectiveTTL
};
// Set in memory cache
this.memoryCache.set(key, entry);
this.enforceMemoryLimit();
// Set in Redis cache
try {
await this.redisClient.setex(
key,
Math.ceil(effectiveTTL / 1000),
JSON.stringify(value)
);
} catch (error) {
console.error('Redis cache set error:', error);
}
}
async invalidate(pattern: string): Promise<void> {
// Invalidate memory cache
for (const key of this.memoryCache.keys()) {
if (this.matchesPattern(key, pattern)) {
this.memoryCache.delete(key);
}
}
// Invalidate Redis cache
try {
const keys = await this.redisClient.keys(pattern);
if (keys.length > 0) {
await this.redisClient.del(...keys);
}
} catch (error) {
console.error('Redis cache invalidation error:', error);
}
}
private enforceMemoryLimit(): void {
if (this.memoryCache.size <= this.config.maxSize) {
return;
}
const entries = Array.from(this.memoryCache.entries());
switch (this.config.evictionPolicy) {
case EvictionPolicy.LRU:
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
break;
case EvictionPolicy.LFU:
// Implementation would need access frequency tracking
entries.sort((a, b) => (a[1].accessCount || 0) - (b[1].accessCount || 0));
break;
case EvictionPolicy.FIFO:
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
break;
}
const toRemove = entries.slice(0, entries.length - this.config.maxSize);
toRemove.forEach(([key]) => this.memoryCache.delete(key));
}
private isExpired(entry: CacheEntry): boolean {
return Date.now() - entry.timestamp > entry.ttl;
}
private matchesPattern(key: string, pattern: string): boolean {
const regex = new RegExp(pattern.replace('*', '.*'));
return regex.test(key);
}
}
interface CacheEntry {
value: any;
timestamp: number;
ttl: number;
accessCount?: number;
}
interface RateLimitConfig {
windowMs: number;
maxRequests: number;
keyGenerator: (req: any) => string;
skipSuccessfulRequests: boolean;
skipFailedRequests: boolean;
}
class DistributedRateLimiter {
private config: RateLimitConfig;
private redisClient: Redis;
constructor(config: RateLimitConfig) {
this.config = config;
this.redisClient = new Redis(process.env.REDIS_URL);
}
async isAllowed(req: any): Promise<{ allowed: boolean; remaining: number; resetTime: number }> {
const key = this.config.keyGenerator(req);
const now = Date.now();
const windowStart = now - this.config.windowMs;
// Remove expired entries
await this.redisClient.zremrangebyscore(key, 0, windowStart);
// Count current requests
const currentRequests = await this.redisClient.zcard(key);
if (currentRequests >= this.config.maxRequests) {
const oldestRequest = await this.redisClient.zrange(key, 0, 0, 'WITHSCORES');
const resetTime = parseInt(oldestRequest[1]) + this.config.windowMs;
return {
allowed: false,
remaining: 0,
resetTime
};
}
// Add current request
await this.redisClient.zadd(key, now, `${now}-${Math.random()}`);
await this.redisClient.expire(key, Math.ceil(this.config.windowMs / 1000));
return {
allowed: true,
remaining: this.config.maxRequests - currentRequests - 1,
resetTime: now + this.config.windowMs
};
}
middleware() {
return async (req: any, res: any, next: any) => {
const result = await this.isAllowed(req);
res.set({
'X-RateLimit-Limit': this.config.maxRequests,
'X-RateLimit-Remaining': result.remaining,
'X-RateLimit-Reset': new Date(result.resetTime).toISOString()
});
if (!result.allowed) {
return res.status(429).json({
error: 'Too many requests',
message: `Rate limit exceeded. Try again in ${Math.ceil((result.resetTime - Date.now()) / 1000)} seconds.`
});
}
next();
};
}
}
// Usage example
const rateLimiter = new DistributedRateLimiter({
windowMs: 60 * 1000, // 1 minute
maxRequests: 100,
keyGenerator: (req) => `rate_limit:${req.user?.id || req.ip}`,
skipSuccessfulRequests: false,
skipFailedRequests: false
});
app.use('/api/', rateLimiter.middleware());
These integration requirements provide comprehensive guidelines for successfully integrating with the MindPeeker platform, covering authentication, SDK usage, webhooks, data synchronization, security, and performance considerations.