@develit-io/backend-sdk 9.0.3 → 9.1.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/index.d.mts CHANGED
@@ -474,6 +474,22 @@ type InferResultType<Tables extends Record<string, unknown>, TableName extends k
474
474
  with: With;
475
475
  }>;
476
476
 
477
+ interface RequestLog {
478
+ method: string;
479
+ path: string;
480
+ query: Record<string, string>;
481
+ headers: Record<string, string>;
482
+ body: Record<string, unknown> | null;
483
+ }
484
+ interface ResponseLog {
485
+ method: string;
486
+ path: string;
487
+ status: number;
488
+ statusText: string;
489
+ headers: Record<string, string>;
490
+ body: unknown;
491
+ }
492
+
477
493
  declare const paginationQuerySchema: z.$ZodObject<Readonly<Readonly<{
478
494
  [k: string]: z.$ZodType<unknown, unknown, z.$ZodTypeInternals<unknown, unknown>>;
479
495
  }>>, z.$ZodObjectConfig>;
@@ -844,4 +860,4 @@ type AsyncMethod<TArgs extends unknown[] = unknown[], TResult = unknown> = (...a
844
860
  declare function cloudflareQueue<TArgs extends unknown[] = unknown[], TResult = unknown>(options: WithRetryCounterOptions): (target: unknown, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<AsyncMethod<TArgs, TResult>>) => void;
845
861
 
846
862
  export { DatabaseTransaction, ENVIRONMENT, RPCResponse, USER_ROLES, action, asNonEmpty, bankAccount, bankAccountMetadataSchema, base, bicSchema, buildMultiFilterConditions, buildRangeFilterConditions, buildSearchConditions, calculateExponentialBackoff, cloudflareQueue, composeWranglerBase, createAuditLogWriter, createInternalError, createSignatureKeyPair, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getSecret, handleAction, ibanSchema, isInternalError, nullToOptional, optionalToNull, paginationQuerySchema, paginationSchema, resolveColumn, service, signPayload, useFetch, useResult, useResultSync, uuidv4, verifyPayloadSignature, workflowInstanceStatusSchema };
847
- export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, BankAccountMetadata, BaseEvent, BuildSearchOptions, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IdempotencyContextVariables, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, UserContextVariables, UserRole, ValidatedInput, WorkflowInstanceStatus };
863
+ export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, BankAccountMetadata, BaseEvent, BuildSearchOptions, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IdempotencyContextVariables, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, RequestLog, ResponseLog, UserContextVariables, UserRole, ValidatedInput, WorkflowInstanceStatus };
package/dist/index.d.ts CHANGED
@@ -474,6 +474,22 @@ type InferResultType<Tables extends Record<string, unknown>, TableName extends k
474
474
  with: With;
475
475
  }>;
476
476
 
477
+ interface RequestLog {
478
+ method: string;
479
+ path: string;
480
+ query: Record<string, string>;
481
+ headers: Record<string, string>;
482
+ body: Record<string, unknown> | null;
483
+ }
484
+ interface ResponseLog {
485
+ method: string;
486
+ path: string;
487
+ status: number;
488
+ statusText: string;
489
+ headers: Record<string, string>;
490
+ body: unknown;
491
+ }
492
+
477
493
  declare const paginationQuerySchema: z.$ZodObject<Readonly<Readonly<{
478
494
  [k: string]: z.$ZodType<unknown, unknown, z.$ZodTypeInternals<unknown, unknown>>;
479
495
  }>>, z.$ZodObjectConfig>;
