cloud-service-kit 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 ADDED
@@ -0,0 +1,200 @@
1
+ # cloud-service-kit
2
+
3
+ A modular cloud service toolkit for Node.js. Handles S3 media uploads, with support for more services (email, etc.) coming soon.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install cloud-service-kit
9
+ ```
10
+
11
+ ## Prerequisites
12
+
13
+ - Node.js >= 18.0.0
14
+ - AWS S3 bucket with access credentials
15
+
16
+ ## Quick Start
17
+
18
+ ```typescript
19
+ import { S3MediaClient } from "cloud-service-kit";
20
+
21
+ const s3 = new S3MediaClient({
22
+ aws: {
23
+ region: "us-east-1",
24
+ bucket: "your-bucket-name",
25
+ accessKey: "YOUR_AWS_ACCESS_KEY",
26
+ secretKey: "YOUR_AWS_SECRET_KEY",
27
+ },
28
+ });
29
+
30
+ // Upload image from a live URL
31
+ const result = await s3.media.uploadFromUrl("https://example.com/photo.jpg", {
32
+ folder: "uploads",
33
+ });
34
+
35
+ // Upload from buffer (e.g. Express + multer)
36
+ const result2 = await s3.media.uploadBuffer(fileBuffer, {
37
+ contentType: "image/jpeg",
38
+ folder: "user-uploads",
39
+ });
40
+
41
+ console.log(result.url); // Full S3 URL
42
+ ```
43
+
44
+ ## Setup
45
+
46
+ ### Using environment variables (recommended)
47
+
48
+ ```typescript
49
+ import { S3MediaClient } from "cloud-service-kit";
50
+
51
+ const s3 = new S3MediaClient({
52
+ aws: {
53
+ region: process.env.AWS_REGION!,
54
+ bucket: process.env.AWS_BUCKET!,
55
+ accessKey: process.env.AWS_ACCESS_KEY!,
56
+ secretKey: process.env.AWS_SECRET_KEY!,
57
+ },
58
+ });
59
+ ```
60
+
61
+ > Initialize once at app startup and reuse the instance throughout your application.
62
+
63
+ ---
64
+
65
+ ## Modules
66
+
67
+ ### Media Module
68
+
69
+ Handles fetching images from URLs or accepting buffers and uploading them to AWS S3.
70
+
71
+ #### `s3.media.uploadFromUrl(url, options?)`
72
+
73
+ Fetches an image from a live URL and uploads it to S3.
74
+
75
+ ```typescript
76
+ const result = await s3.media.uploadFromUrl("https://example.com/photo.jpg");
77
+ ```
78
+
79
+ With options:
80
+
81
+ ```typescript
82
+ const result = await s3.media.uploadFromUrl("https://example.com/photo.jpg", {
83
+ folder: "uploads", // S3 folder prefix
84
+ key: "custom/path/photo.jpg", // or provide a full custom key
85
+ contentType: "image/jpeg", // override auto-detected content type
86
+ });
87
+ ```
88
+
89
+ **Parameters:**
90
+
91
+ | Parameter | Type | Required | Description |
92
+ | --------------------- | -------- | -------- | ------------------------------------------------------------ |
93
+ | `url` | `string` | Yes | The live URL to fetch the image from |
94
+ | `options.key` | `string` | No | Custom S3 key. If omitted, a UUID-based key is auto-generated |
95
+ | `options.folder` | `string` | No | S3 folder prefix (e.g. `"uploads"`). Ignored if `key` is set |
96
+ | `options.contentType` | `string` | No | MIME type override. Auto-detected from response headers |
97
+
98
+ ---
99
+
100
+ #### `s3.media.uploadBuffer(buffer, options?)`
101
+
102
+ Uploads a Buffer directly to S3. Useful for file uploads via multer/express.
103
+
104
+ ```typescript
105
+ // Example with Express + multer
106
+ app.post("/upload", upload.single("image"), async (req, res) => {
107
+ const result = await s3.media.uploadBuffer(req.file.buffer, {
108
+ contentType: req.file.mimetype,
109
+ folder: "user-uploads",
110
+ });
111
+ res.json(result);
112
+ });
113
+ ```
114
+
115
+ **Parameters:**
116
+
117
+ | Parameter | Type | Required | Description |
118
+ | --------------------- | -------- | -------- | ------------------------------------------------------------ |
119
+ | `buffer` | `Buffer` | Yes | The file buffer to upload |
120
+ | `options.key` | `string` | No | Custom S3 key. If omitted, a UUID-based key is auto-generated |
121
+ | `options.folder` | `string` | No | S3 folder prefix. Ignored if `key` is set |
122
+ | `options.contentType` | `string` | No | MIME type. Defaults to `"application/octet-stream"` |
123
+
124
+ ---
125
+
126
+ #### Response Format
127
+
128
+ Both methods return a `MediaUploadResponse`:
129
+
130
+ ```typescript
131
+ {
132
+ key: "uploads/550e8400-e29b-41d4-a716-446655440000.jpg",
133
+ url: "https://your-bucket.s3.us-east-1.amazonaws.com/uploads/550e8400-e29b-41d4-a716-446655440000.jpg",
134
+ bucket: "your-bucket-name",
135
+ contentType: "image/jpeg",
136
+ size: 102400
137
+ }
138
+ ```
139
+
140
+ | Field | Type | Description |
141
+ | ------------- | -------- | ------------------------------------ |
142
+ | `key` | `string` | The S3 object key |
143
+ | `url` | `string` | Full public S3 URL |
144
+ | `bucket` | `string` | S3 bucket name |
145
+ | `contentType` | `string` | MIME content type of the file |
146
+ | `size` | `number` | File size in bytes |
147
+
148
+ ---
149
+
150
+ ## Exports
151
+
152
+ ```typescript
153
+ import {
154
+ S3MediaClient, // Main S3 client class
155
+ S3MediaConfig, // Configuration interface
156
+ AWSConfig, // AWS credentials interface
157
+ MediaUploadOptions, // Upload options
158
+ MediaUploadResponse, // Upload response
159
+ } from "cloud-service-kit";
160
+ ```
161
+
162
+ ## How It Works
163
+
164
+ 1. **Initialization** — `new S3MediaClient(config)` creates an internal S3 client using your AWS credentials.
165
+ 2. **Upload from URL** — `uploadFromUrl` uses native `fetch()` to download the image, auto-detects content type from response headers, generates a UUID-based S3 key (or uses your custom key), and uploads the buffer to S3.
166
+ 3. **Upload from Buffer** — `uploadBuffer` takes a raw Buffer (e.g. from multer) and uploads it directly to S3.
167
+ 4. **Key generation** — When no custom key is provided, a UUID is generated using `crypto.randomUUID()` and the file extension is resolved from the source URL or content type.
168
+
169
+ ## Upcoming Modules
170
+
171
+ This toolkit is designed to be extended with more service modules:
172
+
173
+ - **S3 Media** — Available now
174
+ - **Email** — Coming soon
175
+ - **More** — Additional cloud services planned
176
+
177
+ ## Development
178
+
179
+ ```bash
180
+ # Install dependencies
181
+ npm install
182
+
183
+ # Build
184
+ npm run build
185
+
186
+ # Run structure tests
187
+ node test.js
188
+ ```
189
+
190
+ ## Publishing
191
+
192
+ ```bash
193
+ npm login
194
+ npm publish
195
+ npm info cloud-service-kit
196
+ ```
197
+
198
+ ## License
199
+
200
+ UNLICENSED
@@ -0,0 +1,3 @@
1
+ export { S3MediaClient } from "./s3-media-client";
2
+ export { S3MediaConfig, AWSConfig } from "./types";
3
+ export { MediaUploadOptions, MediaUploadResponse } from "./modules/media";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.S3MediaClient = void 0;
4
+ var s3_media_client_1 = require("./s3-media-client");
5
+ Object.defineProperty(exports, "S3MediaClient", { enumerable: true, get: function () { return s3_media_client_1.S3MediaClient; } });
@@ -0,0 +1,4 @@
1
+ import { S3Client } from "@aws-sdk/client-s3";
2
+ import { AWSConfig } from "../types";
3
+ export declare function createS3Client(config: AWSConfig): S3Client;
4
+ export declare function uploadToS3(client: S3Client, bucket: string, buffer: Buffer, key: string, contentType: string): Promise<string>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createS3Client = createS3Client;
4
+ exports.uploadToS3 = uploadToS3;
5
+ const client_s3_1 = require("@aws-sdk/client-s3");
6
+ function createS3Client(config) {
7
+ return new client_s3_1.S3Client({
8
+ region: config.region,
9
+ credentials: {
10
+ accessKeyId: config.accessKey,
11
+ secretAccessKey: config.secretKey,
12
+ },
13
+ });
14
+ }
15
+ async function uploadToS3(client, bucket, buffer, key, contentType) {
16
+ const command = new client_s3_1.PutObjectCommand({
17
+ Bucket: bucket,
18
+ Key: key,
19
+ Body: buffer,
20
+ ContentType: contentType,
21
+ });
22
+ await client.send(command);
23
+ return key;
24
+ }
@@ -0,0 +1,2 @@
1
+ export { MediaModule } from "./media.module";
2
+ export { MediaUploadOptions, MediaUploadResponse } from "./media.types";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MediaModule = void 0;
4
+ var media_module_1 = require("./media.module");
5
+ Object.defineProperty(exports, "MediaModule", { enumerable: true, get: function () { return media_module_1.MediaModule; } });
@@ -0,0 +1,19 @@
1
+ import { S3Client } from "@aws-sdk/client-s3";
2
+ import { AWSConfig } from "../../types";
3
+ import { MediaUploadOptions, MediaUploadResponse } from "./media.types";
4
+ export declare class MediaModule {
5
+ private s3Client;
6
+ private awsConfig;
7
+ constructor(s3Client: S3Client, awsConfig: AWSConfig);
8
+ /**
9
+ * Fetch an image from a live URL and upload it to S3.
10
+ */
11
+ uploadFromUrl(url: string, options?: MediaUploadOptions): Promise<MediaUploadResponse>;
12
+ /**
13
+ * Upload a buffer (e.g. from multer file upload) directly to S3.
14
+ */
15
+ uploadBuffer(buffer: Buffer, options?: MediaUploadOptions): Promise<MediaUploadResponse>;
16
+ private generateKey;
17
+ private resolveExtension;
18
+ private buildS3Url;
19
+ }
@@ -0,0 +1,91 @@
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.MediaModule = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const s3_client_1 = require("../../lib/s3.client");
10
+ class MediaModule {
11
+ constructor(s3Client, awsConfig) {
12
+ this.s3Client = s3Client;
13
+ this.awsConfig = awsConfig;
14
+ }
15
+ /**
16
+ * Fetch an image from a live URL and upload it to S3.
17
+ */
18
+ async uploadFromUrl(url, options = {}) {
19
+ const response = await fetch(url);
20
+ if (!response.ok) {
21
+ throw new Error(`Failed to fetch image from URL: ${response.status} ${response.statusText}`);
22
+ }
23
+ const arrayBuffer = await response.arrayBuffer();
24
+ const buffer = Buffer.from(arrayBuffer);
25
+ const contentType = options.contentType ||
26
+ response.headers.get("content-type") ||
27
+ "application/octet-stream";
28
+ const key = options.key || this.generateKey(options.folder, url, contentType);
29
+ await (0, s3_client_1.uploadToS3)(this.s3Client, this.awsConfig.bucket, buffer, key, contentType);
30
+ return {
31
+ key,
32
+ url: this.buildS3Url(key),
33
+ bucket: this.awsConfig.bucket,
34
+ contentType,
35
+ size: buffer.length,
36
+ };
37
+ }
38
+ /**
39
+ * Upload a buffer (e.g. from multer file upload) directly to S3.
40
+ */
41
+ async uploadBuffer(buffer, options = {}) {
42
+ const contentType = options.contentType || "application/octet-stream";
43
+ const key = options.key || this.generateKey(options.folder, undefined, contentType);
44
+ await (0, s3_client_1.uploadToS3)(this.s3Client, this.awsConfig.bucket, buffer, key, contentType);
45
+ return {
46
+ key,
47
+ url: this.buildS3Url(key),
48
+ bucket: this.awsConfig.bucket,
49
+ contentType,
50
+ size: buffer.length,
51
+ };
52
+ }
53
+ generateKey(folder, sourceUrl, contentType) {
54
+ const uuid = crypto_1.default.randomUUID();
55
+ const ext = this.resolveExtension(sourceUrl, contentType);
56
+ const filename = `${uuid}${ext}`;
57
+ return folder ? `${folder}/${filename}` : filename;
58
+ }
59
+ resolveExtension(sourceUrl, contentType) {
60
+ // Try to get extension from the source URL
61
+ if (sourceUrl) {
62
+ try {
63
+ const pathname = new URL(sourceUrl).pathname;
64
+ const ext = path_1.default.extname(pathname);
65
+ if (ext)
66
+ return ext;
67
+ }
68
+ catch {
69
+ // invalid URL, fall through
70
+ }
71
+ }
72
+ // Fall back to content type
73
+ const mimeToExt = {
74
+ "image/jpeg": ".jpg",
75
+ "image/png": ".png",
76
+ "image/gif": ".gif",
77
+ "image/webp": ".webp",
78
+ "image/svg+xml": ".svg",
79
+ "image/bmp": ".bmp",
80
+ "image/tiff": ".tiff",
81
+ };
82
+ if (contentType && mimeToExt[contentType]) {
83
+ return mimeToExt[contentType];
84
+ }
85
+ return "";
86
+ }
87
+ buildS3Url(key) {
88
+ return `https://${this.awsConfig.bucket}.s3.${this.awsConfig.region}.amazonaws.com/${key}`;
89
+ }
90
+ }
91
+ exports.MediaModule = MediaModule;
@@ -0,0 +1,20 @@
1
+ export interface MediaUploadOptions {
2
+ /** Custom S3 key. If omitted, a UUID-based key is auto-generated. */
3
+ key?: string;
4
+ /** S3 folder prefix (e.g. "uploads"). Ignored if `key` is provided. */
5
+ folder?: string;
6
+ /** MIME content type (e.g. "image/jpeg"). Auto-detected for URL uploads. */
7
+ contentType?: string;
8
+ }
9
+ export interface MediaUploadResponse {
10
+ /** The S3 object key */
11
+ key: string;
12
+ /** Full S3 URL to the uploaded object */
13
+ url: string;
14
+ /** S3 bucket name */
15
+ bucket: string;
16
+ /** MIME content type of the uploaded file */
17
+ contentType: string;
18
+ /** File size in bytes */
19
+ size: number;
20
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,7 @@
1
+ import { S3MediaConfig } from "./types";
2
+ import { MediaModule } from "./modules/media";
3
+ export declare class S3MediaClient {
4
+ readonly media: MediaModule;
5
+ private s3Client;
6
+ constructor(config: S3MediaConfig);
7
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.S3MediaClient = void 0;
4
+ const s3_client_1 = require("./lib/s3.client");
5
+ const media_1 = require("./modules/media");
6
+ class S3MediaClient {
7
+ constructor(config) {
8
+ this.s3Client = (0, s3_client_1.createS3Client)(config.aws);
9
+ this.media = new media_1.MediaModule(this.s3Client, config.aws);
10
+ }
11
+ }
12
+ exports.S3MediaClient = S3MediaClient;
@@ -0,0 +1,9 @@
1
+ export interface AWSConfig {
2
+ region: string;
3
+ bucket: string;
4
+ accessKey: string;
5
+ secretKey: string;
6
+ }
7
+ export interface S3MediaConfig {
8
+ aws: AWSConfig;
9
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "cloud-service-kit",
3
+ "version": "1.0.0",
4
+ "description": "A modular cloud service toolkit for S3 media uploads, email, and more",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "cloud",
16
+ "s3",
17
+ "media",
18
+ "upload",
19
+ "aws",
20
+ "toolkit"
21
+ ],
22
+ "license": "UNLICENSED",
23
+ "private": false,
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "dependencies": {
28
+ "@aws-sdk/client-s3": "^3.700.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^25.3.0",
32
+ "typescript": "^5.7.0"
33
+ },
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ }
37
+ }