( ′∀`)σ≡σ☆))Д′)レ(゚∀゚;)ヘ=З=З=Зε≡(ノ´_ゝ`)ノ HEX
HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux mail.thebrand.ai 6.8.0-107-generic #107-Ubuntu SMP PREEMPT_DYNAMIC Fri Mar 13 19:51:50 UTC 2026 x86_64
User: www-data (33)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: /var/www/html/tmpr/../tmpr/../tmpr/..//tmpr/../tmpr/../wowZ/ratelimit.md
// Rate Limiter for Fabric.js Layer Modifications
class FabricRateLimiter {
  constructor(options = {}) {
    this.maxRequests = options.maxRequests || 5; // Max requests allowed
    this.timeWindow = options.timeWindow || 10000; // Time window in ms (10 seconds)
    this.cooldownPeriod = options.cooldownPeriod || 5000; // Cooldown after limit hit (5 seconds)
    
    this.requests = [];
    this.isLocked = false;
    this.lockEndTime = null;
    this.pendingOperation = null;
    this.debounceTimer = null;
    this.debounceDelay = options.debounceDelay || 500; // Debounce delay in ms
  }

  // Check if rate limit is exceeded
  isRateLimited() {
    const now = Date.now();
    
    // If currently locked, check if cooldown period has passed
    if (this.isLocked) {
      if (now < this.lockEndTime) {
        return {
          limited: true,
          error: 'Save rate limit exceeded. Please wait a moment.',
          waitTime: Math.ceil((this.lockEndTime - now) / 1000)
        };
      } else {
        // Cooldown period over, unlock
        this.isLocked = false;
        this.lockEndTime = null;
        this.requests = [];
      }
    }

    // Remove requests outside the time window
    this.requests = this.requests.filter(time => now - time < this.timeWindow);

    // Check if limit exceeded
    if (this.requests.length >= this.maxRequests) {
      this.isLocked = true;
      this.lockEndTime = now + this.cooldownPeriod;
      return {
        limited: true,
        error: 'Save rate limit exceeded. Please wait a moment.',
        waitTime: Math.ceil(this.cooldownPeriod / 1000)
      };
    }

    return { limited: false };
  }

  // Record a request
  recordRequest() {
    this.requests.push(Date.now());
  }

  // Attempt to execute an operation with rate limiting
  async execute(operation) {
    const check = this.isRateLimited();
    
    if (check.limited) {
      return {
        success: false,
        error: check.error,
        waitTime: check.waitTime
      };
    }

    this.recordRequest();
    
    try {
      const result = await operation();
      return { success: true, data: result };
    } catch (error) {
      return { success: false, error: error.message };
    }
  }

  // Debounced execution - only executes after user stops making changes
  debounce(operation) {
    clearTimeout(this.debounceTimer);
    this.pendingOperation = operation;

    return new Promise((resolve) => {
      this.debounceTimer = setTimeout(async () => {
        const result = await this.execute(this.pendingOperation);
        resolve(result);
      }, this.debounceDelay);
    });
  }

  // Get current status
  getStatus() {
    const now = Date.now();
    this.requests = this.requests.filter(time => now - time < this.timeWindow);
    
    return {
      isLocked: this.isLocked,
      requestCount: this.requests.length,
      maxRequests: this.maxRequests,
      remainingRequests: Math.max(0, this.maxRequests - this.requests.length),
      waitTime: this.isLocked ? Math.ceil((this.lockEndTime - now) / 1000) : 0
    };
  }

  // Reset the limiter
  reset() {
    this.requests = [];
    this.isLocked = false;
    this.lockEndTime = null;
    clearTimeout(this.debounceTimer);
  }
}

// Example usage with Fabric.js
class FabricCanvasManager {
  constructor(canvasId, options = {}) {
    this.canvas = new fabric.Canvas(canvasId);
    this.rateLimiter = new FabricRateLimiter(options);
    this.setupEventListeners();
  }

  setupEventListeners() {
    // Track object modifications
    this.canvas.on('object:modified', () => {
      this.handleModification();
    });

    // Track object additions
    this.canvas.on('object:added', () => {
      this.handleModification();
    });

    // Track object removals
    this.canvas.on('object:removed', () => {
      this.handleModification();
    });
  }

  // Handle modifications with debouncing and rate limiting
  async handleModification() {
    const result = await this.rateLimiter.debounce(() => this.saveToServer());
    
    if (!result.success) {
      this.showError(result.error, result.waitTime);
    } else {
      this.showSuccess('Changes saved successfully');
    }
  }

  // Simulate server save
  async saveToServer() {
    const canvasData = JSON.stringify(this.canvas.toJSON());
    
    // Simulate API call
    return new Promise((resolve) => {
      setTimeout(() => {
        console.log('Saving to server:', canvasData.substring(0, 100) + '...');
        resolve({ saved: true, timestamp: Date.now() });
      }, 300);
    });
  }

  // Manual save with immediate rate limit check
  async manualSave() {
    const result = await this.rateLimiter.execute(() => this.saveToServer());
    
    if (!result.success) {
      this.showError(result.error, result.waitTime);
      return false;
    } else {
      this.showSuccess('Changes saved successfully');
      return true;
    }
  }

  showError(message, waitTime) {
    console.error(message, waitTime ? `Wait ${waitTime} seconds` : '');
    // You can replace this with your UI notification system
    alert(`${message}${waitTime ? ` Please wait ${waitTime} seconds.` : ''}`);
  }

  showSuccess(message) {
    console.log(message);
    // You can replace this with your UI notification system
  }

  getStatus() {
    return this.rateLimiter.getStatus();
  }
}

// Usage Example:
/*
const canvasManager = new FabricCanvasManager('myCanvas', {
  maxRequests: 5,        // Max 5 saves
  timeWindow: 10000,     // Within 10 seconds
  cooldownPeriod: 5000,  // 5 second cooldown if exceeded
  debounceDelay: 500     // Wait 500ms after last change
});

// Check status
console.log(canvasManager.getStatus());

// Manual save
canvasManager.manualSave();
*/