@rooted-software/piece-sitestacker-http-request 0.1.2
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/.github/workflows/publish.yml +29 -0
- package/package.json +28 -0
- package/src/index.ts +30 -0
- package/src/lib/actions/send-request.ts +162 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Setup Node.js
|
|
15
|
+
uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: '20'
|
|
18
|
+
registry-url: 'https://registry.npmjs.org'
|
|
19
|
+
|
|
20
|
+
- name: Install dependencies
|
|
21
|
+
run: npm install
|
|
22
|
+
|
|
23
|
+
- name: Build
|
|
24
|
+
run: npm run build --if-present
|
|
25
|
+
|
|
26
|
+
- name: Publish to npm
|
|
27
|
+
run: npm publish --access public
|
|
28
|
+
env:
|
|
29
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_DEPLOY }}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rooted-software/piece-sitestacker-http-request",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "HTTP request piece with SiteStacker HMAC authentication",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"activepieces",
|
|
8
|
+
"piece",
|
|
9
|
+
"http",
|
|
10
|
+
"sitestacker",
|
|
11
|
+
"hmac",
|
|
12
|
+
"authentication",
|
|
13
|
+
"api"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/Rooted-Software/piece-sitestacker.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/Rooted-Software/piece-sitestacker/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/Rooted-Software/piece-sitestacker#readme",
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@activepieces/pieces-framework": ">=0.18.0",
|
|
26
|
+
"@activepieces/pieces-common": ">=0.6.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createPiece, PieceAuth, Property } from '@activepieces/pieces-framework';
|
|
2
|
+
import { sendRequest } from './lib/actions/send-request';
|
|
3
|
+
|
|
4
|
+
export const siteStackerAuth = PieceAuth.CustomAuth({
|
|
5
|
+
description: 'SiteStacker HMAC API authentication credentials',
|
|
6
|
+
required: true,
|
|
7
|
+
props: {
|
|
8
|
+
accessKeyId: Property.ShortText({
|
|
9
|
+
displayName: 'Access Key ID',
|
|
10
|
+
description: 'Found in Users component > right-click user > API Access',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
secretAccessKey: PieceAuth.SecretText({
|
|
14
|
+
displayName: 'Secret Access Key',
|
|
15
|
+
description: 'The secret key generated alongside the Access Key ID',
|
|
16
|
+
required: true,
|
|
17
|
+
}),
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const siteStackerHttpRequest = createPiece({
|
|
22
|
+
displayName: 'SiteStacker HTTP Request',
|
|
23
|
+
description: 'Send HTTP requests with automatic SiteStacker HMAC authentication',
|
|
24
|
+
auth: siteStackerAuth,
|
|
25
|
+
minimumSupportedRelease: '0.20.3',
|
|
26
|
+
logoUrl: 'https://cdn.activepieces.com/pieces/http.png',
|
|
27
|
+
authors: [],
|
|
28
|
+
actions: [sendRequest],
|
|
29
|
+
triggers: [],
|
|
30
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { createAction, Property } from '@activepieces/pieces-framework';
|
|
2
|
+
import {
|
|
3
|
+
httpClient,
|
|
4
|
+
HttpMethod,
|
|
5
|
+
HttpRequest,
|
|
6
|
+
} from '@activepieces/pieces-common';
|
|
7
|
+
import { createHmac } from 'crypto';
|
|
8
|
+
import { siteStackerAuth } from '../../index';
|
|
9
|
+
|
|
10
|
+
function formatDateRFC2616(date: Date): string {
|
|
11
|
+
return date.toUTCString().replace('GMT', '+0000');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function computeHmacSignature(
|
|
15
|
+
secretKey: string,
|
|
16
|
+
method: string,
|
|
17
|
+
contentType: string,
|
|
18
|
+
dateStr: string
|
|
19
|
+
): string {
|
|
20
|
+
const stringToSign = `${method}\n${contentType}\n${dateStr}`;
|
|
21
|
+
return createHmac('sha256', secretKey).update(stringToSign).digest('hex');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const sendRequest = createAction({
|
|
25
|
+
auth: siteStackerAuth,
|
|
26
|
+
name: 'send_sitestacker_request',
|
|
27
|
+
displayName: 'Send Request',
|
|
28
|
+
description:
|
|
29
|
+
'Send an HTTP request with automatic SiteStacker HMAC authentication',
|
|
30
|
+
props: {
|
|
31
|
+
method: Property.StaticDropdown({
|
|
32
|
+
displayName: 'Method',
|
|
33
|
+
required: true,
|
|
34
|
+
defaultValue: HttpMethod.GET,
|
|
35
|
+
options: {
|
|
36
|
+
disabled: false,
|
|
37
|
+
options: [
|
|
38
|
+
{ label: 'GET', value: HttpMethod.GET },
|
|
39
|
+
{ label: 'POST', value: HttpMethod.POST },
|
|
40
|
+
{ label: 'PUT', value: HttpMethod.PUT },
|
|
41
|
+
{ label: 'PATCH', value: HttpMethod.PATCH },
|
|
42
|
+
{ label: 'DELETE', value: HttpMethod.DELETE },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
url: Property.ShortText({
|
|
47
|
+
displayName: 'URL',
|
|
48
|
+
description: 'The full URL to send the request to',
|
|
49
|
+
required: true,
|
|
50
|
+
}),
|
|
51
|
+
headers: Property.Object({
|
|
52
|
+
displayName: 'Headers',
|
|
53
|
+
description:
|
|
54
|
+
'Additional headers to include. Authorization, Date/ss-date are set automatically.',
|
|
55
|
+
required: false,
|
|
56
|
+
}),
|
|
57
|
+
body_type: Property.StaticDropdown({
|
|
58
|
+
displayName: 'Body Type',
|
|
59
|
+
required: true,
|
|
60
|
+
defaultValue: 'none',
|
|
61
|
+
options: {
|
|
62
|
+
disabled: false,
|
|
63
|
+
options: [
|
|
64
|
+
{ label: 'None', value: 'none' },
|
|
65
|
+
{ label: 'JSON', value: 'json' },
|
|
66
|
+
{ label: 'Form Data', value: 'form_data' },
|
|
67
|
+
{ label: 'Raw', value: 'raw' },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
body: Property.Json({
|
|
72
|
+
displayName: 'Body (JSON)',
|
|
73
|
+
description: 'Request body when Body Type is JSON',
|
|
74
|
+
required: false,
|
|
75
|
+
}),
|
|
76
|
+
body_raw: Property.LongText({
|
|
77
|
+
displayName: 'Body (Raw)',
|
|
78
|
+
description: 'Request body when Body Type is Raw',
|
|
79
|
+
required: false,
|
|
80
|
+
}),
|
|
81
|
+
timeout: Property.Number({
|
|
82
|
+
displayName: 'Timeout (seconds)',
|
|
83
|
+
required: false,
|
|
84
|
+
defaultValue: 30,
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
async run(context) {
|
|
88
|
+
const { method, url, headers, body_type, body, body_raw, timeout } =
|
|
89
|
+
context.propsValue;
|
|
90
|
+
const { accessKeyId, secretAccessKey } = context.auth;
|
|
91
|
+
|
|
92
|
+
const now = new Date();
|
|
93
|
+
const dateStr = formatDateRFC2616(now);
|
|
94
|
+
|
|
95
|
+
// Determine content type from body type
|
|
96
|
+
let contentType = '';
|
|
97
|
+
let requestBody: unknown = undefined;
|
|
98
|
+
switch (body_type) {
|
|
99
|
+
case 'json':
|
|
100
|
+
contentType = 'application/json';
|
|
101
|
+
requestBody = body;
|
|
102
|
+
break;
|
|
103
|
+
case 'form_data':
|
|
104
|
+
contentType = 'application/x-www-form-urlencoded';
|
|
105
|
+
requestBody = body;
|
|
106
|
+
break;
|
|
107
|
+
case 'raw':
|
|
108
|
+
contentType = 'text/plain';
|
|
109
|
+
requestBody = body_raw;
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
contentType = '';
|
|
113
|
+
requestBody = undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Compute HMAC signature
|
|
117
|
+
const signature = computeHmacSignature(
|
|
118
|
+
secretAccessKey,
|
|
119
|
+
method,
|
|
120
|
+
contentType,
|
|
121
|
+
dateStr
|
|
122
|
+
);
|
|
123
|
+
const authorization = `HMAC ${accessKeyId}:${signature}`;
|
|
124
|
+
|
|
125
|
+
// Build final headers
|
|
126
|
+
const userHeaders: Record<string, string> = (headers as Record<string, string>) ?? {};
|
|
127
|
+
const hasUserDateHeader =
|
|
128
|
+
Object.keys(userHeaders).some((k) => k.toLowerCase() === 'date');
|
|
129
|
+
|
|
130
|
+
const finalHeaders: Record<string, string> = {
|
|
131
|
+
...userHeaders,
|
|
132
|
+
Authorization: authorization,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (contentType) {
|
|
136
|
+
finalHeaders['Content-Type'] = contentType;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Use ss-date if user already set a Date header, otherwise set Date
|
|
140
|
+
if (hasUserDateHeader) {
|
|
141
|
+
finalHeaders['ss-date'] = dateStr;
|
|
142
|
+
} else {
|
|
143
|
+
finalHeaders['Date'] = dateStr;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const request: HttpRequest = {
|
|
147
|
+
method,
|
|
148
|
+
url,
|
|
149
|
+
headers: finalHeaders,
|
|
150
|
+
body: requestBody,
|
|
151
|
+
timeout: timeout ? timeout * 1000 : 30000,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const response = await httpClient.sendRequest(request);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
status: response.status,
|
|
158
|
+
headers: response.headers,
|
|
159
|
+
body: response.body,
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
});
|