@similie/hyphen-command-server-types 1.0.0 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/models/index.d.ts +1 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1 -1
- package/dist/models/index.js.map +1 -1
- package/dist/modules/index.d.ts +2 -2
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +2 -2
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/loader.d.ts +1 -1
- package/dist/modules/loader.d.ts.map +1 -1
- package/dist/modules/types.d.ts +1 -1
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/services/index.d.ts +6 -2
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +6 -2
- package/dist/services/index.js.map +1 -1
- package/dist/services/jwt.d.ts +12 -0
- package/dist/services/jwt.d.ts.map +1 -0
- package/dist/services/jwt.js +36 -0
- package/dist/services/jwt.js.map +1 -0
- package/dist/services/leader-lock.d.ts.map +1 -1
- package/dist/services/leader-lock.js +1 -1
- package/dist/services/leader-lock.js.map +1 -1
- package/dist/services/objectq.d.ts +39 -0
- package/dist/services/objectq.d.ts.map +1 -0
- package/dist/services/objectq.js +201 -0
- package/dist/services/objectq.js.map +1 -0
- package/dist/services/secrets.d.ts +14 -0
- package/dist/services/secrets.d.ts.map +1 -0
- package/dist/services/secrets.js +44 -0
- package/dist/services/secrets.js.map +1 -0
- package/dist/services/sql.d.ts +27 -0
- package/dist/services/sql.d.ts.map +1 -0
- package/dist/services/sql.js +86 -0
- package/dist/services/sql.js.map +1 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/utils.d.ts +1 -1
- package/dist/tools/utils.d.ts.map +1 -1
- package/package.json +11 -1
- package/CHANGELOG.md +0 -5
- package/src/index.ts +0 -4
- package/src/models/base-model.ts +0 -13
- package/src/models/index.ts +0 -1
- package/src/modules/index.ts +0 -2
- package/src/modules/loader.ts +0 -67
- package/src/modules/types.ts +0 -40
- package/src/services/index.ts +0 -2
- package/src/services/leader-lock.ts +0 -113
- package/src/services/redis.ts +0 -69
- package/src/tools/index.ts +0 -1
- package/src/tools/utils.ts +0 -349
- package/tsconfig.json +0 -46
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Client } from "pg";
|
|
2
|
+
export class SQLService {
|
|
3
|
+
_triggers;
|
|
4
|
+
_listeners;
|
|
5
|
+
static instance;
|
|
6
|
+
_client;
|
|
7
|
+
listerCallbacks = {};
|
|
8
|
+
constructor(_triggers = [], _listeners = {}) {
|
|
9
|
+
this._triggers = _triggers;
|
|
10
|
+
this._listeners = _listeners;
|
|
11
|
+
this._client = new Client({
|
|
12
|
+
host: process.env.DB_HOST,
|
|
13
|
+
port: Number(process.env.DB_PORT || 5432),
|
|
14
|
+
user: process.env.DB_USER,
|
|
15
|
+
password: process.env.DB_PASSWORD,
|
|
16
|
+
database: process.env.DB_DATABASE,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
get client() {
|
|
20
|
+
return this._client;
|
|
21
|
+
}
|
|
22
|
+
static get i() {
|
|
23
|
+
if (!this.instance) {
|
|
24
|
+
throw new Error("SQLService not initialized. Call setup() first.");
|
|
25
|
+
}
|
|
26
|
+
return this.instance;
|
|
27
|
+
}
|
|
28
|
+
static setup(triggers = [], listeners = {}) {
|
|
29
|
+
if (!SQLService.instance) {
|
|
30
|
+
SQLService.instance = new SQLService(triggers, listeners);
|
|
31
|
+
}
|
|
32
|
+
return SQLService.instance;
|
|
33
|
+
}
|
|
34
|
+
getStartTriggersSQL() {
|
|
35
|
+
return this._triggers;
|
|
36
|
+
}
|
|
37
|
+
listenToChannel = async (channel) => {
|
|
38
|
+
const rawClient = this._client;
|
|
39
|
+
if (!rawClient) {
|
|
40
|
+
throw new Error("Could not obtain PG raw client. Check TypeORM driver internals.");
|
|
41
|
+
}
|
|
42
|
+
await rawClient.query(`LISTEN ${channel}`);
|
|
43
|
+
console.log(`[PG-LISTENER] Listening for ${channel}`);
|
|
44
|
+
};
|
|
45
|
+
applyClientListeners = () => {
|
|
46
|
+
this.client.on("notification", async (msg) => {
|
|
47
|
+
try {
|
|
48
|
+
// console.log("Notification received:", msg);
|
|
49
|
+
const payload = JSON.parse(msg.payload);
|
|
50
|
+
for (const callback of this.listerCallbacks[payload.context] || []) {
|
|
51
|
+
await callback(payload);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
console.error(`[PG-LISTENER] Failed to process payload:`, err);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
this.client.on("error", (err) => {
|
|
59
|
+
console.error(`[PG-LISTENER] PG error, reconnecting in 5 seconds`, err);
|
|
60
|
+
// setTimeout(() => this.listenToChannel(channel), 5000);
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
seedTriggers = async () => {
|
|
64
|
+
const triggers = this.getStartTriggersSQL();
|
|
65
|
+
await this._client.connect();
|
|
66
|
+
for (const triggerSQL of triggers) {
|
|
67
|
+
await this._client.query(triggerSQL);
|
|
68
|
+
}
|
|
69
|
+
for (const key of Object.keys(this._listeners)) {
|
|
70
|
+
if (!this._listeners[key]) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
await this.listenToChannel(this._listeners[key]);
|
|
74
|
+
}
|
|
75
|
+
this.applyClientListeners();
|
|
76
|
+
};
|
|
77
|
+
addListener = async (key, cb) => {
|
|
78
|
+
const channel = this._listeners[key];
|
|
79
|
+
if (!channel) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.listerCallbacks[key] = this.listerCallbacks[key] || [];
|
|
83
|
+
this.listerCallbacks[key].push(cb);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=sql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../../src/services/sql.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./utils";
|
|
1
|
+
export * from "./utils.js";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
|
package/dist/tools/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./utils";
|
|
1
|
+
export * from "./utils.js";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
|
package/dist/tools/utils.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/tools/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/tools/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@similie/hyphen-command-server-types",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Adds common types for Hyphen Command Center modules.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
9
|
"build": "tsc",
|
|
@@ -20,14 +21,23 @@
|
|
|
20
21
|
"@similie/model-connect-entities": "^1.1.4",
|
|
21
22
|
"crypto-js": "^4.2.0",
|
|
22
23
|
"ioredis": "^5.8.2",
|
|
24
|
+
"jsonwebtoken": "^9.0.3",
|
|
25
|
+
"pg": "^8.16.3",
|
|
23
26
|
"redis": "^5.10.0",
|
|
24
27
|
"redlock": "5.0.0-beta.2",
|
|
25
28
|
"ts-node-dev": "^2.0.0",
|
|
26
29
|
"uuid": "^13.0.0"
|
|
27
30
|
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
28
36
|
"devDependencies": {
|
|
29
37
|
"@types/crypto-js": "^4.2.2",
|
|
38
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
30
39
|
"@types/node": "^24.7.0",
|
|
40
|
+
"@types/pg": "^8.16.0",
|
|
31
41
|
"standard-version": "^9.5.0",
|
|
32
42
|
"typescript": "^5.9.3"
|
|
33
43
|
}
|
package/CHANGELOG.md
DELETED
package/src/index.ts
DELETED
package/src/models/base-model.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { type UUID as _UUID } from "@similie/ellipsies";
|
|
2
|
-
export type UUID = _UUID;
|
|
3
|
-
export interface BaseModel {
|
|
4
|
-
id: number;
|
|
5
|
-
createdAt: Date;
|
|
6
|
-
updatedAt: Date;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface BaseUIDModel {
|
|
10
|
-
uid: UUID; // TODO: needs to be UUID
|
|
11
|
-
createdAt: Date;
|
|
12
|
-
updatedAt: Date;
|
|
13
|
-
}
|
package/src/models/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./base-model";
|
package/src/modules/index.ts
DELETED
package/src/modules/loader.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// src/modules/loader.ts
|
|
2
|
-
import type { HyphenModule, ModuleContext, LoadedModule } from "./types";
|
|
3
|
-
|
|
4
|
-
export async function loadModulesFromEnv(
|
|
5
|
-
ctx: ModuleContext,
|
|
6
|
-
): Promise<LoadedModule[]> {
|
|
7
|
-
const raw = process.env.HYPHEN_MODULES?.trim();
|
|
8
|
-
if (!raw) return [];
|
|
9
|
-
|
|
10
|
-
const specList = raw
|
|
11
|
-
.split(",")
|
|
12
|
-
.map((s) => s.trim())
|
|
13
|
-
.filter(Boolean);
|
|
14
|
-
|
|
15
|
-
const loaded: LoadedModule[] = [];
|
|
16
|
-
|
|
17
|
-
for (const spec of specList) {
|
|
18
|
-
// spec can be:
|
|
19
|
-
// - "hyphen-rtsp-module"
|
|
20
|
-
// - "@similie/hyphen-rtsp-module"
|
|
21
|
-
// - "file:./dist/my-module.js" (dev)
|
|
22
|
-
try {
|
|
23
|
-
const imported = await import(spec);
|
|
24
|
-
const candidate: HyphenModule =
|
|
25
|
-
imported.default ?? imported.module ?? imported;
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
!candidate ||
|
|
29
|
-
typeof candidate.init !== "function" ||
|
|
30
|
-
!candidate.name
|
|
31
|
-
) {
|
|
32
|
-
ctx.log(`[modules] invalid module export: ${spec}`, { spec });
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
ctx.log(`[modules] init ${candidate.name}`, {
|
|
37
|
-
spec,
|
|
38
|
-
version: candidate.version,
|
|
39
|
-
});
|
|
40
|
-
const res = await candidate.init(ctx);
|
|
41
|
-
loaded.push({ mod: candidate, res });
|
|
42
|
-
ctx.log(`[modules] ready ${candidate.name}`, { spec });
|
|
43
|
-
} catch (e: any) {
|
|
44
|
-
ctx.log(`[modules] failed to load: ${spec}`, {
|
|
45
|
-
error: e?.message ?? String(e),
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return loaded;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export async function shutdownModules(
|
|
54
|
-
ctx: ModuleContext,
|
|
55
|
-
loaded: LoadedModule[],
|
|
56
|
-
) {
|
|
57
|
-
for (const { mod, res } of loaded) {
|
|
58
|
-
try {
|
|
59
|
-
res && (await res?.shutdown?.());
|
|
60
|
-
ctx.log(`[modules] shutdown ${mod.name}`);
|
|
61
|
-
} catch (e: any) {
|
|
62
|
-
ctx.log(`[modules] shutdown failed ${mod.name}`, {
|
|
63
|
-
error: e?.message ?? String(e),
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
package/src/modules/types.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// src/modules/types.ts
|
|
2
|
-
import type { RedisCache, LeaderElector } from "../services"; // or your Redis wrapper type
|
|
3
|
-
import type { Ellipsies } from "@similie/ellipsies";
|
|
4
|
-
// If you have an Ellipsies instance type, use it. Otherwise keep as `any` and tighten later.
|
|
5
|
-
export type EllipsiesInstance = Ellipsies;
|
|
6
|
-
|
|
7
|
-
export type CommandCenterSystemIdentity = {
|
|
8
|
-
name: string;
|
|
9
|
-
identity: string;
|
|
10
|
-
host: string;
|
|
11
|
-
port: number;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export type ModuleContext = {
|
|
15
|
-
ellipsies: EllipsiesInstance;
|
|
16
|
-
redis: typeof RedisCache;
|
|
17
|
-
leader: LeaderElector;
|
|
18
|
-
identity?: CommandCenterSystemIdentity;
|
|
19
|
-
log: (msg: string, extra?: Record<string, any>) => void;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type ModuleInitResult = {
|
|
23
|
-
// Optional cleanup hook called on shutdown
|
|
24
|
-
shutdown?: () => Promise<void> | void;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export interface HyphenModule {
|
|
28
|
-
name: string;
|
|
29
|
-
version?: string;
|
|
30
|
-
|
|
31
|
-
// Called once per process on boot
|
|
32
|
-
init(
|
|
33
|
-
ctx: ModuleContext,
|
|
34
|
-
): Promise<ModuleInitResult | void> | (ModuleInitResult | void);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export type LoadedModule = {
|
|
38
|
-
mod: HyphenModule;
|
|
39
|
-
res?: ModuleInitResult | void;
|
|
40
|
-
};
|
package/src/services/index.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "events";
|
|
2
|
-
import { Redis } from "ioredis";
|
|
3
|
-
import Redlock, { Lock } from "redlock";
|
|
4
|
-
import { randomUUID } from "crypto";
|
|
5
|
-
|
|
6
|
-
export type LeaderEvents = {
|
|
7
|
-
elected: () => void;
|
|
8
|
-
revoked: () => void;
|
|
9
|
-
error: (err: Error) => void;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export class LeaderElector extends EventEmitter {
|
|
13
|
-
private static instance: LeaderElector;
|
|
14
|
-
private redlock!: Redlock;
|
|
15
|
-
private redis!: Redis;
|
|
16
|
-
private renewTimer?: NodeJS.Timeout;
|
|
17
|
-
private acquireTimer?: NodeJS.Timeout;
|
|
18
|
-
private lock?: Lock | undefined;
|
|
19
|
-
|
|
20
|
-
private readonly lockKey = "mqtt:leader:lock";
|
|
21
|
-
private readonly ttlMs = 10000; // lock TTL
|
|
22
|
-
private readonly renewEveryMs = 5000; // extend interval (< TTL)
|
|
23
|
-
private readonly retryDelayMs = 1500; // backoff when not leader
|
|
24
|
-
private readonly instanceId = randomUUID(); // for logging/diagnostics
|
|
25
|
-
|
|
26
|
-
private constructor() {
|
|
27
|
-
super();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static get(): LeaderElector {
|
|
31
|
-
if (!this.instance) this.instance = new LeaderElector();
|
|
32
|
-
return this.instance;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
init(redisUrl: string) {
|
|
36
|
-
this.redis = new Redis(redisUrl);
|
|
37
|
-
this.redlock = new Redlock([this.redis], {
|
|
38
|
-
// Redlock will keep retrying for us when we explicitly request it,
|
|
39
|
-
// but we’ll manage our own retry loop here.
|
|
40
|
-
driftFactor: 0.01,
|
|
41
|
-
retryCount: 0,
|
|
42
|
-
retryDelay: 0,
|
|
43
|
-
});
|
|
44
|
-
this.startAcquireLoop();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Pure check: true only if we *currently* hold a live lock. */
|
|
48
|
-
amLeader(): boolean {
|
|
49
|
-
return !!this.lock;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** Stop loops and release if we are leader. */
|
|
53
|
-
async shutdown() {
|
|
54
|
-
clearTimeout(this.acquireTimer as any);
|
|
55
|
-
clearInterval(this.renewTimer as any);
|
|
56
|
-
await this.safeRelease();
|
|
57
|
-
await this.redis?.quit();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// -------- internal --------
|
|
61
|
-
|
|
62
|
-
private startAcquireLoop() {
|
|
63
|
-
const tryAcquire = async () => {
|
|
64
|
-
if (this.lock) return; // already leader
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
// Acquire without retries; we run our own loop.
|
|
68
|
-
const lock = await this.redlock.acquire([this.lockKey], this.ttlMs);
|
|
69
|
-
this.lock = lock;
|
|
70
|
-
this.emit("elected");
|
|
71
|
-
this.startRenewLoop();
|
|
72
|
-
} catch {
|
|
73
|
-
// Not acquired (someone else is leader). Back off a bit (with jitter).
|
|
74
|
-
} finally {
|
|
75
|
-
const jitter = Math.floor(Math.random() * 500);
|
|
76
|
-
this.acquireTimer = setTimeout(tryAcquire, this.retryDelayMs + jitter);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
tryAcquire().catch((err) => this.emit("error", err as Error));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private startRenewLoop() {
|
|
84
|
-
clearInterval(this.renewTimer as any);
|
|
85
|
-
this.renewTimer = setInterval(async () => {
|
|
86
|
-
if (!this.lock) return;
|
|
87
|
-
try {
|
|
88
|
-
this.lock = await this.lock.extend(this.ttlMs);
|
|
89
|
-
} catch (err) {
|
|
90
|
-
// Couldn’t extend: we lost leadership (expired or Redis issue)
|
|
91
|
-
await this.safeRevoke();
|
|
92
|
-
// acquisition loop is already running, it’ll try to reacquire
|
|
93
|
-
}
|
|
94
|
-
}, this.renewEveryMs);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
private async safeRevoke() {
|
|
98
|
-
clearInterval(this.renewTimer as any);
|
|
99
|
-
this.lock = undefined;
|
|
100
|
-
this.emit("revoked");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
private async safeRelease() {
|
|
104
|
-
if (!this.lock) return;
|
|
105
|
-
try {
|
|
106
|
-
await this.lock.release();
|
|
107
|
-
} catch {
|
|
108
|
-
// If already expired or released, ignore.
|
|
109
|
-
} finally {
|
|
110
|
-
await this.safeRevoke();
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
package/src/services/redis.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// src/services/redis.ts
|
|
2
|
-
import { createClient } from "redis";
|
|
3
|
-
const getRedisConfig = () => {
|
|
4
|
-
return process.env.REDIS_CONFIG_URL || "redis://localhost:6379/1";
|
|
5
|
-
};
|
|
6
|
-
export class RedisCache {
|
|
7
|
-
// Create a Redis client instance.
|
|
8
|
-
private static client = createClient({ url: getRedisConfig() });
|
|
9
|
-
private static DEFAULT_EXPIRATION_SECONDS = 900; // 15 minutes
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Initializes the Redis client. Call this once on server startup.
|
|
13
|
-
*/
|
|
14
|
-
public static async init(): Promise<void> {
|
|
15
|
-
if (this.client.isOpen) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
try {
|
|
19
|
-
await this.client.connect();
|
|
20
|
-
console.log("Connected to Redis");
|
|
21
|
-
} catch (err) {
|
|
22
|
-
console.error("Error connecting to Redis:", err);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Caches an object under a specified key.
|
|
28
|
-
* @param key The cache key.
|
|
29
|
-
* @param value The object to cache.
|
|
30
|
-
* @param expirationSeconds Optional expiration time in seconds (defaults to 15 minutes).
|
|
31
|
-
*/
|
|
32
|
-
public static async set(
|
|
33
|
-
key: string,
|
|
34
|
-
value: any,
|
|
35
|
-
expirationSeconds?: number,
|
|
36
|
-
): Promise<void> {
|
|
37
|
-
const exp = expirationSeconds || this.DEFAULT_EXPIRATION_SECONDS;
|
|
38
|
-
const stringValue = JSON.stringify(value);
|
|
39
|
-
await this.client.set(key, stringValue, { EX: exp });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Retrieves a cached object by its key.
|
|
44
|
-
* @param key The cache key.
|
|
45
|
-
* @returns The cached object, or null if not found.
|
|
46
|
-
*/
|
|
47
|
-
public static async get<T>(key: string): Promise<T | null> {
|
|
48
|
-
const data = await this.client.get(key);
|
|
49
|
-
if (data) {
|
|
50
|
-
try {
|
|
51
|
-
return JSON.parse(
|
|
52
|
-
typeof data === "string" ? data : JSON.stringify(data),
|
|
53
|
-
);
|
|
54
|
-
} catch (err) {
|
|
55
|
-
console.error("Error parsing cached data:", err);
|
|
56
|
-
return data as unknown as T | null;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Deletes a cache entry.
|
|
64
|
-
* @param key The cache key.
|
|
65
|
-
*/
|
|
66
|
-
public static async del(key: string): Promise<void> {
|
|
67
|
-
await this.client.del(key);
|
|
68
|
-
}
|
|
69
|
-
}
|
package/src/tools/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./utils";
|