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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x | import {createClient, RedisClientOptions} from 'redis'; import {SessionInterface} from '../types'; import {SessionStorage} from '../session_storage'; import {sessionFromEntries, sessionEntries} from '../session-utils'; import {sanitizeShop} from '../../../utils/shop-validator'; type RedisClient = ReturnType<typeof createClient>; export interface RedisSessionStorageOptions extends RedisClientOptions { sessionKeyPrefix: string; onError?: (...args: any[]) => void; } 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: RedisClient; 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 deleteSessions(ids: string[]): Promise<boolean> { await this.ready; await this.client.del(ids.map((id) => this.fullKey(id))); return true; } public async findSessionsByShop(shop: string): Promise<SessionInterface[]> { await this.ready; const cleanShop = sanitizeShop(shop, true)!; const keys = await this.client.keys('*'); const results: SessionInterface[] = []; for (const key of keys) { const rawResult = await this.client.get(key); if (!rawResult) continue; const session = sessionFromEntries(JSON.parse(rawResult)); if (session.shop === cleanShop) results.push(session); } return results; } 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({ ...this.options, url: this.dbUrl.toString(), }); if (this.options.onError) { this.client.on('error', this.options.onError); } await this.client.connect(); } } |