← back to Watches

sdk/omega-api-client.js

430 lines

/**
 * Omega Watch Price History API - JavaScript/TypeScript Client
 * @version 2.0.0
 * @description Official client library for the Omega Watch Price History API
 */

class OmegaWatchAPI {
  /**
   * Create a new API client instance
   * @param {Object} options - Configuration options
   * @param {string} options.baseURL - Base URL for the API (default: http://45.61.58.125:7600)
   * @param {number} options.timeout - Request timeout in milliseconds (default: 10000)
   * @param {string} options.apiKey - API key for authenticated requests (optional)
   */
  constructor(options = {}) {
    this.baseURL = options.baseURL || 'http://45.61.58.125:7600';
    this.timeout = options.timeout || 10000;
    this.apiKey = options.apiKey || null;
    this.ws = null;
  }

  /**
   * Make an HTTP request
   * @private
   */
  async _request(method, path, data = null, params = null) {
    const url = new URL(`${this.baseURL}${path}`);

    if (params) {
      Object.keys(params).forEach(key => {
        if (params[key] !== undefined && params[key] !== null) {
          url.searchParams.append(key, params[key]);
        }
      });
    }

    const options = {
      method,
      headers: {
        'Content-Type': 'application/json',
      },
      signal: AbortSignal.timeout(this.timeout),
    };

    if (this.apiKey) {
      options.headers['X-API-Key'] = this.apiKey;
    }

    if (data) {
      options.body = JSON.stringify(data);
    }

    try {
      const response = await fetch(url.toString(), options);

      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.error || `HTTP ${response.status}: ${response.statusText}`);
      }

      const contentType = response.headers.get('content-type');
      if (contentType && contentType.includes('application/json')) {
        return await response.json();
      }

