@daisy-workflow/plugin-aws-s3 0.1.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/.dockerignore +9 -0
- package/.github/workflows/release.yml +185 -0
- package/Dockerfile +20 -0
- package/LICENSE +21 -0
- package/README.md +163 -0
- package/index.js +51 -0
- package/lib/actions.js +403 -0
- package/lib/client.js +169 -0
- package/lib/sigv4.js +176 -0
- package/manifest.json +185 -0
- package/package.json +17 -0
- package/publish-docker.sh +97 -0
package/.dockerignore
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# Release pipeline
|
|
3
|
+
# ============================================================================
|
|
4
|
+
# Fires on a SemVer tag push (vMAJOR.MINOR.PATCH). Publishes the package to
|
|
5
|
+
# the npm registry and the container image to Docker Hub, then cuts a
|
|
6
|
+
# GitHub Release with auto-generated notes.
|
|
7
|
+
#
|
|
8
|
+
# To release:
|
|
9
|
+
# 1. Bump "version" in package.json
|
|
10
|
+
# 2. git commit + git push
|
|
11
|
+
# 3. git tag v0.1.0 && git push origin v0.1.0
|
|
12
|
+
# 4. Sit back — GitHub Actions does the rest.
|
|
13
|
+
#
|
|
14
|
+
# Required repository secrets (Settings → Secrets and variables → Actions):
|
|
15
|
+
# NPM_TOKEN npm automation token with publish access to the
|
|
16
|
+
# @daisy-workflow scope. Create at npmjs.com → Access
|
|
17
|
+
# Tokens → Generate New Token → Automation.
|
|
18
|
+
# DOCKERHUB_USERNAME Docker Hub user that owns the daisy-plugin-* images
|
|
19
|
+
# (currently: vivek13186).
|
|
20
|
+
# DOCKERHUB_TOKEN Docker Hub access token (Account → Security →
|
|
21
|
+
# New Access Token, read + write + delete).
|
|
22
|
+
#
|
|
23
|
+
# Derived values (no need to edit per repo):
|
|
24
|
+
# npm name = read from package.json (e.g. @daisy-workflow/plugin-jira)
|
|
25
|
+
# docker img = vivek13186/daisy-<plugin-name> (e.g. vivek13186/daisy-plugin-jira)
|
|
26
|
+
# version = read from package.json; tag must match.
|
|
27
|
+
#
|
|
28
|
+
# Action versions:
|
|
29
|
+
# actions/checkout@v5 and actions/setup-node@v5 run on Node 24 — required
|
|
30
|
+
# to avoid the deprecation warning announced 2025-09-19, where GitHub
|
|
31
|
+
# begins migrating JavaScript-based actions off Node 20 on 2026-06-02.
|
|
32
|
+
# The docker/* actions (login@v3, setup-buildx@v3, setup-qemu@v3,
|
|
33
|
+
# build-push@v6) are already on majors that ship Node 24-compatible
|
|
34
|
+
# minor releases, so no version bump needed there.
|
|
35
|
+
# ============================================================================
|
|
36
|
+
|
|
37
|
+
name: release
|
|
38
|
+
|
|
39
|
+
on:
|
|
40
|
+
push:
|
|
41
|
+
tags:
|
|
42
|
+
- "v*.*.*"
|
|
43
|
+
workflow_dispatch:
|
|
44
|
+
# Manual fallback — useful when a previous run failed half-way through
|
|
45
|
+
# and we want to re-publish without bumping the tag.
|
|
46
|
+
|
|
47
|
+
permissions:
|
|
48
|
+
contents: write # for `gh release create`
|
|
49
|
+
id-token: write # for `npm publish --provenance`
|
|
50
|
+
|
|
51
|
+
jobs:
|
|
52
|
+
|
|
53
|
+
# --------------------------------------------------------------------------
|
|
54
|
+
# 1. Verify — package.json sanity check + derive image name once.
|
|
55
|
+
# --------------------------------------------------------------------------
|
|
56
|
+
verify:
|
|
57
|
+
name: Verify version
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
outputs:
|
|
60
|
+
version: ${{ steps.read.outputs.version }}
|
|
61
|
+
image: ${{ steps.read.outputs.image }}
|
|
62
|
+
npm: ${{ steps.read.outputs.npm }}
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v5
|
|
65
|
+
|
|
66
|
+
- name: Read package.json
|
|
67
|
+
id: read
|
|
68
|
+
shell: bash
|
|
69
|
+
run: |
|
|
70
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
71
|
+
NPM_NAME=$(node -p "require('./package.json').name")
|
|
72
|
+
# Strip the @daisy-workflow/ scope to get the short plugin name,
|
|
73
|
+
# then prefix with the Docker Hub namespace + daisy- convention.
|
|
74
|
+
SHORT_NAME="${NPM_NAME#@daisy-workflow/}"
|
|
75
|
+
IMAGE="vivek13186/daisy-${SHORT_NAME}"
|
|
76
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
77
|
+
echo "npm=$NPM_NAME" >> "$GITHUB_OUTPUT"
|
|
78
|
+
echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
|
|
79
|
+
echo "::notice::Releasing $NPM_NAME@$VERSION → $IMAGE:$VERSION"
|
|
80
|
+
|
|
81
|
+
- name: Check tag matches package.json
|
|
82
|
+
if: github.event_name == 'push'
|
|
83
|
+
shell: bash
|
|
84
|
+
run: |
|
|
85
|
+
TAG="${GITHUB_REF#refs/tags/}"
|
|
86
|
+
EXPECTED="v${{ steps.read.outputs.version }}"
|
|
87
|
+
if [ "$TAG" != "$EXPECTED" ]; then
|
|
88
|
+
echo "::error::Tag '$TAG' does not match package.json version '$EXPECTED'."
|
|
89
|
+
echo "Either fix the tag or bump package.json before tagging."
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# --------------------------------------------------------------------------
|
|
94
|
+
# 2. npm — publish with provenance attestation.
|
|
95
|
+
# --------------------------------------------------------------------------
|
|
96
|
+
npm:
|
|
97
|
+
name: Publish to npm
|
|
98
|
+
needs: verify
|
|
99
|
+
runs-on: ubuntu-latest
|
|
100
|
+
steps:
|
|
101
|
+
- uses: actions/checkout@v5
|
|
102
|
+
|
|
103
|
+
- uses: actions/setup-node@v5 # v5 runs on Node 24
|
|
104
|
+
with:
|
|
105
|
+
node-version: "22" # Node version used to BUILD/PUBLISH the package (independent of the runtime above)
|
|
106
|
+
registry-url: "https://registry.npmjs.org"
|
|
107
|
+
|
|
108
|
+
- name: Install dependencies
|
|
109
|
+
run: npm install --omit=dev
|
|
110
|
+
|
|
111
|
+
- name: npm publish
|
|
112
|
+
# --access public is required for scoped packages on the free tier.
|
|
113
|
+
# --provenance ties the package to this exact workflow run; npm
|
|
114
|
+
# surfaces it as a "verified" badge on the package page.
|
|
115
|
+
run: npm publish --access public --provenance
|
|
116
|
+
env:
|
|
117
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
118
|
+
|
|
119
|
+
# --------------------------------------------------------------------------
|
|
120
|
+
# 3. Docker — multi-arch build + push to Docker Hub.
|
|
121
|
+
# --------------------------------------------------------------------------
|
|
122
|
+
docker:
|
|
123
|
+
name: Build & push Docker image
|
|
124
|
+
needs: verify
|
|
125
|
+
runs-on: ubuntu-latest
|
|
126
|
+
steps:
|
|
127
|
+
- uses: actions/checkout@v5
|
|
128
|
+
|
|
129
|
+
- name: Set up QEMU
|
|
130
|
+
# Lets us cross-compile arm64 on an amd64 GitHub runner.
|
|
131
|
+
uses: docker/setup-qemu-action@v3
|
|
132
|
+
|
|
133
|
+
- name: Set up Buildx
|
|
134
|
+
uses: docker/setup-buildx-action@v3
|
|
135
|
+
|
|
136
|
+
- name: Log in to Docker Hub
|
|
137
|
+
uses: docker/login-action@v3
|
|
138
|
+
with:
|
|
139
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
140
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
141
|
+
|
|
142
|
+
- name: Build & push (linux/amd64, linux/arm64)
|
|
143
|
+
uses: docker/build-push-action@v6
|
|
144
|
+
with:
|
|
145
|
+
context: .
|
|
146
|
+
file: ./Dockerfile
|
|
147
|
+
platforms: linux/amd64,linux/arm64
|
|
148
|
+
push: true
|
|
149
|
+
tags: |
|
|
150
|
+
${{ needs.verify.outputs.image }}:${{ needs.verify.outputs.version }}
|
|
151
|
+
${{ needs.verify.outputs.image }}:latest
|
|
152
|
+
# GitHub Actions cache keeps subsequent builds fast (especially
|
|
153
|
+
# the arm64 leg, which would otherwise emulate from scratch).
|
|
154
|
+
cache-from: type=gha
|
|
155
|
+
cache-to: type=gha,mode=max
|
|
156
|
+
# OCI labels so the image is self-describing on Docker Hub.
|
|
157
|
+
labels: |
|
|
158
|
+
org.opencontainers.image.title=${{ needs.verify.outputs.npm }}
|
|
159
|
+
org.opencontainers.image.version=${{ needs.verify.outputs.version }}
|
|
160
|
+
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
|
161
|
+
org.opencontainers.image.revision=${{ github.sha }}
|
|
162
|
+
org.opencontainers.image.licenses=MIT
|
|
163
|
+
|
|
164
|
+
# --------------------------------------------------------------------------
|
|
165
|
+
# 4. Cut a GitHub Release with notes generated from commit history.
|
|
166
|
+
# Skipped on workflow_dispatch — manual re-publishes don't create a
|
|
167
|
+
# release because there's no new tag to attach it to.
|
|
168
|
+
# --------------------------------------------------------------------------
|
|
169
|
+
github-release:
|
|
170
|
+
name: GitHub release
|
|
171
|
+
needs: [verify, npm, docker]
|
|
172
|
+
if: github.event_name == 'push'
|
|
173
|
+
runs-on: ubuntu-latest
|
|
174
|
+
steps:
|
|
175
|
+
- uses: actions/checkout@v5
|
|
176
|
+
with:
|
|
177
|
+
fetch-depth: 0 # full history so generate-notes can diff to prev tag
|
|
178
|
+
|
|
179
|
+
- name: Create release
|
|
180
|
+
env:
|
|
181
|
+
GH_TOKEN: ${{ github.token }}
|
|
182
|
+
run: |
|
|
183
|
+
gh release create "${GITHUB_REF#refs/tags/}" \
|
|
184
|
+
--title "v${{ needs.verify.outputs.version }}" \
|
|
185
|
+
--generate-notes
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# aws-s3 plugin — one container, one endpoint, many operations.
|
|
2
|
+
#
|
|
3
|
+
# Self-contained: pulls @daisy-workflow/plugin-sdk from npm; no
|
|
4
|
+
# repo-root build context needed. No native or system dependencies —
|
|
5
|
+
# SigV4 (both header-based and query-string presigned) is hand-rolled
|
|
6
|
+
# against node:crypto.
|
|
7
|
+
|
|
8
|
+
FROM node:22-alpine
|
|
9
|
+
WORKDIR /workspace
|
|
10
|
+
|
|
11
|
+
COPY package.json ./
|
|
12
|
+
RUN npm install --omit=dev
|
|
13
|
+
|
|
14
|
+
COPY . ./
|
|
15
|
+
|
|
16
|
+
ENV PORT=8080
|
|
17
|
+
EXPOSE 8080
|
|
18
|
+
USER node
|
|
19
|
+
|
|
20
|
+
CMD ["node", "index.js"]
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vivek Gangadharan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# aws-s3 plugin for Daisy-workflow
|
|
2
|
+
|
|
3
|
+
One Daisy node that talks to **AWS S3 specifically** — with full
|
|
4
|
+
support for AWS-only features: storage classes, server-side encryption,
|
|
5
|
+
KMS, object tags, requester pays, and presigned URLs. Mirrors n8n's
|
|
6
|
+
[AWS S3 node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.awss3/).
|
|
7
|
+
|
|
8
|
+
[](https://hub.docker.com/repository/docker/vivek13186/daisy-plugin-aws-s3)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
> Need Wasabi, MinIO, Cloudflare R2, DigitalOcean Spaces, Backblaze B2,
|
|
12
|
+
> or another S3-compatible provider? Use the **generic `s3` plugin**
|
|
13
|
+
> instead — it accepts an arbitrary endpoint and `forcePathStyle`.
|
|
14
|
+
|
|
15
|
+
The action is selected per-node via the **operation** dropdown.
|
|
16
|
+
|
|
17
|
+
## Operations
|
|
18
|
+
|
|
19
|
+
| operation | What it does |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `bucket.getAll` | List all buckets the credential has access to. |
|
|
22
|
+
| `bucket.create` | Create a new bucket. Sends `LocationConstraint` when region ≠ us-east-1. |
|
|
23
|
+
| `bucket.delete` | Delete a bucket (bucket must be empty). |
|
|
24
|
+
| `bucket.search` | List objects in a bucket matching a `prefix`. |
|
|
25
|
+
| `bucket.location` | Get the bucket's region (`?location` API). |
|
|
26
|
+
| `file.getAll` | List objects with optional `prefix` + pagination. |
|
|
27
|
+
| `file.head` | Get object metadata only (no body) — content-type, size, storage class, SSE config, user metadata. |
|
|
28
|
+
| `file.upload` | PUT an object. Supports `storageClass`, `serverSideEncryption`, `ssekmsKeyId`, `tags`, `acl`, `requesterPays`. |
|
|
29
|
+
| `file.download` | GET an object. Body returned as `base64` (default) or `utf8`. |
|
|
30
|
+
| `file.copy` | Copy with optional destination SSE / storage class / tags. |
|
|
31
|
+
| `file.delete` | Delete an object. |
|
|
32
|
+
| `file.presignedUrl` | Generate a time-limited URL that anyone can use to GET (or PUT) the object — no API call, pure crypto. |
|
|
33
|
+
| `folder.create` | Create a "folder" placeholder (empty object with key ending in `/`). |
|
|
34
|
+
| `folder.getAll` | List "folders" using `delimiter` (default `/`) → CommonPrefixes. |
|
|
35
|
+
| `folder.delete` | Recursively delete everything under a prefix via bulk MultiObjectDelete. |
|
|
36
|
+
|
|
37
|
+
## Configure auth
|
|
38
|
+
|
|
39
|
+
Create one **generic** config on the **Configurations** page (default
|
|
40
|
+
name `aws-s3`):
|
|
41
|
+
|
|
42
|
+
| Key | Example | Notes |
|
|
43
|
+
|-------------------|-----------------------------------------------|----------------------------------------------------|
|
|
44
|
+
| `accessKeyId` | `AKIA…` | IAM user or role access key |
|
|
45
|
+
| `secretAccessKey` | `…` | |
|
|
46
|
+
| `region` | `us-east-1` / `eu-central-1` / `ap-south-1` | Drives the endpoint and the SigV4 signing region |
|
|
47
|
+
| `sessionToken` | `…` | Optional — STS / AssumeRole / EC2 instance role |
|
|
48
|
+
| `customEndpoint` | `https://bucket.vpce-….s3.us-east-1.vpce.amazonaws.com` | Optional — VPC interface endpoint, S3 Transfer Acceleration host, AWS GovCloud, AWS China |
|
|
49
|
+
|
|
50
|
+
The endpoint is auto-derived from the region. `cn-*` regions
|
|
51
|
+
automatically use `amazonaws.com.cn`. Override with `customEndpoint`
|
|
52
|
+
for transfer acceleration (`s3-accelerate.amazonaws.com`),
|
|
53
|
+
VPC interface endpoints, or air-gapped AWS partitions.
|
|
54
|
+
|
|
55
|
+
A node can override the config name per-call via the `config` input
|
|
56
|
+
and the region per-call via the `region` input.
|
|
57
|
+
|
|
58
|
+
## AWS-specific upload extras
|
|
59
|
+
|
|
60
|
+
| Input | Sent as | Notes |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `storageClass` | `x-amz-storage-class` | `STANDARD` / `INTELLIGENT_TIERING` / `STANDARD_IA` / `ONEZONE_IA` / `GLACIER` / `DEEP_ARCHIVE` / `GLACIER_IR` / `REDUCED_REDUNDANCY` |
|
|
63
|
+
| `serverSideEncryption` | `x-amz-server-side-encryption` | `AES256` / `aws:kms` / `aws:kms:dsse` |
|
|
64
|
+
| `ssekmsKeyId` | `x-amz-server-side-encryption-aws-kms-key-id` | KMS key ID or ARN when using `aws:kms*` |
|
|
65
|
+
| `tags` | `x-amz-tagging` | Object → URL-encoded query string: `{env:'prod', team:'data'}` → `env=prod&team=data` |
|
|
66
|
+
| `acl` | `x-amz-acl` | Note: many AWS accounts disable ACLs ("Object Ownership = Bucket owner enforced"); the header is then rejected |
|
|
67
|
+
| `requesterPays` | `x-amz-request-payer: requester` | Required when reading from a Requester-Pays bucket |
|
|
68
|
+
| `metadata` | `x-amz-meta-<key>` | Custom user metadata; one header per key |
|
|
69
|
+
|
|
70
|
+
`file.copy` accepts the same extras for the destination object.
|
|
71
|
+
|
|
72
|
+
## Presigned URLs
|
|
73
|
+
|
|
74
|
+
```jsonc
|
|
75
|
+
// input
|
|
76
|
+
{
|
|
77
|
+
"operation": "file.presignedUrl",
|
|
78
|
+
"bucket": "uploads",
|
|
79
|
+
"key": "users/42/avatar.png",
|
|
80
|
+
"presignedMethod": "PUT", // GET (default) or PUT
|
|
81
|
+
"presignedExpiresIn": 3600 // seconds; max 604800 (7 days)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// result
|
|
85
|
+
{
|
|
86
|
+
"bucket": "uploads",
|
|
87
|
+
"key": "users/42/avatar.png",
|
|
88
|
+
"method": "PUT",
|
|
89
|
+
"expiresIn": 3600,
|
|
90
|
+
"expiresAt": "2026-05-18T09:30:00.000Z",
|
|
91
|
+
"url": "https://uploads.s3.us-east-1.amazonaws.com/users/42/avatar.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=…&X-Amz-Signature=…"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
No HTTP call is made — pure crypto. Hand this URL to a browser or curl
|
|
96
|
+
to upload / download without exposing the AWS credentials.
|
|
97
|
+
|
|
98
|
+
## Install
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
docker compose -f docker-compose.yml -f docker-compose.plugins.yml \
|
|
102
|
+
--profile aws-s3 up -d
|
|
103
|
+
|
|
104
|
+
npm run install-plugin -- --endpoint http://daisy-aws-s3:8080
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Output envelope
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"ok": true,
|
|
112
|
+
"operation": "file.upload",
|
|
113
|
+
"status": 200,
|
|
114
|
+
"result": { "bucket": "logs", "key": "2026/05/event.json", "size": 412, "etag": "ab12…", "serverSideEncryption": "AES256" },
|
|
115
|
+
"url": "https://logs.s3.us-east-1.amazonaws.com/2026/05/event.json"
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Operation-specific `result` shapes are documented inline in
|
|
120
|
+
`lib/actions.js`. Highlights:
|
|
121
|
+
|
|
122
|
+
- `bucket.getAll` → `{ owner, buckets: [{ name, creationDate }], count }`
|
|
123
|
+
- `bucket.location` → `{ bucket, location }` (always real region; us-east-1 is normalized from the empty `<LocationConstraint/>` AWS returns)
|
|
124
|
+
- `file.head` → `{ bucket, key, contentType, contentLength, etag, lastModified, versionId, storageClass, serverSideEncryption, ssekmsKeyId, metadata }`
|
|
125
|
+
- `file.download` → `{ bucket, key, contentType, contentLength, etag, lastModified, encoding, data }`
|
|
126
|
+
- `file.presignedUrl` → see above
|
|
127
|
+
|
|
128
|
+
## Auth model — why hand-rolled SigV4?
|
|
129
|
+
|
|
130
|
+
Both header-based signing and query-string presigning are implemented in
|
|
131
|
+
`lib/sigv4.js` (~200 lines, zero deps, validated against AWS's
|
|
132
|
+
canonical S3 test vector). Keeps the container tiny and the dependency
|
|
133
|
+
surface to exactly one package (the Daisy plugin SDK).
|
|
134
|
+
|
|
135
|
+
If you need features that go beyond what this plugin offers — multipart
|
|
136
|
+
upload for >5GB files, S3 Select, batch operations, event subscriptions,
|
|
137
|
+
inventory configs — drop in `@aws-sdk/client-s3` and add a new
|
|
138
|
+
operation that uses it.
|
|
139
|
+
|
|
140
|
+
## Files
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
plugins-external/aws-s3/
|
|
144
|
+
├── manifest.json
|
|
145
|
+
├── index.js
|
|
146
|
+
├── lib/
|
|
147
|
+
│ ├── sigv4.js # signRequest() + presignUrl(), no deps
|
|
148
|
+
│ ├── client.js # auth + endpoint derivation + signed fetch
|
|
149
|
+
│ └── actions.js # one async handler per operation
|
|
150
|
+
├── package.json
|
|
151
|
+
├── Dockerfile
|
|
152
|
+
├── publish-docker.sh
|
|
153
|
+
└── README.md
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Publish the image
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
docker login
|
|
160
|
+
./publish-docker.sh
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Env overrides: `IMAGE=foo/bar`, `PLATFORMS=linux/amd64`, `PUSH=0`, `NO_LATEST=1`.
|
package/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// aws-s3 — AWS-specific S3 from a workflow. The action is selected
|
|
2
|
+
// per-node via the `operation` input. Mirrors n8n's AWS S3 node.
|
|
3
|
+
//
|
|
4
|
+
// Wire it up:
|
|
5
|
+
// 1. `docker compose -f docker-compose.yml -f docker-compose.plugins.yml \
|
|
6
|
+
// --profile aws-s3 up -d`
|
|
7
|
+
// `npm run install-plugin -- --endpoint http://daisy-aws-s3:8080`
|
|
8
|
+
// 2. Create a workspace `generic` config named "aws-s3" with:
|
|
9
|
+
// accessKeyId, secretAccessKey, region (+ optional sessionToken, customEndpoint)
|
|
10
|
+
// 3. Use the node in any workflow.
|
|
11
|
+
//
|
|
12
|
+
// For non-AWS S3-compatible providers (Wasabi, MinIO, R2, B2…) use the
|
|
13
|
+
// generic `s3` plugin instead — it accepts an arbitrary endpoint and
|
|
14
|
+
// supports forcePathStyle.
|
|
15
|
+
|
|
16
|
+
import { servePlugin } from "@daisy-workflow/plugin-sdk";
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
|
|
19
|
+
import { loadAwsAuth } from "./lib/client.js";
|
|
20
|
+
import { OPERATIONS } from "./lib/actions.js";
|
|
21
|
+
|
|
22
|
+
const manifest = JSON.parse(
|
|
23
|
+
fs.readFileSync(new URL("./manifest.json", import.meta.url), "utf8"),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
servePlugin({
|
|
27
|
+
manifest,
|
|
28
|
+
async execute(input, ctx) {
|
|
29
|
+
const { operation, config = "aws-s3" } = input || {};
|
|
30
|
+
if (!operation) throw new Error("`operation` is required (see manifest enum for valid values)");
|
|
31
|
+
|
|
32
|
+
const handler = OPERATIONS[operation];
|
|
33
|
+
if (!handler) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`unknown operation "${operation}". Valid: ${Object.keys(OPERATIONS).join(", ")}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const auth = loadAwsAuth(ctx, config, input?.region);
|
|
40
|
+
const { status, result, url } = await handler(auth, input, ctx?.signal);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
operation,
|
|
45
|
+
status,
|
|
46
|
+
result,
|
|
47
|
+
url,
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
async readyz() { return true; },
|
|
51
|
+
});
|