blob: 4f367dcb1c31f031da05dd01796ebca313e48e95 [file]
import { Watcher } from 'casbin';
import { RedisClusterConnection, RedisConnection, RedisClient } from './redis';
import { ClusterNode, ClusterOptions, RedisOptions } from 'ioredis';
export interface WatcherOptions extends RedisOptions {
channel?: string;
}
export interface WatcherClusterOptions extends ClusterOptions {
channel?: string;
}
export class RedisWatcher implements Watcher {
private pubConnection: RedisConnection | RedisClusterConnection;
private subConnection: RedisConnection | RedisClusterConnection;
private callback: () => void;
private channel = 'casbin';
/**
* newWatcher creates a watcher on the single Redis.
* @param options - https://github.com/luin/ioredis/blob/master/API.md#new-redisport-host-options
* @example
* newWatcher('redis://user:password@redis-service.com:6379')
* newWatcher('//localhost:6379')
* newWatcher({
* port: 6379, // Redis port
* host: "127.0.0.1", // Redis host
* family: 4, // 4 (IPv4) or 6 (IPv6)
* password: "auth",
* db: 0,
* channel: "casbin"
* })
*/
public static async newWatcher(options?: WatcherOptions | string): Promise<RedisWatcher> {
let channel = '';
if (typeof options === 'object' && options.channel) {
channel = options.channel;
}
const pubConnection = new RedisConnection(options);
const subConnection = new RedisConnection(options);
return this.init(pubConnection, subConnection, channel);
}
/**
* newWatcherWithCluster creates a watcher on the Redis cluster.
* @param nodes - An array of nodes in the cluster, [{ port: number, host: string }]
* @param clusterOptions - https://github.com/luin/ioredis/blob/master/API.md#new-clusterstartupnodes-options
* @example
* newWatcherWithCluster([{ port: 6380, host: "127.0.0.1"}, { port: 6381, host: "127.0.0.1"})
*/
public static async newWatcherWithCluster(nodes: ClusterNode[] = [], clusterOptions: WatcherClusterOptions = {}): Promise<RedisWatcher> {
const pubConnection = new RedisClusterConnection(nodes, clusterOptions);
const subConnection = new RedisClusterConnection(nodes, clusterOptions);
return this.init(pubConnection, subConnection, clusterOptions.channel);
}
private static async init(
pubConnection: RedisConnection | RedisClusterConnection,
subConnection: RedisConnection | RedisClusterConnection,
channel?: string
): Promise<RedisWatcher> {
const watcher = new RedisWatcher();
watcher.pubConnection = pubConnection;
watcher.subConnection = subConnection;
if (channel) {
watcher.channel = channel;
}
const client = await watcher.subConnection.getRedisClient();
// @ts-ignore - subscribe is exists.
await client.subscribe(watcher.channel);
client.on('message', (chan: string) => {
if (chan !== watcher.channel) {
return;
}
if (watcher.callback) {
watcher.callback();
}
});
return watcher;
}
private constructor() {}
public async update(): Promise<boolean> {
const client = await this.pubConnection.getRedisClient();
// @ts-ignore - publish is exists.
await client.publish(this.channel, 'casbin rules updated');
return true;
}
public setUpdateCallback(callback: () => void) {
this.callback = callback;
}
public async close(): Promise<void> {
this.pubConnection.close();
this.subConnection.close();
}
}