@sudobility/ratelimit_service 1.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/CLAUDE.md +160 -0
- package/dist/helpers/EntitlementHelper.cjs +75 -0
- package/dist/helpers/EntitlementHelper.d.ts +52 -0
- package/dist/helpers/EntitlementHelper.d.ts.map +1 -0
- package/dist/helpers/EntitlementHelper.js +75 -0
- package/dist/helpers/EntitlementHelper.js.map +1 -0
- package/dist/helpers/RateLimitChecker.cjs +264 -0
- package/dist/helpers/RateLimitChecker.d.ts +90 -0
- package/dist/helpers/RateLimitChecker.d.ts.map +1 -0
- package/dist/helpers/RateLimitChecker.js +264 -0
- package/dist/helpers/RateLimitChecker.js.map +1 -0
- package/dist/helpers/RateLimitRouteHandler.cjs +191 -0
- package/dist/helpers/RateLimitRouteHandler.d.ts +70 -0
- package/dist/helpers/RateLimitRouteHandler.d.ts.map +1 -0
- package/dist/helpers/RateLimitRouteHandler.js +191 -0
- package/dist/helpers/RateLimitRouteHandler.js.map +1 -0
- package/dist/helpers/RevenueCatHelper.cjs +96 -0
- package/dist/helpers/RevenueCatHelper.d.ts +51 -0
- package/dist/helpers/RevenueCatHelper.d.ts.map +1 -0
- package/dist/helpers/RevenueCatHelper.js +96 -0
- package/dist/helpers/RevenueCatHelper.js.map +1 -0
- package/dist/helpers/index.cjs +10 -0
- package/dist/helpers/index.d.ts +4 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +10 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/index.cjs +36 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/hono.cjs +94 -0
- package/dist/middleware/hono.d.ts +63 -0
- package/dist/middleware/hono.d.ts.map +1 -0
- package/dist/middleware/hono.js +94 -0
- package/dist/middleware/hono.js.map +1 -0
- package/dist/schema/rate-limits.cjs +136 -0
- package/dist/schema/rate-limits.d.ts +333 -0
- package/dist/schema/rate-limits.d.ts.map +1 -0
- package/dist/schema/rate-limits.js +136 -0
- package/dist/schema/rate-limits.js.map +1 -0
- package/dist/types/entitlements.cjs +9 -0
- package/dist/types/entitlements.d.ts +29 -0
- package/dist/types/entitlements.d.ts.map +1 -0
- package/dist/types/entitlements.js +9 -0
- package/dist/types/entitlements.js.map +1 -0
- package/dist/types/index.cjs +20 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +20 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/rate-limits.cjs +3 -0
- package/dist/types/rate-limits.d.ts +34 -0
- package/dist/types/rate-limits.d.ts.map +1 -0
- package/dist/types/rate-limits.js +3 -0
- package/dist/types/rate-limits.js.map +1 -0
- package/dist/types/responses.cjs +13 -0
- package/dist/types/responses.d.ts +85 -0
- package/dist/types/responses.d.ts.map +1 -0
- package/dist/types/responses.js +13 -0
- package/dist/types/responses.js.map +1 -0
- package/dist/utils/time.cjs +180 -0
- package/dist/utils/time.d.ts +80 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +180 -0
- package/dist/utils/time.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRateLimitMiddleware = createRateLimitMiddleware;
|
|
4
|
+
const RevenueCatHelper_1 = require("../helpers/RevenueCatHelper");
|
|
5
|
+
const EntitlementHelper_1 = require("../helpers/EntitlementHelper");
|
|
6
|
+
const RateLimitChecker_1 = require("../helpers/RateLimitChecker");
|
|
7
|
+
const types_1 = require("../types");
|
|
8
|
+
/**
|
|
9
|
+
* Create a Hono middleware for rate limiting based on RevenueCat entitlements.
|
|
10
|
+
*
|
|
11
|
+
* This middleware:
|
|
12
|
+
* 1. Fetches user's subscription info from RevenueCat
|
|
13
|
+
* 2. Resolves rate limits based on entitlements
|
|
14
|
+
* 3. Checks and increments counters
|
|
15
|
+
* 4. Returns 429 if rate limit exceeded
|
|
16
|
+
* 5. Sets X-RateLimit-* headers
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { createRateLimitMiddleware } from "@sudobility/ratelimit_service/middleware/hono";
|
|
21
|
+
* import { db, rateLimitCounters } from "./db";
|
|
22
|
+
*
|
|
23
|
+
* const rateLimitMiddleware = createRateLimitMiddleware({
|
|
24
|
+
* revenueCatApiKey: process.env.REVENUECAT_API_KEY!,
|
|
25
|
+
* rateLimitsConfig: {
|
|
26
|
+
* none: { hourly: 5, daily: 20, monthly: 100 },
|
|
27
|
+
* pro: { hourly: undefined, daily: undefined, monthly: undefined },
|
|
28
|
+
* },
|
|
29
|
+
* db,
|
|
30
|
+
* rateLimitsTable: rateLimitCounters,
|
|
31
|
+
* getUserId: (c) => c.get("firebaseUser").uid,
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* app.use("/api/*", rateLimitMiddleware);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function createRateLimitMiddleware(config) {
|
|
38
|
+
const rcHelper = new RevenueCatHelper_1.RevenueCatHelper({ apiKey: config.revenueCatApiKey });
|
|
39
|
+
const entitlementHelper = new EntitlementHelper_1.EntitlementHelper(config.rateLimitsConfig);
|
|
40
|
+
const rateLimitChecker = new RateLimitChecker_1.RateLimitChecker({
|
|
41
|
+
db: config.db,
|
|
42
|
+
table: config.rateLimitsTable,
|
|
43
|
+
});
|
|
44
|
+
return async (c, next) => {
|
|
45
|
+
// Check if rate limiting should be skipped
|
|
46
|
+
if (config.shouldSkip) {
|
|
47
|
+
const skip = await config.shouldSkip(c);
|
|
48
|
+
if (skip) {
|
|
49
|
+
await next();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Get user ID
|
|
54
|
+
const userId = await config.getUserId(c);
|
|
55
|
+
// Get user's subscription info from RevenueCat
|
|
56
|
+
let entitlements;
|
|
57
|
+
let subscriptionStartedAt = null;
|
|
58
|
+
try {
|
|
59
|
+
const subscriptionInfo = await rcHelper.getSubscriptionInfo(userId);
|
|
60
|
+
entitlements = subscriptionInfo.entitlements;
|
|
61
|
+
subscriptionStartedAt = subscriptionInfo.subscriptionStartedAt;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error("RevenueCat error, using 'none' entitlement:", error);
|
|
65
|
+
entitlements = [types_1.NONE_ENTITLEMENT];
|
|
66
|
+
}
|
|
67
|
+
// Get rate limits for user's entitlements
|
|
68
|
+
const limits = entitlementHelper.getRateLimits(entitlements);
|
|
69
|
+
// Check and increment rate limits
|
|
70
|
+
const result = await rateLimitChecker.checkAndIncrement(userId, limits, subscriptionStartedAt);
|
|
71
|
+
// Set rate limit headers
|
|
72
|
+
if (result.remaining.hourly !== undefined) {
|
|
73
|
+
c.header("X-RateLimit-Hourly-Remaining", result.remaining.hourly.toString());
|
|
74
|
+
}
|
|
75
|
+
if (result.remaining.daily !== undefined) {
|
|
76
|
+
c.header("X-RateLimit-Daily-Remaining", result.remaining.daily.toString());
|
|
77
|
+
}
|
|
78
|
+
if (result.remaining.monthly !== undefined) {
|
|
79
|
+
c.header("X-RateLimit-Monthly-Remaining", result.remaining.monthly.toString());
|
|
80
|
+
}
|
|
81
|
+
if (!result.allowed) {
|
|
82
|
+
return c.json({
|
|
83
|
+
success: false,
|
|
84
|
+
error: "Rate limit exceeded",
|
|
85
|
+
message: `You have exceeded your ${result.exceededLimit} request limit. Please try again later or upgrade your subscription.`,
|
|
86
|
+
remaining: result.remaining,
|
|
87
|
+
exceededLimit: result.exceededLimit,
|
|
88
|
+
timestamp: new Date().toISOString(),
|
|
89
|
+
}, 429);
|
|
90
|
+
}
|
|
91
|
+
await next();
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=hono.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Context, Next } from "hono";
|
|
2
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
3
|
+
import type { PgTable, TableConfig } from "drizzle-orm/pg-core";
|
|
4
|
+
import { type RateLimitsConfig } from "../types";
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for the rate limit middleware factory.
|
|
7
|
+
*/
|
|
8
|
+
export interface RateLimitMiddlewareConfig {
|
|
9
|
+
/** RevenueCat API key */
|
|
10
|
+
revenueCatApiKey: string;
|
|
11
|
+
/** Rate limits configuration */
|
|
12
|
+
rateLimitsConfig: RateLimitsConfig;
|
|
13
|
+
/** Drizzle database instance */
|
|
14
|
+
db: PostgresJsDatabase<any>;
|
|
15
|
+
/** The rate_limit_counters table from your schema */
|
|
16
|
+
rateLimitsTable: PgTable<TableConfig>;
|
|
17
|
+
/** Function to extract user ID from context */
|
|
18
|
+
getUserId: (c: Context) => string | Promise<string>;
|
|
19
|
+
/** Optional: Skip rate limiting for certain conditions (e.g., admin tokens) */
|
|
20
|
+
shouldSkip?: (c: Context) => boolean | Promise<boolean>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a Hono middleware for rate limiting based on RevenueCat entitlements.
|
|
24
|
+
*
|
|
25
|
+
* This middleware:
|
|
26
|
+
* 1. Fetches user's subscription info from RevenueCat
|
|
27
|
+
* 2. Resolves rate limits based on entitlements
|
|
28
|
+
* 3. Checks and increments counters
|
|
29
|
+
* 4. Returns 429 if rate limit exceeded
|
|
30
|
+
* 5. Sets X-RateLimit-* headers
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* import { createRateLimitMiddleware } from "@sudobility/ratelimit_service/middleware/hono";
|
|
35
|
+
* import { db, rateLimitCounters } from "./db";
|
|
36
|
+
*
|
|
37
|
+
* const rateLimitMiddleware = createRateLimitMiddleware({
|
|
38
|
+
* revenueCatApiKey: process.env.REVENUECAT_API_KEY!,
|
|
39
|
+
* rateLimitsConfig: {
|
|
40
|
+
* none: { hourly: 5, daily: 20, monthly: 100 },
|
|
41
|
+
* pro: { hourly: undefined, daily: undefined, monthly: undefined },
|
|
42
|
+
* },
|
|
43
|
+
* db,
|
|
44
|
+
* rateLimitsTable: rateLimitCounters,
|
|
45
|
+
* getUserId: (c) => c.get("firebaseUser").uid,
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* app.use("/api/*", rateLimitMiddleware);
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function createRateLimitMiddleware(config: RateLimitMiddlewareConfig): (c: Context, next: Next) => Promise<(Response & import("hono").TypedResponse<{
|
|
52
|
+
success: false;
|
|
53
|
+
error: string;
|
|
54
|
+
message: string;
|
|
55
|
+
remaining: {
|
|
56
|
+
hourly?: number | undefined;
|
|
57
|
+
daily?: number | undefined;
|
|
58
|
+
monthly?: number | undefined;
|
|
59
|
+
};
|
|
60
|
+
exceededLimit: import("..").ExceededLimit | undefined;
|
|
61
|
+
timestamp: string;
|
|
62
|
+
}, 429, "json">) | undefined>;
|
|
63
|
+
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIhE,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,yBAAyB;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gCAAgC;IAChC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,gCAAgC;IAChC,EAAE,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAC5B,qDAAqD;IACrD,eAAe,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,+CAA+C;IAC/C,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,yBAAyB,IAQ3D,GAAG,OAAO,EAAE,MAAM,IAAI;;;;;;;;;;;8BAuErC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRateLimitMiddleware = createRateLimitMiddleware;
|
|
4
|
+
const RevenueCatHelper_1 = require("../helpers/RevenueCatHelper");
|
|
5
|
+
const EntitlementHelper_1 = require("../helpers/EntitlementHelper");
|
|
6
|
+
const RateLimitChecker_1 = require("../helpers/RateLimitChecker");
|
|
7
|
+
const types_1 = require("../types");
|
|
8
|
+
/**
|
|
9
|
+
* Create a Hono middleware for rate limiting based on RevenueCat entitlements.
|
|
10
|
+
*
|
|
11
|
+
* This middleware:
|
|
12
|
+
* 1. Fetches user's subscription info from RevenueCat
|
|
13
|
+
* 2. Resolves rate limits based on entitlements
|
|
14
|
+
* 3. Checks and increments counters
|
|
15
|
+
* 4. Returns 429 if rate limit exceeded
|
|
16
|
+
* 5. Sets X-RateLimit-* headers
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { createRateLimitMiddleware } from "@sudobility/ratelimit_service/middleware/hono";
|
|
21
|
+
* import { db, rateLimitCounters } from "./db";
|
|
22
|
+
*
|
|
23
|
+
* const rateLimitMiddleware = createRateLimitMiddleware({
|
|
24
|
+
* revenueCatApiKey: process.env.REVENUECAT_API_KEY!,
|
|
25
|
+
* rateLimitsConfig: {
|
|
26
|
+
* none: { hourly: 5, daily: 20, monthly: 100 },
|
|
27
|
+
* pro: { hourly: undefined, daily: undefined, monthly: undefined },
|
|
28
|
+
* },
|
|
29
|
+
* db,
|
|
30
|
+
* rateLimitsTable: rateLimitCounters,
|
|
31
|
+
* getUserId: (c) => c.get("firebaseUser").uid,
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* app.use("/api/*", rateLimitMiddleware);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function createRateLimitMiddleware(config) {
|
|
38
|
+
const rcHelper = new RevenueCatHelper_1.RevenueCatHelper({ apiKey: config.revenueCatApiKey });
|
|
39
|
+
const entitlementHelper = new EntitlementHelper_1.EntitlementHelper(config.rateLimitsConfig);
|
|
40
|
+
const rateLimitChecker = new RateLimitChecker_1.RateLimitChecker({
|
|
41
|
+
db: config.db,
|
|
42
|
+
table: config.rateLimitsTable,
|
|
43
|
+
});
|
|
44
|
+
return async (c, next) => {
|
|
45
|
+
// Check if rate limiting should be skipped
|
|
46
|
+
if (config.shouldSkip) {
|
|
47
|
+
const skip = await config.shouldSkip(c);
|
|
48
|
+
if (skip) {
|
|
49
|
+
await next();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Get user ID
|
|
54
|
+
const userId = await config.getUserId(c);
|
|
55
|
+
// Get user's subscription info from RevenueCat
|
|
56
|
+
let entitlements;
|
|
57
|
+
let subscriptionStartedAt = null;
|
|
58
|
+
try {
|
|
59
|
+
const subscriptionInfo = await rcHelper.getSubscriptionInfo(userId);
|
|
60
|
+
entitlements = subscriptionInfo.entitlements;
|
|
61
|
+
subscriptionStartedAt = subscriptionInfo.subscriptionStartedAt;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error("RevenueCat error, using 'none' entitlement:", error);
|
|
65
|
+
entitlements = [types_1.NONE_ENTITLEMENT];
|
|
66
|
+
}
|
|
67
|
+
// Get rate limits for user's entitlements
|
|
68
|
+
const limits = entitlementHelper.getRateLimits(entitlements);
|
|
69
|
+
// Check and increment rate limits
|
|
70
|
+
const result = await rateLimitChecker.checkAndIncrement(userId, limits, subscriptionStartedAt);
|
|
71
|
+
// Set rate limit headers
|
|
72
|
+
if (result.remaining.hourly !== undefined) {
|
|
73
|
+
c.header("X-RateLimit-Hourly-Remaining", result.remaining.hourly.toString());
|
|
74
|
+
}
|
|
75
|
+
if (result.remaining.daily !== undefined) {
|
|
76
|
+
c.header("X-RateLimit-Daily-Remaining", result.remaining.daily.toString());
|
|
77
|
+
}
|
|
78
|
+
if (result.remaining.monthly !== undefined) {
|
|
79
|
+
c.header("X-RateLimit-Monthly-Remaining", result.remaining.monthly.toString());
|
|
80
|
+
}
|
|
81
|
+
if (!result.allowed) {
|
|
82
|
+
return c.json({
|
|
83
|
+
success: false,
|
|
84
|
+
error: "Rate limit exceeded",
|
|
85
|
+
message: `You have exceeded your ${result.exceededLimit} request limit. Please try again later or upgrade your subscription.`,
|
|
86
|
+
remaining: result.remaining,
|
|
87
|
+
exceededLimit: result.exceededLimit,
|
|
88
|
+
timestamp: new Date().toISOString(),
|
|
89
|
+
}, 429);
|
|
90
|
+
}
|
|
91
|
+
await next();
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=hono.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":";;AAuDA,8DA+EC;AAnID,kEAA+D;AAC/D,oEAAiE;AACjE,kEAA+D;AAC/D,oCAAmE;AAoBnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,SAAgB,yBAAyB,CAAC,MAAiC;IACzE,MAAM,QAAQ,GAAG,IAAI,mCAAgB,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC3E,MAAM,iBAAiB,GAAG,IAAI,qCAAiB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACzE,MAAM,gBAAgB,GAAG,IAAI,mCAAgB,CAAC;QAC5C,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,KAAK,EAAE,MAAM,CAAC,eAAe;KAC9B,CAAC,CAAC;IAEH,OAAO,KAAK,EAAE,CAAU,EAAE,IAAU,EAAE,EAAE;QACtC,2CAA2C;QAC3C,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACxC,IAAI,IAAI,EAAE,CAAC;gBACT,MAAM,IAAI,EAAE,CAAC;gBACb,OAAO;YACT,CAAC;QACH,CAAC;QAED,cAAc;QACd,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAEzC,+CAA+C;QAC/C,IAAI,YAAsB,CAAC;QAC3B,IAAI,qBAAqB,GAAgB,IAAI,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,gBAAgB,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;YACpE,YAAY,GAAG,gBAAgB,CAAC,YAAY,CAAC;YAC7C,qBAAqB,GAAG,gBAAgB,CAAC,qBAAqB,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,6CAA6C,EAAE,KAAK,CAAC,CAAC;YACpE,YAAY,GAAG,CAAC,wBAAgB,CAAC,CAAC;QACpC,CAAC;QAED,0CAA0C;QAC1C,MAAM,MAAM,GAAG,iBAAiB,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;QAE7D,kCAAkC;QAClC,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,iBAAiB,CACrD,MAAM,EACN,MAAM,EACN,qBAAqB,CACtB,CAAC;QAEF,yBAAyB;QACzB,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1C,CAAC,CAAC,MAAM,CACN,8BAA8B,EAC9B,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,CACnC,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YACzC,CAAC,CAAC,MAAM,CACN,6BAA6B,EAC7B,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,CAClC,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC3C,CAAC,CAAC,MAAM,CACN,+BAA+B,EAC/B,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CACpC,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,CAAC,IAAI,CACX;gBACE,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,qBAAqB;gBAC5B,OAAO,EAAE,0BAA0B,MAAM,CAAC,aAAa,sEAAsE;gBAC7H,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,aAAa,EAAE,MAAM,CAAC,aAAa;gBACnC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,EACD,GAAG,CACJ,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Drizzle schema and database initialization for rate limit tracking.
|
|
4
|
+
*
|
|
5
|
+
* Provides:
|
|
6
|
+
* - createRateLimitCountersTable: Factory for Drizzle table with custom schema
|
|
7
|
+
* - initRateLimitTable: SQL initialization for the table
|
|
8
|
+
* - Default rateLimitCounters table for public schema
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.rateLimitCounters = void 0;
|
|
12
|
+
exports.createRateLimitCountersTable = createRateLimitCountersTable;
|
|
13
|
+
exports.createRateLimitCountersTablePublic = createRateLimitCountersTablePublic;
|
|
14
|
+
exports.initRateLimitTable = initRateLimitTable;
|
|
15
|
+
const pg_core_1 = require("drizzle-orm/pg-core");
|
|
16
|
+
/**
|
|
17
|
+
* Create a rate limit counters table for a specific PostgreSQL schema.
|
|
18
|
+
*
|
|
19
|
+
* @param schema - The Drizzle pgSchema object (e.g., pgSchema("shapeshyft"))
|
|
20
|
+
* @param indexPrefix - Prefix for index names to avoid conflicts
|
|
21
|
+
* @returns Drizzle table definition
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { pgSchema } from "drizzle-orm/pg-core";
|
|
26
|
+
* import { createRateLimitCountersTable } from "@sudobility/ratelimit_service";
|
|
27
|
+
*
|
|
28
|
+
* const mySchema = pgSchema("myapp");
|
|
29
|
+
* export const rateLimitCounters = createRateLimitCountersTable(mySchema, "myapp");
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function createRateLimitCountersTable(schema, indexPrefix) {
|
|
33
|
+
return schema.table("rate_limit_counters", {
|
|
34
|
+
id: (0, pg_core_1.uuid)("id").primaryKey().defaultRandom(),
|
|
35
|
+
user_id: (0, pg_core_1.varchar)("user_id", { length: 128 }).notNull(),
|
|
36
|
+
period_type: (0, pg_core_1.varchar)("period_type", { length: 16 }).notNull(),
|
|
37
|
+
period_start: (0, pg_core_1.timestamp)("period_start", { withTimezone: true }).notNull(),
|
|
38
|
+
request_count: (0, pg_core_1.integer)("request_count").notNull().default(0),
|
|
39
|
+
created_at: (0, pg_core_1.timestamp)("created_at", { withTimezone: true }).defaultNow(),
|
|
40
|
+
updated_at: (0, pg_core_1.timestamp)("updated_at", { withTimezone: true }).defaultNow(),
|
|
41
|
+
}, (table) => ({
|
|
42
|
+
userPeriodUniqueIdx: (0, pg_core_1.uniqueIndex)(`${indexPrefix}_rate_limit_user_period_idx`).on(table.user_id, table.period_type, table.period_start),
|
|
43
|
+
userTypeIdx: (0, pg_core_1.index)(`${indexPrefix}_rate_limit_user_type_idx`).on(table.user_id, table.period_type),
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create a rate limit counters table for the public schema.
|
|
48
|
+
*
|
|
49
|
+
* @param indexPrefix - Prefix for index names
|
|
50
|
+
* @returns Drizzle table definition
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* import { createRateLimitCountersTablePublic } from "@sudobility/ratelimit_service";
|
|
55
|
+
*
|
|
56
|
+
* export const rateLimitCounters = createRateLimitCountersTablePublic("sudojo");
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
function createRateLimitCountersTablePublic(indexPrefix) {
|
|
60
|
+
return (0, pg_core_1.pgTable)("rate_limit_counters", {
|
|
61
|
+
id: (0, pg_core_1.uuid)("id").primaryKey().defaultRandom(),
|
|
62
|
+
user_id: (0, pg_core_1.varchar)("user_id", { length: 128 }).notNull(),
|
|
63
|
+
period_type: (0, pg_core_1.varchar)("period_type", { length: 16 }).notNull(),
|
|
64
|
+
period_start: (0, pg_core_1.timestamp)("period_start", { withTimezone: true }).notNull(),
|
|
65
|
+
request_count: (0, pg_core_1.integer)("request_count").notNull().default(0),
|
|
66
|
+
created_at: (0, pg_core_1.timestamp)("created_at", { withTimezone: true }).defaultNow(),
|
|
67
|
+
updated_at: (0, pg_core_1.timestamp)("updated_at", { withTimezone: true }).defaultNow(),
|
|
68
|
+
}, table => ({
|
|
69
|
+
userPeriodUniqueIdx: (0, pg_core_1.uniqueIndex)(`${indexPrefix}_rate_limit_user_period_idx`).on(table.user_id, table.period_type, table.period_start),
|
|
70
|
+
userTypeIdx: (0, pg_core_1.index)(`${indexPrefix}_rate_limit_user_type_idx`).on(table.user_id, table.period_type),
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Default rate limit counters table for public schema.
|
|
75
|
+
* Use createRateLimitCountersTable for custom schemas.
|
|
76
|
+
*/
|
|
77
|
+
exports.rateLimitCounters = (0, pg_core_1.pgTable)("rate_limit_counters", {
|
|
78
|
+
id: (0, pg_core_1.uuid)("id").primaryKey().defaultRandom(),
|
|
79
|
+
user_id: (0, pg_core_1.varchar)("user_id", { length: 128 }).notNull(),
|
|
80
|
+
period_type: (0, pg_core_1.varchar)("period_type", { length: 16 }).notNull(),
|
|
81
|
+
period_start: (0, pg_core_1.timestamp)("period_start", { withTimezone: true }).notNull(),
|
|
82
|
+
request_count: (0, pg_core_1.integer)("request_count").notNull().default(0),
|
|
83
|
+
created_at: (0, pg_core_1.timestamp)("created_at", { withTimezone: true }).defaultNow(),
|
|
84
|
+
updated_at: (0, pg_core_1.timestamp)("updated_at", { withTimezone: true }).defaultNow(),
|
|
85
|
+
}, table => ({
|
|
86
|
+
userPeriodUniqueIdx: (0, pg_core_1.uniqueIndex)("rate_limit_counters_user_period_idx").on(table.user_id, table.period_type, table.period_start),
|
|
87
|
+
userTypeIdx: (0, pg_core_1.index)("rate_limit_counters_user_type_idx").on(table.user_id, table.period_type),
|
|
88
|
+
}));
|
|
89
|
+
/**
|
|
90
|
+
* Initialize the rate limit counters table in the database.
|
|
91
|
+
*
|
|
92
|
+
* @param client - postgres-js client instance
|
|
93
|
+
* @param schemaName - PostgreSQL schema name (e.g., "shapeshyft", "whisperly", or null for public)
|
|
94
|
+
* @param indexPrefix - Prefix for index names to avoid conflicts
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* import postgres from "postgres";
|
|
99
|
+
* import { initRateLimitTable } from "@sudobility/ratelimit_service";
|
|
100
|
+
*
|
|
101
|
+
* const client = postgres(connectionString);
|
|
102
|
+
*
|
|
103
|
+
* // For public schema
|
|
104
|
+
* await initRateLimitTable(client, null, "sudojo");
|
|
105
|
+
*
|
|
106
|
+
* // For custom schema
|
|
107
|
+
* await initRateLimitTable(client, "shapeshyft", "shapeshyft");
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
async function initRateLimitTable(client, schemaName, indexPrefix) {
|
|
111
|
+
const tableName = schemaName
|
|
112
|
+
? `${schemaName}.rate_limit_counters`
|
|
113
|
+
: "rate_limit_counters";
|
|
114
|
+
const uniqueIdxName = `${indexPrefix}_rate_limit_user_period_idx`;
|
|
115
|
+
const typeIdxName = `${indexPrefix}_rate_limit_user_type_idx`;
|
|
116
|
+
await client.unsafe(`
|
|
117
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
118
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
119
|
+
user_id VARCHAR(128) NOT NULL,
|
|
120
|
+
period_type VARCHAR(16) NOT NULL,
|
|
121
|
+
period_start TIMESTAMPTZ NOT NULL,
|
|
122
|
+
request_count INTEGER NOT NULL DEFAULT 0,
|
|
123
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
124
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
125
|
+
)
|
|
126
|
+
`);
|
|
127
|
+
await client.unsafe(`
|
|
128
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ${uniqueIdxName}
|
|
129
|
+
ON ${tableName} (user_id, period_type, period_start)
|
|
130
|
+
`);
|
|
131
|
+
await client.unsafe(`
|
|
132
|
+
CREATE INDEX IF NOT EXISTS ${typeIdxName}
|
|
133
|
+
ON ${tableName} (user_id, period_type)
|
|
134
|
+
`);
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=rate-limits.js.map
|