@flink-app/s3-plugin 0.12.1-alpha.9 → 0.13.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/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # @flink-app/s3-plugin
2
+
3
+ ## 0.13.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed invalid types and improve typescript error message during schema compilation
8
+ - Updated dependencies
9
+ - @flink-app/flink@0.13.1
10
+
11
+ ## 0.13.0
12
+
13
+ ### Minor Changes
14
+
15
+ - Migrate to pnpm and streamlines build process.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+ - @flink-app/flink@0.13.0
package/dist/index.js CHANGED
@@ -35,8 +35,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
35
35
  });
36
36
  };
37
37
  var __generator = (this && this.__generator) || function (thisArg, body) {
38
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
39
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
38
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
39
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
40
40
  function verb(n) { return function (v) { return step([n, v]); }; }
41
41
  function step(op) {
42
42
  if (f) throw new TypeError("Generator is already executing.");
@@ -79,6 +79,8 @@ var s3Plugin = function (options) {
79
79
  bucket: options.bucket,
80
80
  s3Acl: options.s3Acl,
81
81
  endpoint: options.endpoint,
82
+ signatureVersion: options.signatureVersion,
83
+ region: options.region,
82
84
  });
83
85
  return {
84
86
  id: "s3Plugin",
@@ -1,5 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
1
  import AWS from "aws-sdk";
4
2
  import S3, { ManagedUpload, ObjectIdentifierList } from "aws-sdk/clients/s3";
5
3
  import { ListObjectResponse } from "./schemas/ListObjectsResponse";
package/dist/s3Client.js CHANGED
@@ -9,8 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
14
  function verb(n) { return function (v) { return step([n, v]); }; }
15
15
  function step(op) {
16
16
  if (f) throw new TypeError("Generator is already executing.");
@@ -57,6 +57,8 @@ var S3Client = /** @class */ (function () {
57
57
  },
58
58
  // @ts-ignore
59
59
  endpoint: this.s3Endpoint ? new aws_sdk_1.default.Endpoint(this.s3Endpoint) : undefined,
60
+ signatureVersion: options.signatureVersion,
61
+ region: options.region,
60
62
  });
61
63
  }
62
64
  S3Client.prototype.checkIfExists = function (fileName) {
@@ -4,4 +4,6 @@ export interface s3ClientOptions {
4
4
  bucket: string;
5
5
  s3Acl?: string;
6
6
  endpoint?: string;
7
+ region?: string;
8
+ signatureVersion?: string;
7
9
  }
@@ -8,4 +8,6 @@ export interface s3PluginOptions {
8
8
  enableUpload: boolean;
9
9
  uploadUrl?: string;
10
10
  uploadPermissionRequired?: string;
11
+ signatureVersion?: string;
12
+ region?: string;
11
13
  }
package/package.json CHANGED
@@ -1,34 +1,31 @@
1
1
  {
2
- "name": "@flink-app/s3-plugin",
3
- "version": "0.12.1-alpha.9",
4
- "description": "Flink plugin to work with s3",
5
- "scripts": {
6
- "test": "echo \"Error: no test specified\"",
7
- "prepare": "tsc"
8
- },
9
- "author": "johan@frost.se",
10
- "publishConfig": {
11
- "access": "public"
12
- },
13
- "license": "MIT",
14
- "types": "dist/index.d.ts",
15
- "main": "dist/index.js",
16
- "dependencies": {
17
- "@types/mime-types": "^2.1.1",
18
- "aws-sdk": "^2.1117.0",
19
- "express": "^4.17.1",
20
- "express-fileupload": "^1.3.1",
21
- "mime-types": "^2.1.35",
22
- "node-color-log": "^5.3.1"
23
- },
24
- "devDependencies": {
25
- "@flink-app/flink": "^0.12.1-alpha.9",
26
- "@types/express": "4.17.13",
27
- "@types/express-fileupload": "^1.2.2",
28
- "@types/node": "22.13.10",
29
- "@types/uuid": "^8.3.4",
30
- "ts-node": "^9.1.1",
31
- "typescript": "5.4.5"
32
- },
33
- "gitHead": "3007ad036607014176930446fde56203bde8d6f5"
34
- }
2
+ "name": "@flink-app/s3-plugin",
3
+ "version": "0.13.1",
4
+ "description": "Flink plugin to work with s3",
5
+ "author": "johan@frost.se",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "license": "MIT",
10
+ "types": "dist/index.d.ts",
11
+ "main": "dist/index.js",
12
+ "dependencies": {
13
+ "@types/mime-types": "^2.1.1",
14
+ "aws-sdk": "^2.1117.0",
15
+ "express-fileupload": "^1.3.1",
16
+ "mime-types": "^2.1.35",
17
+ "node-color-log": "^5.3.1",
18
+ "@flink-app/flink": "0.13.1"
19
+ },
20
+ "devDependencies": {
21
+ "@types/express-fileupload": "^1.2.2",
22
+ "@types/node": "22.13.10",
23
+ "@types/uuid": "^8.3.4"
24
+ },
25
+ "gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113",
26
+ "scripts": {
27
+ "test": "echo \"Error: no test specified\"",
28
+ "build": "tsc",
29
+ "clean": "rimraf dist .flink"
30
+ }
31
+ }
package/readme.md CHANGED
@@ -1,57 +1,301 @@
1
- # Flink API Docs
1
+ # S3 Plugin
2
2
 
3
- **WORK IN PROGRESS 👷‍♀️👷🏻‍♂️**
3
+ A Flink plugin for AWS S3 integration, providing file storage, upload, and management operations. Works with AWS S3 and S3-compatible services.
4
4
 
5
- A FLINK plugin that lets you work with s3.
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @flink-app/s3-plugin
9
+ ```
6
10
 
7
11
  ## Usage
8
12
 
9
- Install plugin to your flink app project:
13
+ Add and configure the plugin in your app startup:
10
14
 
15
+ ```typescript
16
+ import { s3Plugin } from "@flink-app/s3-plugin";
17
+ import { FlinkApp } from "@flink-app/flink";
18
+ import AppContext from "./ApplicationContext";
19
+
20
+ async function start() {
21
+ await new FlinkApp<AppContext>({
22
+ name: "My app",
23
+ plugins: [
24
+ s3Plugin({
25
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
26
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
27
+ bucket: "my-bucket-name",
28
+ region: "us-east-1",
29
+ s3Acl: "public-read",
30
+ enableUpload: true,
31
+ uploadUrl: "/file-upload",
32
+ maxFileSize: 10, // MB
33
+ uploadPermissionRequired: "authenticated",
34
+ }),
35
+ ],
36
+ }).start();
37
+ }
11
38
  ```
12
- npm i -S @flink-app/s3-plugin
39
+
40
+ Add the plugin context to your application context:
41
+
42
+ ```typescript
43
+ import { s3PluginContext } from "@flink-app/s3-plugin";
44
+ import { FlinkContext } from "@flink-app/flink";
45
+
46
+ interface ApplicationContext extends FlinkContext<s3PluginContext> {
47
+ // your context here
48
+ }
49
+
50
+ export default ApplicationContext;
13
51
  ```
14
52
 
15
- Add and configure plugin in your app startup (probable the `index.ts` in root project):
53
+ ## Configuration Options
16
54
 
55
+ ```typescript
56
+ interface s3PluginOptions {
57
+ accessKeyId: string; // AWS access key ID (required)
58
+ secretAccessKey: string; // AWS secret access key (required)
59
+ bucket: string; // S3 bucket name (required)
60
+ s3Acl?: string; // Access control list (e.g., "public-read", "private")
61
+ endpoint?: string; // Custom S3 endpoint (for S3-compatible services)
62
+ signatureVersion?: string; // AWS signature version
63
+ region?: string; // AWS region (e.g., "us-east-1")
64
+ enableUpload: boolean; // Enable file upload endpoint (required)
65
+ uploadUrl?: string; // Custom upload endpoint path (default: "/file-upload")
66
+ maxFileSize?: number; // Max file size in MB (default: 10)
67
+ uploadPermissionRequired?: string; // Permission required for uploads
68
+ }
17
69
  ```
18
- import { s3Plugin } from "@flink-app/s3-plugin";
19
70
 
71
+ ## Built-in Upload Endpoint
20
72
 
21
- function start() {
22
- new FlinkApp<AppContext>({
23
- name: "My app",
24
- plugins: [
25
- // Register plugin
26
- s3Plugin({
27
- accessKeyId: "XXX",
28
- secretAccessKey: "YYY",
29
- bucket: "ZZZ",
30
- s3Acl: "public-read",
31
- enableUpload: true,
32
- }),
33
- ],
34
- }).start();
73
+ When `enableUpload` is set to `true`, the plugin registers a file upload endpoint:
74
+
75
+ ### POST /file-upload (or custom uploadUrl)
76
+
77
+ Upload a file using multipart form data:
78
+
79
+ ```bash
80
+ curl -X POST http://localhost:3333/file-upload?path=images/ \
81
+ -F "file=@photo.jpg" \
82
+ -H "Authorization: Bearer <token>"
83
+ ```
84
+
85
+ **Query Parameters:**
86
+ - `path` (optional): S3 path prefix for the uploaded file
87
+
88
+ **Response:**
89
+ ```json
90
+ {
91
+ "status": 200,
92
+ "data": {
93
+ "url": "https://my-bucket.s3.amazonaws.com/images/photo-1234567890.jpg"
94
+ }
35
95
  }
36
96
  ```
37
97
 
38
- Finally add the s3PluginContext to your `Ctx.ts`
98
+ **Permission Protection:**
99
+ Set `uploadPermissionRequired` to require authentication:
100
+ ```typescript
101
+ s3Plugin({
102
+ // ...
103
+ enableUpload: true,
104
+ uploadPermissionRequired: "authenticated", // or any custom permission
105
+ })
106
+ ```
107
+
108
+ ## S3Client API
109
+
110
+ Access the S3 client from your handlers:
111
+
112
+ ```typescript
113
+ import { Handler } from "@flink-app/flink";
114
+ import AppContext from "../ApplicationContext";
115
+
116
+ const UploadFile: Handler<AppContext, UploadReq, UploadRes> = async ({ ctx, req }) => {
117
+ const s3Client = ctx.plugins.s3Plugin.s3Client;
118
+
119
+ // Your S3 operations here
120
+ };
121
+ ```
122
+
123
+ ### Available Methods
124
+
125
+ #### uploadFile(fileName, data, mime)
126
+ Upload a file to S3.
127
+
128
+ ```typescript
129
+ const url = await ctx.plugins.s3Plugin.s3Client.uploadFile(
130
+ "documents/report.pdf",
131
+ fileBuffer,
132
+ "application/pdf"
133
+ );
39
134
  ```
40
- import { s3PluginContext } from "@flink-app/s3-plugin"
41
- export interface Ctx extends FlinkContext<s3PluginContext> {
42
135
 
136
+ #### getObject(key)
137
+ Retrieve a file from S3.
138
+
139
+ ```typescript
140
+ const file = await ctx.plugins.s3Plugin.s3Client.getObject("documents/report.pdf");
141
+ ```
142
+
143
+ #### getObjects()
144
+ List all objects in the bucket.
145
+
146
+ ```typescript
147
+ const objects = await ctx.plugins.s3Plugin.s3Client.getObjects();
148
+ ```
149
+
150
+ #### deleteObject(file, version?)
151
+ Delete a single object.
152
+
153
+ ```typescript
154
+ await ctx.plugins.s3Plugin.s3Client.deleteObject("documents/old-report.pdf");
155
+ ```
156
+
157
+ #### deleteObjects(files)
158
+ Bulk delete multiple objects.
159
+
160
+ ```typescript
161
+ await ctx.plugins.s3Plugin.s3Client.deleteObjects([
162
+ "file1.jpg",
163
+ "file2.jpg",
164
+ "file3.jpg",
165
+ ]);
166
+ ```
167
+
168
+ #### getSignedUrl(key, expires)
169
+ Generate a temporary signed URL for private objects.
170
+
171
+ ```typescript
172
+ const signedUrl = await ctx.plugins.s3Plugin.s3Client.getSignedUrl(
173
+ "private/document.pdf",
174
+ 3600 // expires in seconds
175
+ );
176
+ ```
177
+
178
+ #### checkIfExists(fileName)
179
+ Check if a file exists in the bucket.
180
+
181
+ ```typescript
182
+ const exists = await ctx.plugins.s3Plugin.s3Client.checkIfExists("documents/report.pdf");
183
+ ```
184
+
185
+ ## Complete Example
186
+
187
+ ```typescript
188
+ import { Handler } from "@flink-app/flink";
189
+ import AppContext from "../ApplicationContext";
190
+
191
+ interface UploadDocumentReq {
192
+ fileName: string;
193
+ fileData: string; // base64 encoded
194
+ mimeType: string;
195
+ }
196
+
197
+ interface UploadDocumentRes {
198
+ url: string;
199
+ exists: boolean;
43
200
  }
201
+
202
+ export const Route = {
203
+ path: "/document/upload",
204
+ };
205
+
206
+ const UploadDocument: Handler<AppContext, UploadDocumentReq, UploadDocumentRes> = async ({ ctx, req }) => {
207
+ const { fileName, fileData, mimeType } = req.body;
208
+ const s3Client = ctx.plugins.s3Plugin.s3Client;
209
+
210
+ // Check if file already exists
211
+ const exists = await s3Client.checkIfExists(fileName);
212
+
213
+ if (exists) {
214
+ return {
215
+ status: 409,
216
+ data: {
217
+ url: "",
218
+ exists: true,
219
+ },
220
+ };
221
+ }
222
+
223
+ // Upload file
224
+ const buffer = Buffer.from(fileData, "base64");
225
+ const url = await s3Client.uploadFile(fileName, buffer, mimeType);
226
+
227
+ return {
228
+ status: 200,
229
+ data: {
230
+ url,
231
+ exists: false,
232
+ },
233
+ };
234
+ };
235
+
236
+ export default UploadDocument;
44
237
  ```
45
238
 
239
+ ## S3-Compatible Services
46
240
 
241
+ This plugin works with S3-compatible services like MinIO, DigitalOcean Spaces, etc.:
242
+
243
+ ```typescript
244
+ s3Plugin({
245
+ accessKeyId: "your-key",
246
+ secretAccessKey: "your-secret",
247
+ bucket: "my-bucket",
248
+ endpoint: "https://nyc3.digitaloceanspaces.com", // Custom endpoint
249
+ region: "nyc3",
250
+ s3Acl: "public-read",
251
+ enableUpload: true,
252
+ })
253
+ ```
254
+
255
+ ## Access Control Lists (ACL)
256
+
257
+ Common ACL values:
258
+ - `private`: Owner gets full control, no one else has access
259
+ - `public-read`: Owner gets full control, public gets read access
260
+ - `public-read-write`: Owner gets full control, public gets read/write access
261
+ - `authenticated-read`: Owner gets full control, authenticated users get read access
262
+
263
+ ## Environment Variables
264
+
265
+ Recommended setup using environment variables:
266
+
267
+ ```bash
268
+ AWS_ACCESS_KEY_ID=your-access-key-id
269
+ AWS_SECRET_ACCESS_KEY=your-secret-access-key
270
+ AWS_REGION=us-east-1
271
+ S3_BUCKET=my-bucket-name
272
+ ```
273
+
274
+ ```typescript
275
+ s3Plugin({
276
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
277
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
278
+ bucket: process.env.S3_BUCKET!,
279
+ region: process.env.AWS_REGION,
280
+ s3Acl: "public-read",
281
+ enableUpload: true,
282
+ })
283
+ ```
47
284
 
48
- ## Using built in upload endpoint
49
- Set `enableUpload = true` to enable the /file-upload endpoint.
50
- Post a multipart file upload to that endpoint to upload the file to S3.
51
- Protect this route by specifying the required permission by specifying `uploadPermissionRequired` property to the required permission. Eg. `uploadPermissionRequired="authenticated"` setting.
285
+ ## Features
52
286
 
287
+ - ✅ AWS S3 integration
288
+ - ✅ S3-compatible service support
289
+ - ✅ Built-in file upload endpoint
290
+ - ✅ Permission-based access control
291
+ - ✅ Multipart file upload support
292
+ - ✅ File size limits
293
+ - ✅ Signed URL generation
294
+ - ✅ Bulk operations
295
+ - ✅ File existence checking
53
296
 
54
- ## Using the s3Client
55
- From your handlers, access the s3Client by `ctx.plugins.s3Plugin.s3Client`.
56
- You will find methods for upload, delete and so on.
297
+ ## Requirements
57
298
 
299
+ - AWS account with S3 access (or S3-compatible service)
300
+ - Valid AWS credentials (access key ID and secret access key)
301
+ - S3 bucket created and accessible
package/src/index.ts CHANGED
@@ -14,6 +14,8 @@ export const s3Plugin = (options: s3PluginOptions): FlinkPlugin => {
14
14
  bucket: options.bucket,
15
15
  s3Acl: options.s3Acl,
16
16
  endpoint: options.endpoint,
17
+ signatureVersion: options.signatureVersion,
18
+ region: options.region,
17
19
  });
18
20
 
19
21
  return {
package/src/s3Client.ts CHANGED
@@ -29,6 +29,8 @@ class S3Client {
29
29
  },
30
30
  // @ts-ignore
31
31
  endpoint: this.s3Endpoint ? new AWS.Endpoint(this.s3Endpoint) : undefined,
32
+ signatureVersion: options.signatureVersion,
33
+ region: options.region,
32
34
  });
33
35
  }
34
36
 
@@ -4,4 +4,6 @@ export interface s3ClientOptions {
4
4
  bucket: string;
5
5
  s3Acl?: string;
6
6
  endpoint?: string;
7
+ region?: string;
8
+ signatureVersion?: string;
7
9
  }
@@ -8,4 +8,6 @@ export interface s3PluginOptions {
8
8
  enableUpload: boolean;
9
9
  uploadUrl?: string;
10
10
  uploadPermissionRequired?: string;
11
+ signatureVersion?: string;
12
+ region?: string;
11
13
  }
package/tsconfig.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "noEmit": false,
16
16
  "declaration": true,
17
17
  "experimentalDecorators": true,
18
- "checkJs": true,
18
+ "checkJs": false,
19
19
  "outDir": "dist"
20
20
  },
21
21
  "include": ["./src/*"],