@usageflow/express 0.1.2 → 0.1.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/plugin.d.ts CHANGED
@@ -15,4 +15,5 @@ export declare class ExpressUsageFlowAPI extends UsageFlowAPI {
15
15
  private collectRequestMetadata;
16
16
  private executeRequestWithMetadata;
17
17
  createMiddleware(routes: Route[], whitelistRoutes?: Route[]): (request: Request, response: Response, next: NextFunction) => Promise<void>;
18
+ protected guessLedgerId(request: Request): string;
18
19
  }
package/dist/plugin.js CHANGED
@@ -15,12 +15,12 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
15
15
  method: request.method,
16
16
  url: request.route?.path || request.path || request.url || "/",
17
17
  rawUrl: request.originalUrl || "/",
18
- clientIP: request.ip || "unknown",
18
+ clientIP: clientIP || "unknown",
19
19
  userAgent: request.headers["user-agent"],
20
20
  timestamp: new Date().toISOString(),
21
21
  headers,
22
- queryParams: request.query,
23
- pathParams: request.params,
22
+ queryParams: Object.keys(request.query).length > 0 ? request.query : null,
23
+ pathParams: Object.keys(request.params).length > 0 ? request.params : null,
24
24
  body: request.body,
25
25
  };
26
26
  return metadata;
@@ -129,6 +129,10 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
129
129
  "x-usage-key": self.apiKey,
130
130
  "Content-Type": "application/json",
131
131
  };
132
+ if (request.usageflow?.startTime) {
133
+ metadata.requestDuration =
134
+ Date.now() - request.usageflow.startTime;
135
+ }
132
136
  const payload = {
133
137
  alias: `${request.method} ${request.route?.path || request.path || request.url || "/"}`,
134
138
  amount: 1,
@@ -142,7 +146,7 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
142
146
  }).catch((error) => {
143
147
  console.error("Error finalizing request:", error);
144
148
  });
145
- // Handle the different overloads
149
+ // Handle the different overloadsx
146
150
  if (typeof chunk === "function") {
147
151
  return originalEnd.call(this, undefined, "utf8", chunk);
148
152
  }
@@ -163,6 +167,44 @@ class ExpressUsageFlowAPI extends core_1.UsageFlowAPI {
163
167
  }
164
168
  };
165
169
  }
170
+ guessLedgerId(request) {
171
+ const method = request.method;
172
+ const url = request.route?.path || request.path;
173
+ const config = this.apiConfig;
174
+ if (!config?.identityFieldName || !config?.identityFieldLocation) {
175
+ return `${method} ${url}`;
176
+ }
177
+ const fieldName = config.identityFieldName;
178
+ const location = config.identityFieldLocation;
179
+ switch (location) {
180
+ case "path_params":
181
+ if (request.params?.[fieldName]) {
182
+ return `${method} ${url} ${request.params[fieldName]}`;
183
+ }
184
+ break;
185
+ case "query_params":
186
+ if (request.query?.[fieldName]) {
187
+ return `${method} ${url} ${request.query[fieldName]}`;
188
+ }
189
+ break;
190
+ case "body":
191
+ if (request.body?.[fieldName]) {
192
+ return `${method} ${url} ${request.body[fieldName]}`;
193
+ }
194
+ break;
195
+ case "bearer_token":
196
+ const authHeader = request.headers.authorization;
197
+ const token = this.extractBearerToken(authHeader);
198
+ if (token) {
199
+ const claims = this.decodeJwtUnverified(token);
200
+ if (claims?.[fieldName]) {
201
+ return `${method} ${url} ${claims[fieldName]}`;
202
+ }
203
+ }
204
+ break;
205
+ }
206
+ return `${method} ${url}`;
207
+ }
166
208
  }
167
209
  exports.ExpressUsageFlowAPI = ExpressUsageFlowAPI;
168
210
  //# sourceMappingURL=plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usageflow/express",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "UsageFlow plugin for Express applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,7 +10,7 @@
10
10
  "prepare": "npm run build"
