@rx-ted/packages-honest-plugins 1.0.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 +13 -0
- package/README.md +45 -0
- package/api-doc/.turbo/turbo-build.log +1 -0
- package/api-doc/.turbo/turbo-test.log +11 -0
- package/api-doc/.turbo/turbo-typecheck.log +1 -0
- package/api-doc/CHANGELOG.md +22 -0
- package/api-doc/README.md +80 -0
- package/api-doc/dist/api-doc.plugin.d.ts +53 -0
- package/api-doc/dist/api-doc.plugin.js +452 -0
- package/api-doc/dist/index.d.ts +5 -0
- package/api-doc/dist/index.js +3 -0
- package/api-doc/dist/interfaces/renderer.interface.d.ts +7 -0
- package/api-doc/dist/interfaces/renderer.interface.js +1 -0
- package/api-doc/dist/renderers/scalar.renderer.d.ts +10 -0
- package/api-doc/dist/renderers/scalar.renderer.js +76 -0
- package/api-doc/dist/renderers/swagger.renderer.d.ts +10 -0
- package/api-doc/dist/renderers/swagger.renderer.js +83 -0
- package/api-doc/node_modules/.bin/esbuild +22 -0
- package/api-doc/node_modules/.bin/jiti +22 -0
- package/api-doc/node_modules/.bin/sass +22 -0
- package/api-doc/node_modules/.bin/tsx +22 -0
- package/api-doc/node_modules/.bin/vite +22 -0
- package/api-doc/node_modules/.bin/vitest +22 -0
- package/api-doc/node_modules/.bin/yaml +22 -0
- package/api-doc/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/api-doc/package.json +48 -0
- package/api-doc/src/api-doc.plugin.ts +554 -0
- package/api-doc/src/index.ts +5 -0
- package/api-doc/src/interfaces/renderer.interface.ts +5 -0
- package/api-doc/src/plugin.test.ts +740 -0
- package/api-doc/src/renderers/scalar.renderer.ts +82 -0
- package/api-doc/src/renderers/swagger.renderer.ts +89 -0
- package/api-doc/tsconfig.json +21 -0
- package/api-doc/vitest.config.ts +13 -0
- package/cache/.turbo/turbo-build.log +1 -0
- package/cache/.turbo/turbo-test.log +16 -0
- package/cache/CHANGELOG.md +23 -0
- package/cache/README.md +79 -0
- package/cache/dist/cache-service.d.ts +8 -0
- package/cache/dist/cache-service.js +21 -0
- package/cache/dist/cacheable.d.ts +2 -0
- package/cache/dist/cacheable.js +10 -0
- package/cache/dist/cloudflare/driver.d.ts +23 -0
- package/cache/dist/cloudflare/driver.js +102 -0
- package/cache/dist/cloudflare/index.d.ts +3 -0
- package/cache/dist/cloudflare/index.js +2 -0
- package/cache/dist/cloudflare/plugin.d.ts +19 -0
- package/cache/dist/cloudflare/plugin.js +58 -0
- package/cache/dist/index.d.ts +11 -0
- package/cache/dist/index.js +14 -0
- package/cache/dist/local/driver.d.ts +2 -0
- package/cache/dist/local/driver.js +98 -0
- package/cache/dist/local/index.d.ts +2 -0
- package/cache/dist/local/index.js +2 -0
- package/cache/dist/local/plugin.d.ts +14 -0
- package/cache/dist/local/plugin.js +24 -0
- package/cache/dist/redis/driver.d.ts +10 -0
- package/cache/dist/redis/driver.js +102 -0
- package/cache/dist/redis/index.d.ts +4 -0
- package/cache/dist/redis/index.js +2 -0
- package/cache/dist/redis/plugin.d.ts +25 -0
- package/cache/dist/redis/plugin.js +61 -0
- package/cache/dist/resolve.d.ts +24 -0
- package/cache/dist/resolve.js +114 -0
- package/cache/dist/types.d.ts +18 -0
- package/cache/dist/types.js +1 -0
- package/cache/node_modules/.bin/esbuild +22 -0
- package/cache/node_modules/.bin/jiti +22 -0
- package/cache/node_modules/.bin/sass +22 -0
- package/cache/node_modules/.bin/tsx +22 -0
- package/cache/node_modules/.bin/vite +22 -0
- package/cache/node_modules/.bin/vitest +22 -0
- package/cache/node_modules/.bin/yaml +22 -0
- package/cache/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/cache/package.json +46 -0
- package/cache/src/cache-service.test.ts +16 -0
- package/cache/src/cache-service.ts +15 -0
- package/cache/src/cacheable.test.ts +78 -0
- package/cache/src/cacheable.ts +16 -0
- package/cache/src/cloudflare/driver.test.ts +188 -0
- package/cache/src/cloudflare/driver.ts +118 -0
- package/cache/src/cloudflare/index.ts +3 -0
- package/cache/src/cloudflare/plugin.ts +71 -0
- package/cache/src/index.test.ts +68 -0
- package/cache/src/index.ts +20 -0
- package/cache/src/local/driver.ts +117 -0
- package/cache/src/local/index.ts +2 -0
- package/cache/src/local/plugin.ts +34 -0
- package/cache/src/redis/driver.test.ts +71 -0
- package/cache/src/redis/driver.ts +124 -0
- package/cache/src/redis/index.ts +4 -0
- package/cache/src/redis/plugin.ts +84 -0
- package/cache/src/resolve.test.ts +257 -0
- package/cache/src/resolve.ts +140 -0
- package/cache/src/types.ts +14 -0
- package/cache/tsconfig.json +21 -0
- package/cache/vitest.config.ts +13 -0
- package/db/.turbo/turbo-build.log +1 -0
- package/db/.turbo/turbo-test.log +21 -0
- package/db/CHANGELOG.md +26 -0
- package/db/README.md +99 -0
- package/db/dist/constants.d.ts +1 -0
- package/db/dist/constants.js +1 -0
- package/db/dist/d1/index.d.ts +3 -0
- package/db/dist/d1/index.js +2 -0
- package/db/dist/d1/plugin.d.ts +18 -0
- package/db/dist/d1/plugin.js +104 -0
- package/db/dist/db-service.d.ts +7 -0
- package/db/dist/db-service.js +21 -0
- package/db/dist/index.d.ts +15 -0
- package/db/dist/index.js +20 -0
- package/db/dist/mysql/client.d.ts +36 -0
- package/db/dist/mysql/client.js +84 -0
- package/db/dist/mysql/driver.d.ts +18 -0
- package/db/dist/mysql/driver.js +107 -0
- package/db/dist/mysql/index.d.ts +11 -0
- package/db/dist/mysql/index.js +11 -0
- package/db/dist/mysql/plugin.d.ts +51 -0
- package/db/dist/mysql/plugin.js +94 -0
- package/db/dist/mysql/types.d.ts +38 -0
- package/db/dist/mysql/types.js +1 -0
- package/db/dist/postgres/index.d.ts +2 -0
- package/db/dist/postgres/index.js +3 -0
- package/db/dist/repository.d.ts +14 -0
- package/db/dist/repository.js +79 -0
- package/db/dist/resolve.d.ts +6 -0
- package/db/dist/resolve.js +81 -0
- package/db/dist/schema-builder/column-builder.d.ts +26 -0
- package/db/dist/schema-builder/column-builder.js +53 -0
- package/db/dist/schema-builder/compile-d1.d.ts +5 -0
- package/db/dist/schema-builder/compile-d1.js +67 -0
- package/db/dist/schema-builder/compile-mysql.d.ts +12 -0
- package/db/dist/schema-builder/compile-mysql.js +78 -0
- package/db/dist/schema-builder/index.d.ts +8 -0
- package/db/dist/schema-builder/index.js +22 -0
- package/db/dist/schema-builder/table-builder.d.ts +16 -0
- package/db/dist/schema-builder/table-builder.js +38 -0
- package/db/dist/schema-builder/types.d.ts +26 -0
- package/db/dist/schema-builder/types.js +1 -0
- package/db/dist/schema-builder/zod-bridge.d.ts +24 -0
- package/db/dist/schema-builder/zod-bridge.js +53 -0
- package/db/dist/sqlite/index.d.ts +4 -0
- package/db/dist/sqlite/index.js +2 -0
- package/db/dist/sqlite/plugin.d.ts +17 -0
- package/db/dist/sqlite/plugin.js +33 -0
- package/db/dist/types.d.ts +42 -0
- package/db/dist/types.js +1 -0
- package/db/node_modules/.bin/esbuild +22 -0
- package/db/node_modules/.bin/jiti +22 -0
- package/db/node_modules/.bin/sass +22 -0
- package/db/node_modules/.bin/tsx +22 -0
- package/db/node_modules/.bin/vite +22 -0
- package/db/node_modules/.bin/vitest +22 -0
- package/db/node_modules/.bin/yaml +22 -0
- package/db/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/db/package.json +59 -0
- package/db/src/constants.test.ts +8 -0
- package/db/src/constants.ts +1 -0
- package/db/src/d1/index.ts +13 -0
- package/db/src/d1/plugin.ts +135 -0
- package/db/src/db-service.ts +14 -0
- package/db/src/index.test.ts +63 -0
- package/db/src/index.ts +57 -0
- package/db/src/mysql/client.ts +112 -0
- package/db/src/mysql/driver.test.ts +190 -0
- package/db/src/mysql/driver.ts +116 -0
- package/db/src/mysql/index.ts +51 -0
- package/db/src/mysql/plugin.test.ts +228 -0
- package/db/src/mysql/plugin.ts +128 -0
- package/db/src/mysql/types.ts +32 -0
- package/db/src/postgres/index.ts +15 -0
- package/db/src/repository.test.ts +195 -0
- package/db/src/repository.ts +95 -0
- package/db/src/resolve.ts +105 -0
- package/db/src/schema-builder/column-builder.test.ts +55 -0
- package/db/src/schema-builder/column-builder.ts +87 -0
- package/db/src/schema-builder/compile-d1.test.ts +60 -0
- package/db/src/schema-builder/compile-d1.ts +70 -0
- package/db/src/schema-builder/compile-mysql.test.ts +64 -0
- package/db/src/schema-builder/compile-mysql.ts +102 -0
- package/db/src/schema-builder/index.test.ts +36 -0
- package/db/src/schema-builder/index.ts +41 -0
- package/db/src/schema-builder/table-builder.test.ts +55 -0
- package/db/src/schema-builder/table-builder.ts +50 -0
- package/db/src/schema-builder/types.test.ts +68 -0
- package/db/src/schema-builder/types.ts +30 -0
- package/db/src/schema-builder/zod-bridge.ts +89 -0
- package/db/src/sqlite/index.ts +14 -0
- package/db/src/sqlite/plugin.ts +46 -0
- package/db/src/types.ts +45 -0
- package/db/tsconfig.json +21 -0
- package/db/vitest.config.ts +13 -0
- package/mail/.turbo/turbo-build.log +1 -0
- package/mail/.turbo/turbo-test.log +12 -0
- package/mail/.turbo/turbo-typecheck.log +1 -0
- package/mail/CHANGELOG.md +25 -0
- package/mail/README.md +214 -0
- package/mail/dist/index.d.ts +4 -0
- package/mail/dist/index.js +2 -0
- package/mail/dist/plugin.d.ts +64 -0
- package/mail/dist/plugin.js +232 -0
- package/mail/dist/providers/brevo.provider.d.ts +15 -0
- package/mail/dist/providers/brevo.provider.js +75 -0
- package/mail/dist/providers/custom.provider.d.ts +9 -0
- package/mail/dist/providers/custom.provider.js +22 -0
- package/mail/dist/providers/resend.provider.d.ts +16 -0
- package/mail/dist/providers/resend.provider.js +77 -0
- package/mail/dist/providers/smtp.provider.d.ts +21 -0
- package/mail/dist/providers/smtp.provider.js +86 -0
- package/mail/dist/rate-limiter.d.ts +16 -0
- package/mail/dist/rate-limiter.js +49 -0
- package/mail/dist/types.d.ts +68 -0
- package/mail/dist/types.js +6 -0
- package/mail/node_modules/.bin/esbuild +22 -0
- package/mail/node_modules/.bin/jiti +22 -0
- package/mail/node_modules/.bin/sass +22 -0
- package/mail/node_modules/.bin/tsx +22 -0
- package/mail/node_modules/.bin/vite +22 -0
- package/mail/node_modules/.bin/vitest +22 -0
- package/mail/node_modules/.bin/yaml +22 -0
- package/mail/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/mail/package.json +52 -0
- package/mail/src/index.ts +4 -0
- package/mail/src/plugin.test.ts +219 -0
- package/mail/src/plugin.ts +313 -0
- package/mail/src/providers/brevo.provider.ts +78 -0
- package/mail/src/providers/custom.provider.ts +29 -0
- package/mail/src/providers/resend.provider.ts +74 -0
- package/mail/src/providers/smtp.provider.ts +85 -0
- package/mail/src/rate-limiter.test.ts +81 -0
- package/mail/src/rate-limiter.ts +54 -0
- package/mail/src/types.ts +81 -0
- package/mail/tsconfig.json +21 -0
- package/mail/vitest.config.ts +13 -0
- package/package.json +36 -0
- package/s3/.turbo/turbo-build.log +1 -0
- package/s3/.turbo/turbo-test.log +11 -0
- package/s3/CHANGELOG.md +20 -0
- package/s3/README.md +90 -0
- package/s3/dist/index.d.ts +6 -0
- package/s3/dist/index.js +4 -0
- package/s3/dist/s3-service.d.ts +7 -0
- package/s3/dist/s3-service.js +21 -0
- package/s3/dist/s3.driver.d.ts +6 -0
- package/s3/dist/s3.driver.js +221 -0
- package/s3/dist/s3.plugin.d.ts +24 -0
- package/s3/dist/s3.plugin.js +58 -0
- package/s3/dist/types.d.ts +30 -0
- package/s3/dist/types.js +1 -0
- package/s3/node_modules/.bin/esbuild +22 -0
- package/s3/node_modules/.bin/jiti +22 -0
- package/s3/node_modules/.bin/sass +22 -0
- package/s3/node_modules/.bin/tsx +22 -0
- package/s3/node_modules/.bin/vite +22 -0
- package/s3/node_modules/.bin/vitest +22 -0
- package/s3/node_modules/.bin/yaml +22 -0
- package/s3/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/s3/package.json +47 -0
- package/s3/src/index.test.ts +20 -0
- package/s3/src/index.ts +13 -0
- package/s3/src/s3-service.ts +14 -0
- package/s3/src/s3.driver.ts +259 -0
- package/s3/src/s3.plugin.ts +80 -0
- package/s3/src/types.ts +37 -0
- package/s3/tsconfig.json +21 -0
- package/s3/vitest.config.ts +13 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import type { Logger } from '@rx-ted/packages-core';
|
|
2
|
+
import type { FileDriver, FileMetadata, FileUploadOptions, S3Options } from './types';
|
|
3
|
+
|
|
4
|
+
let awsS3: any = null;
|
|
5
|
+
async function getAwsS3() {
|
|
6
|
+
if (!awsS3) {
|
|
7
|
+
const name = '@aws-sdk/client-s3';
|
|
8
|
+
awsS3 = await import(name);
|
|
9
|
+
}
|
|
10
|
+
return awsS3;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let awsPresigner: any = null;
|
|
14
|
+
async function getAwsPresigner() {
|
|
15
|
+
if (!awsPresigner) {
|
|
16
|
+
const name = '@aws-sdk/s3-request-presigner';
|
|
17
|
+
awsPresigner = await import(name);
|
|
18
|
+
}
|
|
19
|
+
return awsPresigner;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type S3DriverOptions = S3Options & {
|
|
23
|
+
logger?: Logger;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export async function createS3Driver(options: S3DriverOptions): Promise<FileDriver> {
|
|
27
|
+
const logger = options.logger;
|
|
28
|
+
if (logger) {
|
|
29
|
+
logger.debug(
|
|
30
|
+
{
|
|
31
|
+
bucket: options.bucket,
|
|
32
|
+
endpoint: options.endpoint,
|
|
33
|
+
region: options.region,
|
|
34
|
+
},
|
|
35
|
+
'S3Driver: initializing',
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const s3 = await getAwsS3();
|
|
40
|
+
const client = new s3.S3Client({
|
|
41
|
+
endpoint: options.endpoint,
|
|
42
|
+
region: options.region || 'us-east-1',
|
|
43
|
+
credentials: {
|
|
44
|
+
accessKeyId: options.accessKeyId,
|
|
45
|
+
secretAccessKey: options.secretAccessKey,
|
|
46
|
+
},
|
|
47
|
+
forcePathStyle: options.forcePathStyle,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const driver = new S3Driver(client, options, logger);
|
|
51
|
+
|
|
52
|
+
if (logger) {
|
|
53
|
+
logger.info('S3Driver: initialized');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return driver;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
class S3Driver implements FileDriver {
|
|
60
|
+
constructor(
|
|
61
|
+
private client: any,
|
|
62
|
+
private options: S3Options & { logger?: Logger },
|
|
63
|
+
private logger?: Logger,
|
|
64
|
+
) {}
|
|
65
|
+
|
|
66
|
+
async upload(
|
|
67
|
+
key: string,
|
|
68
|
+
data: Uint8Array | ReadableStream,
|
|
69
|
+
uploadOptions?: FileUploadOptions,
|
|
70
|
+
): Promise<FileMetadata> {
|
|
71
|
+
let body: Uint8Array | undefined;
|
|
72
|
+
if (data instanceof Uint8Array) {
|
|
73
|
+
body = data;
|
|
74
|
+
} else {
|
|
75
|
+
const chunks: Uint8Array[] = [];
|
|
76
|
+
const reader = data.getReader();
|
|
77
|
+
while (true) {
|
|
78
|
+
const { done, value } = await reader.read();
|
|
79
|
+
if (done) break;
|
|
80
|
+
chunks.push(value);
|
|
81
|
+
}
|
|
82
|
+
const totalLength = chunks.reduce((sum, c) => sum + c.byteLength, 0);
|
|
83
|
+
const merged = new Uint8Array(totalLength);
|
|
84
|
+
let offset = 0;
|
|
85
|
+
for (const chunk of chunks) {
|
|
86
|
+
merged.set(chunk, offset);
|
|
87
|
+
offset += chunk.byteLength;
|
|
88
|
+
}
|
|
89
|
+
body = merged;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const s3 = await getAwsS3();
|
|
93
|
+
const command = new s3.PutObjectCommand({
|
|
94
|
+
Bucket: this.options.bucket,
|
|
95
|
+
Key: key,
|
|
96
|
+
Body: body,
|
|
97
|
+
ContentType: uploadOptions?.contentType,
|
|
98
|
+
Metadata: uploadOptions?.metadata,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await this.client.send(command);
|
|
102
|
+
|
|
103
|
+
const url = this.options.baseUrl
|
|
104
|
+
? `${this.options.baseUrl.replace(/\/$/, '')}/${key}`
|
|
105
|
+
: undefined;
|
|
106
|
+
|
|
107
|
+
if (this.logger) {
|
|
108
|
+
this.logger.debug({ key, size: body?.length }, 'S3Driver: uploaded');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
key,
|
|
113
|
+
size: body?.length || 0,
|
|
114
|
+
contentType: uploadOptions?.contentType || this.guessContentType(key),
|
|
115
|
+
lastModified: new Date(),
|
|
116
|
+
url,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async download(key: string): Promise<Uint8Array> {
|
|
121
|
+
const s3 = await getAwsS3();
|
|
122
|
+
const command = new s3.GetObjectCommand({
|
|
123
|
+
Bucket: this.options.bucket,
|
|
124
|
+
Key: key,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const response = await this.client.send(command);
|
|
128
|
+
if (!response.Body) {
|
|
129
|
+
return new Uint8Array();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const chunks: Uint8Array[] = [];
|
|
133
|
+
for await (const chunk of response.Body) {
|
|
134
|
+
chunks.push(chunk);
|
|
135
|
+
}
|
|
136
|
+
const totalLength = chunks.reduce((sum, c) => sum + c.byteLength, 0);
|
|
137
|
+
const merged = new Uint8Array(totalLength);
|
|
138
|
+
let offset = 0;
|
|
139
|
+
for (const chunk of chunks) {
|
|
140
|
+
merged.set(chunk, offset);
|
|
141
|
+
offset += chunk.byteLength;
|
|
142
|
+
}
|
|
143
|
+
return merged;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async getUrl(key: string, expiresIn?: number): Promise<string> {
|
|
147
|
+
if (this.options.baseUrl) {
|
|
148
|
+
return `${this.options.baseUrl.replace(/\/$/, '')}/${key}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const [s3, presigner] = await Promise.all([getAwsS3(), getAwsPresigner()]);
|
|
152
|
+
const command = new s3.GetObjectCommand({
|
|
153
|
+
Bucket: this.options.bucket,
|
|
154
|
+
Key: key,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return presigner.getSignedUrl(this.client, command, {
|
|
158
|
+
expiresIn: expiresIn || 3600,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async delete(key: string): Promise<boolean> {
|
|
163
|
+
try {
|
|
164
|
+
const s3 = await getAwsS3();
|
|
165
|
+
const command = new s3.DeleteObjectCommand({
|
|
166
|
+
Bucket: this.options.bucket,
|
|
167
|
+
Key: key,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await this.client.send(command);
|
|
171
|
+
|
|
172
|
+
if (this.logger) {
|
|
173
|
+
this.logger.debug({ key }, 'S3Driver: deleted');
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
} catch {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async exists(key: string): Promise<boolean> {
|
|
182
|
+
try {
|
|
183
|
+
const s3 = await getAwsS3();
|
|
184
|
+
const command = new s3.HeadObjectCommand({
|
|
185
|
+
Bucket: this.options.bucket,
|
|
186
|
+
Key: key,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
await this.client.send(command);
|
|
190
|
+
return true;
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async list(prefix?: string): Promise<FileMetadata[]> {
|
|
197
|
+
const s3 = await getAwsS3();
|
|
198
|
+
const command = new s3.ListObjectsV2Command({
|
|
199
|
+
Bucket: this.options.bucket,
|
|
200
|
+
Prefix: prefix,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const response = await this.client.send(command);
|
|
204
|
+
const results: FileMetadata[] = [];
|
|
205
|
+
|
|
206
|
+
if (response.Contents) {
|
|
207
|
+
for (const item of response.Contents) {
|
|
208
|
+
results.push({
|
|
209
|
+
key: item.Key || '',
|
|
210
|
+
size: item.Size || 0,
|
|
211
|
+
contentType: this.guessContentType(item.Key || ''),
|
|
212
|
+
lastModified: item.LastModified || new Date(),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return results;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async close(): Promise<void> {
|
|
221
|
+
this.client.destroy();
|
|
222
|
+
if (this.logger) {
|
|
223
|
+
this.logger.info('S3Driver: closed');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async healthCheck(): Promise<boolean> {
|
|
228
|
+
try {
|
|
229
|
+
const s3 = await getAwsS3();
|
|
230
|
+
const command = new s3.ListObjectsV2Command({
|
|
231
|
+
Bucket: this.options.bucket,
|
|
232
|
+
MaxKeys: 1,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
await this.client.send(command);
|
|
236
|
+
return true;
|
|
237
|
+
} catch {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private guessContentType(key: string): string {
|
|
243
|
+
const ext = key.split('.').pop()?.toLowerCase() || '';
|
|
244
|
+
const mimeTypes: Record<string, string> = {
|
|
245
|
+
html: 'text/html',
|
|
246
|
+
css: 'text/css',
|
|
247
|
+
js: 'application/javascript',
|
|
248
|
+
json: 'application/json',
|
|
249
|
+
png: 'image/png',
|
|
250
|
+
jpg: 'image/jpeg',
|
|
251
|
+
jpeg: 'image/jpeg',
|
|
252
|
+
gif: 'image/gif',
|
|
253
|
+
svg: 'image/svg+xml',
|
|
254
|
+
pdf: 'application/pdf',
|
|
255
|
+
txt: 'text/plain',
|
|
256
|
+
};
|
|
257
|
+
return mimeTypes[ext] || 'application/octet-default';
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { ILogger } from '@rx-ted/packages-core';
|
|
3
|
+
import type { Application, IPlugin } from '@rx-ted/packages-honest';
|
|
4
|
+
import { ComponentManager, maskSensitive } from '@rx-ted/packages-honest';
|
|
5
|
+
import { createS3Driver } from './s3.driver';
|
|
6
|
+
import type { FileDriver, S3Options } from './types';
|
|
7
|
+
|
|
8
|
+
export const S3_CONTEXT_KEY = 'honest:s3';
|
|
9
|
+
export const S3_GLOBAL_KEY = 's3';
|
|
10
|
+
|
|
11
|
+
export interface S3PluginOptions {
|
|
12
|
+
connection: S3Options;
|
|
13
|
+
contextKey?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class S3Plugin implements IPlugin {
|
|
17
|
+
static readonly globalKey = S3_GLOBAL_KEY;
|
|
18
|
+
readonly name = 's3-plugin';
|
|
19
|
+
readonly version = '1.0.0';
|
|
20
|
+
|
|
21
|
+
logger?: ILogger;
|
|
22
|
+
|
|
23
|
+
private driver: FileDriver | null = null;
|
|
24
|
+
private readonly connection: S3Options;
|
|
25
|
+
private readonly contextKey: string;
|
|
26
|
+
|
|
27
|
+
constructor(options: S3PluginOptions) {
|
|
28
|
+
this.connection = options.connection;
|
|
29
|
+
this.contextKey = options.contextKey ?? S3_CONTEXT_KEY;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getClient(): FileDriver {
|
|
33
|
+
if (!this.driver) {
|
|
34
|
+
throw new Error('S3 not connected. Ensure beforeModulesRegistered has run.');
|
|
35
|
+
}
|
|
36
|
+
return this.driver;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async beforeModulesRegistered(app: Application, _hono: Hono): Promise<void> {
|
|
40
|
+
this.logger?.info('S3: initializing...');
|
|
41
|
+
|
|
42
|
+
const connInfo: Record<string, unknown> = {
|
|
43
|
+
category: 'plugins',
|
|
44
|
+
endpoint: this.connection.endpoint,
|
|
45
|
+
region: this.connection.region,
|
|
46
|
+
bucket: this.connection.bucket,
|
|
47
|
+
accessKeyId: maskSensitive(this.connection.accessKeyId),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
this.driver = await createS3Driver(this.connection);
|
|
52
|
+
app.getContext().set(this.contextKey, this.driver);
|
|
53
|
+
ComponentManager.registerPlugin(S3_GLOBAL_KEY, this.driver);
|
|
54
|
+
|
|
55
|
+
const ok = await this.driver.healthCheck();
|
|
56
|
+
if (!ok) throw new Error('S3: health check failed');
|
|
57
|
+
this.logger?.info(connInfo, 'success to s3');
|
|
58
|
+
} catch (error) {
|
|
59
|
+
this.logger?.error(
|
|
60
|
+
{ ...connInfo, error: error instanceof Error ? error.message : String(error) },
|
|
61
|
+
'fail to connect s3',
|
|
62
|
+
);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async afterModulesRegistered(_app: Application, _hono: Hono): Promise<void> {
|
|
68
|
+
if (this.driver) {
|
|
69
|
+
const ok = await this.driver.healthCheck();
|
|
70
|
+
if (ok) {
|
|
71
|
+
this.logger?.info('S3: health check passed');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async close(): Promise<void> {
|
|
77
|
+
await this.driver?.close();
|
|
78
|
+
this.driver = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
package/s3/src/types.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface FileUploadOptions {
|
|
2
|
+
contentType?: string;
|
|
3
|
+
metadata?: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface FileMetadata {
|
|
7
|
+
key: string;
|
|
8
|
+
size: number;
|
|
9
|
+
contentType: string;
|
|
10
|
+
lastModified: Date;
|
|
11
|
+
url?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface S3Options {
|
|
15
|
+
endpoint?: string;
|
|
16
|
+
accessKeyId: string;
|
|
17
|
+
secretAccessKey: string;
|
|
18
|
+
bucket: string;
|
|
19
|
+
region?: string;
|
|
20
|
+
forcePathStyle?: boolean;
|
|
21
|
+
baseUrl?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface FileDriver {
|
|
25
|
+
upload(
|
|
26
|
+
key: string,
|
|
27
|
+
data: Uint8Array | ReadableStream,
|
|
28
|
+
options?: FileUploadOptions,
|
|
29
|
+
): Promise<FileMetadata>;
|
|
30
|
+
download(key: string): Promise<Uint8Array>;
|
|
31
|
+
getUrl(key: string, expiresIn?: number): Promise<string>;
|
|
32
|
+
delete(key: string): Promise<boolean>;
|
|
33
|
+
exists(key: string): Promise<boolean>;
|
|
34
|
+
list(prefix?: string): Promise<FileMetadata[]>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
healthCheck(): Promise<boolean>;
|
|
37
|
+
}
|
package/s3/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"sourceMap": false,
|
|
9
|
+
"experimentalDecorators": true,
|
|
10
|
+
"emitDecoratorMetadata": true,
|
|
11
|
+
"types": ["node"],
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"verbatimModuleSyntax": true,
|
|
16
|
+
"rootDir": "./src",
|
|
17
|
+
"outDir": "./dist"
|
|
18
|
+
},
|
|
19
|
+
"include": ["src"],
|
|
20
|
+
"exclude": ["**/*.test.ts"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
import { getWorkspaceAliases } from '../../../vitest.workspace-aliases';
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
resolve: {
|
|
6
|
+
alias: getWorkspaceAliases(),
|
|
7
|
+
},
|
|
8
|
+
test: {
|
|
9
|
+
globals: true,
|
|
10
|
+
environment: 'node',
|
|
11
|
+
exclude: ['node_modules'],
|
|
12
|
+
},
|
|
13
|
+
});
|