@@ -844,4 +860,4 @@ type AsyncMethod<TArgs extends unknown[] = unknown[], TResult = unknown> = (...a
844
860
  declare function cloudflareQueue<TArgs extends unknown[] = unknown[], TResult = unknown>(options: WithRetryCounterOptions): (target: unknown, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<AsyncMethod<TArgs, TResult>>) => void;
845
861
 
846
862
  export { DatabaseTransaction, ENVIRONMENT, RPCResponse, USER_ROLES, action, asNonEmpty, bankAccount, bankAccountMetadataSchema, base, bicSchema, buildMultiFilterConditions, buildRangeFilterConditions, buildSearchConditions, calculateExponentialBackoff, cloudflareQueue, composeWranglerBase, createAuditLogWriter, createInternalError, createSignatureKeyPair, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getSecret, handleAction, ibanSchema, isInternalError, nullToOptional, optionalToNull, paginationQuerySchema, paginationSchema, resolveColumn, service, signPayload, useFetch, useResult, useResultSync, uuidv4, verifyPayloadSignature, workflowInstanceStatusSchema };
847
- export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, BankAccountMetadata, BaseEvent, BuildSearchOptions, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IdempotencyContextVariables, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, UserContextVariables, UserRole, ValidatedInput, WorkflowInstanceStatus };
863
+ export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, BankAccountMetadata, BaseEvent, BuildSearchOptions, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IdempotencyContextVariables, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, RequestLog, ResponseLog, UserContextVariables, UserRole, ValidatedInput, WorkflowInstanceStatus };
@@ -25,89 +25,94 @@ const logRequest = (log) => {
25
25
  const logResponse = (log) => {
26
26
  console.log(`RESPONSE | An outgoing response has been recorded.`, log);
27
27
  };
28
+ const getRequestIpAddress = (request) => {
29
+ return request.header("cf-connecting-ip") || request.header("x-real-ip") || request.header("x-forwarded-for") || "UNKNOWN";
30
+ };
31
+ const getRequestUserAgent = (request) => {
32
+ return request.header("user-agent") || "UNKNOWN";
33
+ };
28
34
 
29
35
  const idempotency = () => {
30
36
  return createMiddleware(async (context, next) => {
31
- const idempotencyKeyHeader = context.req.header("x-idempotency-key");
32
- if (!idempotencyKeyHeader) {
33
- throw new HTTPException(401, {
34
- message: `The 'x-idempotency-key' header must exist and must have a value.`
35
- });
36
- }
37
- const existingIdempotencyRecord = await context.env.IDEMPOTENCY_KV.get(idempotencyKeyHeader);
38
- if (existingIdempotencyRecord) {
39
- throw new HTTPException(409, {
40
- message: "The identical request has already been processed. The idempotency key is not unique."
37
+ if (!context.env.MIDDLEWARE_IDEMPOTENCY_DISABLED) {
38
+ const idempotencyKeyHeader = context.req.header("x-idempotency-key");
39
+ if (!idempotencyKeyHeader) {
40
+ throw new HTTPException(401, {
41
+ message: `The 'x-idempotency-key' header must exist and must have a value.`
42
+ });
43
+ }
44
+ const existingIdempotencyRecord = await context.env.IDEMPOTENCY_KV.get(idempotencyKeyHeader);
45
+ if (existingIdempotencyRecord) {
46
+ throw new HTTPException(409, {
47
+ message: "The identical request has already been processed. The idempotency key is not unique."
48
+ });
49
+ }
50
+ await context.env.IDEMPOTENCY_KV.put(
51
+ idempotencyKeyHeader,
52
+ idempotencyKeyHeader,
53
+ {
54
+ expirationTtl: 60 * 60 * 24 * 3
55
+ // 3 days
56
+ }
57
+ );
58
+ context.set("idempotency", {
59
+ key: idempotencyKeyHeader
41
60
  });
42
61
  }
43
- await context.env.IDEMPOTENCY_KV.put(
44
- idempotencyKeyHeader,
45
- idempotencyKeyHeader,
46
- {
47
- expirationTtl: 60 * 60 * 24 * 3
48
- // 3 days
49
- }
50
- );
51
- context.set("idempotency", {
52
- key: idempotencyKeyHeader
53
- });
54
62
  await next();
55
63
  });
56
64
  };
57
65
 
58
66
  const jwt = () => {
59
67
  return createMiddleware(async (context, next) => {
60
- const authorizationHeader = context.req.header("authorization");
61
- if (!authorizationHeader) {
62
- throw new HTTPException(401, {
63
- message: `The 'authorization' header must exist and must have a value.`
64
- });
65
- }
66
- if (!validateBearerScheme(authorizationHeader)) {
67
- throw new HTTPException(401, {
68
- message: `The 'authorization' header value must use the Bearer scheme.`
69
- });
70
- }
71
- const bearerToken = extractBearerToken(authorizationHeader);
72
- if (!validateBearerToken(bearerToken)) {
73
- throw new HTTPException(401, {
74
- message: `The Bearer token in the 'authorization' header value must be a JWT.`
75
- });
76
- }
77
- const { data, error } = await context.env.AUTH_SERVICE.verifyAccessToken({
78
- accessToken: bearerToken
79
- });
80
- if (!data || error) {
81
- throw new HTTPException(401, {
82
- message: "The JWT must contain valid user information."
68
+ if (!context.env.MIDDLEWARE_JWT_DISABLED) {
69
+ const authorizationHeader = context.req.header("authorization");
70
+ if (!authorizationHeader) {
71
+ throw new HTTPException(401, {
72
+ message: `The 'authorization' header must exist and must have a value.`
73
+ });
74
+ }
75
+ if (!validateBearerScheme(authorizationHeader)) {
76
+ throw new HTTPException(401, {
77
+ message: `The 'authorization' header value must use the Bearer scheme.`
78
+ });
79
+ }
80
+ const bearerToken = extractBearerToken(authorizationHeader);
81
+ if (!validateBearerToken(bearerToken)) {
82
+ throw new HTTPException(401, {
83
+ message: `The Bearer token in the 'authorization' header value must be a JWT.`
84
+ });
85
+ }
86
+ const { data, error } = await context.env.AUTH_SERVICE.verifyAccessToken({
87
+ accessToken: bearerToken
83
88
  });
84
- }
85
- const rawUserMetaDataString = data.payload.user.rawUserMetaData;
86
- const rawUserMetaData = rawUserMetaDataString ? JSON.parse(rawUserMetaDataString) : null;
87
- const organizationId = rawUserMetaData?.organizationId ?? null;
88
- if (!organizationId) {
89
- throw new HTTPException(422, {
90
- message: "User data integrity check failed."
89
+ if (!data || error) {
90
+ throw new HTTPException(401, {
91
+ message: "The JWT must contain valid user information."
92
+ });
93
+ }
94
+ const rawUserMetaDataString = data.payload.user.rawUserMetaData;
95
+ const rawUserMetaData = rawUserMetaDataString ? JSON.parse(rawUserMetaDataString) : null;
96
+ const organizationId = rawUserMetaData?.organizationId ?? null;
97
+ if (!organizationId) {
98
+ throw new HTTPException(422, {
99
+ message: "User data integrity check failed."
100
+ });
101
+ }
102
+ context.set("user", {
103
+ email: data.payload.user.email,
104
+ role: data.payload.user.role,
105
+ organizationId
91
106
  });
92
107
  }
93
- context.set("user", {
94
- email: data.payload.user.email,
95
- role: data.payload.user.role,
96
- organizationId
97
- });
98
108
  await next();
99
109
  });
100
110
  };
101
111
 
102
112
  const ip = () => {
103
113
  return createMiddleware(async (context, next) => {
104
- if (!["localhost", "dev"].includes(context.env.ENVIRONMENT)) {
105
- const requestIp = context.req.header("cf-connecting-ip") || context.req.header("x-forwarded-for");
106
- if (!requestIp) {
107
- throw new HTTPException(401, {
108
- message: "Failed to retrieve request IP address."
109
- });
110
- }
114
+ if (!context.env.MIDDLEWARE_IP_DISABLED) {
115
+ const requestIp = getRequestIpAddress(context.req);
111
116
  const user = context.get("user");
112
117
  if (!user.organizationId) {
113
118
  throw new HTTPException(401, {
@@ -156,22 +161,56 @@ const composeResponseLog = async (response, method, path) => {
156
161
 
157
162
  const logger = () => {
158
163
  return createMiddleware(async (context, next) => {
159
- const requestLog = await composeRequestLog(context.req);
160
- logRequest(requestLog);
164
+ if (!context.env.MIDDLEWARE_LOGGER_DISABLED) {
165
+ const requestLog = await composeRequestLog(context.req);
166
+ logRequest(requestLog);
167
+ if (!context.env.MIDDLEWARE_LOGGER_AUDITLOG_DISABLED) {
168
+ const user = context.get("user") || {};
169
+ await context.env.AUDITLOG_SERVICE.processLog({
170
+ type: "REQUEST",
171
+ actor: {
172
+ email: user.email || "UNKNOWN",
173
+ organizationId: user.organizationId || "UNKNOWN"
174
+ },
175
+ metadata: {
176
+ ip: getRequestIpAddress(context.req),
177
+ userAgent: getRequestUserAgent(context.req)
178
+ },
179
+ log: requestLog
180
+ });
181
+ }
182
+ }
161
183
  await next();
162
- const response = context.res.clone();
163
- const responseLog = await composeResponseLog(
164
- response,
165
- context.req.method,
166
- context.req.url
167
- );
168
- logResponse(responseLog);
184
+ if (!context.env.MIDDLEWARE_LOGGER_DISABLED) {
185
+ const response = context.res.clone();
186
+ const responseLog = await composeResponseLog(
187
+ response,
188
+ context.req.method,
189
+ context.req.url
190
+ );
191
+ logResponse(responseLog);
192
+ if (!context.env.MIDDLEWARE_LOGGER_AUDITLOG_DISABLED) {
193
+ const user = context.get("user") || {};
194
+ await context.env.AUDITLOG_SERVICE.processLog({
195
+ type: "RESPONSE",
196
+ actor: {
197
+ email: user.email || "UNKNOWN",
198
+ organizationId: user.organizationId || "UNKNOWN"
199
+ },
200
+ metadata: {
201
+ ip: getRequestIpAddress(context.req),
202
+ userAgent: getRequestUserAgent(context.req)
203
+ },
204
+ log: responseLog
205
+ });
206
+ }
207
+ }
169
208
  });
170
209
  };
171
210
 
172
211
  const signature = () => {
173
212
  return createMiddleware(async (context, next) => {
174
- if (!["localhost", "dev"].includes(context.env.ENVIRONMENT)) {
213
+ if (!context.env.MIDDLEWARE_SIGNATURE_DISABLED) {
175
214
  const signatureHeader = context.req.header("x-signature");
176
215
  if (!signatureHeader) {
177
216
  throw new HTTPException(401, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@develit-io/backend-sdk",
3
- "version": "9.0.3",
3
+ "version": "9.1.1",
4
4
  "description": "Develit Backend SDK",
5
5
  "author": "Develit.io",
6
6
  "license": "ISC",