@volcanicminds/tools 0.0.3 → 0.0.5
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 +96 -22
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/mailer/index.d.ts +43 -0
- package/dist/lib/mailer/index.d.ts.map +1 -0
- package/dist/lib/mailer/index.js +73 -0
- package/dist/lib/mailer/index.js.map +1 -0
- package/dist/lib/storage/index.d.ts +40 -0
- package/dist/lib/storage/index.d.ts.map +1 -0
- package/dist/lib/storage/index.js +84 -0
- package/dist/lib/storage/index.js.map +1 -0
- package/dist/lib/transfer/index.d.ts +33 -0
- package/dist/lib/transfer/index.d.ts.map +1 -0
- package/dist/lib/transfer/index.js +71 -0
- package/dist/lib/transfer/index.js.map +1 -0
- package/lib/mailer/index.ts +132 -0
- package/lib/storage/index.ts +135 -0
- package/lib/transfer/index.ts +115 -0
- package/package.json +49 -9
package/README.md
CHANGED
|
@@ -19,46 +19,120 @@ npm install @volcanicminds/tools
|
|
|
19
19
|
npm run upgrade-deps
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
## Environment
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# or automatically use LOG_LEVEL
|
|
26
|
-
SOME_KEY=true
|
|
27
|
-
```
|
|
28
|
-
|
|
29
22
|
## Usage
|
|
30
23
|
|
|
31
|
-
This package supports both root imports and sub-path imports to optimize bundle size.
|
|
32
|
-
|
|
33
|
-
### Import everything
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
import { mfa, log } from '@volcanicminds/tools'
|
|
37
|
-
```
|
|
24
|
+
This package supports both root imports and sub-path imports to optimize bundle size and tree-shaking.
|
|
38
25
|
|
|
39
|
-
### Import specific features (Recommended
|
|
26
|
+
### Import specific features (Recommended)
|
|
40
27
|
|
|
41
28
|
```typescript
|
|
42
29
|
import * as mfa from '@volcanicminds/tools/mfa'
|
|
30
|
+
import { Mailer } from '@volcanicminds/tools/mailer'
|
|
43
31
|
import * as logger from '@volcanicminds/tools/logger'
|
|
44
32
|
```
|
|
45
33
|
|
|
34
|
+
---
|
|
35
|
+
|
|
46
36
|
## Features
|
|
47
37
|
|
|
48
38
|
### MFA (Multi-Factor Authentication)
|
|
49
39
|
|
|
50
|
-
Utilities for generating secrets, QR codes, and verifying TOTP tokens
|
|
40
|
+
Utilities for generating secrets, QR codes, and verifying TOTP tokens based on `otpauth`.
|
|
51
41
|
|
|
52
42
|
```typescript
|
|
53
43
|
import * as mfa from '@volcanicminds/tools/mfa'
|
|
54
44
|
|
|
55
|
-
// Generate
|
|
56
|
-
const
|
|
45
|
+
// 1. Generate a generic base32 secret (Optional, useful if you need to store it before setup)
|
|
46
|
+
const secret = mfa.generateSecret()
|
|
47
|
+
|
|
48
|
+
// 2. Generate Setup Details for the User (returns secret, otpauth URI, and QR Code Data URL)
|
|
49
|
+
// If secret is omitted, a new one is generated automatically.
|
|
50
|
+
const setup = await mfa.generateSetupDetails('MyApp', 'user@example.com', secret)
|
|
51
|
+
|
|
52
|
+
console.log(setup.secret) // Save this to DB
|
|
53
|
+
console.log(setup.qrCode) // Send this to Frontend to display QR
|
|
54
|
+
|
|
55
|
+
// 3. Verify a Token provided by the user
|
|
56
|
+
const userToken = '123456' // From input
|
|
57
|
+
const isValid = mfa.verifyToken(userToken, setup.secret)
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
if (isValid) {
|
|
60
|
+
// Proceed with login/action
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 4. Generate a valid token (Useful for testing or recovery codes)
|
|
64
|
+
const currentToken = mfa.generateToken(setup.secret)
|
|
60
65
|
```
|
|
61
66
|
|
|
62
|
-
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### Mailer
|
|
70
|
+
|
|
71
|
+
A wrapper around `nodemailer` designed for simplicity and configuration injection. It automatically handles HTML-to-Text conversion if the text body is missing.
|
|
63
72
|
|
|
64
|
-
|
|
73
|
+
#### Configuration & Initialization
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { Mailer } from '@volcanicminds/tools/mailer'
|
|
77
|
+
|
|
78
|
+
// Initialize with a config object (not bound to process.env)
|
|
79
|
+
const mailer = new Mailer({
|
|
80
|
+
host: 'smtp.example.com',
|
|
81
|
+
port: 587,
|
|
82
|
+
secure: false, // true for 465, false for other ports
|
|
83
|
+
auth: {
|
|
84
|
+
user: 'my-user',
|
|
85
|
+
pass: 'my-password'
|
|
86
|
+
},
|
|
87
|
+
defaultFrom: '"My Service" <noreply@example.com>', // Optional: used if not specified in send()
|
|
88
|
+
defaultReplyTo: 'support@example.com' // Optional
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Optional: Verify connection on startup
|
|
92
|
+
const isConnected = await mailer.verifyConnection()
|
|
93
|
+
if (isConnected) console.log('SMTP Ready')
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Sending Emails
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
try {
|
|
100
|
+
const info = await mailer.send({
|
|
101
|
+
// Optional if defaultFrom is set in config, otherwise Mandatory
|
|
102
|
+
from: '"Support Team" <support@example.com>',
|
|
103
|
+
|
|
104
|
+
to: 'user@destination.com', // Can be a string or array of strings
|
|
105
|
+
cc: ['manager@destination.com'],
|
|
106
|
+
subject: 'Welcome to Volcanic Tools',
|
|
107
|
+
|
|
108
|
+
// Text version is automatically generated from HTML if omitted,
|
|
109
|
+
// converting <br> to newlines and stripping tags.
|
|
110
|
+
text: 'Hello, World! Welcome aboard.',
|
|
111
|
+
html: '<p>Hello, <strong>World</strong>!<br/>Welcome aboard.</p>',
|
|
112
|
+
|
|
113
|
+
attachments: [
|
|
114
|
+
{
|
|
115
|
+
filename: 'license.txt',
|
|
116
|
+
content: 'MIT License...'
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
console.log('Message sent: %s', info.messageId)
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error('Error sending email:', error)
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
### Logging
|
|
130
|
+
|
|
131
|
+
Use Pino logger wrapper if in your project you have a `global.log` with a valid instance.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import * as log from '@volcanicminds/tools/logger'
|
|
135
|
+
|
|
136
|
+
log.info('Application started')
|
|
137
|
+
log.error({ err: new Error('Oops') }, 'Something went wrong')
|
|
138
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export * as mfa from './lib/mfa/index.js';
|
|
2
|
+
export * as mailer from './lib/mailer/index.js';
|
|
2
3
|
export * as log from './lib/util/logger.js';
|
|
4
|
+
export * as storage from './lib/storage/index.js';
|
|
5
|
+
export * as transfer from './lib/transfer/index.js';
|
|
3
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AACzC,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAA;AAC/C,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAC3C,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AACjD,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export * as mfa from './lib/mfa/index.js';
|
|
2
|
+
export * as mailer from './lib/mailer/index.js';
|
|
2
3
|
export * as log from './lib/util/logger.js';
|
|
4
|
+
export * as storage from './lib/storage/index.js';
|
|
5
|
+
export * as transfer from './lib/transfer/index.js';
|
|
3
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AACzC,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAA;AAC/C,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAC3C,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AACjD,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface MailerConfig {
|
|
2
|
+
host: string;
|
|
3
|
+
port: number;
|
|
4
|
+
secure?: boolean;
|
|
5
|
+
auth?: {
|
|
6
|
+
user: string;
|
|
7
|
+
pass: string;
|
|
8
|
+
};
|
|
9
|
+
defaultFrom?: string;
|
|
10
|
+
defaultReplyTo?: string;
|
|
11
|
+
tls?: {
|
|
12
|
+
rejectUnauthorized?: boolean;
|
|
13
|
+
ciphers?: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface MailAttachment {
|
|
17
|
+
filename: string;
|
|
18
|
+
content?: string | Buffer;
|
|
19
|
+
path?: string;
|
|
20
|
+
contentType?: string;
|
|
21
|
+
cid?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface MailOptions {
|
|
24
|
+
to: string | string[];
|
|
25
|
+
subject: string;
|
|
26
|
+
html: string;
|
|
27
|
+
text?: string;
|
|
28
|
+
from?: string;
|
|
29
|
+
cc?: string | string[];
|
|
30
|
+
bcc?: string | string[];
|
|
31
|
+
replyTo?: string;
|
|
32
|
+
attachments?: MailAttachment[];
|
|
33
|
+
}
|
|
34
|
+
export declare class Mailer {
|
|
35
|
+
private transporter;
|
|
36
|
+
private defaultFrom?;
|
|
37
|
+
private defaultReplyTo?;
|
|
38
|
+
constructor(config: MailerConfig);
|
|
39
|
+
private stripHtml;
|
|
40
|
+
send(options: MailOptions): Promise<any>;
|
|
41
|
+
verifyConnection(): Promise<boolean>;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/mailer/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,GAAG,CAAC,EAAE;QACJ,kBAAkB,CAAC,EAAE,OAAO,CAAA;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAA;KACjB,CAAA;CACF;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAA;CAC/B;AAMD,qBAAa,MAAM;IACjB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,WAAW,CAAC,CAAQ;IAC5B,OAAO,CAAC,cAAc,CAAC,CAAQ;gBAEnB,MAAM,EAAE,YAAY;IAqBhC,OAAO,CAAC,SAAS;IAcJ,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC;IAwCxC,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;CASlD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
export class Mailer {
|
|
3
|
+
transporter;
|
|
4
|
+
defaultFrom;
|
|
5
|
+
defaultReplyTo;
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.defaultFrom = config.defaultFrom;
|
|
8
|
+
this.defaultReplyTo = config.defaultReplyTo;
|
|
9
|
+
this.transporter = nodemailer.createTransport({
|
|
10
|
+
host: config.host,
|
|
11
|
+
port: config.port,
|
|
12
|
+
secure: config.secure ?? config.port === 465,
|
|
13
|
+
auth: config.auth
|
|
14
|
+
? {
|
|
15
|
+
user: config.auth.user,
|
|
16
|
+
pass: config.auth.pass
|
|
17
|
+
}
|
|
18
|
+
: undefined,
|
|
19
|
+
tls: config.tls
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
stripHtml(html) {
|
|
23
|
+
if (!html)
|
|
24
|
+
return '';
|
|
25
|
+
return html
|
|
26
|
+
.replace(/<br\s*\/?>/gi, '\n')
|
|
27
|
+
.replace(/<\/p>/gi, '\n\n')
|
|
28
|
+
.replace(/<[^>]*>?/gm, '')
|
|
29
|
+
.trim();
|
|
30
|
+
}
|
|
31
|
+
async send(options) {
|
|
32
|
+
const from = options.from || this.defaultFrom;
|
|
33
|
+
const replyTo = options.replyTo || this.defaultReplyTo;
|
|
34
|
+
if (!from) {
|
|
35
|
+
throw new Error('Email "from" address is required (either in options or config.defaultFrom)');
|
|
36
|
+
}
|
|
37
|
+
const mailPayload = {
|
|
38
|
+
from,
|
|
39
|
+
to: Array.isArray(options.to) ? options.to.join(', ') : options.to,
|
|
40
|
+
subject: options.subject,
|
|
41
|
+
html: options.html,
|
|
42
|
+
text: options.text || this.stripHtml(options.html),
|
|
43
|
+
attachments: options.attachments
|
|
44
|
+
};
|
|
45
|
+
if (options.cc) {
|
|
46
|
+
mailPayload.cc = Array.isArray(options.cc) ? options.cc.join(', ') : options.cc;
|
|
47
|
+
}
|
|
48
|
+
if (options.bcc) {
|
|
49
|
+
mailPayload.bcc = Array.isArray(options.bcc) ? options.bcc.join(', ') : options.bcc;
|
|
50
|
+
}
|
|
51
|
+
if (replyTo) {
|
|
52
|
+
mailPayload.replyTo = replyTo;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const info = await this.transporter.sendMail(mailPayload);
|
|
56
|
+
return info;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw new Error(`Failed to send email to ${mailPayload.to}: ${error.message}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async verifyConnection() {
|
|
63
|
+
try {
|
|
64
|
+
await this.transporter.verify();
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error('Mailer connection verification failed:', error);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/mailer/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Client, BucketItemStat } from 'minio';
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
export interface StorageConfig {
|
|
4
|
+
endPoint: string;
|
|
5
|
+
port: number;
|
|
6
|
+
useSSL: boolean;
|
|
7
|
+
accessKey: string;
|
|
8
|
+
secretKey: string;
|
|
9
|
+
bucket: string;
|
|
10
|
+
region?: string;
|
|
11
|
+
pathStyle?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface UploadOptions {
|
|
14
|
+
size?: number;
|
|
15
|
+
contentType?: string;
|
|
16
|
+
metadata?: Record<string, string | number | boolean>;
|
|
17
|
+
}
|
|
18
|
+
export interface UploadedObjectInfo {
|
|
19
|
+
etag: string;
|
|
20
|
+
versionId: string | null;
|
|
21
|
+
}
|
|
22
|
+
export declare class StorageManager {
|
|
23
|
+
private client;
|
|
24
|
+
private bucket;
|
|
25
|
+
private region;
|
|
26
|
+
constructor(config: StorageConfig);
|
|
27
|
+
getClient(): Client;
|
|
28
|
+
getBucketName(): string;
|
|
29
|
+
verifyConnection(): Promise<boolean>;
|
|
30
|
+
ensureBucket(bucketName?: string): Promise<void>;
|
|
31
|
+
uploadFile(objectName: string, stream: Readable | Buffer | string, options?: UploadOptions): Promise<UploadedObjectInfo>;
|
|
32
|
+
getFileUrl(objectName: string, expiry?: number): Promise<string>;
|
|
33
|
+
getUploadUrl(objectName: string, expiry?: number): Promise<string>;
|
|
34
|
+
deleteFile(objectName: string): Promise<void>;
|
|
35
|
+
deleteFiles(objectNames: string[]): Promise<void>;
|
|
36
|
+
fileExists(objectName: string): Promise<boolean>;
|
|
37
|
+
getFileStream(objectName: string): Promise<Readable>;
|
|
38
|
+
getFileStat(objectName: string): Promise<BucketItemStat>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqC,cAAc,EAAE,MAAM,OAAO,CAAA;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;CACrD;AAID,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,MAAM,CAAQ;gBAEV,MAAM,EAAE,aAAa;IAiB1B,SAAS,IAAI,MAAM;IAInB,aAAa,IAAI,MAAM;IAIjB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAUpC,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYhD,UAAU,CACrB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,EAClC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,kBAAkB,CAAC;IAgBjB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,MAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IAI9E,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,MAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IAIhF,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYhD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIpD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAGtE"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Client } from 'minio';
|
|
2
|
+
export class StorageManager {
|
|
3
|
+
client;
|
|
4
|
+
bucket;
|
|
5
|
+
region;
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.bucket = config.bucket;
|
|
8
|
+
this.region = config.region || 'us-east-1';
|
|
9
|
+
const clientOptions = {
|
|
10
|
+
endPoint: config.endPoint,
|
|
11
|
+
port: config.port,
|
|
12
|
+
useSSL: config.useSSL,
|
|
13
|
+
accessKey: config.accessKey,
|
|
14
|
+
secretKey: config.secretKey,
|
|
15
|
+
region: this.region,
|
|
16
|
+
pathStyle: config.pathStyle
|
|
17
|
+
};
|
|
18
|
+
this.client = new Client(clientOptions);
|
|
19
|
+
}
|
|
20
|
+
getClient() {
|
|
21
|
+
return this.client;
|
|
22
|
+
}
|
|
23
|
+
getBucketName() {
|
|
24
|
+
return this.bucket;
|
|
25
|
+
}
|
|
26
|
+
async verifyConnection() {
|
|
27
|
+
try {
|
|
28
|
+
await this.client.listBuckets();
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error('Storage connection verification failed:', error);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async ensureBucket(bucketName) {
|
|
37
|
+
const targetBucket = bucketName || this.bucket;
|
|
38
|
+
try {
|
|
39
|
+
const exists = await this.client.bucketExists(targetBucket);
|
|
40
|
+
if (!exists) {
|
|
41
|
+
await this.client.makeBucket(targetBucket, this.region);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw new Error(`Failed to ensure bucket ${targetBucket}: ${error}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async uploadFile(objectName, stream, options = {}) {
|
|
49
|
+
await this.ensureBucket();
|
|
50
|
+
const metaData = options.metadata || {};
|
|
51
|
+
return this.client.putObject(this.bucket, objectName, stream, options.size, metaData);
|
|
52
|
+
}
|
|
53
|
+
async getFileUrl(objectName, expiry = 24 * 60 * 60) {
|
|
54
|
+
return this.client.presignedGetObject(this.bucket, objectName, expiry);
|
|
55
|
+
}
|
|
56
|
+
async getUploadUrl(objectName, expiry = 24 * 60 * 60) {
|
|
57
|
+
return this.client.presignedPutObject(this.bucket, objectName, expiry);
|
|
58
|
+
}
|
|
59
|
+
async deleteFile(objectName) {
|
|
60
|
+
await this.client.removeObject(this.bucket, objectName);
|
|
61
|
+
}
|
|
62
|
+
async deleteFiles(objectNames) {
|
|
63
|
+
await this.client.removeObjects(this.bucket, objectNames);
|
|
64
|
+
}
|
|
65
|
+
async fileExists(objectName) {
|
|
66
|
+
try {
|
|
67
|
+
await this.client.statObject(this.bucket, objectName);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error.code === 'NotFound') {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async getFileStream(objectName) {
|
|
78
|
+
return this.client.getObject(this.bucket, objectName);
|
|
79
|
+
}
|
|
80
|
+
async getFileStat(objectName) {
|
|
81
|
+
return this.client.statObject(this.bucket, objectName);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/storage/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Server, Upload } from '@tus/server';
|
|
2
|
+
import type { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
+
export interface TransferConfig {
|
|
4
|
+
driver: 'local' | 's3';
|
|
5
|
+
maxSize?: number;
|
|
6
|
+
path: string;
|
|
7
|
+
local?: {
|
|
8
|
+
directory: string;
|
|
9
|
+
};
|
|
10
|
+
s3?: {
|
|
11
|
+
bucket: string;
|
|
12
|
+
endPoint: string;
|
|
13
|
+
port?: number;
|
|
14
|
+
useSSL?: boolean;
|
|
15
|
+
accessKey: string;
|
|
16
|
+
secretKey: string;
|
|
17
|
+
region?: string;
|
|
18
|
+
partSize?: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export type TransferEventCallback = (uploadOrId: Upload | string, req?: IncomingMessage, res?: ServerResponse) => void;
|
|
22
|
+
export declare class TransferManager {
|
|
23
|
+
private server;
|
|
24
|
+
private config;
|
|
25
|
+
constructor(config: TransferConfig);
|
|
26
|
+
private createStore;
|
|
27
|
+
getServer(): Server;
|
|
28
|
+
onUploadCreate(callback: TransferEventCallback): void;
|
|
29
|
+
onUploadFinish(callback: TransferEventCallback): void;
|
|
30
|
+
onUploadTerminate(callback: TransferEventCallback): void;
|
|
31
|
+
handle(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/transfer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAA;AAE3D,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,GAAG,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IAGZ,KAAK,CAAC,EAAE;QACN,SAAS,EAAE,MAAM,CAAA;KAClB,CAAA;IAGD,EAAE,CAAC,EAAE;QACH,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,EAAE,MAAM,CAAA;QAChB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;QACjB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAA;CACF;AAGD,MAAM,MAAM,qBAAqB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,cAAc,KAAK,IAAI,CAAA;AAEtH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,EAAE,cAAc;IAYlC,OAAO,CAAC,WAAW;IAsCZ,SAAS,IAAI,MAAM;IAMnB,cAAc,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI;IAMrD,cAAc,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI;IAMrD,iBAAiB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI;IAQxD,MAAM,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAGxE"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Server, EVENTS } from '@tus/server';
|
|
2
|
+
import { FileStore } from '@tus/file-store';
|
|
3
|
+
import { S3Store } from '@tus/s3-store';
|
|
4
|
+
export class TransferManager {
|
|
5
|
+
server;
|
|
6
|
+
config;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
const store = this.createStore();
|
|
10
|
+
this.server = new Server({
|
|
11
|
+
path: config.path,
|
|
12
|
+
datastore: store,
|
|
13
|
+
maxSize: config.maxSize,
|
|
14
|
+
respectForwardedHeaders: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
createStore() {
|
|
18
|
+
if (this.config.driver === 'local') {
|
|
19
|
+
if (!this.config.local?.directory) {
|
|
20
|
+
throw new Error('TransferManager: Local driver requires "directory" path');
|
|
21
|
+
}
|
|
22
|
+
return new FileStore({
|
|
23
|
+
directory: this.config.local.directory
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (this.config.driver === 's3') {
|
|
27
|
+
if (!this.config.s3) {
|
|
28
|
+
throw new Error('TransferManager: S3 driver requires s3 config object');
|
|
29
|
+
}
|
|
30
|
+
const { endPoint, port, useSSL, accessKey, secretKey, bucket, region, partSize } = this.config.s3;
|
|
31
|
+
const protocol = useSSL ? 'https:' : 'http:';
|
|
32
|
+
const endpointUrl = port ? `${protocol}//${endPoint}:${port}` : `${protocol}//${endPoint}`;
|
|
33
|
+
return new S3Store({
|
|
34
|
+
partSize: partSize || 8 * 1024 * 1024,
|
|
35
|
+
s3ClientConfig: {
|
|
36
|
+
bucket: bucket,
|
|
37
|
+
region: region || 'us-east-1',
|
|
38
|
+
endpoint: endpointUrl,
|
|
39
|
+
credentials: {
|
|
40
|
+
accessKeyId: accessKey,
|
|
41
|
+
secretAccessKey: secretKey
|
|
42
|
+
},
|
|
43
|
+
forcePathStyle: true
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
throw new Error(`TransferManager: Unsupported driver ${this.config.driver}`);
|
|
48
|
+
}
|
|
49
|
+
getServer() {
|
|
50
|
+
return this.server;
|
|
51
|
+
}
|
|
52
|
+
onUploadCreate(callback) {
|
|
53
|
+
this.server.on(EVENTS.POST_CREATE, (req, res, upload) => {
|
|
54
|
+
callback(upload, req, res);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
onUploadFinish(callback) {
|
|
58
|
+
this.server.on(EVENTS.POST_FINISH, (req, res, upload) => {
|
|
59
|
+
callback(upload, req, res);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
onUploadTerminate(callback) {
|
|
63
|
+
this.server.on(EVENTS.POST_TERMINATE, (req, res, id) => {
|
|
64
|
+
callback(id, req, res);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
handle(req, res) {
|
|
68
|
+
return this.server.handle(req, res);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/transfer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAU,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AA6BvC,MAAM,OAAO,eAAe;IAClB,MAAM,CAAQ;IACd,MAAM,CAAgB;IAE9B,YAAY,MAAsB;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QAEhC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,uBAAuB,EAAE,IAAI;SAC9B,CAAC,CAAA;IACJ,CAAC;IAEO,WAAW;QACjB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;YAC5E,CAAC;YACD,OAAO,IAAI,SAAS,CAAC;gBACnB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS;aACvC,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAA;YACzE,CAAC;YAED,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAA;YAEjG,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAA;YAC5C,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,KAAK,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,QAAQ,KAAK,QAAQ,EAAE,CAAA;YAE1F,OAAO,IAAI,OAAO,CAAC;gBACjB,QAAQ,EAAE,QAAQ,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;gBACrC,cAAc,EAAE;oBACd,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,MAAM,IAAI,WAAW;oBAC7B,QAAQ,EAAE,WAAW;oBACrB,WAAW,EAAE;wBACX,WAAW,EAAE,SAAS;wBACtB,eAAe,EAAE,SAAS;qBAC3B;oBACD,cAAc,EAAE,IAAI;iBACrB;aACF,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,uCAAuC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IAC9E,CAAC;IAEM,SAAS;QACd,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAIM,cAAc,CAAC,QAA+B;QACnD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE;YACtD,QAAQ,CAAC,MAAM,EAAE,GAAiC,EAAE,GAAgC,CAAC,CAAA;QACvF,CAAC,CAAC,CAAA;IACJ,CAAC;IAEM,cAAc,CAAC,QAA+B;QACnD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE;YACtD,QAAQ,CAAC,MAAM,EAAE,GAAiC,EAAE,GAAgC,CAAC,CAAA;QACvF,CAAC,CAAC,CAAA;IACJ,CAAC;IAEM,iBAAiB,CAAC,QAA+B;QAEtD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE;YACrD,QAAQ,CAAC,EAAE,EAAE,GAAiC,EAAE,GAAgC,CAAC,CAAA;QACnF,CAAC,CAAC,CAAA;IACJ,CAAC;IAGM,MAAM,CAAC,GAAoB,EAAE,GAAmB;QACrD,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACrC,CAAC;CACF"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import nodemailer, { Transporter, SendMailOptions } from 'nodemailer'
|
|
2
|
+
|
|
3
|
+
export interface MailerConfig {
|
|
4
|
+
host: string
|
|
5
|
+
port: number
|
|
6
|
+
secure?: boolean // true for 465, false for other ports
|
|
7
|
+
auth?: {
|
|
8
|
+
user: string
|
|
9
|
+
pass: string
|
|
10
|
+
}
|
|
11
|
+
defaultFrom?: string
|
|
12
|
+
defaultReplyTo?: string
|
|
13
|
+
tls?: {
|
|
14
|
+
rejectUnauthorized?: boolean
|
|
15
|
+
ciphers?: string
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MailAttachment {
|
|
20
|
+
filename: string
|
|
21
|
+
content?: string | Buffer
|
|
22
|
+
path?: string
|
|
23
|
+
contentType?: string
|
|
24
|
+
cid?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MailOptions {
|
|
28
|
+
to: string | string[]
|
|
29
|
+
subject: string
|
|
30
|
+
html: string
|
|
31
|
+
text?: string // If not provided, it will be generated from html
|
|
32
|
+
from?: string
|
|
33
|
+
cc?: string | string[]
|
|
34
|
+
bcc?: string | string[]
|
|
35
|
+
replyTo?: string
|
|
36
|
+
attachments?: MailAttachment[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Mailer class wrapper around nodemailer.
|
|
41
|
+
* Designed to be tree-shakeable and configuration-driven without relying on process.env directly.
|
|
42
|
+
*/
|
|
43
|
+
export class Mailer {
|
|
44
|
+
private transporter: Transporter
|
|
45
|
+
private defaultFrom?: string
|
|
46
|
+
private defaultReplyTo?: string
|
|
47
|
+
|
|
48
|
+
constructor(config: MailerConfig) {
|
|
49
|
+
this.defaultFrom = config.defaultFrom
|
|
50
|
+
this.defaultReplyTo = config.defaultReplyTo
|
|
51
|
+
|
|
52
|
+
this.transporter = nodemailer.createTransport({
|
|
53
|
+
host: config.host,
|
|
54
|
+
port: config.port,
|
|
55
|
+
secure: config.secure ?? config.port === 465, // Default secure if port is 465
|
|
56
|
+
auth: config.auth
|
|
57
|
+
? {
|
|
58
|
+
user: config.auth.user,
|
|
59
|
+
pass: config.auth.pass
|
|
60
|
+
}
|
|
61
|
+
: undefined,
|
|
62
|
+
tls: config.tls
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Helper to strip HTML tags for plain text fallback
|
|
68
|
+
*/
|
|
69
|
+
private stripHtml(html: string): string {
|
|
70
|
+
if (!html) return ''
|
|
71
|
+
return html
|
|
72
|
+
.replace(/<br\s*\/?>/gi, '\n') // Replace <br> with newlines
|
|
73
|
+
.replace(/<\/p>/gi, '\n\n') // Replace </p> with double newlines
|
|
74
|
+
.replace(/<[^>]*>?/gm, '') // Remove all other tags
|
|
75
|
+
.trim()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Sends an email.
|
|
80
|
+
* @param options Mail options including to, subject, html, etc.
|
|
81
|
+
* @returns The info object from nodemailer.
|
|
82
|
+
*/
|
|
83
|
+
public async send(options: MailOptions): Promise<any> {
|
|
84
|
+
const from = options.from || this.defaultFrom
|
|
85
|
+
const replyTo = options.replyTo || this.defaultReplyTo
|
|
86
|
+
|
|
87
|
+
if (!from) {
|
|
88
|
+
throw new Error('Email "from" address is required (either in options or config.defaultFrom)')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const mailPayload: SendMailOptions = {
|
|
92
|
+
from,
|
|
93
|
+
to: Array.isArray(options.to) ? options.to.join(', ') : options.to,
|
|
94
|
+
subject: options.subject,
|
|
95
|
+
html: options.html,
|
|
96
|
+
text: options.text || this.stripHtml(options.html),
|
|
97
|
+
attachments: options.attachments
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (options.cc) {
|
|
101
|
+
mailPayload.cc = Array.isArray(options.cc) ? options.cc.join(', ') : options.cc
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (options.bcc) {
|
|
105
|
+
mailPayload.bcc = Array.isArray(options.bcc) ? options.bcc.join(', ') : options.bcc
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (replyTo) {
|
|
109
|
+
mailPayload.replyTo = replyTo
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const info = await this.transporter.sendMail(mailPayload)
|
|
114
|
+
return info
|
|
115
|
+
} catch (error: any) {
|
|
116
|
+
throw new Error(`Failed to send email to ${mailPayload.to}: ${error.message}`)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Verifies the connection configuration.
|
|
122
|
+
*/
|
|
123
|
+
public async verifyConnection(): Promise<boolean> {
|
|
124
|
+
try {
|
|
125
|
+
await this.transporter.verify()
|
|
126
|
+
return true
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error('Mailer connection verification failed:', error)
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Client, ClientOptions, ItemBucketMetadata, BucketItemStat } from 'minio'
|
|
2
|
+
import { Readable } from 'stream'
|
|
3
|
+
|
|
4
|
+
export interface StorageConfig {
|
|
5
|
+
endPoint: string
|
|
6
|
+
port: number
|
|
7
|
+
useSSL: boolean
|
|
8
|
+
accessKey: string
|
|
9
|
+
secretKey: string
|
|
10
|
+
bucket: string
|
|
11
|
+
region?: string
|
|
12
|
+
pathStyle?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface UploadOptions {
|
|
16
|
+
size?: number
|
|
17
|
+
contentType?: string
|
|
18
|
+
metadata?: Record<string, string | number | boolean>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Definizione manuale del tipo di ritorno di putObject
|
|
22
|
+
// Necessario perché @types/minio non esporta 'UploadedObjectInfo' o 'Result' pubblicamente
|
|
23
|
+
export interface UploadedObjectInfo {
|
|
24
|
+
etag: string
|
|
25
|
+
versionId: string | null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class StorageManager {
|
|
29
|
+
private client: Client
|
|
30
|
+
private bucket: string
|
|
31
|
+
private region: string
|
|
32
|
+
|
|
33
|
+
constructor(config: StorageConfig) {
|
|
34
|
+
this.bucket = config.bucket
|
|
35
|
+
this.region = config.region || 'us-east-1'
|
|
36
|
+
|
|
37
|
+
const clientOptions: ClientOptions = {
|
|
38
|
+
endPoint: config.endPoint,
|
|
39
|
+
port: config.port,
|
|
40
|
+
useSSL: config.useSSL,
|
|
41
|
+
accessKey: config.accessKey,
|
|
42
|
+
secretKey: config.secretKey,
|
|
43
|
+
region: this.region,
|
|
44
|
+
pathStyle: config.pathStyle
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.client = new Client(clientOptions)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public getClient(): Client {
|
|
51
|
+
return this.client
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public getBucketName(): string {
|
|
55
|
+
return this.bucket
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public async verifyConnection(): Promise<boolean> {
|
|
59
|
+
try {
|
|
60
|
+
await this.client.listBuckets()
|
|
61
|
+
return true
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('Storage connection verification failed:', error)
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public async ensureBucket(bucketName?: string): Promise<void> {
|
|
69
|
+
const targetBucket = bucketName || this.bucket
|
|
70
|
+
try {
|
|
71
|
+
const exists = await this.client.bucketExists(targetBucket)
|
|
72
|
+
if (!exists) {
|
|
73
|
+
await this.client.makeBucket(targetBucket, this.region)
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
throw new Error(`Failed to ensure bucket ${targetBucket}: ${error}`)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public async uploadFile(
|
|
81
|
+
objectName: string,
|
|
82
|
+
stream: Readable | Buffer | string,
|
|
83
|
+
options: UploadOptions = {}
|
|
84
|
+
): Promise<UploadedObjectInfo> {
|
|
85
|
+
await this.ensureBucket()
|
|
86
|
+
|
|
87
|
+
const metaData = options.metadata || {}
|
|
88
|
+
|
|
89
|
+
// Casting a any/Promise<UploadedObjectInfo> per compatibilità TS
|
|
90
|
+
// putObject ritorna un oggetto compatibile con l'interfaccia definita sopra
|
|
91
|
+
return this.client.putObject(
|
|
92
|
+
this.bucket,
|
|
93
|
+
objectName,
|
|
94
|
+
stream,
|
|
95
|
+
options.size,
|
|
96
|
+
metaData as ItemBucketMetadata
|
|
97
|
+
) as Promise<UploadedObjectInfo>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public async getFileUrl(objectName: string, expiry: number = 24 * 60 * 60): Promise<string> {
|
|
101
|
+
return this.client.presignedGetObject(this.bucket, objectName, expiry)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public async getUploadUrl(objectName: string, expiry: number = 24 * 60 * 60): Promise<string> {
|
|
105
|
+
return this.client.presignedPutObject(this.bucket, objectName, expiry)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public async deleteFile(objectName: string): Promise<void> {
|
|
109
|
+
await this.client.removeObject(this.bucket, objectName)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public async deleteFiles(objectNames: string[]): Promise<void> {
|
|
113
|
+
await this.client.removeObjects(this.bucket, objectNames)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public async fileExists(objectName: string): Promise<boolean> {
|
|
117
|
+
try {
|
|
118
|
+
await this.client.statObject(this.bucket, objectName)
|
|
119
|
+
return true
|
|
120
|
+
} catch (error: any) {
|
|
121
|
+
if (error.code === 'NotFound') {
|
|
122
|
+
return false
|
|
123
|
+
}
|
|
124
|
+
throw error
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public async getFileStream(objectName: string): Promise<Readable> {
|
|
129
|
+
return this.client.getObject(this.bucket, objectName)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public async getFileStat(objectName: string): Promise<BucketItemStat> {
|
|
133
|
+
return this.client.statObject(this.bucket, objectName)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Server, EVENTS, Upload } from '@tus/server'
|
|
2
|
+
import { FileStore } from '@tus/file-store'
|
|
3
|
+
import { S3Store } from '@tus/s3-store'
|
|
4
|
+
import type { IncomingMessage, ServerResponse } from 'http'
|
|
5
|
+
|
|
6
|
+
export interface TransferConfig {
|
|
7
|
+
driver: 'local' | 's3'
|
|
8
|
+
maxSize?: number // Bytes
|
|
9
|
+
path: string // URL Path prefix (e.g. /files)
|
|
10
|
+
|
|
11
|
+
// Local Config
|
|
12
|
+
local?: {
|
|
13
|
+
directory: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// S3/Minio Config
|
|
17
|
+
s3?: {
|
|
18
|
+
bucket: string
|
|
19
|
+
endPoint: string
|
|
20
|
+
port?: number
|
|
21
|
+
useSSL?: boolean
|
|
22
|
+
accessKey: string
|
|
23
|
+
secretKey: string
|
|
24
|
+
region?: string
|
|
25
|
+
partSize?: number // Minimum 5MB for S3
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Updated definition to handle 'string' ID from POST_TERMINATE
|
|
30
|
+
export type TransferEventCallback = (uploadOrId: Upload | string, req?: IncomingMessage, res?: ServerResponse) => void
|
|
31
|
+
|
|
32
|
+
export class TransferManager {
|
|
33
|
+
private server: Server
|
|
34
|
+
private config: TransferConfig
|
|
35
|
+
|
|
36
|
+
constructor(config: TransferConfig) {
|
|
37
|
+
this.config = config
|
|
38
|
+
const store = this.createStore()
|
|
39
|
+
|
|
40
|
+
this.server = new Server({
|
|
41
|
+
path: config.path,
|
|
42
|
+
datastore: store,
|
|
43
|
+
maxSize: config.maxSize,
|
|
44
|
+
respectForwardedHeaders: true
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private createStore() {
|
|
49
|
+
if (this.config.driver === 'local') {
|
|
50
|
+
if (!this.config.local?.directory) {
|
|
51
|
+
throw new Error('TransferManager: Local driver requires "directory" path')
|
|
52
|
+
}
|
|
53
|
+
return new FileStore({
|
|
54
|
+
directory: this.config.local.directory
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (this.config.driver === 's3') {
|
|
59
|
+
if (!this.config.s3) {
|
|
60
|
+
throw new Error('TransferManager: S3 driver requires s3 config object')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { endPoint, port, useSSL, accessKey, secretKey, bucket, region, partSize } = this.config.s3
|
|
64
|
+
|
|
65
|
+
const protocol = useSSL ? 'https:' : 'http:'
|
|
66
|
+
const endpointUrl = port ? `${protocol}//${endPoint}:${port}` : `${protocol}//${endPoint}`
|
|
67
|
+
|
|
68
|
+
return new S3Store({
|
|
69
|
+
partSize: partSize || 8 * 1024 * 1024,
|
|
70
|
+
s3ClientConfig: {
|
|
71
|
+
bucket: bucket, // Required inside s3ClientConfig by @tus/s3-store types
|
|
72
|
+
region: region || 'us-east-1',
|
|
73
|
+
endpoint: endpointUrl,
|
|
74
|
+
credentials: {
|
|
75
|
+
accessKeyId: accessKey,
|
|
76
|
+
secretAccessKey: secretKey
|
|
77
|
+
},
|
|
78
|
+
forcePathStyle: true
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
throw new Error(`TransferManager: Unsupported driver ${this.config.driver}`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public getServer(): Server {
|
|
87
|
+
return this.server
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Hook system wrappers with double casting (as unknown) to bypass TS structural check
|
|
91
|
+
|
|
92
|
+
public onUploadCreate(callback: TransferEventCallback): void {
|
|
93
|
+
this.server.on(EVENTS.POST_CREATE, (req, res, upload) => {
|
|
94
|
+
callback(upload, req as unknown as IncomingMessage, res as unknown as ServerResponse)
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public onUploadFinish(callback: TransferEventCallback): void {
|
|
99
|
+
this.server.on(EVENTS.POST_FINISH, (req, res, upload) => {
|
|
100
|
+
callback(upload, req as unknown as IncomingMessage, res as unknown as ServerResponse)
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public onUploadTerminate(callback: TransferEventCallback): void {
|
|
105
|
+
// Note: POST_TERMINATE passes 'id' (string) instead of 'upload' object in recent tus versions
|
|
106
|
+
this.server.on(EVENTS.POST_TERMINATE, (req, res, id) => {
|
|
107
|
+
callback(id, req as unknown as IncomingMessage, res as unknown as ServerResponse)
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Handle Request helper
|
|
112
|
+
public handle(req: IncomingMessage, res: ServerResponse): Promise<void> {
|
|
113
|
+
return this.server.handle(req, res)
|
|
114
|
+
}
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volcanicminds/tools",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Tools for the volcanic (minds) backend",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"volcanic",
|
|
@@ -10,7 +10,15 @@
|
|
|
10
10
|
"esm",
|
|
11
11
|
"mfa",
|
|
12
12
|
"otp",
|
|
13
|
-
"totp"
|
|
13
|
+
"totp",
|
|
14
|
+
"mailer",
|
|
15
|
+
"nodemailer",
|
|
16
|
+
"storage",
|
|
17
|
+
"minio",
|
|
18
|
+
"s3",
|
|
19
|
+
"tus",
|
|
20
|
+
"upload",
|
|
21
|
+
"transfer"
|
|
14
22
|
],
|
|
15
23
|
"homepage": "https://volcanicminds.com",
|
|
16
24
|
"bugs": {
|
|
@@ -41,10 +49,25 @@
|
|
|
41
49
|
"import": "./dist/lib/mfa/index.js",
|
|
42
50
|
"require": "./dist/lib/mfa/index.js"
|
|
43
51
|
},
|
|
52
|
+
"./mailer": {
|
|
53
|
+
"types": "./dist/lib/mailer/index.d.ts",
|
|
54
|
+
"import": "./dist/lib/mailer/index.js",
|
|
55
|
+
"require": "./dist/lib/mailer/index.js"
|
|
56
|
+
},
|
|
44
57
|
"./logger": {
|
|
45
58
|
"types": "./dist/lib/util/logger.d.ts",
|
|
46
59
|
"import": "./dist/lib/util/logger.js",
|
|
47
60
|
"require": "./dist/lib/util/logger.js"
|
|
61
|
+
},
|
|
62
|
+
"./storage": {
|
|
63
|
+
"types": "./dist/lib/storage/index.d.ts",
|
|
64
|
+
"import": "./dist/lib/storage/index.js",
|
|
65
|
+
"require": "./dist/lib/storage/index.js"
|
|
66
|
+
},
|
|
67
|
+
"./transfer": {
|
|
68
|
+
"types": "./dist/lib/transfer/index.d.ts",
|
|
69
|
+
"import": "./dist/lib/transfer/index.js",
|
|
70
|
+
"require": "./dist/lib/transfer/index.js"
|
|
48
71
|
}
|
|
49
72
|
},
|
|
50
73
|
"main": "dist/index.js",
|
|
@@ -69,18 +92,26 @@
|
|
|
69
92
|
"combine": "node combine.js"
|
|
70
93
|
},
|
|
71
94
|
"dependencies": {
|
|
72
|
-
"
|
|
95
|
+
"@aws-sdk/client-s3": "^3.954.0",
|
|
96
|
+
"@tus/file-store": "^2.0.0",
|
|
97
|
+
"@tus/s3-store": "^2.0.1",
|
|
98
|
+
"@tus/server": "^2.3.0",
|
|
99
|
+
"minio": "^8.0.6",
|
|
100
|
+
"nodemailer": "^7.0.11",
|
|
101
|
+
"otpauth": "^9.4.1",
|
|
73
102
|
"qrcode": "^1.5.4"
|
|
74
103
|
},
|
|
75
104
|
"devDependencies": {
|
|
76
|
-
"@eslint/js": "^9.39.
|
|
77
|
-
"@types/
|
|
78
|
-
"@types/
|
|
79
|
-
"
|
|
105
|
+
"@eslint/js": "^9.39.2",
|
|
106
|
+
"@types/minio": "^7.1.1",
|
|
107
|
+
"@types/node": "^25.0.3",
|
|
108
|
+
"@types/nodemailer": "^7.0.4",
|
|
109
|
+
"@types/qrcode": "^1.5.6",
|
|
110
|
+
"eslint": "^9.39.2",
|
|
80
111
|
"globals": "^16.5.0",
|
|
81
|
-
"tsx": "^4.
|
|
112
|
+
"tsx": "^4.21.0",
|
|
82
113
|
"typescript": "^5.9.3",
|
|
83
|
-
"typescript-eslint": "^8.
|
|
114
|
+
"typescript-eslint": "^8.50.0"
|
|
84
115
|
},
|
|
85
116
|
"engines": {
|
|
86
117
|
"node": ">=24"
|
|
@@ -95,8 +126,17 @@
|
|
|
95
126
|
"mfa": [
|
|
96
127
|
"dist/lib/mfa/index.d.ts"
|
|
97
128
|
],
|
|
129
|
+
"mailer": [
|
|
130
|
+
"dist/lib/mailer/index.d.ts"
|
|
131
|
+
],
|
|
98
132
|
"logger": [
|
|
99
133
|
"dist/lib/util/logger.d.ts"
|
|
134
|
+
],
|
|
135
|
+
"storage": [
|
|
136
|
+
"dist/lib/storage/index.d.ts"
|
|
137
|
+
],
|
|
138
|
+
"transfer": [
|
|
139
|
+
"dist/lib/transfer/index.d.ts"
|
|
100
140
|
]
|
|
101
141
|
}
|
|
102
142
|
}
|