storageService.js

11.12 KB
12/10/2025 04:50
JS
storageService.js
/**
 * Storage Service - บริการสำหรับจัดเก็บข้อมูลใน localStorage
 */
(function(global) {
  'use strict';

  /**
   * คลาส StorageService
   * บริการสำหรับจัดเก็บข้อมูลใน localStorage
   */
  class StorageService {
    constructor(editor) {
      this.editor = editor;
      this.prefix = 'editor_';
      this.encryptionEnabled = false;
      this.encryptionKey = null;
    }

    /**
     * บันทึกข้อมูล
     * @param {string} key - คีย์
     * @param {*} data - ข้อมูลที่จะบันทึก
     * @param {Object} options - ตัวเลือก (optional)
     */
    save(key, data, options = {}) {
      try {
        const prefixedKey = this.prefix + key;

        // แปลงข้อมูลเป็น JSON
        let serializedData = JSON.stringify({
          data,
          timestamp: Date.now(),
          version: options.version || '1.0.0'
        });

        // เข้ารหัสถ้าเปิดใช้งาน
        if (this.encryptionEnabled) {
          serializedData = this.encrypt(serializedData);
        }

        // บันทึกใน localStorage
        localStorage.setItem(prefixedKey, serializedData);

        // ส่งเหตุการณ์
        this.editor.emit('storage:saved', {key, data});

        if (this.editor.config.debug) {
          console.log(`Data saved to storage: ${key}`);
        }

        return true;
      } catch (error) {
        console.error(`Failed to save data to storage: ${key}`, error);

        // ส่งเหตุการณ์
        this.editor.emit('storage:save-error', {key, error});

        return false;
      }
    }

    /**
     * โหลดข้อมูล
     * @param {string} key - คีย์
     * @param {*} defaultValue - ค่าเริ่มต้น (optional)
     * @param {Object} options - ตัวเลือก (optional)
     * @returns {*} ข้อมูลที่โหลด
     */
    load(key, defaultValue = null, options = {}) {
      try {
        const prefixedKey = this.prefix + key;

        // โหลดจาก localStorage
        let serializedData = localStorage.getItem(prefixedKey);

        if (!serializedData) {
          return defaultValue;
        }

        // ถอดรหัสถ้าเปิดใช้งาน
        if (this.encryptionEnabled) {
          serializedData = this.decrypt(serializedData);
        }

        // แปลงจาก JSON
        const parsedData = JSON.parse(serializedData);

        // ตรวจสอบเวอร์ชัน
        if (options.version && parsedData.version !== options.version) {
          console.warn(`Version mismatch for ${key}: expected ${options.version}, got ${parsedData.version}`);

          // ส่งเหตุการณ์
          this.editor.emit('storage:version-mismatch', {
            key,
            expected: options.version,
            actual: parsedData.version
          });

          return defaultValue;
        }

        // ส่งเหตุการณ์
        this.editor.emit('storage:loaded', {key, data: parsedData.data});

        if (this.editor.config.debug) {
          console.log(`Data loaded from storage: ${key}`);
        }

        return parsedData.data;
      } catch (error) {
        console.error(`Failed to load data from storage: ${key}`, error);

        // ส่งเหตุการณ์
        this.editor.emit('storage:load-error', {key, error});

        return defaultValue;
      }
    }

    /**
     * ลบข้อมูล
     * @param {string} key - คีย์
     * @returns {boolean} สำเร็จหรือไม่
     */
    remove(key) {
      try {
        const prefixedKey = this.prefix + key;

        // ตรวจสอบว่ามีข้อมูลหรือไม่
        if (!localStorage.getItem(prefixedKey)) {
          return false;
        }

        // ลบข้อมูล
        localStorage.removeItem(prefixedKey);

        // ส่งเหตุการณ์
        this.editor.emit('storage:removed', {key});

        if (this.editor.config.debug) {
          console.log(`Data removed from storage: ${key}`);
        }

        return true;
      } catch (error) {
        console.error(`Failed to remove data from storage: ${key}`, error);

        // ส่งเหตุการณ์
        this.editor.emit('storage:remove-error', {key, error});

        return false;
      }
    }

    /**
     * ตรวจสอบว่ามีข้อมูลหรือไม่
     * @param {string} key - คีย์
     * @returns {boolean} มีข้อมูลหรือไม่
     */
    exists(key) {
      const prefixedKey = this.prefix + key;
      return localStorage.getItem(prefixedKey) !== null;
    }

    /**
     * รับคีย์ทั้งหมด
     * @returns {Array} รายการคีย์ทั้งหมด
     */
    getAllKeys() {
      const keys = [];

      for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);

        if (key && key.startsWith(this.prefix)) {
          // ลบ prefix
          keys.push(key.substring(this.prefix.length));
        }
      }

      return keys;
    }

    /**
     * ล้างข้อมูลทั้งหมด
     */
    clear() {
      const keys = this.getAllKeys();

      keys.forEach(key => {
        this.remove(key);
      });

      // ส่งเหตุการณ์
      this.editor.emit('storage:cleared');

      if (this.editor.config.debug) {
        console.log('All data cleared from storage');
      }
    }

    /**
     * รับขนาดข้อมูลทั้งหมด
     * @returns {number} ขนาดข้อมูล (bytes)
     */
    getSize() {
      let size = 0;

      for (let key in localStorage) {
        if (localStorage.hasOwnProperty(key) && key.startsWith(this.prefix)) {
          size += localStorage[key].length + key.length;
        }
      }

      return size;
    }

    /**
     * ตั้งค่าการเข้ารหัส
     * @param {boolean} enabled - เปิดใช้งานหรือไม่
     * @param {string} key - คีย์เข้ารหัส (optional)
     */
    setEncryption(enabled, key = null) {
      this.encryptionEnabled = enabled;

      if (enabled && key) {
        this.encryptionKey = key;
      }
    }

    /**
     * เข้ารหัสข้อมูล
     * @param {string} data - ข้อมูลที่จะเข้ารหัส
     * @returns {string} ข้อมูลที่เข้ารหัสแล้ว
     */
    encrypt(data) {
      if (!this.encryptionEnabled || !this.encryptionKey) {
        return data;
      }

      try {
        // ใช้งานง่ายๆ ด้วย XOR
        let encrypted = '';

        for (let i = 0; i < data.length; i++) {
          const charCode = data.charCodeAt(i);
          const keyChar = this.encryptionKey.charCodeAt(i % this.encryptionKey.length);
          encrypted += String.fromCharCode(charCode ^ keyChar);
        }

        // แปลงเป็น Base64
        return btoa(encrypted);
      } catch (error) {
        console.error('Failed to encrypt data:', error);
        return data;
      }
    }

    /**
     * ถอดรหัสข้อมูล
     * @param {string} encryptedData - ข้อมูลที่เข้ารหัส
     * @returns {string} ข้อมูลที่ถอดรหัสแล้ว
     */
    decrypt(encryptedData) {
      if (!this.encryptionEnabled || !this.encryptionKey) {
        return encryptedData;
      }

      try {
        // แปลงจาก Base64
        const data = atob(encryptedData);

        // ใช้งานง่ายๆ ด้วย XOR
        let decrypted = '';

        for (let i = 0; i < data.length; i++) {
          const charCode = data.charCodeAt(i);
          const keyChar = this.encryptionKey.charCodeAt(i % this.encryptionKey.length);
          decrypted += String.fromCharCode(charCode ^ keyChar);
        }

        return decrypted;
      } catch (error) {
        console.error('Failed to decrypt data:', error);
        return encryptedData;
      }
    }

    /**
     * ส่งออกข้อมูลเป็นไฟล์
     * @param {string} key - คีย์
     * @param {string} filename - ชื่อไฟล์
     * @param {string} type - ประเภทไฟล์ (optional)
     */
    exportToFile(key, filename, type = 'application/json') {
      try {
        const data = this.load(key);

        if (data === null) {
          throw new Error(`Data not found for key: ${key}`);
        }

        // สร้าง Blob
        const blob = new Blob([JSON.stringify(data, null, 2)], {type});

        // สร้าง URL
        const url = URL.createObjectURL(blob);

        // สร้างลิงก์ดาวน์โหลด
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);

        // ลบ URL
        URL.revokeObjectURL(url);

        // ส่งเหตุการณ์
        this.editor.emit('storage:exported', {key, filename});

        return true;
      } catch (error) {
        console.error(`Failed to export data to file: ${key}`, error);

        // ส่งเหตุการณ์
        this.editor.emit('storage:export-error', {key, error});

        return false;
      }
    }

    /**
     * นำเข้าข้อมูลจากไฟล์
     * @param {string} key - คีย์
     * @param {File} file - ไฟล์ที่จะนำเข้า
     * @returns {Promise} Promise ที่ส่งคืนผลการนำเข้า
     */
    importFromFile(key, file) {
      return new Promise((resolve, reject) => {
        try {
          const reader = new FileReader();

          reader.onload = (e) => {
            try {
              const data = JSON.parse(e.target.result);

              // บันทึกข้อมูล
              const success = this.save(key, data);

              if (success) {
                // ส่งเหตุการณ์
                this.editor.emit('storage:imported', {key, file});

                resolve(data);
              } else {
                reject(new Error(`Failed to save imported data for key: ${key}`));
              }
            } catch (error) {
              reject(new Error(`Failed to parse imported file: ${error.message}`));
            }
          };

          reader.onerror = () => {
            reject(new Error('Failed to read file'));
          };

          reader.readAsText(file);
        } catch (error) {
          reject(error);
        }
      });
    }
  }

  // เปิดเผยคลาส StorageService ทั่วโลก
  global.StorageService = StorageService;

})(typeof window !== 'undefined' ? window : this);