      return await response.text();
    } catch (error) {
      if (error.name === 'AbortError') {
        throw new Error(`Request timeout after ${this.timeout}ms`);
      }
      throw error;
    }
  }

  /**
   * Health & System
   */

  /**
   * Get system health status
   * @returns {Promise<Object>} Health information
   */
  async getHealth() {
    return this._request('GET', '/api/health');
  }

  /**
   * Watches
   */

  /**
   * Get all watches with optional filtering and pagination
   * @param {Object} filters - Filter options
   * @param {string} filters.series - Filter by series
   * @param {number} filters.minYear - Minimum year introduced
   * @param {number} filters.maxYear - Maximum year introduced
   * @param {number} filters.minPrice - Minimum current price
   * @param {number} filters.maxPrice - Maximum current price
   * @param {number} filters.page - Page number (default: 1)
   * @param {number} filters.limit - Items per page (default: 50)
   * @returns {Promise<Object>} Paginated watch list
   */
  async getWatches(filters = {}) {
    return this._request('GET', '/api/watches', null, filters);
  }

  /**
   * Get watch by ID
   * @param {string} id - Watch ID
   * @returns {Promise<Object>} Watch details and price history
   */
  async getWatchById(id) {
    return this._request('GET', `/api/watches/${id}/history`);
  }

  /**
   * Get trending watches
   * @param {number} limit - Number of results (default: 10)
   * @returns {Promise<Object>} Trending watches
   */
  async getTrending(limit = 10) {
    return this._request('GET', '/api/watches/trending', null, { limit });
  }

  /**
   * Search
   */

  /**
   * Search watches with advanced filters
   * @param {string} query - Search query
   * @param {Object} filters - Additional filters
   * @returns {Promise<Object>} Search results
   */
  async search(query, filters = {}) {
    return this._request('GET', '/api/search', null, { q: query, ...filters });
  }

  /**
   * Analytics
   */

  /**
   * Get price predictions for a watch
   * @param {string} id - Watch ID
   * @returns {Promise<Object>} Price predictions
   */
  async getPricePredictions(id) {
    return this._request('GET', `/api/price-predictions/${id}`);
  }

  /**
   * Get market statistics
   * @returns {Promise<Object>} Comprehensive statistics
   */
  async getStatistics() {
    return this._request('GET', '/api/statistics');
  }

  /**
   * Get advanced analytics predictions
   * @returns {Promise<Object>} ML-generated predictions
   */
  async getAnalyticsPredictions() {
    return this._request('GET', '/api/analytics/predictions');
  }

  /**
   * Get market analysis
   * @returns {Promise<Object>} Market analysis data
   */
  async getMarketAnalysis() {
    return this._request('GET', '/api/analytics/market');
  }

  /**
   * Get statistical insights
   * @returns {Promise<Object>} Statistical insights
   */
  async getStatisticalInsights() {
    return this._request('GET', '/api/analytics/statistics');
  }

  /**
   * Get investment opportunities
   * @returns {Promise<Object>} Investment opportunities
   */
  async getInvestmentOpportunities() {
    return this._request('GET', '/api/analytics/investment-opportunities');
  }

  /**
   * Get risk metrics
   * @returns {Promise<Object>} Risk-adjusted metrics
   */
  async getRiskMetrics() {
    return this._request('GET', '/api/analytics/risk-metrics');
  }

  /**
   * Collections
   */

  /**
   * Get all collections with statistics
   * @returns {Promise<Object>} Collections data
   */
  async getCollections() {
    return this._request('GET', '/api/collections');
  }

  /**
   * Comparison
   */

  /**
   * Compare multiple watches
   * @param {string[]} watchIds - Array of watch IDs to compare
   * @returns {Promise<Object>} Comparison results
   */
  async compareWatches(watchIds) {
    return this._request('POST', '/api/compare', { watchIds });
  }

  /**
   * Export
   */

  /**
   * Export data in specified format
   * @param {string} format - Export format ('json' or 'csv')
   * @returns {Promise<string|Object>} Exported data
   */
  async exportData(format = 'json') {
    return this._request('GET', `/api/export/${format}`);
  }

  /**
   * Watchlist
   */

  /**
   * Add watch to watchlist
   * @param {string} userId - User ID
   * @param {string} watchId - Watch ID
   * @returns {Promise<Object>} Updated watchlist
   */
  async addToWatchlist(userId, watchId) {
    return this._request('POST', '/api/watchlist', {
      userId,
      watchId,
      action: 'add'
    });
  }

  /**
   * Remove watch from watchlist
   * @param {string} userId - User ID
   * @param {string} watchId - Watch ID
   * @returns {Promise<Object>} Updated watchlist
   */
  async removeFromWatchlist(userId, watchId) {
    return this._request('POST', '/api/watchlist', {
      userId,
      watchId,
      action: 'remove'
    });
  }

  /**
   * Get user watchlist
   * @param {string} userId - User ID
   * @returns {Promise<Object>} User watchlist
   */
  async getWatchlist(userId) {
    return this._request('GET', `/api/watchlist/${userId}`);
  }

  /**
   * Admin
   */

  /**
   * Clear cache (requires admin access)
   * @returns {Promise<Object>} Success response
   */
  async clearCache() {
    return this._request('POST', '/api/admin/clear-cache');
  }

  /**
   * Create database backup (requires admin access)
   * @returns {Promise<Object>} Backup information
   */
  async createBackup() {
    return this._request('GET', '/api/admin/backup');
  }

  /**
   * WebSocket
   */

  /**
   * Connect to WebSocket for real-time updates
   * @param {Object} options - WebSocket options
   * @param {Function} options.onUpdate - Callback for updates
   * @param {Function} options.onError - Callback for errors
   * @param {Function} options.onClose - Callback for close event
   * @returns {WebSocket} WebSocket instance
   */
  connectWebSocket(options = {}) {
    const wsURL = this.baseURL.replace('http', 'ws');
    this.ws = new WebSocket(wsURL);

    this.ws.onopen = () => {
      console.log('WebSocket connected');
    };

    this.ws.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        if (options.onUpdate) {
          options.onUpdate(data);
        }
      } catch (error) {
        console.error('WebSocket message parse error:', error);
      }
    };

    this.ws.onerror = (error) => {
      console.error('WebSocket error:', error);
      if (options.onError) {
        options.onError(error);
      }
    };

    this.ws.onclose = () => {
      console.log('WebSocket disconnected');
      if (options.onClose) {
        options.onClose();
      }
    };

    return this.ws;
  }

  /**
   * Subscribe to watch updates via WebSocket
   * @param {string} watchId - Watch ID to subscribe to
   */
  subscribeToWatch(watchId) {
    if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
      throw new Error('WebSocket not connected. Call connectWebSocket() first.');
    }

    this.ws.send(JSON.stringify({
      type: 'subscribe',
      watchId
    }));
  }

  /**
   * Disconnect WebSocket
   */
  disconnectWebSocket() {
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  }

  /**
   * Helper Methods
   */

  /**
   * Calculate appreciation for a watch
   * @param {Object} watch - Watch object with priceHistory
   * @returns {number} Appreciation percentage
   */
  static calculateAppreciation(watch) {
    if (!watch.priceHistory || watch.priceHistory.length < 2) {
      return 0;
    }

    const firstPrice = watch.priceHistory[0].price;
    const lastPrice = watch.priceHistory[watch.priceHistory.length - 1].price;

    return ((lastPrice - firstPrice) / firstPrice) * 100;
  }

  /**
   * Get current price for a watch
   * @param {Object} watch - Watch object with priceHistory
   * @returns {number} Current price
   */
  static getCurrentPrice(watch) {
    if (!watch.priceHistory || watch.priceHistory.length === 0) {
      return 0;
    }

    return watch.priceHistory[watch.priceHistory.length - 1].price;
  }

  /**
   * Format price as USD
   * @param {number} price - Price value
   * @returns {string} Formatted price
   */
  static formatPrice(price) {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD',
      minimumFractionDigits: 0,
      maximumFractionDigits: 0
    }).format(price);
  }
}

// Export for different module systems
if (typeof module !== 'undefined' && module.exports) {
  module.exports = OmegaWatchAPI;
}

if (typeof window !== 'undefined') {
  window.OmegaWatchAPI = OmegaWatchAPI;
}

export default OmegaWatchAPI;