@newcms/database 0.0.1
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/cache/index.d.ts +3 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +2 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/object-cache.d.ts +139 -0
- package/dist/cache/object-cache.d.ts.map +1 -0
- package/dist/cache/object-cache.js +321 -0
- package/dist/cache/object-cache.js.map +1 -0
- package/dist/connection.d.ts +18 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +32 -0
- package/dist/connection.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/repositories/options-repository.d.ts +59 -0
- package/dist/repositories/options-repository.d.ts.map +1 -0
- package/dist/repositories/options-repository.js +222 -0
- package/dist/repositories/options-repository.js.map +1 -0
- package/dist/schema/comments.d.ts +369 -0
- package/dist/schema/comments.d.ts.map +1 -0
- package/dist/schema/comments.js +47 -0
- package/dist/schema/comments.js.map +1 -0
- package/dist/schema/index.d.ts +9 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +9 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/links.d.ts +245 -0
- package/dist/schema/links.d.ts.map +1 -0
- package/dist/schema/links.js +17 -0
- package/dist/schema/links.js.map +1 -0
- package/dist/schema/options.d.ts +95 -0
- package/dist/schema/options.d.ts.map +1 -0
- package/dist/schema/options.js +12 -0
- package/dist/schema/options.js.map +1 -0
- package/dist/schema/posts.d.ts +509 -0
- package/dist/schema/posts.d.ts.map +1 -0
- package/dist/schema/posts.js +51 -0
- package/dist/schema/posts.js.map +1 -0
- package/dist/schema/scheduled-events.d.ts +156 -0
- package/dist/schema/scheduled-events.d.ts.map +1 -0
- package/dist/schema/scheduled-events.js +22 -0
- package/dist/schema/scheduled-events.js.map +1 -0
- package/dist/schema/sessions.d.ts +157 -0
- package/dist/schema/sessions.d.ts.map +1 -0
- package/dist/schema/sessions.js +26 -0
- package/dist/schema/sessions.js.map +1 -0
- package/dist/schema/terms.d.ts +343 -0
- package/dist/schema/terms.d.ts.map +1 -0
- package/dist/schema/terms.js +45 -0
- package/dist/schema/terms.js.map +1 -0
- package/dist/schema/users.d.ts +288 -0
- package/dist/schema/users.d.ts.map +1 -0
- package/dist/schema/users.js +30 -0
- package/dist/schema/users.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
export interface ObjectCacheConfig {
|
|
3
|
+
host: string;
|
|
4
|
+
port: number;
|
|
5
|
+
keyPrefix?: string;
|
|
6
|
+
defaultTtl?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface CacheGroupConfig {
|
|
9
|
+
/** TTL in seconds. 0 = no expiry */
|
|
10
|
+
ttl: number;
|
|
11
|
+
/** Whether this group is global (shared across sites in multisite) */
|
|
12
|
+
global: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Redis-backed object cache implementing the CMS cache API.
|
|
16
|
+
*
|
|
17
|
+
* Features:
|
|
18
|
+
* - Group-based key namespacing
|
|
19
|
+
* - Global vs per-site groups (multisite support)
|
|
20
|
+
* - Configurable TTL per group
|
|
21
|
+
* - Batch get/set operations via pipeline
|
|
22
|
+
* - Flush by group or total flush
|
|
23
|
+
* - Increment/decrement operations
|
|
24
|
+
*/
|
|
25
|
+
export declare class ObjectCache {
|
|
26
|
+
private redis;
|
|
27
|
+
private siteId;
|
|
28
|
+
private keyPrefix;
|
|
29
|
+
private defaultTtl;
|
|
30
|
+
/** Groups that are shared across all sites (multisite) */
|
|
31
|
+
private globalGroups;
|
|
32
|
+
/** Per-group TTL overrides (in seconds) */
|
|
33
|
+
private groupTtl;
|
|
34
|
+
/** Local in-memory cache for the current request (non-persistent) */
|
|
35
|
+
private localCache;
|
|
36
|
+
constructor(config: ObjectCacheConfig);
|
|
37
|
+
connect(): Promise<void>;
|
|
38
|
+
disconnect(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Set the current site ID (for multisite per-site cache isolation).
|
|
41
|
+
*/
|
|
42
|
+
setSiteId(siteId: number): void;
|
|
43
|
+
/**
|
|
44
|
+
* Register groups as global (shared across sites in multisite).
|
|
45
|
+
*/
|
|
46
|
+
addGlobalGroups(groups: string[]): void;
|
|
47
|
+
/**
|
|
48
|
+
* Set TTL for a specific cache group.
|
|
49
|
+
*/
|
|
50
|
+
setGroupTtl(group: string, ttlSeconds: number): void;
|
|
51
|
+
/**
|
|
52
|
+
* Build the Redis key for a cache entry.
|
|
53
|
+
*/
|
|
54
|
+
private buildKey;
|
|
55
|
+
/**
|
|
56
|
+
* Build a local cache key (in-memory, for current request).
|
|
57
|
+
*/
|
|
58
|
+
private buildLocalKey;
|
|
59
|
+
/**
|
|
60
|
+
* Get TTL for a group in seconds.
|
|
61
|
+
*/
|
|
62
|
+
private getTtl;
|
|
63
|
+
/**
|
|
64
|
+
* Get a cached value.
|
|
65
|
+
*
|
|
66
|
+
* @returns The cached value, or undefined if not found
|
|
67
|
+
*/
|
|
68
|
+
get<T = unknown>(key: string, group?: string): Promise<T | undefined>;
|
|
69
|
+
/**
|
|
70
|
+
* Set a cached value.
|
|
71
|
+
*
|
|
72
|
+
* @param key - Cache key
|
|
73
|
+
* @param value - Value to cache (will be JSON serialized)
|
|
74
|
+
* @param group - Cache group
|
|
75
|
+
* @param ttl - TTL in seconds (overrides group default). 0 = no expiry.
|
|
76
|
+
* @returns true on success
|
|
77
|
+
*/
|
|
78
|
+
set(key: string, value: unknown, group?: string, ttl?: number): Promise<boolean>;
|
|
79
|
+
/**
|
|
80
|
+
* Add a cached value only if it doesn't already exist.
|
|
81
|
+
*
|
|
82
|
+
* @returns true if the value was added, false if key already exists
|
|
83
|
+
*/
|
|
84
|
+
add(key: string, value: unknown, group?: string, ttl?: number): Promise<boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* Delete a cached value.
|
|
87
|
+
*
|
|
88
|
+
* @returns true if the key existed and was deleted
|
|
89
|
+
*/
|
|
90
|
+
delete(key: string, group?: string): Promise<boolean>;
|
|
91
|
+
/**
|
|
92
|
+
* Increment a numeric cached value.
|
|
93
|
+
*
|
|
94
|
+
* @returns The new value, or false if key doesn't exist
|
|
95
|
+
*/
|
|
96
|
+
incr(key: string, group?: string, offset?: number): Promise<number | false>;
|
|
97
|
+
/**
|
|
98
|
+
* Decrement a numeric cached value.
|
|
99
|
+
*
|
|
100
|
+
* @returns The new value, or false if key doesn't exist
|
|
101
|
+
*/
|
|
102
|
+
decr(key: string, group?: string, offset?: number): Promise<number | false>;
|
|
103
|
+
/**
|
|
104
|
+
* Get multiple cached values at once.
|
|
105
|
+
*
|
|
106
|
+
* @returns Map of key -> value (missing keys are not included)
|
|
107
|
+
*/
|
|
108
|
+
getMultiple(keys: string[], group?: string): Promise<Map<string, unknown>>;
|
|
109
|
+
/**
|
|
110
|
+
* Set multiple cached values at once.
|
|
111
|
+
*/
|
|
112
|
+
setMultiple(entries: Map<string, unknown> | Record<string, unknown>, group?: string, ttl?: number): Promise<boolean>;
|
|
113
|
+
/**
|
|
114
|
+
* Flush all keys in a specific group using SCAN + pipeline DEL.
|
|
115
|
+
*
|
|
116
|
+
* @returns Number of keys deleted
|
|
117
|
+
*/
|
|
118
|
+
flushGroup(group: string): Promise<number>;
|
|
119
|
+
/**
|
|
120
|
+
* Flush all cached data (all groups, all sites).
|
|
121
|
+
* Uses SCAN + DEL to only clear cache keys (not other Redis data).
|
|
122
|
+
*
|
|
123
|
+
* @returns Number of keys deleted
|
|
124
|
+
*/
|
|
125
|
+
flushAll(): Promise<number>;
|
|
126
|
+
/**
|
|
127
|
+
* Clear only the local in-memory cache (for request boundary cleanup).
|
|
128
|
+
*/
|
|
129
|
+
clearLocalCache(): void;
|
|
130
|
+
/**
|
|
131
|
+
* Check if a key exists in cache.
|
|
132
|
+
*/
|
|
133
|
+
exists(key: string, group?: string): Promise<boolean>;
|
|
134
|
+
/**
|
|
135
|
+
* Get the underlying Redis instance (for advanced operations).
|
|
136
|
+
*/
|
|
137
|
+
getRedis(): Redis;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=object-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object-cache.d.ts","sourceRoot":"","sources":["../../src/cache/object-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
/**
|
|
3
|
+
* Redis-backed object cache implementing the CMS cache API.
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Group-based key namespacing
|
|
7
|
+
* - Global vs per-site groups (multisite support)
|
|
8
|
+
* - Configurable TTL per group
|
|
9
|
+
* - Batch get/set operations via pipeline
|
|
10
|
+
* - Flush by group or total flush
|
|
11
|
+
* - Increment/decrement operations
|
|
12
|
+
*/
|
|
13
|
+
export class ObjectCache {
|
|
14
|
+
redis;
|
|
15
|
+
siteId = 1;
|
|
16
|
+
keyPrefix;
|
|
17
|
+
defaultTtl;
|
|
18
|
+
/** Groups that are shared across all sites (multisite) */
|
|
19
|
+
globalGroups = new Set();
|
|
20
|
+
/** Per-group TTL overrides (in seconds) */
|
|
21
|
+
groupTtl = new Map();
|
|
22
|
+
/** Local in-memory cache for the current request (non-persistent) */
|
|
23
|
+
localCache = new Map();
|
|
24
|
+
constructor(config) {
|
|
25
|
+
this.redis = new Redis({
|
|
26
|
+
host: config.host,
|
|
27
|
+
port: config.port,
|
|
28
|
+
lazyConnect: true,
|
|
29
|
+
maxRetriesPerRequest: 3,
|
|
30
|
+
});
|
|
31
|
+
this.keyPrefix = config.keyPrefix ?? 'cache';
|
|
32
|
+
this.defaultTtl = config.defaultTtl ?? 0;
|
|
33
|
+
}
|
|
34
|
+
async connect() {
|
|
35
|
+
await this.redis.connect();
|
|
36
|
+
}
|
|
37
|
+
async disconnect() {
|
|
38
|
+
await this.redis.quit();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Set the current site ID (for multisite per-site cache isolation).
|
|
42
|
+
*/
|
|
43
|
+
setSiteId(siteId) {
|
|
44
|
+
this.siteId = siteId;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Register groups as global (shared across sites in multisite).
|
|
48
|
+
*/
|
|
49
|
+
addGlobalGroups(groups) {
|
|
50
|
+
for (const group of groups) {
|
|
51
|
+
this.globalGroups.add(group);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Set TTL for a specific cache group.
|
|
56
|
+
*/
|
|
57
|
+
setGroupTtl(group, ttlSeconds) {
|
|
58
|
+
this.groupTtl.set(group, ttlSeconds);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build the Redis key for a cache entry.
|
|
62
|
+
*/
|
|
63
|
+
buildKey(key, group = 'default') {
|
|
64
|
+
if (this.globalGroups.has(group)) {
|
|
65
|
+
return `${this.keyPrefix}:global:${group}:${key}`;
|
|
66
|
+
}
|
|
67
|
+
return `${this.keyPrefix}:site:${this.siteId}:${group}:${key}`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build a local cache key (in-memory, for current request).
|
|
71
|
+
*/
|
|
72
|
+
buildLocalKey(key, group = 'default') {
|
|
73
|
+
if (this.globalGroups.has(group)) {
|
|
74
|
+
return `global:${group}:${key}`;
|
|
75
|
+
}
|
|
76
|
+
return `site:${this.siteId}:${group}:${key}`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get TTL for a group in seconds.
|
|
80
|
+
*/
|
|
81
|
+
getTtl(group) {
|
|
82
|
+
return this.groupTtl.get(group) ?? this.defaultTtl;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get a cached value.
|
|
86
|
+
*
|
|
87
|
+
* @returns The cached value, or undefined if not found
|
|
88
|
+
*/
|
|
89
|
+
async get(key, group = 'default') {
|
|
90
|
+
// Check local cache first
|
|
91
|
+
const localKey = this.buildLocalKey(key, group);
|
|
92
|
+
if (this.localCache.has(localKey)) {
|
|
93
|
+
return this.localCache.get(localKey);
|
|
94
|
+
}
|
|
95
|
+
const redisKey = this.buildKey(key, group);
|
|
96
|
+
const raw = await this.redis.get(redisKey);
|
|
97
|
+
if (raw === null)
|
|
98
|
+
return undefined;
|
|
99
|
+
try {
|
|
100
|
+
const value = JSON.parse(raw);
|
|
101
|
+
this.localCache.set(localKey, value);
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return raw;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Set a cached value.
|
|
110
|
+
*
|
|
111
|
+
* @param key - Cache key
|
|
112
|
+
* @param value - Value to cache (will be JSON serialized)
|
|
113
|
+
* @param group - Cache group
|
|
114
|
+
* @param ttl - TTL in seconds (overrides group default). 0 = no expiry.
|
|
115
|
+
* @returns true on success
|
|
116
|
+
*/
|
|
117
|
+
async set(key, value, group = 'default', ttl) {
|
|
118
|
+
const redisKey = this.buildKey(key, group);
|
|
119
|
+
const serialized = JSON.stringify(value);
|
|
120
|
+
const effectiveTtl = ttl ?? this.getTtl(group);
|
|
121
|
+
if (effectiveTtl > 0) {
|
|
122
|
+
await this.redis.setex(redisKey, effectiveTtl, serialized);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
await this.redis.set(redisKey, serialized);
|
|
126
|
+
}
|
|
127
|
+
const localKey = this.buildLocalKey(key, group);
|
|
128
|
+
this.localCache.set(localKey, value);
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Add a cached value only if it doesn't already exist.
|
|
133
|
+
*
|
|
134
|
+
* @returns true if the value was added, false if key already exists
|
|
135
|
+
*/
|
|
136
|
+
async add(key, value, group = 'default', ttl) {
|
|
137
|
+
const redisKey = this.buildKey(key, group);
|
|
138
|
+
const serialized = JSON.stringify(value);
|
|
139
|
+
const effectiveTtl = ttl ?? this.getTtl(group);
|
|
140
|
+
let result;
|
|
141
|
+
if (effectiveTtl > 0) {
|
|
142
|
+
result = await this.redis.set(redisKey, serialized, 'EX', effectiveTtl, 'NX');
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
result = await this.redis.set(redisKey, serialized, 'NX');
|
|
146
|
+
}
|
|
147
|
+
if (result === 'OK') {
|
|
148
|
+
const localKey = this.buildLocalKey(key, group);
|
|
149
|
+
this.localCache.set(localKey, value);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Delete a cached value.
|
|
156
|
+
*
|
|
157
|
+
* @returns true if the key existed and was deleted
|
|
158
|
+
*/
|
|
159
|
+
async delete(key, group = 'default') {
|
|
160
|
+
const redisKey = this.buildKey(key, group);
|
|
161
|
+
const count = await this.redis.del(redisKey);
|
|
162
|
+
const localKey = this.buildLocalKey(key, group);
|
|
163
|
+
this.localCache.delete(localKey);
|
|
164
|
+
return count > 0;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Increment a numeric cached value.
|
|
168
|
+
*
|
|
169
|
+
* @returns The new value, or false if key doesn't exist
|
|
170
|
+
*/
|
|
171
|
+
async incr(key, group = 'default', offset = 1) {
|
|
172
|
+
const redisKey = this.buildKey(key, group);
|
|
173
|
+
const keyExists = await this.redis.exists(redisKey);
|
|
174
|
+
if (!keyExists)
|
|
175
|
+
return false;
|
|
176
|
+
const current = await this.get(key, group);
|
|
177
|
+
if (current === undefined || typeof current !== 'number')
|
|
178
|
+
return false;
|
|
179
|
+
const newValue = current + offset;
|
|
180
|
+
await this.set(key, newValue, group);
|
|
181
|
+
return newValue;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Decrement a numeric cached value.
|
|
185
|
+
*
|
|
186
|
+
* @returns The new value, or false if key doesn't exist
|
|
187
|
+
*/
|
|
188
|
+
async decr(key, group = 'default', offset = 1) {
|
|
189
|
+
return this.incr(key, group, -offset);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get multiple cached values at once.
|
|
193
|
+
*
|
|
194
|
+
* @returns Map of key -> value (missing keys are not included)
|
|
195
|
+
*/
|
|
196
|
+
async getMultiple(keys, group = 'default') {
|
|
197
|
+
const result = new Map();
|
|
198
|
+
if (keys.length === 0)
|
|
199
|
+
return result;
|
|
200
|
+
const redisKeys = keys.map((k) => this.buildKey(k, group));
|
|
201
|
+
const values = await this.redis.mget(...redisKeys);
|
|
202
|
+
for (let i = 0; i < keys.length; i++) {
|
|
203
|
+
const raw = values[i];
|
|
204
|
+
if (raw !== null) {
|
|
205
|
+
try {
|
|
206
|
+
const value = JSON.parse(raw);
|
|
207
|
+
result.set(keys[i], value);
|
|
208
|
+
const localKey = this.buildLocalKey(keys[i], group);
|
|
209
|
+
this.localCache.set(localKey, value);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
result.set(keys[i], raw);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Set multiple cached values at once.
|
|
220
|
+
*/
|
|
221
|
+
async setMultiple(entries, group = 'default', ttl) {
|
|
222
|
+
const items = entries instanceof Map ? entries : new Map(Object.entries(entries));
|
|
223
|
+
if (items.size === 0)
|
|
224
|
+
return true;
|
|
225
|
+
const effectiveTtl = ttl ?? this.getTtl(group);
|
|
226
|
+
const pipeline = this.redis.pipeline();
|
|
227
|
+
for (const [key, value] of items) {
|
|
228
|
+
const redisKey = this.buildKey(key, group);
|
|
229
|
+
const serialized = JSON.stringify(value);
|
|
230
|
+
if (effectiveTtl > 0) {
|
|
231
|
+
pipeline.setex(redisKey, effectiveTtl, serialized);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
pipeline.set(redisKey, serialized);
|
|
235
|
+
}
|
|
236
|
+
const localKey = this.buildLocalKey(key, group);
|
|
237
|
+
this.localCache.set(localKey, value);
|
|
238
|
+
}
|
|
239
|
+
await pipeline.exec();
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Flush all keys in a specific group using SCAN + pipeline DEL.
|
|
244
|
+
*
|
|
245
|
+
* @returns Number of keys deleted
|
|
246
|
+
*/
|
|
247
|
+
async flushGroup(group) {
|
|
248
|
+
const pattern = this.globalGroups.has(group)
|
|
249
|
+
? `${this.keyPrefix}:global:${group}:*`
|
|
250
|
+
: `${this.keyPrefix}:site:${this.siteId}:${group}:*`;
|
|
251
|
+
let deleted = 0;
|
|
252
|
+
let cursor = '0';
|
|
253
|
+
do {
|
|
254
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
|
|
255
|
+
cursor = nextCursor;
|
|
256
|
+
if (keys.length > 0) {
|
|
257
|
+
const pipeline = this.redis.pipeline();
|
|
258
|
+
for (const key of keys) {
|
|
259
|
+
pipeline.del(key);
|
|
260
|
+
}
|
|
261
|
+
await pipeline.exec();
|
|
262
|
+
deleted += keys.length;
|
|
263
|
+
}
|
|
264
|
+
} while (cursor !== '0');
|
|
265
|
+
// Clear local cache for this group
|
|
266
|
+
for (const localKey of this.localCache.keys()) {
|
|
267
|
+
if (localKey.startsWith(`${group}:`)) {
|
|
268
|
+
this.localCache.delete(localKey);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return deleted;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Flush all cached data (all groups, all sites).
|
|
275
|
+
* Uses SCAN + DEL to only clear cache keys (not other Redis data).
|
|
276
|
+
*
|
|
277
|
+
* @returns Number of keys deleted
|
|
278
|
+
*/
|
|
279
|
+
async flushAll() {
|
|
280
|
+
const pattern = `${this.keyPrefix}:*`;
|
|
281
|
+
let deleted = 0;
|
|
282
|
+
let cursor = '0';
|
|
283
|
+
do {
|
|
284
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
|
|
285
|
+
cursor = nextCursor;
|
|
286
|
+
if (keys.length > 0) {
|
|
287
|
+
const pipeline = this.redis.pipeline();
|
|
288
|
+
for (const key of keys) {
|
|
289
|
+
pipeline.del(key);
|
|
290
|
+
}
|
|
291
|
+
await pipeline.exec();
|
|
292
|
+
deleted += keys.length;
|
|
293
|
+
}
|
|
294
|
+
} while (cursor !== '0');
|
|
295
|
+
this.localCache.clear();
|
|
296
|
+
return deleted;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Clear only the local in-memory cache (for request boundary cleanup).
|
|
300
|
+
*/
|
|
301
|
+
clearLocalCache() {
|
|
302
|
+
this.localCache.clear();
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Check if a key exists in cache.
|
|
306
|
+
*/
|
|
307
|
+
async exists(key, group = 'default') {
|
|
308
|
+
const localKey = this.buildLocalKey(key, group);
|
|
309
|
+
if (this.localCache.has(localKey))
|
|
310
|
+
return true;
|
|
311
|
+
const redisKey = this.buildKey(key, group);
|
|
312
|
+
return (await this.redis.exists(redisKey)) === 1;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Get the underlying Redis instance (for advanced operations).
|
|
316
|
+
*/
|
|
317
|
+
getRedis() {
|
|
318
|
+
return this.redis;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=object-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object-cache.js","sourceRoot":"","sources":["../../src/cache/object-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import postgres from 'postgres';
|
|
2
|
+
import * as schema from './schema/index.js';
|
|
3
|
+
export interface DatabaseConfig {
|
|
4
|
+
host: string;
|
|
5
|
+
port: number;
|
|
6
|
+
database: string;
|
|
7
|
+
user: string;
|
|
8
|
+
password: string;
|
|
9
|
+
maxConnections?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function createConnection(config?: DatabaseConfig): {
|
|
12
|
+
db: import("drizzle-orm/postgres-js").PostgresJsDatabase<typeof schema> & {
|
|
13
|
+
$client: postgres.Sql<{}>;
|
|
14
|
+
};
|
|
15
|
+
client: postgres.Sql<{}>;
|
|
16
|
+
};
|
|
17
|
+
export type Database = ReturnType<typeof createConnection>['db'];
|
|
18
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AACA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAqBD,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,cAAc;;;;;EAevD;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
2
|
+
import postgres from 'postgres';
|
|
3
|
+
import * as schema from './schema/index.js';
|
|
4
|
+
function getConfigFromEnv() {
|
|
5
|
+
const password = process.env['DB_PASSWORD'];
|
|
6
|
+
if (!password) {
|
|
7
|
+
throw new Error('DB_PASSWORD environment variable is required. ' +
|
|
8
|
+
'Set it in your .env file or environment.');
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
host: process.env['DB_HOST'] ?? 'localhost',
|
|
12
|
+
port: parseInt(process.env['DB_PORT'] ?? '5432', 10),
|
|
13
|
+
database: process.env['DB_NAME'] ?? 'newcms',
|
|
14
|
+
user: process.env['DB_USER'] ?? 'newcms',
|
|
15
|
+
password,
|
|
16
|
+
maxConnections: parseInt(process.env['DB_MAX_CONNECTIONS'] ?? '10', 10),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function createConnection(config) {
|
|
20
|
+
const cfg = config ?? getConfigFromEnv();
|
|
21
|
+
const client = postgres({
|
|
22
|
+
host: cfg.host,
|
|
23
|
+
port: cfg.port,
|
|
24
|
+
database: cfg.database,
|
|
25
|
+
user: cfg.user,
|
|
26
|
+
password: cfg.password,
|
|
27
|
+
max: cfg.maxConnections ?? 10,
|
|
28
|
+
});
|
|
29
|
+
const db = drizzle(client, { schema });
|
|
30
|
+
return { db, client };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAW5C,SAAS,gBAAgB;IACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACd,gDAAgD;YAC/C,0CAA0C,CAC3C,CAAC;IACH,CAAC;IAED,OAAO;QACN,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,WAAW;QAC3C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,MAAM,EAAE,EAAE,CAAC;QACpD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,QAAQ;QAC5C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,QAAQ;QACxC,QAAQ;QACR,cAAc,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;KACvE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAuB;IACvD,MAAM,GAAG,GAAG,MAAM,IAAI,gBAAgB,EAAE,CAAC;IAEzC,MAAM,MAAM,GAAG,QAAQ,CAAC;QACvB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,GAAG,EAAE,GAAG,CAAC,cAAc,IAAI,EAAE;KAC7B,CAAC,CAAC;IAEH,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAEvC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AACvB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createConnection } from './connection.js';
|
|
2
|
+
export type { Database, DatabaseConfig } from './connection.js';
|
|
3
|
+
export * from './schema/index.js';
|
|
4
|
+
export { ObjectCache } from './cache/index.js';
|
|
5
|
+
export type { ObjectCacheConfig, CacheGroupConfig } from './cache/index.js';
|
|
6
|
+
export { OptionsRepository } from './repositories/options-repository.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAChE,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Database } from '../connection.js';
|
|
2
|
+
import type { ObjectCache } from '../cache/object-cache.js';
|
|
3
|
+
/**
|
|
4
|
+
* Options repository — CRUD for site options with integrated Redis cache.
|
|
5
|
+
*
|
|
6
|
+
* Behaviors per spec:
|
|
7
|
+
* - Options marked as autoload=true are pre-loaded into cache together
|
|
8
|
+
* - Cache of "not found" keys prevents repeated DB misses
|
|
9
|
+
* - Complex values (objects/arrays) stored as JSONB in addition to text
|
|
10
|
+
* - Write operations invalidate cache granularly
|
|
11
|
+
*/
|
|
12
|
+
export declare class OptionsRepository {
|
|
13
|
+
private db;
|
|
14
|
+
private cache;
|
|
15
|
+
constructor(db: Database, cache: ObjectCache);
|
|
16
|
+
/**
|
|
17
|
+
* Get an option value.
|
|
18
|
+
*
|
|
19
|
+
* Lookup order:
|
|
20
|
+
* 1. Redis cache (group "options")
|
|
21
|
+
* 2. "Not found" cache (avoids repeated DB queries for missing keys)
|
|
22
|
+
* 3. Database
|
|
23
|
+
*/
|
|
24
|
+
getOption<T = string>(name: string, defaultValue?: T): Promise<T | undefined>;
|
|
25
|
+
/**
|
|
26
|
+
* Add a new option. Fails if option already exists.
|
|
27
|
+
*
|
|
28
|
+
* @returns true if the option was created
|
|
29
|
+
*/
|
|
30
|
+
addOption(name: string, value: unknown, autoload?: boolean): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Update an existing option, or create it if it doesn't exist.
|
|
33
|
+
*
|
|
34
|
+
* @returns true if the value was changed
|
|
35
|
+
*/
|
|
36
|
+
updateOption(name: string, value: unknown, autoload?: boolean): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Delete an option.
|
|
39
|
+
*
|
|
40
|
+
* @returns true if the option existed and was deleted
|
|
41
|
+
*/
|
|
42
|
+
deleteOption(name: string): Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Load all autoloaded options into cache at once.
|
|
45
|
+
* Called during bootstrap to pre-warm the cache.
|
|
46
|
+
*/
|
|
47
|
+
loadAutoloadedOptions(): Promise<Map<string, unknown>>;
|
|
48
|
+
/**
|
|
49
|
+
* Serialize a value for storage.
|
|
50
|
+
* Complex types (objects, arrays) are stored in both text and JSONB columns.
|
|
51
|
+
*/
|
|
52
|
+
private serializeValue;
|
|
53
|
+
/**
|
|
54
|
+
* Deserialize a value from the database row.
|
|
55
|
+
* Prefers JSONB column when available (already parsed).
|
|
56
|
+
*/
|
|
57
|
+
private deserializeValue;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=options-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options-repository.d.ts","sourceRoot":"","sources":["../../src/repositories/options-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAM5D;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAE5B,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;gBADL,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,WAAW;IAG3B;;;;;;;OAOG;IACG,SAAS,CAAC,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IA+BnF;;;;OAIG;IACG,SAAS,CACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,QAAQ,GAAE,OAAc,GACtB,OAAO,CAAC,OAAO,CAAC;IA+BnB;;;;OAIG;IACG,YAAY,CACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,QAAQ,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,OAAO,CAAC;IA8CnB;;;;OAIG;IACG,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBlD;;;OAGG;IACG,qBAAqB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAsC5D;;;OAGG;IACH,OAAO,CAAC,cAAc;IA2BtB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;CAgBxB"}
|