forked from Potat-Industries/eval-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathredis.ts
More file actions
135 lines (111 loc) · 3.25 KB
/
redis.ts
File metadata and controls
135 lines (111 loc) · 3.25 KB
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import Redis from 'ioredis';
import Logger from '../util/logger.js';
import config from '../../config.json' with { type: 'json' };
const validateData = (data: unknown): data is string | number | boolean | object => {
if (typeof data === 'string') {
return data.length <= 10000;
} else if (typeof data === 'number') {
return data.toString().length <= 10000;
} else if (typeof data === 'boolean') {
return true;
} else if (typeof data === 'object') {
return JSON.stringify(data).length <= 10000;
} else {
return false;
}
};
class PotatStore {
static #instance: PotatStore;
#client: Redis;
#isReady = false;
get ready() {
return this.#isReady;
}
public constructor() {
this.#client = new Redis({
host: config.redisHost ?? 'localhost',
port: config.redisPort ?? 6777,
});
this.#client.on('close', (err: Error) => {
this.#isReady = false;
Logger.error(`Redis Error: ${err}`);
});
this.#client.on('error', (err) => Logger.error(`Redis Error: ${err}`));
this.#client.on('ready', () => this.#isReady = true);
this.#client.once('ready', () => Logger.debug('Redis Connected'));
}
public static get getInstance(): PotatStore {
if (!this.#instance) {
this.#instance = new PotatStore();
}
return this.#instance;
}
public async hset(
primaryKey: string,
key: string,
value: unknown,
): Promise<number> {
if (typeof key !== 'string' || key.length > 100) {
throw new Error('Key mmust be a string less than 100 characters');
}
if (!validateData(value)) {
throw new Error(`Invalid value input.`);
}
const data = typeof value === 'object' ? JSON.stringify(value) : value.toString();
if (data.length > 10000) {
throw new Error('Data too large to store in Redis');
}
return this.#client.hset(primaryKey, key, data);
}
public async hdel(key: string | Buffer, ...fields: string[]): Promise<number> {
return this.#client.hdel(key, ...fields);
}
public async del(key: string | Buffer): Promise<number> {
return this.#client.del(key);
}
public async hmget<T = unknown>(
primaryKey: string,
key: string,
): Promise<T | undefined> {
const data = await this.#client.hmget(primaryKey, key);
if (!data[0]){
return;
}
try {
return JSON.parse(data[0]);
} catch {
return data[0] as T;
}
}
public async hgetall<T = unknown>(key: string | Buffer): Promise<Map<string, T>> {
const data = await this.#client.hgetall(key);
const out = new Map<string, T>();
for (const k in data) {
const value = data[k];
if (!value) {
continue;
}
let parsedValue: T;
try {
parsedValue = JSON.parse(value);
} catch {
parsedValue = value as T;
}
out.set(k, parsedValue);
}
return out;
}
public async hlen(key: string | Buffer): Promise<number> {
return this.#client.hlen(key);
}
public async hexpire(
key: string | Buffer,
seconds: number,
field: string,
mode: 'NX' | 'XX' | 'GT' | 'LT' = 'NX',
): Promise<unknown> {
return this.#client.call('HEXPIRE', key, seconds, mode, 'FIELDS', 1, field);
}
}
const store = PotatStore.getInstance;
export default store;