11
11
  },
12
12
  "dependencies": {
13
- "@usageflow/core": "^0.1.2",
13
+ "@usageflow/core": "^0.1.3",
14
14
  "express": "^4.18.0"
15
15
  },
16
16
  "homepage": "https://usageflow.io",
package/src/plugin.ts CHANGED
@@ -2,217 +2,266 @@ import { Request, Response, NextFunction } from "express";
2
2
  import { UsageFlowAPI, Route, RequestMetadata } from "@usageflow/core";
3
3
 
4
4
  declare global {
5
- namespace Express {
6
- interface Request {
7
- usageflow?: {
8
- startTime: number;
9
- eventId?: string;
10
- metadata?: RequestMetadata;
11
- };
5
+ namespace Express {
6
+ interface Request {
7
+ usageflow?: {
8
+ startTime: number;
9
+ eventId?: string;
10
+ metadata?: RequestMetadata;
11
+ };
12
+ }
12
13
  }
13
- }
14
14
  }
15
15
 
16
16
  export class ExpressUsageFlowAPI extends UsageFlowAPI {
17
- private async collectRequestMetadata(
18
- request: Request,
19
- ): Promise<RequestMetadata> {
20
- const headers = this.sanitizeHeaders(
21
- request.headers as Record<string, string>,
22
- );
23
-
24
- // Get client IP, handling forwarded headers
25
- let clientIP = request.ip;
26
- const forwardedFor = request.headers["x-forwarded-for"];
27
- if (forwardedFor && typeof forwardedFor === "string") {
28
- clientIP = forwardedFor.split(",")[0].trim();
29
- }
17
+ private async collectRequestMetadata(
18
+ request: Request,
19
+ ): Promise<RequestMetadata> {
20
+ const headers = this.sanitizeHeaders(
21
+ request.headers as Record<string, string>,
22
+ );
30
23
 
31
- const metadata: RequestMetadata = {
32
- method: request.method,
33
- url: request.route?.path || request.path || request.url || "/",
34
- rawUrl: request.originalUrl || "/",
35
- clientIP: request.ip || "unknown",
36
- userAgent: request.headers["user-agent"] as string,
37
- timestamp: new Date().toISOString(),
38
- headers,
39
- queryParams: request.query as Record<string, any>,
40
- pathParams: request.params,
41
- body: request.body,
42
- };
43
-
44
- return metadata;
45
- }
46
-
47
- private async executeRequestWithMetadata(
48
- ledgerId: string,
49
- metadata: RequestMetadata,
50
- request: Request,
51
- response: Response,
52
- ): Promise<void> {
53
- if (!this.apiKey) {
54
- throw new Error("API key not initialized");
55
- }
24
+ // Get client IP, handling forwarded headers
25
+ let clientIP = request.ip;
26
+ const forwardedFor = request.headers["x-forwarded-for"];
27
+ if (forwardedFor && typeof forwardedFor === "string") {
28
+ clientIP = forwardedFor.split(",")[0].trim();
29
+ }
56
30
 
57
- const headers = {
58
- "x-usage-key": this.apiKey,
59
- "Content-Type": "application/json",
60
- };
61
-
62
- const payload = {
63
- alias: ledgerId,
64
- amount: 1,
65
- metadata,
66
- };
67
-
68
- try {
69
- const response = await fetch(
70
- `${this.usageflowUrl}/api/v1/ledgers/measure/allocate`,
71
- {
72
- method: "POST",
73
- headers,
74
- body: JSON.stringify(payload),
75
- },
76
- );
77
-
78
- const data = await response.json();
79
-
80
- if (response.status === 400) {
81
- throw new Error(data.message);
82
- }
83
-
84
- if (response.ok) {
85
- request.usageflow!.eventId = data.eventId;
86
- request.usageflow!.metadata = metadata;
87
- } else {
88
- throw new Error(data.message || "Unknown error occurred");
89
- }
90
- } catch (error: any) {
91
- if (
92
- error.message ==
93
- "Failed to use resource after retries: Faile to preform operation"
94
- ) {
95
- throw new Error("Failed to allocate resource");
96
- }
97
- throw error;
31
+ const metadata: RequestMetadata = {
32
+ method: request.method,
33
+ url: request.route?.path || request.path || request.url || "/",
34
+ rawUrl: request.originalUrl || "/",
35
+ clientIP: clientIP || "unknown",
36
+ userAgent: request.headers["user-agent"] as string,
37
+ timestamp: new Date().toISOString(),
38
+ headers,
39
+ queryParams: Object.keys(request.query).length > 0 ? request.query : null,
40
+ pathParams: Object.keys(request.params).length > 0 ? request.params : null,
41
+ body: request.body,
42
+ };
43
+
44
+ return metadata;
98
45
  }
99
- }
100
46
 
101
- public createMiddleware(routes: Route[], whitelistRoutes: Route[] = []) {
102
- const routesMap = this.createRoutesMap(routes);
103
- const whitelistMap = this.createRoutesMap(whitelistRoutes);
104
- const self = this;
47
+ private async executeRequestWithMetadata(
48
+ ledgerId: string,
49
+ metadata: RequestMetadata,
50
+ request: Request,
51
+ response: Response,
52
+ ): Promise<void> {
53
+ if (!this.apiKey) {
54
+ throw new Error("API key not initialized");
55
+ }
105
56
 
106
- return async (request: Request, response: Response, next: NextFunction) => {
107
- const method = request.method;
108
- const url = request.route?.path || request.path || request.url;
57
+ const headers = {
58
+ "x-usage-key": this.apiKey,
59
+ "Content-Type": "application/json",
60
+ };
109
61
 
110
- request.usageflow = {
111
- startTime: Date.now(),
112
- };
62
+ const payload = {
63
+ alias: ledgerId,
64
+ amount: 1,
65
+ metadata,
66
+ };
113
67
 
114
- if (this.shouldSkipRoute(method, url || "", whitelistMap)) {
115
- return next();
116
- }
68
+ try {
69
+ const response = await fetch(
70
+ `${this.usageflowUrl}/api/v1/ledgers/measure/allocate`,
71
+ {
72
+ method: "POST",
73
+ headers,
74
+ body: JSON.stringify(payload),
75
+ },
76
+ );
117
77
 
118
- if (!this.shouldMonitorRoute(method, url || "", routesMap)) {
119
- return next();
120
- }
78
+ const data = await response.json();
121
79
 
122
- const metadata = await this.collectRequestMetadata(request);
80
+ if (response.status === 400) {
81
+ throw new Error(data.message);
82
+ }
123
83
 
124
- try {
125
- await this.executeRequestWithMetadata(
126
- `${method} ${url}`,
127
- metadata,
128
- request,
129
- response,
130
- );
84
+ if (response.ok) {
85
+ request.usageflow!.eventId = data.eventId;
86
+ request.usageflow!.metadata = metadata;
87
+ } else {
88
+ throw new Error(data.message || "Unknown error occurred");
89
+ }
90
+ } catch (error: any) {
91
+ if (
92
+ error.message ==
93
+ "Failed to use resource after retries: Faile to preform operation"
94
+ ) {
95
+ throw new Error("Failed to allocate resource");
96
+ }
97
+ throw error;
98
+ }
99
+ }
100
+
101
+ public createMiddleware(routes: Route[], whitelistRoutes: Route[] = []) {
102
+ const routesMap = this.createRoutesMap(routes);
103
+ const whitelistMap = this.createRoutesMap(whitelistRoutes);
104
+ const self = this;
105
+
106
+ return async (request: Request, response: Response, next: NextFunction) => {
107
+ const method = request.method;
108
+ const url = request.route?.path || request.path || request.url;
109
+
110
+ request.usageflow = {
111
+ startTime: Date.now(),
112
+ };
113
+
114
+ if (this.shouldSkipRoute(method, url || "", whitelistMap)) {
115
+ return next();
116
+ }
117
+
118
+ if (!this.shouldMonitorRoute(method, url || "", routesMap)) {
119
+ return next();
120
+ }
121
+
122
+ const metadata = await this.collectRequestMetadata(request);
131
123
 
132
- // Capture response data
133
- const originalEnd = response.end;
134
- response.end = function (
135
- this: Response,
136
- chunk?: any,
137
- encoding?: BufferEncoding,
138
- cb?: () => void,
139
- ) {
140
- if (!request.usageflow?.eventId) {
141
- return originalEnd.call(this, chunk, encoding || "utf8", cb);
142
- }
143
-
144
- const metadata = request.usageflow?.metadata || {};
145
- (metadata as any).responseStatusCode = response.statusCode;
146
-
147
- // Add response body to metadata if it exists
148
- if (chunk) {
149
124
  try {
150
- if (typeof chunk === "string") {
151
- // Try to parse as JSON if it's a string
152
- try {
153
- const parsed = JSON.parse(chunk);
154
- (metadata as any).body = parsed;
155
- } catch {
156
- // If not valid JSON, use the string as is
157
- (metadata as any).body = chunk;
158
- }
159
- } else if (Buffer.isBuffer(chunk)) {
160
- const str = chunk.toString("utf8");
161
- // Try to parse as JSON if it's a buffer
162
- try {
163
- const parsed = JSON.parse(str);
164
- (metadata as any).body = parsed;
165
- } catch {
166
- // If not valid JSON, use the string as is
167
- (metadata as any).body = str;
168
- }
169
- } else if (typeof chunk === "object") {
170
- (metadata as any).body = chunk;
171
- }
172
- } catch (error) {
173
- console.error("Error parsing response body:", error);
125
+ await this.executeRequestWithMetadata(
126
+ `${method} ${url}`,
127
+ metadata,
128
+ request,
129
+ response,
130
+ );
131
+
132
+ // Capture response data
133
+ const originalEnd = response.end;
134
+ response.end = function (
135
+ this: Response,
136
+ chunk?: any,
137
+ encoding?: BufferEncoding,
138
+ cb?: () => void,
139
+ ) {
140
+ if (!request.usageflow?.eventId) {
141
+ return originalEnd.call(this, chunk, encoding || "utf8", cb);
142
+ }
143
+
144
+ const metadata: Record<string, any> =
145
+ request.usageflow?.metadata || {};
146
+ metadata.responseStatusCode = response.statusCode;
147
+
148
+ // Add response body to metadata if it exists
149
+ if (chunk) {
150
+ try {
151
+ if (typeof chunk === "string") {
152
+ // Try to parse as JSON if it's a string
153
+ try {
154
+ const parsed = JSON.parse(chunk);
155
+ (metadata as any).body = parsed;
156
+ } catch {
157
+ // If not valid JSON, use the string as is
158
+ (metadata as any).body = chunk;
159
+ }
160
+ } else if (Buffer.isBuffer(chunk)) {
161
+ const str = chunk.toString("utf8");
162
+ // Try to parse as JSON if it's a buffer
163
+ try {
164
+ const parsed = JSON.parse(str);
165
+ (metadata as any).body = parsed;
166
+ } catch {
167
+ // If not valid JSON, use the string as is
168
+ (metadata as any).body = str;
169
+ }
170
+ } else if (typeof chunk === "object") {
171
+ (metadata as any).body = chunk;
172
+ }
173
+ } catch (error) {
174
+ console.error("Error parsing response body:", error);
175
+ }
176
+ }
177
+
178
+ const headers = {
179
+ "x-usage-key": self.apiKey!,
180
+ "Content-Type": "application/json",
181
+ };
182
+
183
+ if (request.usageflow?.startTime) {
184
+ metadata.requestDuration =
185
+ Date.now() - request.usageflow.startTime;
186
+ }
187
+
188
+ const payload = {
189
+ alias: `${request.method} ${request.route?.path || request.path || request.url || "/"}`,
190
+ amount: 1,
191
+ allocationId: request.usageflow?.eventId,
192
+ metadata,
193
+ };
194
+
195
+ fetch(`${self.usageflowUrl}/api/v1/ledgers/measure/allocate/use`, {
196
+ method: "POST",
197
+ headers,
198
+ body: JSON.stringify(payload),
199
+ }).catch((error) => {
200
+ console.error("Error finalizing request:", error);
201
+ });
202
+
203
+ // Handle the different overloadsx
204
+ if (typeof chunk === "function") {
205
+ return originalEnd.call(this, undefined, "utf8", chunk);
206
+ }
207
+ if (typeof encoding === "function") {
208
+ return originalEnd.call(this, chunk, "utf8", encoding);
209
+ }
210
+ return originalEnd.call(this, chunk, encoding || "utf8", cb);
211
+ } as typeof response.end;
212
+
213
+ next();
214
+ } catch (error: any) {
215
+ console.error("Error executing request with metadata:", error);
216
+ response.status(400).json({
217
+ message: error.message,
218
+ blocked: true,
219
+ });
220
+ return;
174
221
  }
175
- }
222
+ };
223
+ }
176
224
 
177
- const headers = {
178
- "x-usage-key": self.apiKey!,
179
- "Content-Type": "application/json",
180
- };
225
+ protected guessLedgerId(request: Request): string {
226
+ const method = request.method;
227
+ const url = request.route?.path || request.path;
228
+ const config = this.apiConfig
181
229
 
182
- const payload = {
183
- alias: `${request.method} ${request.route?.path || request.path || request.url || "/"}`,
184
- amount: 1,
185
- allocationId: request.usageflow?.eventId,
186
- metadata,
187
- };
230
+ if (!config?.identityFieldName || !config?.identityFieldLocation) {
231
+ return `${method} ${url}`;
232
+ }
188
233
 
189
- fetch(`${self.usageflowUrl}/api/v1/ledgers/measure/allocate/use`, {
190
- method: "POST",
191
- headers,
192
- body: JSON.stringify(payload),
193
- }).catch((error) => {
194
- console.error("Error finalizing request:", error);
195
- });
196
-
197
- // Handle the different overloads
198
- if (typeof chunk === "function") {
199
- return originalEnd.call(this, undefined, "utf8", chunk);
200
- }
201
- if (typeof encoding === "function") {
202
- return originalEnd.call(this, chunk, "utf8", encoding);
203
- }
204
- return originalEnd.call(this, chunk, encoding || "utf8", cb);
205
- } as typeof response.end;
206
-
207
- next();
208
- } catch (error: any) {
209
- console.error("Error executing request with metadata:", error);
210
- response.status(400).json({
211
- message: error.message,
212
- blocked: true,
213
- });
214
- return;
215
- }
216
- };
217
- }
234
+ const fieldName = config.identityFieldName;
235
+ const location = config.identityFieldLocation;
236
+
237
+ switch (location) {
238
+ case "path_params":
239
+ if (request.params?.[fieldName]) {
240
+ return `${method} ${url} ${request.params[fieldName]}`;
241
+ }
242
+ break;
243
+ case "query_params":
244
+ if (request.query?.[fieldName]) {
245
+ return `${method} ${url} ${request.query[fieldName]}`;
246
+ }
247
+ break;
248
+ case "body":
249
+ if (request.body?.[fieldName]) {
250
+ return `${method} ${url} ${request.body[fieldName]}`;
251
+ }
252
+ break;
253
+ case "bearer_token":
254
+ const authHeader = request.headers.authorization;
255
+ const token = this.extractBearerToken(authHeader);
256
+ if (token) {
257
+ const claims = this.decodeJwtUnverified(token);
258
+ if (claims?.[fieldName]) {
259
+ return `${method} ${url} ${claims[fieldName]}`;
260
+ }
261
+ }
262
+ break;
263
+ }
264
+
265
+ return `${method} ${url}`;
266
+ }
218
267
  }