go-scheduler-node-sdk 1.0.0
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/README.md +467 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.js +31 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +42 -0
- package/dist/resources/attendees.d.ts +31 -0
- package/dist/resources/attendees.js +51 -0
- package/dist/resources/calendarMembers.d.ts +13 -0
- package/dist/resources/calendarMembers.js +27 -0
- package/dist/resources/calendars.d.ts +30 -0
- package/dist/resources/calendars.js +95 -0
- package/dist/resources/events.d.ts +18 -0
- package/dist/resources/events.js +40 -0
- package/dist/resources/reminders.d.ts +22 -0
- package/dist/resources/reminders.js +27 -0
- package/dist/resources/webhooks.d.ts +12 -0
- package/dist/resources/webhooks.js +31 -0
- package/dist/types.d.ts +338 -0
- package/dist/types.js +31 -0
- package/dist/utils/http.d.ts +14 -0
- package/dist/utils/http.js +99 -0
- package/dist/utils/webhook.d.ts +112 -0
- package/dist/utils/webhook.js +262 -0
- package/package.json +33 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AxiosRequestConfig } from "axios";
|
|
2
|
+
export declare class HttpClient {
|
|
3
|
+
private client;
|
|
4
|
+
constructor(baseURL: string, timeout?: number, headers?: Record<string, string>);
|
|
5
|
+
get<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
|
|
6
|
+
post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
|
|
7
|
+
postForm<T>(url: string, formData: FormData, config?: AxiosRequestConfig): Promise<T>;
|
|
8
|
+
put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
|
|
9
|
+
patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
|
|
10
|
+
delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
|
|
11
|
+
private handleError;
|
|
12
|
+
setHeader(key: string, value: string): void;
|
|
13
|
+
removeHeader(key: string): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HttpClient = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
class HttpClient {
|
|
9
|
+
constructor(baseURL, timeout = 30000, headers) {
|
|
10
|
+
this.client = axios_1.default.create({
|
|
11
|
+
baseURL,
|
|
12
|
+
timeout,
|
|
13
|
+
headers: {
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
...headers,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async get(url, config) {
|
|
20
|
+
try {
|
|
21
|
+
const response = await this.client.get(url, config);
|
|
22
|
+
return response.data;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw this.handleError(error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async post(url, data, config) {
|
|
29
|
+
try {
|
|
30
|
+
const response = await this.client.post(url, data, config);
|
|
31
|
+
return response.data;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw this.handleError(error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async postForm(url, formData, config) {
|
|
38
|
+
try {
|
|
39
|
+
const response = await this.client.post(url, formData, {
|
|
40
|
+
...config,
|
|
41
|
+
headers: {
|
|
42
|
+
...config?.headers,
|
|
43
|
+
"Content-Type": "multipart/form-data",
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
return response.data;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw this.handleError(error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async put(url, data, config) {
|
|
53
|
+
try {
|
|
54
|
+
const response = await this.client.put(url, data, config);
|
|
55
|
+
return response.data;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw this.handleError(error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async patch(url, data, config) {
|
|
62
|
+
try {
|
|
63
|
+
const response = await this.client.patch(url, data, config);
|
|
64
|
+
return response.data;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw this.handleError(error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async delete(url, config) {
|
|
71
|
+
try {
|
|
72
|
+
const response = await this.client.delete(url, config);
|
|
73
|
+
return response.data;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
throw this.handleError(error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
handleError(error) {
|
|
80
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
81
|
+
const errorData = error.response?.data;
|
|
82
|
+
const message = errorData?.error || error.message;
|
|
83
|
+
const details = errorData?.details;
|
|
84
|
+
const errorMessage = details ? `${message}: ${details}` : message;
|
|
85
|
+
const customError = new Error(errorMessage);
|
|
86
|
+
customError.status = error.response?.status;
|
|
87
|
+
customError.response = error.response;
|
|
88
|
+
return customError;
|
|
89
|
+
}
|
|
90
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
91
|
+
}
|
|
92
|
+
setHeader(key, value) {
|
|
93
|
+
this.client.defaults.headers.common[key] = value;
|
|
94
|
+
}
|
|
95
|
+
removeHeader(key) {
|
|
96
|
+
delete this.client.defaults.headers.common[key];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.HttpClient = HttpClient;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { WebhookPayload, WebhookVerificationResult } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Webhook utility class for verifying and handling webhook payloads
|
|
4
|
+
*/
|
|
5
|
+
export declare class WebhookUtils {
|
|
6
|
+
/**
|
|
7
|
+
* Verify a webhook signature using HMAC-SHA256
|
|
8
|
+
* @param payload - The raw webhook payload (as string or Buffer)
|
|
9
|
+
* @param signature - The signature from the X-Webhook-Signature header
|
|
10
|
+
* @param secret - The webhook secret
|
|
11
|
+
* @returns Verification result with validity and optional error message
|
|
12
|
+
*/
|
|
13
|
+
static verifySignature(payload: string | Buffer, signature: string, secret: string): WebhookVerificationResult;
|
|
14
|
+
/**
|
|
15
|
+
* Compute the signature for a webhook payload
|
|
16
|
+
* Useful for testing webhook endpoints
|
|
17
|
+
* @param payload - The webhook payload object or string
|
|
18
|
+
* @param secret - The webhook secret
|
|
19
|
+
* @returns Signature in the format "sha256=<hex>"
|
|
20
|
+
*/
|
|
21
|
+
static computeSignature(payload: WebhookPayload | string | Buffer, secret: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Parse and verify a webhook payload
|
|
24
|
+
* @param rawPayload - The raw webhook payload (as received from request)
|
|
25
|
+
* @param signature - The signature from the X-Webhook-Signature header
|
|
26
|
+
* @param secret - The webhook secret
|
|
27
|
+
* @returns Parsed payload if valid, null otherwise
|
|
28
|
+
*/
|
|
29
|
+
static parseAndVerify<T = any>(rawPayload: string | Buffer, signature: string, secret: string): {
|
|
30
|
+
payload: WebhookPayload<T>;
|
|
31
|
+
error?: string;
|
|
32
|
+
} | null;
|
|
33
|
+
/**
|
|
34
|
+
* Validate webhook payload structure
|
|
35
|
+
* @param payload - The parsed webhook payload
|
|
36
|
+
* @returns True if payload has required fields
|
|
37
|
+
*/
|
|
38
|
+
static validatePayloadStructure(payload: any): payload is WebhookPayload;
|
|
39
|
+
/**
|
|
40
|
+
* Check if a webhook timestamp is fresh (within acceptable time window)
|
|
41
|
+
* Helps prevent replay attacks
|
|
42
|
+
* @param timestamp - The webhook timestamp (Unix timestamp in seconds)
|
|
43
|
+
* @param toleranceSeconds - Maximum age of webhook in seconds (default: 300 = 5 minutes)
|
|
44
|
+
* @returns True if timestamp is within tolerance
|
|
45
|
+
*/
|
|
46
|
+
static isTimestampFresh(timestamp: number, toleranceSeconds?: number): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Verify both signature and timestamp freshness
|
|
49
|
+
* @param rawPayload - The raw webhook payload
|
|
50
|
+
* @param signature - The signature from the X-Webhook-Signature header
|
|
51
|
+
* @param secret - The webhook secret
|
|
52
|
+
* @param toleranceSeconds - Maximum age of webhook in seconds (default: 300)
|
|
53
|
+
* @returns Verification result
|
|
54
|
+
*/
|
|
55
|
+
static verifyWithTimestamp(rawPayload: string | Buffer, signature: string, secret: string, toleranceSeconds?: number): WebhookVerificationResult;
|
|
56
|
+
/**
|
|
57
|
+
* Extract webhook signature from request headers
|
|
58
|
+
* Supports multiple header formats
|
|
59
|
+
* @param headers - Request headers object
|
|
60
|
+
* @returns Signature string or null if not found
|
|
61
|
+
*/
|
|
62
|
+
static extractSignature(headers: Record<string, string | string[] | undefined>): string | null;
|
|
63
|
+
/**
|
|
64
|
+
* Create a webhook response helper for Express/HTTP frameworks
|
|
65
|
+
* @param statusCode - HTTP status code to return
|
|
66
|
+
* @param message - Optional message
|
|
67
|
+
* @returns Response object
|
|
68
|
+
*/
|
|
69
|
+
static createResponse(statusCode: number, message?: string): {
|
|
70
|
+
statusCode: number;
|
|
71
|
+
body: string | undefined;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Success response for webhook acknowledgment
|
|
75
|
+
*/
|
|
76
|
+
static success(): {
|
|
77
|
+
statusCode: number;
|
|
78
|
+
body: string | undefined;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Error response for invalid signature
|
|
82
|
+
*/
|
|
83
|
+
static invalidSignature(): {
|
|
84
|
+
statusCode: number;
|
|
85
|
+
body: string | undefined;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Error response for stale timestamp
|
|
89
|
+
*/
|
|
90
|
+
static staleTimestamp(): {
|
|
91
|
+
statusCode: number;
|
|
92
|
+
body: string | undefined;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Error response for invalid payload
|
|
96
|
+
*/
|
|
97
|
+
static invalidPayload(): {
|
|
98
|
+
statusCode: number;
|
|
99
|
+
body: string | undefined;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Express middleware factory for webhook verification
|
|
104
|
+
* @param secret - The webhook secret
|
|
105
|
+
* @param options - Optional configuration
|
|
106
|
+
* @returns Express middleware function
|
|
107
|
+
*/
|
|
108
|
+
export declare function createWebhookVerificationMiddleware(secret: string, options?: {
|
|
109
|
+
toleranceSeconds?: number;
|
|
110
|
+
rawBodyKey?: string;
|
|
111
|
+
}): (req: any, res: any, next: any) => void;
|
|
112
|
+
export declare const verifySignature: typeof WebhookUtils.verifySignature, computeSignature: typeof WebhookUtils.computeSignature, parseAndVerify: typeof WebhookUtils.parseAndVerify, validatePayloadStructure: typeof WebhookUtils.validatePayloadStructure, isTimestampFresh: typeof WebhookUtils.isTimestampFresh, verifyWithTimestamp: typeof WebhookUtils.verifyWithTimestamp, extractSignature: typeof WebhookUtils.extractSignature;
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractSignature = exports.verifyWithTimestamp = exports.isTimestampFresh = exports.validatePayloadStructure = exports.parseAndVerify = exports.computeSignature = exports.verifySignature = exports.WebhookUtils = void 0;
|
|
4
|
+
exports.createWebhookVerificationMiddleware = createWebhookVerificationMiddleware;
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
/**
|
|
7
|
+
* Webhook utility class for verifying and handling webhook payloads
|
|
8
|
+
*/
|
|
9
|
+
class WebhookUtils {
|
|
10
|
+
/**
|
|
11
|
+
* Verify a webhook signature using HMAC-SHA256
|
|
12
|
+
* @param payload - The raw webhook payload (as string or Buffer)
|
|
13
|
+
* @param signature - The signature from the X-Webhook-Signature header
|
|
14
|
+
* @param secret - The webhook secret
|
|
15
|
+
* @returns Verification result with validity and optional error message
|
|
16
|
+
*/
|
|
17
|
+
static verifySignature(payload, signature, secret) {
|
|
18
|
+
try {
|
|
19
|
+
// Normalize payload to string
|
|
20
|
+
const payloadString = typeof payload === "string" ? payload : payload.toString("utf8");
|
|
21
|
+
// Extract the signature value (format: "sha256=<hex>")
|
|
22
|
+
if (!signature || !signature.startsWith("sha256=")) {
|
|
23
|
+
return {
|
|
24
|
+
valid: false,
|
|
25
|
+
error: "Invalid signature format. Expected 'sha256=<hex>'",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const providedSignature = signature.substring(7); // Remove "sha256=" prefix
|
|
29
|
+
// Compute expected signature
|
|
30
|
+
const expectedSignature = (0, crypto_1.createHmac)("sha256", secret)
|
|
31
|
+
.update(payloadString)
|
|
32
|
+
.digest("hex");
|
|
33
|
+
// Use timing-safe comparison to prevent timing attacks
|
|
34
|
+
const providedBuffer = Buffer.from(providedSignature, "hex");
|
|
35
|
+
const expectedBuffer = Buffer.from(expectedSignature, "hex");
|
|
36
|
+
if (providedBuffer.length !== expectedBuffer.length) {
|
|
37
|
+
return {
|
|
38
|
+
valid: false,
|
|
39
|
+
error: "Signature length mismatch",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const isValid = (0, crypto_1.timingSafeEqual)(providedBuffer, expectedBuffer);
|
|
43
|
+
return {
|
|
44
|
+
valid: isValid,
|
|
45
|
+
error: isValid ? undefined : "Signature verification failed",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
valid: false,
|
|
51
|
+
error: `Verification error: ${error instanceof Error ? error.message : String(error)}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compute the signature for a webhook payload
|
|
57
|
+
* Useful for testing webhook endpoints
|
|
58
|
+
* @param payload - The webhook payload object or string
|
|
59
|
+
* @param secret - The webhook secret
|
|
60
|
+
* @returns Signature in the format "sha256=<hex>"
|
|
61
|
+
*/
|
|
62
|
+
static computeSignature(payload, secret) {
|
|
63
|
+
const payloadString = typeof payload === "string"
|
|
64
|
+
? payload
|
|
65
|
+
: Buffer.isBuffer(payload)
|
|
66
|
+
? payload.toString("utf8")
|
|
67
|
+
: JSON.stringify(payload);
|
|
68
|
+
const signature = (0, crypto_1.createHmac)("sha256", secret)
|
|
69
|
+
.update(payloadString)
|
|
70
|
+
.digest("hex");
|
|
71
|
+
return `sha256=${signature}`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parse and verify a webhook payload
|
|
75
|
+
* @param rawPayload - The raw webhook payload (as received from request)
|
|
76
|
+
* @param signature - The signature from the X-Webhook-Signature header
|
|
77
|
+
* @param secret - The webhook secret
|
|
78
|
+
* @returns Parsed payload if valid, null otherwise
|
|
79
|
+
*/
|
|
80
|
+
static parseAndVerify(rawPayload, signature, secret) {
|
|
81
|
+
// Verify signature first
|
|
82
|
+
const verification = this.verifySignature(rawPayload, signature, secret);
|
|
83
|
+
if (!verification.valid) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
// Parse payload
|
|
87
|
+
try {
|
|
88
|
+
const payloadString = typeof rawPayload === "string"
|
|
89
|
+
? rawPayload
|
|
90
|
+
: rawPayload.toString("utf8");
|
|
91
|
+
const payload = JSON.parse(payloadString);
|
|
92
|
+
return { payload };
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Validate webhook payload structure
|
|
100
|
+
* @param payload - The parsed webhook payload
|
|
101
|
+
* @returns True if payload has required fields
|
|
102
|
+
*/
|
|
103
|
+
static validatePayloadStructure(payload) {
|
|
104
|
+
return (typeof payload === "object" &&
|
|
105
|
+
payload !== null &&
|
|
106
|
+
typeof payload.webhook_uid === "string" &&
|
|
107
|
+
typeof payload.event_type === "string" &&
|
|
108
|
+
typeof payload.delivery_id === "string" &&
|
|
109
|
+
typeof payload.timestamp === "number" &&
|
|
110
|
+
"data" in payload);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check if a webhook timestamp is fresh (within acceptable time window)
|
|
114
|
+
* Helps prevent replay attacks
|
|
115
|
+
* @param timestamp - The webhook timestamp (Unix timestamp in seconds)
|
|
116
|
+
* @param toleranceSeconds - Maximum age of webhook in seconds (default: 300 = 5 minutes)
|
|
117
|
+
* @returns True if timestamp is within tolerance
|
|
118
|
+
*/
|
|
119
|
+
static isTimestampFresh(timestamp, toleranceSeconds = 300) {
|
|
120
|
+
const now = Math.floor(Date.now() / 1000);
|
|
121
|
+
const age = Math.abs(now - timestamp);
|
|
122
|
+
return age <= toleranceSeconds;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Verify both signature and timestamp freshness
|
|
126
|
+
* @param rawPayload - The raw webhook payload
|
|
127
|
+
* @param signature - The signature from the X-Webhook-Signature header
|
|
128
|
+
* @param secret - The webhook secret
|
|
129
|
+
* @param toleranceSeconds - Maximum age of webhook in seconds (default: 300)
|
|
130
|
+
* @returns Verification result
|
|
131
|
+
*/
|
|
132
|
+
static verifyWithTimestamp(rawPayload, signature, secret, toleranceSeconds = 300) {
|
|
133
|
+
// Verify signature
|
|
134
|
+
const signatureResult = this.verifySignature(rawPayload, signature, secret);
|
|
135
|
+
if (!signatureResult.valid) {
|
|
136
|
+
return signatureResult;
|
|
137
|
+
}
|
|
138
|
+
// Parse and check timestamp
|
|
139
|
+
try {
|
|
140
|
+
const payloadString = typeof rawPayload === "string"
|
|
141
|
+
? rawPayload
|
|
142
|
+
: rawPayload.toString("utf8");
|
|
143
|
+
const payload = JSON.parse(payloadString);
|
|
144
|
+
if (!this.isTimestampFresh(payload.timestamp, toleranceSeconds)) {
|
|
145
|
+
return {
|
|
146
|
+
valid: false,
|
|
147
|
+
error: `Webhook timestamp is too old. Age exceeds ${toleranceSeconds} seconds.`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return { valid: true };
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return {
|
|
154
|
+
valid: false,
|
|
155
|
+
error: "Failed to parse payload for timestamp verification",
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Extract webhook signature from request headers
|
|
161
|
+
* Supports multiple header formats
|
|
162
|
+
* @param headers - Request headers object
|
|
163
|
+
* @returns Signature string or null if not found
|
|
164
|
+
*/
|
|
165
|
+
static extractSignature(headers) {
|
|
166
|
+
// Try different header formats
|
|
167
|
+
const signature = headers["x-webhook-signature"] ||
|
|
168
|
+
headers["X-Webhook-Signature"] ||
|
|
169
|
+
headers["x-signature"] ||
|
|
170
|
+
headers["X-Signature"];
|
|
171
|
+
if (Array.isArray(signature)) {
|
|
172
|
+
return signature[0] || null;
|
|
173
|
+
}
|
|
174
|
+
return signature || null;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Create a webhook response helper for Express/HTTP frameworks
|
|
178
|
+
* @param statusCode - HTTP status code to return
|
|
179
|
+
* @param message - Optional message
|
|
180
|
+
* @returns Response object
|
|
181
|
+
*/
|
|
182
|
+
static createResponse(statusCode, message) {
|
|
183
|
+
return {
|
|
184
|
+
statusCode,
|
|
185
|
+
body: message ? JSON.stringify({ message }) : undefined,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Success response for webhook acknowledgment
|
|
190
|
+
*/
|
|
191
|
+
static success() {
|
|
192
|
+
return this.createResponse(200, "Webhook received");
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Error response for invalid signature
|
|
196
|
+
*/
|
|
197
|
+
static invalidSignature() {
|
|
198
|
+
return this.createResponse(401, "Invalid webhook signature");
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Error response for stale timestamp
|
|
202
|
+
*/
|
|
203
|
+
static staleTimestamp() {
|
|
204
|
+
return this.createResponse(400, "Webhook timestamp is too old");
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Error response for invalid payload
|
|
208
|
+
*/
|
|
209
|
+
static invalidPayload() {
|
|
210
|
+
return this.createResponse(400, "Invalid webhook payload");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
exports.WebhookUtils = WebhookUtils;
|
|
214
|
+
/**
|
|
215
|
+
* Express middleware factory for webhook verification
|
|
216
|
+
* @param secret - The webhook secret
|
|
217
|
+
* @param options - Optional configuration
|
|
218
|
+
* @returns Express middleware function
|
|
219
|
+
*/
|
|
220
|
+
function createWebhookVerificationMiddleware(secret, options = {}) {
|
|
221
|
+
const { toleranceSeconds = 300, rawBodyKey = "rawBody" } = options;
|
|
222
|
+
return (req, res, next) => {
|
|
223
|
+
try {
|
|
224
|
+
// Extract signature from headers
|
|
225
|
+
const signature = WebhookUtils.extractSignature(req.headers);
|
|
226
|
+
if (!signature) {
|
|
227
|
+
res.status(401).json({ error: "Missing webhook signature" });
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// Get raw body (must be preserved by body parser)
|
|
231
|
+
const rawBody = req[rawBodyKey] || req.body;
|
|
232
|
+
if (!rawBody) {
|
|
233
|
+
res.status(400).json({ error: "Missing request body" });
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// Verify signature and timestamp
|
|
237
|
+
const verification = WebhookUtils.verifyWithTimestamp(rawBody, signature, secret, toleranceSeconds);
|
|
238
|
+
if (!verification.valid) {
|
|
239
|
+
res.status(401).json({ error: verification.error });
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
// Attach parsed payload to request
|
|
243
|
+
try {
|
|
244
|
+
const payloadString = typeof rawBody === "string" ? rawBody : rawBody.toString("utf8");
|
|
245
|
+
req.webhookPayload = JSON.parse(payloadString);
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
res.status(400).json({ error: "Invalid JSON payload" });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
next();
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
res.status(500).json({
|
|
255
|
+
error: "Webhook verification failed",
|
|
256
|
+
details: error instanceof Error ? error.message : String(error),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
// Export both the class and individual functions for convenience
|
|
262
|
+
exports.verifySignature = WebhookUtils.verifySignature, exports.computeSignature = WebhookUtils.computeSignature, exports.parseAndVerify = WebhookUtils.parseAndVerify, exports.validatePayloadStructure = WebhookUtils.validatePayloadStructure, exports.isTimestampFresh = WebhookUtils.isTimestampFresh, exports.verifyWithTimestamp = WebhookUtils.verifyWithTimestamp, exports.extractSignature = WebhookUtils.extractSignature;
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "go-scheduler-node-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TypeScript SDK for interacting with go-scheduler REST API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "yarn build",
|
|
13
|
+
"watch": "tsc --watch",
|
|
14
|
+
"clean": "rm -rf dist"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"scheduler",
|
|
18
|
+
"calendar",
|
|
19
|
+
"events",
|
|
20
|
+
"reminders",
|
|
21
|
+
"go-scheduler"
|
|
22
|
+
],
|
|
23
|
+
"author": "jaysongiroux",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"private": false,
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"axios": "^1.13.2"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.10.5",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
|
+
}
|
|
33
|
+
}
|