chamesu
Version:
This package contains all packages of the chat application.
158 lines (125 loc) • 4.73 kB
text/typescript
/*
* Copyright (c) 2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/
import {Redis} from "ioredis";
export interface StatusOptions<T> {
recordType?: string,
redisPrefix: string,
redisSuffix?: string,
redisGetKey?: (key: T) => string
}
export interface OnlineIdsResponse {
data: Record<string, any>[],
meta: {
limit?: number,
offset?: number
total: number
}
}
export class RecordStatus<T extends Record<string, any> | undefined | number | string> {
constructor(public readonly redisDatabase : Redis, public readonly options: StatusOptions<T>) {
}
public async setOffline(id: string | number, key?: T) : Promise<void> {
await this.redisDatabase.zrem(this.getRedisKey(key), id);
await this.dropMeta(id, key);
}
public async setOnline(id: string | number, key?: T, meta?: Record<string, any>) : Promise<void> {
const seconds = (Date.now() / 1000).toFixed();
await this.redisDatabase.zadd(this.getRedisKey(key), seconds, id);
if(typeof meta !== 'undefined') {
await this.setMeta(id, meta, key);
}
}
async getOnlineCount(key?: T) {
return await this.redisDatabase.zcard(this.getRedisKey(key));
}
public async getOnline(key?: T, offset?: number, limit?: number) : Promise<OnlineIdsResponse> {
try {
let args : (string | number)[] = [
this.getRedisKey(key),
'+inf',
'-inf',
'WITHSCORES'
];
if(typeof offset !== 'undefined' || typeof limit !== 'undefined') {
offset = offset ?? 0;
limit = limit ?? 50;
args.push(offset);
args.push(limit);
}
// @ts-ignore
const set = await this.redisDatabase.zrevrangebyscore(...args);
let clients : Record<string, any>[] = [];
for(let i=0; i<set.length; i=i+2) {
const id: number | string = Number.isNaN(set[i]) ? set[i] : parseInt(set[i]);
let client : Record<string, any> = {
id: id,
meta: {
firstSeen: parseInt(set[i+1]) * 1000
}
};
if(typeof this.options.recordType !== 'undefined') {
client.type = this.options.recordType;
}
clients.push(client);
}
return {
data: clients,
meta: {
limit,
offset,
total: await this.getOnlineCount(key)
}};
} catch (e) {
console.log(e);
return {
data: [],
meta: {
limit,
offset,
total: 0
}
};
}
}
//--------------------------------------------------------------------
public async setMeta(id: string | number, meta: Record<string,any>, key?: T) {
const redisPath : string = this.getRedisMetaKey(id, key);
for(let index in meta) {
if(!meta.hasOwnProperty(index)) continue;
await this.redisDatabase.hset(redisPath, index, meta[index]);
}
}
public async setMetaProperty(id: string | number, metaKey: string, metaValue: any, key?: T) {
const redisPath : string = this.getRedisMetaKey(id, key);
await this.redisDatabase.hset(redisPath, metaKey, metaValue);
}
public async dropMeta(id: string | number, key?: T) {
const redisPath : string = this.getRedisMetaKey(id, key);
await this.redisDatabase.del(redisPath);
}
//--------------------------------------------------------------------
public getRedisKey(key?: T) : string {
let prefix : string = this.options.redisPrefix + '.status';
if(typeof this.options.redisSuffix !== 'undefined') {
prefix += '.' + this.options.redisSuffix;
}
if(typeof key === 'undefined') {
return prefix;
}
if(typeof this.options.redisGetKey !== 'undefined') {
return prefix + '.' + this.options.redisGetKey(key);
}
if(typeof key === 'string' || typeof key === 'number') {
return prefix + ':' + key;
}
return prefix;
}
public getRedisMetaKey(id: string | number, key?: T) {
const redisKey = this.getRedisKey(key);
return redisKey + 'meta:' + id;
}
}