All files / src/auth/session/storage redis.ts

24.62% Statements 16/65
0% Branches 0/19
5.26% Functions 1/19
25% Lines 16/64

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89  22x 22x 22x 22x 22x 22x     22x                   22x     22x                             22x                                     22x                             22x                       22x     22x    
import {createClient, RedisClientType} from 'redis';
 
import {SessionInterface} from '../types';
import {SessionStorage} from '../session_storage';
import {sessionFromEntries, sessionEntries} from '../session-utils';
 
export interface RedisSessionStorageOptions {
  sessionKeyPrefix: string;
}
const defaultRedisSessionStorageOptions: RedisSessionStorageOptions = {
  sessionKeyPrefix: 'shopify_sessions',
};

export class RedisSessionStorage implements SessionStorage {
  static withCredentials(
    host: string,
    db: number,
    username: string,
    password: string,
    opts: Partial<RedisSessionStorageOptions>,
  ) {
    return new RedisSessionStorage(
      new URL(
        `redis://${encodeURIComponent(username)}:${encodeURIComponent(
          password,
        )}@${host}/${db}`,
      ),
      opts,
    );
  }
 
  public readonly ready: Promise<void>;
  private options: RedisSessionStorageOptions;
  private client: RedisClientType;
 
  constructor(
    private dbUrl: URL,
    opts: Partial<RedisSessionStorageOptions> = {},
  ) {
    if (typeof this.dbUrl === 'string') {
      this.dbUrl = new URL(this.dbUrl);
    }
    this.options = {...defaultRedisSessionStorageOptions, ...opts};
    this.ready = this.init();
  }

  public async storeSession(session: SessionInterface): Promise<boolean> {
    await this.ready;

    await this.client.set(
      this.fullKey(session.id),
      JSON.stringify(sessionEntries(session)),
    );
    return true;
  }
 
  public async loadSession(id: string): Promise<SessionInterface | undefined> {
    await this.ready;

    let rawResult: any = await this.client.get(this.fullKey(id));

    if (!rawResult) return undefined;
    rawResult = JSON.parse(rawResult);

    return sessionFromEntries(rawResult);
  }

  public async deleteSession(id: string): Promise<boolean> {
    await this.ready;
    await this.client.del(this.fullKey(id));
    return true;
  }

  public async disconnect(): Promise<void> {
    await this.client.quit();
  }
 
  private fullKey(name: string): string {
    return `${this.options.sessionKeyPrefix}_${name}`;
  }
 
  private async init() {
    this.client = createClient({
      url: this.dbUrl.toString(),
    });
    await this.client.connect();
  }
}