@guren/server 1.0.0-rc.24 → 1.0.0-rc.26
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/{Application-D4_90raL.d.ts → Application-BnsyCKXY.d.ts} +3 -2
- package/dist/AuthManager-SfhCNkAU.d.ts +79 -0
- package/dist/{ConsoleKernel-BPYcP-2f.d.ts → ConsoleKernel-BDtBETjm.d.ts} +1 -1
- package/dist/{api-token-BGOsfuI9.d.ts → api-token-BSSCLlFW.d.ts} +12 -69
- package/dist/auth/index.d.ts +8 -327
- package/dist/auth/index.js +1 -1
- package/dist/{chunk-F3664MFN.js → chunk-2T6JN4VR.js} +52 -6
- package/dist/{chunk-IM6XKMP5.js → chunk-NRLZJILN.js} +12 -5
- package/dist/chunk-UY3AZSYL.js +14 -0
- package/dist/client-CKXJLsTe.d.ts +232 -0
- package/dist/email-verification-CAeArjui.d.ts +327 -0
- package/dist/index.d.ts +27 -238
- package/dist/index.js +25 -20
- package/dist/lambda/index.d.ts +4 -3
- package/dist/mcp/index.d.ts +3 -2
- package/dist/notifications/index.d.ts +4 -3
- package/dist/queue/index.d.ts +4 -3
- package/dist/redis/index.d.ts +366 -0
- package/dist/redis/index.js +597 -0
- package/dist/runtime/index.d.ts +4 -3
- package/dist/runtime/index.js +5 -3
- package/package.json +12 -5
|
@@ -455,11 +455,31 @@ var ModelUserProvider = class extends BaseUserProvider {
|
|
|
455
455
|
getId(user) {
|
|
456
456
|
return user[this.idColumn];
|
|
457
457
|
}
|
|
458
|
+
/**
|
|
459
|
+
* Strip the password hash, remember token, and the model's `hidden`
|
|
460
|
+
* fields before the record leaves the auth layer. Credential
|
|
461
|
+
* validation happens on the raw record before sanitizing, so this
|
|
462
|
+
* never affects login — only what `auth.user()` exposes.
|
|
463
|
+
*/
|
|
464
|
+
sanitize(user) {
|
|
465
|
+
const blocked = /* @__PURE__ */ new Set([
|
|
466
|
+
this.passwordColumn,
|
|
467
|
+
this.rememberTokenColumn,
|
|
468
|
+
...this.model.hidden ?? []
|
|
469
|
+
]);
|
|
470
|
+
const clean = {};
|
|
471
|
+
for (const [key, value] of Object.entries(user)) {
|
|
472
|
+
if (!blocked.has(key)) {
|
|
473
|
+
clean[key] = value;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return clean;
|
|
477
|
+
}
|
|
458
478
|
async setRememberToken(user, token) {
|
|
459
479
|
if (typeof user[this.rememberTokenColumn] !== "undefined") {
|
|
460
480
|
;
|
|
461
481
|
user[this.rememberTokenColumn] = token;
|
|
462
|
-
await this.model.
|
|
482
|
+
await this.model.forceUpdate({ [this.idColumn]: this.getId(user) }, { [this.rememberTokenColumn]: token });
|
|
463
483
|
}
|
|
464
484
|
}
|
|
465
485
|
async getRememberToken(user) {
|
|
@@ -554,9 +574,14 @@ var SessionGuard = class {
|
|
|
554
574
|
this.currentSession.forget(this.rememberSessionKey());
|
|
555
575
|
return null;
|
|
556
576
|
}
|
|
557
|
-
this.cachedUser = user;
|
|
558
577
|
await this.provider.setRememberToken?.(user, rememberToken);
|
|
559
|
-
|
|
578
|
+
const sanitized = this.sanitizeUser(user);
|
|
579
|
+
this.cachedUser = sanitized;
|
|
580
|
+
return sanitized;
|
|
581
|
+
}
|
|
582
|
+
/** Strip auth-internal fields before a record is cached or exposed. */
|
|
583
|
+
sanitizeUser(user) {
|
|
584
|
+
return this.provider.sanitize ? this.provider.sanitize(user) : user;
|
|
560
585
|
}
|
|
561
586
|
async resolveUser() {
|
|
562
587
|
if (this.cachedUser !== void 0) {
|
|
@@ -579,8 +604,9 @@ var SessionGuard = class {
|
|
|
579
604
|
this.cachedUser = null;
|
|
580
605
|
return null;
|
|
581
606
|
}
|
|
582
|
-
|
|
583
|
-
|
|
607
|
+
const sanitized = this.sanitizeUser(user);
|
|
608
|
+
this.cachedUser = sanitized;
|
|
609
|
+
return sanitized;
|
|
584
610
|
}
|
|
585
611
|
async check() {
|
|
586
612
|
return await this.resolveUser() !== null;
|
|
@@ -616,7 +642,7 @@ var SessionGuard = class {
|
|
|
616
642
|
session.regenerate();
|
|
617
643
|
const identifier = this.provider.getId(castUser);
|
|
618
644
|
session.set(this.sessionKey(), identifier);
|
|
619
|
-
this.cachedUser = castUser;
|
|
645
|
+
this.cachedUser = this.sanitizeUser(castUser);
|
|
620
646
|
if (remember) {
|
|
621
647
|
await this.remember(castUser);
|
|
622
648
|
} else {
|
|
@@ -1250,9 +1276,29 @@ var DEFAULT_STATE_LENGTH = 24;
|
|
|
1250
1276
|
var DEFAULT_STATE_HASH_ALGORITHM = "sha256";
|
|
1251
1277
|
var MemoryOAuthStateStore = class {
|
|
1252
1278
|
states = /* @__PURE__ */ new Map();
|
|
1279
|
+
maxEntries;
|
|
1280
|
+
constructor(options = {}) {
|
|
1281
|
+
this.maxEntries = options.maxEntries ?? 1e4;
|
|
1282
|
+
}
|
|
1253
1283
|
async store(stateHash, payload) {
|
|
1284
|
+
if (this.states.size >= this.maxEntries) {
|
|
1285
|
+
this.sweepExpired();
|
|
1286
|
+
}
|
|
1287
|
+
while (this.states.size >= this.maxEntries) {
|
|
1288
|
+
const oldest = this.states.keys().next().value;
|
|
1289
|
+
if (oldest === void 0) break;
|
|
1290
|
+
this.states.delete(oldest);
|
|
1291
|
+
}
|
|
1254
1292
|
this.states.set(stateHash, payload);
|
|
1255
1293
|
}
|
|
1294
|
+
sweepExpired() {
|
|
1295
|
+
const now = Date.now();
|
|
1296
|
+
for (const [hash, payload] of this.states) {
|
|
1297
|
+
if (payload.expiresAt.getTime() <= now) {
|
|
1298
|
+
this.states.delete(hash);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1256
1302
|
async find(stateHash) {
|
|
1257
1303
|
const payload = this.states.get(stateHash);
|
|
1258
1304
|
if (!payload) return null;
|
|
@@ -6,7 +6,7 @@ import standard from "figlet/importable-fonts/Standard.js";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@guren/server",
|
|
9
|
-
version: "1.0.0-rc.
|
|
9
|
+
version: "1.0.0-rc.26",
|
|
10
10
|
type: "module",
|
|
11
11
|
license: "MIT",
|
|
12
12
|
repository: {
|
|
@@ -102,6 +102,10 @@ var package_default = {
|
|
|
102
102
|
"./lambda": {
|
|
103
103
|
types: "./dist/lambda/index.d.ts",
|
|
104
104
|
default: "./dist/lambda/index.js"
|
|
105
|
+
},
|
|
106
|
+
"./redis": {
|
|
107
|
+
types: "./dist/redis/index.d.ts",
|
|
108
|
+
default: "./dist/redis/index.js"
|
|
105
109
|
}
|
|
106
110
|
},
|
|
107
111
|
scripts: {
|
|
@@ -111,21 +115,21 @@ var package_default = {
|
|
|
111
115
|
test: "bun test"
|
|
112
116
|
},
|
|
113
117
|
dependencies: {
|
|
114
|
-
"@guren/
|
|
115
|
-
"@guren/orm": "^1.0.0-rc.25",
|
|
118
|
+
"@guren/orm": "^1.0.0-rc.27",
|
|
116
119
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
117
120
|
chalk: "^5.3.0",
|
|
118
121
|
consola: "^3.4.2",
|
|
119
122
|
figlet: "^1.7.0",
|
|
120
123
|
hono: "^4.12.18",
|
|
121
124
|
ioredis: "^5.9.1",
|
|
122
|
-
nodemailer: "^
|
|
125
|
+
nodemailer: "^9.0.1"
|
|
123
126
|
},
|
|
124
127
|
peerDependencies: {
|
|
125
128
|
"@aws-sdk/client-s3": "^3.0.0",
|
|
126
129
|
"@aws-sdk/client-sqs": "^3.0.0",
|
|
127
130
|
"@aws-sdk/s3-request-presigner": "^3.0.0",
|
|
128
|
-
vite: ">=7.0.0"
|
|
131
|
+
vite: ">=7.0.0",
|
|
132
|
+
"@guren/inertia-client": "^1.0.0-rc.25"
|
|
129
133
|
},
|
|
130
134
|
peerDependenciesMeta: {
|
|
131
135
|
"@aws-sdk/client-s3": {
|
|
@@ -136,6 +140,9 @@ var package_default = {
|
|
|
136
140
|
},
|
|
137
141
|
"@aws-sdk/s3-request-presigner": {
|
|
138
142
|
optional: true
|
|
143
|
+
},
|
|
144
|
+
"@guren/inertia-client": {
|
|
145
|
+
optional: true
|
|
139
146
|
}
|
|
140
147
|
},
|
|
141
148
|
devDependencies: {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/redis/client.ts
|
|
2
|
+
import Redis from "ioredis";
|
|
3
|
+
function createRedisClient(options = {}) {
|
|
4
|
+
const { url, ...redisOptions } = options;
|
|
5
|
+
if (url) {
|
|
6
|
+
return new Redis(url, redisOptions);
|
|
7
|
+
}
|
|
8
|
+
return new Redis(redisOptions);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
Redis,
|
|
13
|
+
createRedisClient
|
|
14
|
+
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { Context, MiddlewareHandler } from 'hono';
|
|
2
|
+
import Redis, { RedisOptions } from 'ioredis';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Rate limit entry stored in the backing store.
|
|
6
|
+
*/
|
|
7
|
+
interface RateLimitEntry {
|
|
8
|
+
count: number;
|
|
9
|
+
resetAt: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Store interface for rate limit data.
|
|
13
|
+
* Implement this for Redis or database-backed storage.
|
|
14
|
+
*/
|
|
15
|
+
interface RateLimitStore {
|
|
16
|
+
/**
|
|
17
|
+
* Get the current rate limit entry for a key.
|
|
18
|
+
*/
|
|
19
|
+
get(key: string): Promise<RateLimitEntry | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Increment the count for a key and return the new entry.
|
|
22
|
+
* If the key doesn't exist, create it with count=1.
|
|
23
|
+
*/
|
|
24
|
+
increment(key: string, windowMs: number): Promise<RateLimitEntry>;
|
|
25
|
+
/**
|
|
26
|
+
* Reset the count for a key.
|
|
27
|
+
*/
|
|
28
|
+
reset(key: string): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Base class for in-memory rate limit stores with automatic cleanup.
|
|
32
|
+
*/
|
|
33
|
+
declare abstract class BaseMemoryStore implements RateLimitStore {
|
|
34
|
+
protected cleanupInterval?: ReturnType<typeof setInterval>;
|
|
35
|
+
constructor(cleanupIntervalMs?: number);
|
|
36
|
+
abstract get(key: string): Promise<RateLimitEntry | null>;
|
|
37
|
+
abstract increment(key: string, windowMs: number): Promise<RateLimitEntry>;
|
|
38
|
+
abstract reset(key: string): Promise<void>;
|
|
39
|
+
abstract cleanup(): void;
|
|
40
|
+
abstract clear(): void;
|
|
41
|
+
abstract get size(): number;
|
|
42
|
+
destroy(): void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* In-memory rate limit store.
|
|
46
|
+
* Suitable for single-process development, not for production clusters.
|
|
47
|
+
*/
|
|
48
|
+
declare class MemoryRateLimitStore extends BaseMemoryStore {
|
|
49
|
+
private entries;
|
|
50
|
+
get(key: string): Promise<RateLimitEntry | null>;
|
|
51
|
+
increment(key: string, windowMs: number): Promise<RateLimitEntry>;
|
|
52
|
+
reset(key: string): Promise<void>;
|
|
53
|
+
cleanup(): void;
|
|
54
|
+
clear(): void;
|
|
55
|
+
get size(): number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Configuration options for rate limiting.
|
|
59
|
+
*/
|
|
60
|
+
interface RateLimitOptions {
|
|
61
|
+
/**
|
|
62
|
+
* Maximum number of requests allowed in the time window.
|
|
63
|
+
* @default 100
|
|
64
|
+
*/
|
|
65
|
+
limit?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Time window in milliseconds.
|
|
68
|
+
* @default 60000 (1 minute)
|
|
69
|
+
*/
|
|
70
|
+
windowMs?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Function to extract the rate limit key from the request.
|
|
73
|
+
* Defaults to using the client IP address.
|
|
74
|
+
*/
|
|
75
|
+
keyGenerator?: (ctx: Context) => string | Promise<string>;
|
|
76
|
+
/**
|
|
77
|
+
* Rate limit store implementation.
|
|
78
|
+
* Defaults to in-memory store.
|
|
79
|
+
*/
|
|
80
|
+
store?: RateLimitStore;
|
|
81
|
+
/**
|
|
82
|
+
* Whether to skip rate limiting for certain requests.
|
|
83
|
+
*/
|
|
84
|
+
skip?: (ctx: Context) => boolean | Promise<boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* Custom handler when rate limit is exceeded.
|
|
87
|
+
*/
|
|
88
|
+
onRateLimited?: (ctx: Context, retryAfter: number) => Response | Promise<Response>;
|
|
89
|
+
/**
|
|
90
|
+
* Whether to add rate limit headers to all responses.
|
|
91
|
+
* @default true
|
|
92
|
+
*/
|
|
93
|
+
headers?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Custom message when rate limit is exceeded.
|
|
96
|
+
* @default 'Too many requests, please try again later.'
|
|
97
|
+
*/
|
|
98
|
+
message?: string;
|
|
99
|
+
/**
|
|
100
|
+
* HTTP status code when rate limit is exceeded.
|
|
101
|
+
* @default 429
|
|
102
|
+
*/
|
|
103
|
+
statusCode?: number;
|
|
104
|
+
/**
|
|
105
|
+
* Prefix for rate limit keys.
|
|
106
|
+
* @default 'rl:'
|
|
107
|
+
*/
|
|
108
|
+
keyPrefix?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Trust reverse-proxy headers for client IP resolution, checked in order:
|
|
111
|
+
* `CF-Connecting-IP`, `True-Client-IP`, `X-Real-IP`, then the first entry
|
|
112
|
+
* of `X-Forwarded-For`. Falls back to `server.requestIP()` when none are set.
|
|
113
|
+
*
|
|
114
|
+
* Enable ONLY when every request passes through a proxy that sets or strips
|
|
115
|
+
* these headers — on direct deployments clients can spoof them to bypass
|
|
116
|
+
* per-client limits. Ignored when a custom `keyGenerator` is supplied.
|
|
117
|
+
* @default false
|
|
118
|
+
*/
|
|
119
|
+
trustProxy?: boolean;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create a rate limiting middleware.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* // Basic usage - 100 requests per minute per IP
|
|
127
|
+
* app.use('*', createRateLimitMiddleware())
|
|
128
|
+
*
|
|
129
|
+
* // Stricter limit for login endpoint
|
|
130
|
+
* router.post('/login', [AuthController, 'login'],
|
|
131
|
+
* createRateLimitMiddleware({
|
|
132
|
+
* limit: 5,
|
|
133
|
+
* windowMs: 15 * 60 * 1000, // 15 minutes
|
|
134
|
+
* })
|
|
135
|
+
* )
|
|
136
|
+
*
|
|
137
|
+
* // Custom key based on authenticated user
|
|
138
|
+
* app.use('/api/*', createRateLimitMiddleware({
|
|
139
|
+
* limit: 1000,
|
|
140
|
+
* windowMs: 60 * 60 * 1000, // 1 hour
|
|
141
|
+
* keyGenerator: async (ctx) => {
|
|
142
|
+
* const user = await ctx.get('user')
|
|
143
|
+
* return user?.id?.toString() ?? defaultKeyGenerator(ctx)
|
|
144
|
+
* },
|
|
145
|
+
* }))
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function createRateLimitMiddleware(options?: RateLimitOptions): MiddlewareHandler;
|
|
149
|
+
/**
|
|
150
|
+
* Rate limit result information.
|
|
151
|
+
*/
|
|
152
|
+
interface RateLimitInfo {
|
|
153
|
+
limit: number;
|
|
154
|
+
remaining: number;
|
|
155
|
+
resetAt: Date;
|
|
156
|
+
isLimited: boolean;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get rate limit information for a key without incrementing.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* const info = await getRateLimitInfo('user:123', store, { limit: 100 })
|
|
164
|
+
* console.log(`${info.remaining} requests remaining`)
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
declare function getRateLimitInfo(key: string, store: RateLimitStore, options?: {
|
|
168
|
+
limit: number;
|
|
169
|
+
keyPrefix?: string;
|
|
170
|
+
}): Promise<RateLimitInfo>;
|
|
171
|
+
/**
|
|
172
|
+
* Reset rate limit for a specific key.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* // Reset rate limit after successful captcha
|
|
177
|
+
* await resetRateLimit(clientIp, store)
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
declare function resetRateLimit(key: string, store: RateLimitStore, options?: {
|
|
181
|
+
keyPrefix?: string;
|
|
182
|
+
}): Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Sliding window rate limit store.
|
|
185
|
+
* More accurate than fixed window but uses more memory.
|
|
186
|
+
*/
|
|
187
|
+
declare class SlidingWindowRateLimitStore extends BaseMemoryStore {
|
|
188
|
+
private requests;
|
|
189
|
+
get(key: string): Promise<RateLimitEntry | null>;
|
|
190
|
+
increment(key: string, windowMs: number): Promise<RateLimitEntry>;
|
|
191
|
+
reset(key: string): Promise<void>;
|
|
192
|
+
cleanup(): void;
|
|
193
|
+
clear(): void;
|
|
194
|
+
get size(): number;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Options for creating a Redis client.
|
|
199
|
+
*/
|
|
200
|
+
interface RedisClientOptions extends RedisOptions {
|
|
201
|
+
/**
|
|
202
|
+
* Redis connection URL (e.g., 'redis://localhost:6379').
|
|
203
|
+
* If provided, overrides host/port/password.
|
|
204
|
+
*/
|
|
205
|
+
url?: string;
|
|
206
|
+
/**
|
|
207
|
+
* Key prefix for all operations.
|
|
208
|
+
* @default ''
|
|
209
|
+
*/
|
|
210
|
+
keyPrefix?: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Create a Redis client with the given options.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```ts
|
|
217
|
+
* // Using URL
|
|
218
|
+
* const redis = createRedisClient({ url: 'redis://localhost:6379' })
|
|
219
|
+
*
|
|
220
|
+
* // Using host/port
|
|
221
|
+
* const redis = createRedisClient({ host: 'localhost', port: 6379 })
|
|
222
|
+
*
|
|
223
|
+
* // With key prefix
|
|
224
|
+
* const redis = createRedisClient({
|
|
225
|
+
* url: process.env.REDIS_URL,
|
|
226
|
+
* keyPrefix: 'myapp:',
|
|
227
|
+
* })
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
declare function createRedisClient(options?: RedisClientOptions): Redis;
|
|
231
|
+
|
|
232
|
+
export { MemoryRateLimitStore as M, type RateLimitStore as R, SlidingWindowRateLimitStore as S, type RateLimitEntry as a, type RedisClientOptions as b, createRedisClient as c, type RateLimitInfo as d, type RateLimitOptions as e, createRateLimitMiddleware as f, getRateLimitInfo as g, resetRateLimit as r };
|