@volcanicminds/tools 0.0.4 → 0.0.6

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/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * as mfa from './lib/mfa/index.js';
2
2
  export * as mailer from './lib/mailer/index.js';
3
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';
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -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,MAAM,MAAM,uBAAuB,CAAA;AAC/C,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,4 +1,6 @@
1
1
  export * as mfa from './lib/mfa/index.js';
2
2
  export * as mailer from './lib/mailer/index.js';
3
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';
4
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,MAAM,MAAM,uBAAuB,CAAA;AAC/C,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,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,36 @@
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 type TransferValidator = (req: IncomingMessage, res: ServerResponse) => Promise<void>;
23
+ export declare class TransferManager {
24
+ private server;
25
+ private config;
26
+ private validator;
27
+ constructor(config: TransferConfig);
28
+ setValidator(validator: TransferValidator): void;
29
+ private createStore;
30
+ getServer(): Server;
31
+ onUploadCreate(callback: TransferEventCallback): void;
32
+ onUploadFinish(callback: TransferEventCallback): void;
33
+ onUploadTerminate(callback: TransferEventCallback): void;
34
+ handle(req: IncomingMessage, res: ServerResponse): Promise<void>;
35
+ }
36
+ //# 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;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,cAAc,KAAK,IAAI,CAAA;AACtH,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE5F,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,SAAS,CAAiC;gBAEtC,MAAM,EAAE,cAAc;IAiB3B,YAAY,CAAC,SAAS,EAAE,iBAAiB;IAIhD,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,80 @@
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
+ validator = null;
8
+ constructor(config) {
9
+ this.config = config;
10
+ const store = this.createStore();
11
+ this.server = new Server({
12
+ path: config.path,
13
+ datastore: store,
14
+ maxSize: config.maxSize,
15
+ respectForwardedHeaders: true,
16
+ onIncomingRequest: async (req, res) => {
17
+ if (this.validator) {
18
+ await this.validator(req, res);
19
+ }
20
+ }
21
+ });
22
+ }
23
+ setValidator(validator) {
24
+ this.validator = validator;
25
+ }
26
+ createStore() {
27
+ if (this.config.driver === 'local') {
28
+ if (!this.config.local?.directory) {
29
+ throw new Error('TransferManager: Local driver requires "directory" path');
30
+ }
31
+ return new FileStore({
32
+ directory: this.config.local.directory
33
+ });
34
+ }
35
+ if (this.config.driver === 's3') {
36
+ if (!this.config.s3) {
37
+ throw new Error('TransferManager: S3 driver requires s3 config object');
38
+ }
39
+ const { endPoint, port, useSSL, accessKey, secretKey, bucket, region, partSize } = this.config.s3;
40
+ const protocol = useSSL ? 'https:' : 'http:';
41
+ const endpointUrl = port ? `${protocol}//${endPoint}:${port}` : `${protocol}//${endPoint}`;
42
+ return new S3Store({
43
+ partSize: partSize || 8 * 1024 * 1024,
44
+ s3ClientConfig: {
45
+ bucket: bucket,
46
+ region: region || 'us-east-1',
47
+ endpoint: endpointUrl,
48
+ credentials: {
49
+ accessKeyId: accessKey,
50
+ secretAccessKey: secretKey
51
+ },
52
+ forcePathStyle: true
53
+ }
54
+ });
55
+ }
56
+ throw new Error(`TransferManager: Unsupported driver ${this.config.driver}`);
57
+ }
58
+ getServer() {
59
+ return this.server;
60
+ }
61
+ onUploadCreate(callback) {
62
+ this.server.on(EVENTS.POST_CREATE, (req, res, upload) => {
63
+ callback(upload, req, res);
64
+ });
65
+ }
66
+ onUploadFinish(callback) {
67
+ this.server.on(EVENTS.POST_FINISH, (req, res, upload) => {
68
+ callback(upload, req, res);
69
+ });
70
+ }
71
+ onUploadTerminate(callback) {
72
+ this.server.on(EVENTS.POST_TERMINATE, (req, res, id) => {
73
+ callback(id, req, res);
74
+ });
75
+ }
76
+ handle(req, res) {
77
+ return this.server.handle(req, res);
78
+ }
79
+ }
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/transfer/index.ts"],"names":[],"mappings":"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"}
@@ -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,125 @@
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
+ export type TransferEventCallback = (uploadOrId: Upload | string, req?: IncomingMessage, res?: ServerResponse) => void
30
+ export type TransferValidator = (req: IncomingMessage, res: ServerResponse) => Promise<void>
31
+
32
+ export class TransferManager {
33
+ private server: Server
34
+ private config: TransferConfig
35
+ private validator: TransferValidator | null = null
36
+
37
+ constructor(config: TransferConfig) {
38
+ this.config = config
39
+ const store = this.createStore()
40
+
41
+ this.server = new Server({
42
+ path: config.path,
43
+ datastore: store,
44
+ maxSize: config.maxSize,
45
+ respectForwardedHeaders: true,
46
+ onIncomingRequest: async (req, res) => {
47
+ if (this.validator) {
48
+ await this.validator(req as unknown as IncomingMessage, res as unknown as ServerResponse)
49
+ }
50
+ }
51
+ })
52
+ }
53
+
54
+ public setValidator(validator: TransferValidator) {
55
+ this.validator = validator
56
+ }
57
+
58
+ private createStore() {
59
+ if (this.config.driver === 'local') {
60
+ if (!this.config.local?.directory) {
61
+ throw new Error('TransferManager: Local driver requires "directory" path')
62
+ }
63
+ return new FileStore({
64
+ directory: this.config.local.directory
65
+ })
66
+ }
67
+
68
+ if (this.config.driver === 's3') {
69
+ if (!this.config.s3) {
70
+ throw new Error('TransferManager: S3 driver requires s3 config object')
71
+ }
72
+
73
+ const { endPoint, port, useSSL, accessKey, secretKey, bucket, region, partSize } = this.config.s3
74
+
75
+ const protocol = useSSL ? 'https:' : 'http:'
76
+ const endpointUrl = port ? `${protocol}//${endPoint}:${port}` : `${protocol}//${endPoint}`
77
+
78
+ return new S3Store({
79
+ partSize: partSize || 8 * 1024 * 1024,
80
+ s3ClientConfig: {
81
+ bucket: bucket, // Required inside s3ClientConfig by @tus/s3-store types
82
+ region: region || 'us-east-1',
83
+ endpoint: endpointUrl,
84
+ credentials: {
85
+ accessKeyId: accessKey,
86
+ secretAccessKey: secretKey
87
+ },
88
+ forcePathStyle: true
89
+ }
90
+ })
91
+ }
92
+
93
+ throw new Error(`TransferManager: Unsupported driver ${this.config.driver}`)
94
+ }
95
+
96
+ public getServer(): Server {
97
+ return this.server
98
+ }
99
+
100
+ // Hook system wrappers with double casting (as unknown) to bypass TS structural check
101
+
102
+ public onUploadCreate(callback: TransferEventCallback): void {
103
+ this.server.on(EVENTS.POST_CREATE, (req, res, upload) => {
104
+ callback(upload, req as unknown as IncomingMessage, res as unknown as ServerResponse)
105
+ })
106
+ }
107
+
108
+ public onUploadFinish(callback: TransferEventCallback): void {
109
+ this.server.on(EVENTS.POST_FINISH, (req, res, upload) => {
110
+ callback(upload, req as unknown as IncomingMessage, res as unknown as ServerResponse)
111
+ })
112
+ }
113
+
114
+ public onUploadTerminate(callback: TransferEventCallback): void {
115
+ // Note: POST_TERMINATE passes 'id' (string) instead of 'upload' object in recent tus versions
116
+ this.server.on(EVENTS.POST_TERMINATE, (req, res, id) => {
117
+ callback(id, req as unknown as IncomingMessage, res as unknown as ServerResponse)
118
+ })
119
+ }
120
+
121
+ // Handle Request helper
122
+ public handle(req: IncomingMessage, res: ServerResponse): Promise<void> {
123
+ return this.server.handle(req, res)
124
+ }
125
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volcanicminds/tools",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Tools for the volcanic (minds) backend",
5
5
  "keywords": [
6
6
  "volcanic",
@@ -12,7 +12,13 @@
12
12
  "otp",
13
13
  "totp",
14
14
  "mailer",
15
- "nodemailer"
15
+ "nodemailer",
16
+ "storage",
17
+ "minio",
18
+ "s3",
19
+ "tus",
20
+ "upload",
21
+ "transfer"
16
22
  ],
17
23
  "homepage": "https://volcanicminds.com",
18
24
  "bugs": {
@@ -52,6 +58,16 @@
52
58
  "types": "./dist/lib/util/logger.d.ts",
53
59
  "import": "./dist/lib/util/logger.js",
54
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"
55
71
  }
56
72
  },
57
73
  "main": "dist/index.js",
@@ -76,20 +92,26 @@
76
92
  "combine": "node combine.js"
77
93
  },
78
94
  "dependencies": {
79
- "nodemailer": "^6.10.1",
80
- "otpauth": "^9.3.5",
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",
81
102
  "qrcode": "^1.5.4"
82
103
  },
83
104
  "devDependencies": {
84
- "@eslint/js": "^9.39.1",
85
- "@types/node": "^24.10.1",
86
- "@types/nodemailer": "^6.4.14",
87
- "@types/qrcode": "^1.5.5",
88
- "eslint": "^9.39.1",
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",
89
111
  "globals": "^16.5.0",
90
- "tsx": "^4.19.2",
112
+ "tsx": "^4.21.0",
91
113
  "typescript": "^5.9.3",
92
- "typescript-eslint": "^8.48.0"
114
+ "typescript-eslint": "^8.50.0"
93
115
  },
94
116
  "engines": {
95
117
  "node": ">=24"
@@ -109,6 +131,12 @@
109
131
  ],
110
132
  "logger": [
111
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"
112
140
  ]
113
141
  }
114
142
  }