码桶

发现社区成员的开源项目

sync.js2 KB
/**
 * sync.js - Upstash Redis 数据同步层
 * 在 localStorage 和 Upstash Redis 之间同步数据
 *
 * 工作原理:
 * - init(): 页面加载时从 Redis 拉取最新数据,覆盖到 localStorage
 * - save(): 数据变更时 fire-and-forget 推送到 Redis
 * - 如果 Redis 不可用(本地开发),静默回退到纯 localStorage 模式
 */

const DataSync = {
  kvAvailable: false,
  initialized: false,

  /**
   * 从 Redis 拉取数据到 localStorage
   * 前台和后台都会调用
   */
  async init() {
    if (this.initialized) return;

    try {
      const resp = await fetch('/api/data', { method: 'GET' });
      if (!resp.ok) throw new Error('API error: ' + resp.status);
      const data = await resp.json();

      // 只在 Redis 有实际数据时覆盖 localStorage
      // Redis 返回 null 表示尚未写入过,保留本地数据
      if (data.domains !== null && data.domains !== undefined) {
        localStorage.setItem('domain_market_data', JSON.stringify(data.domains));
      }
      if (data.config !== null && data.config !== undefined) {
        localStorage.setItem('domain_market_site_config', JSON.stringify(data.config));
      }

      this.kvAvailable = true;
    } catch (e) {
      console.log('[DataSync] Redis not available, using localStorage only');
      this.kvAvailable = false;
    }

    this.initialized = true;
  },

  /**
   * 推送数据到 Redis(fire-and-forget)
   * 仅在 Redis 可用且有有效 token 时执行
   */
  save(key, value) {
    if (!this.kvAvailable) return;

    // 只有 API 模式的 token 才能写 Redis
    const token = sessionStorage.getItem('admin_token');
    if (!token || token.startsWith('local_')) return;

    fetch('/api/data', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + token
      },
      body: JSON.stringify({ key, value })
    }).catch(e => console.error('[DataSync] Redis sync failed:', e));
  }
};