Source: server/util/ReduxPersistServerCookieStorage.js

/* global WP_DEFINE_DEVELOPMENT */
import debugLib from 'debug';
import { INDEX_KEY, KEY_PREFIX } from '../../app/data/persistCookieSettings';
import isLocalOrDevelopment from './isLocalOrDevelopment';

const debug = debugLib('SlimmingWorld:ReduxPersistServerCookieStorage');

/**
 * Storage engine for redux-persist, which will store secure cookies on the server side.
 *
 * Uses callback syntax for compatibility with redux-persist
 *
 * Note: only the `INDEX_KEY` is prefixed with `KEY_PREFIX`, because redux-persist will prefix
 * the other keys for us
 */
class ReduxPersistServerCookieStorage {
  useSecure = !isLocalOrDevelopment();

  /**
   * @param cookies {object}
   * @param cookies.jar {object} The req.cookies object from express
   * @param cookies.set {function} The response.setCookie function from express
   * @param cookies.clear {function} The response.clearCookie function from express
   */
  constructor({ jar, set, clear }) {
    this.setCookie = set;
    this.clearCookie = clear;
    this.cookies = jar;
  }

  getItem = (key, callback) => {
    callback(null, this.cookies[key] || 'null');
  };

  setItem = (key, value, callback) => {
    const currentCookieValue = this.cookies[key];
    if (currentCookieValue !== value) {
      this.setCookie(key, value, { secure: this.useSecure });
    }

    this.getAllKeys((error, allKeys) => {
      if (!allKeys.includes(key)) {
        allKeys.push(key);
        this.setAllKeysCookie(allKeys);
      }
      callback(null);
    });
  };

  removeItem = (key, callback) => {
    this.clearCookie(key);

    this.getAllKeys((error, allKeys) => {
      this.setAllKeysCookie(allKeys.filter(k => k !== key));
      callback(null);
    });
  };

  setAllKeysCookie = allKeys => {
    const currentAllKeysCookie = this.cookies[`${KEY_PREFIX}${INDEX_KEY}`];
    const newAllKeysCookie = JSON.stringify(allKeys);

    if (currentAllKeysCookie !== newAllKeysCookie) {
      this.setCookie(`${KEY_PREFIX}${INDEX_KEY}`, newAllKeysCookie, { secure: this.useSecure });
    }
  };

  getAllKeys = callback => {
    const cookie = this.cookies[`${KEY_PREFIX}${INDEX_KEY}`];
    let parsedCookie;

    try {
      parsedCookie = cookie ? JSON.parse(cookie) : [];
    } catch (e) {
      debug(`Unable to parse cookie to JSON: "${cookie}"`);
      callback(null, []);
      return;
    }

    callback(null, parsedCookie);
  };
}

export default ReduxPersistServerCookieStorage;