hetzner-storage-sdk 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sajan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,539 @@
1
+ # Hetzner Storage SDK
2
+
3
+ A comprehensive TypeScript SDK for interacting with Hetzner Storage Box and Storage Share (Nextcloud).
4
+
5
+ ## Features
6
+
7
+ - 🔧 **Dual Provider Support**: Works with both Storage Box and Storage Share
8
+ - 📦 **WebDAV Protocol**: Reliable file operations via WebDAV
9
+ - 🔒 **Type-Safe**: Full TypeScript support with complete type definitions
10
+ - 🎯 **Factory Pattern**: Easy provider switching with unified interface
11
+ - 📊 **Management APIs**: Full support for Robot API and Nextcloud OCS API
12
+ - 🚀 **Modern Async/Await**: Promise-based API throughout
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install hetzner-storage-sdk
18
+ # or
19
+ yarn add hetzner-storage-sdk
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### Storage Box (WebDAV)
25
+
26
+ ```typescript
27
+ import { HetznerStorageClient } from 'hetzner-storage-sdk';
28
+
29
+ const client = HetznerStorageClient.create({
30
+ type: 'box',
31
+ username: 'u123456',
32
+ password: 'your-password',
33
+ storageBoxId: '12345',
34
+ robotUsername: 'robot-user', // Optional: for management operations
35
+ robotPassword: 'robot-pass'
36
+ });
37
+
38
+ // List files
39
+ const files = await client.listFiles('/');
40
+
41
+ // Upload a file
42
+ await client.uploadFile('./local-file.txt', '/remote-file.txt');
43
+
44
+ // Download a file
45
+ await client.downloadFile('/remote-file.txt', './downloaded-file.txt');
46
+
47
+ // Create snapshot (requires Robot API credentials)
48
+ const snapshot = await client.createSnapshot('My backup');
49
+ ```
50
+
51
+ ### Storage Share (Nextcloud)
52
+
53
+ ```typescript
54
+ import { HetznerStorageClient } from 'hetzner-storage-sdk';
55
+
56
+ const client = HetznerStorageClient.create({
57
+ type: 'share',
58
+ username: 'your-username',
59
+ password: 'your-password',
60
+ instance: 'u123456' // Your instance ID
61
+ });
62
+
63
+ // List files
64
+ const files = await client.listFiles('/');
65
+
66
+ // Upload a file
67
+ await client.uploadFile('./document.pdf', '/documents/document.pdf');
68
+
69
+ // Create a public share link
70
+ const share = await client.createPublicShare(
71
+ '/documents/document.pdf',
72
+ 'optional-password',
73
+ '2024-12-31', // Expiration date
74
+ 1 // Permissions (1 = read)
75
+ );
76
+
77
+ console.log('Share URL:', share.url);
78
+ ```
79
+
80
+ ## Configuration
81
+
82
+ ### Storage Box Configuration
83
+
84
+ ```typescript
85
+ interface StorageBoxConfig {
86
+ type: 'box';
87
+ username: string; // Storage Box username (e.g., 'u123456')
88
+ password: string; // Storage Box password
89
+ storageBoxId?: string; // Required for management operations
90
+ robotUsername?: string; // Robot API username
91
+ robotPassword?: string; // Robot API password
92
+ }
93
+ ```
94
+
95
+ ### Storage Share Configuration
96
+
97
+ ```typescript
98
+ interface StorageShareConfig {
99
+ type: 'share';
100
+ username: string; // Your Nextcloud username
101
+ password: string; // Your Nextcloud password
102
+ instance: string; // Instance ID (e.g., 'u123456')
103
+ }
104
+ ```
105
+
106
+ ## API Reference
107
+
108
+ ### Common Methods (Both Providers)
109
+
110
+ #### File Operations
111
+
112
+ ```typescript
113
+ // List files in a directory
114
+ listFiles(remotePath?: string): Promise<FileMetadata[]>
115
+
116
+ // Upload a file
117
+ uploadFile(
118
+ localPath: string | Buffer | Readable,
119
+ remotePath: string,
120
+ options?: UploadOptions
121
+ ): Promise<void>
122
+
123
+ // Download a file
124
+ downloadFile(
125
+ remotePath: string,
126
+ localPath?: string,
127
+ options?: DownloadOptions
128
+ ): Promise<Buffer | void>
129
+
130
+ // Delete a file or directory
131
+ deleteFile(remotePath: string): Promise<void>
132
+
133
+ // Create a directory
134
+ createDirectory(remotePath: string): Promise<void>
135
+
136
+ // Move/rename a file
137
+ moveFile(fromPath: string, toPath: string): Promise<void>
138
+
139
+ // Copy a file
140
+ copyFile(fromPath: string, toPath: string): Promise<void>
141
+
142
+ // Check if file exists
143
+ exists(remotePath: string): Promise<boolean>
144
+
145
+ // Get file metadata
146
+ getMetadata(remotePath: string): Promise<FileMetadata>
147
+
148
+ // Close connections
149
+ disconnect(): Promise<void>
150
+ ```
151
+
152
+ ### Storage Box Specific Methods
153
+
154
+ #### Robot API Management
155
+
156
+ ```typescript
157
+ // List all storage boxes
158
+ listStorageBoxes(): Promise<StorageBoxDetails[]>
159
+
160
+ // Get storage box details
161
+ getStorageBoxDetails(): Promise<StorageBoxDetails>
162
+
163
+ // Toggle services (SSH, Samba, WebDAV)
164
+ toggleServices(services: {
165
+ webdav?: boolean;
166
+ samba?: boolean;
167
+ ssh?: boolean;
168
+ }): Promise<StorageBoxDetails>
169
+
170
+ // Snapshot management
171
+ createSnapshot(comment?: string): Promise<SnapshotInfo>
172
+ listSnapshots(): Promise<SnapshotInfo[]>
173
+ deleteSnapshot(snapshotName: string): Promise<void>
174
+ revertToSnapshot(snapshotName: string): Promise<void>
175
+ ```
176
+
177
+ ### Storage Share Specific Methods
178
+
179
+ #### OCS API Share Management
180
+
181
+ ```typescript
182
+ // Create a public link share
183
+ createPublicShare(
184
+ path: string,
185
+ password?: string,
186
+ expireDate?: string,
187
+ permissions?: number
188
+ ): Promise<OCSShareData>
189
+
190
+ // Create a user share
191
+ createUserShare(
192
+ path: string,
193
+ shareWith: string,
194
+ permissions?: number
195
+ ): Promise<OCSShareData>
196
+
197
+ // Create a group share
198
+ createGroupShare(
199
+ path: string,
200
+ shareWith: string,
201
+ permissions?: number
202
+ ): Promise<OCSShareData>
203
+
204
+ // List all shares
205
+ listShares(): Promise<OCSShareData[]>
206
+
207
+ // Get share details
208
+ getShare(shareId: string): Promise<OCSShareData>
209
+
210
+ // Update a share
211
+ updateShare(
212
+ shareId: string,
213
+ options: Partial<CreateShareOptions>
214
+ ): Promise<OCSShareData>
215
+
216
+ // Delete a share
217
+ deleteShare(shareId: string): Promise<void>
218
+
219
+ // Get shares for a specific path
220
+ getSharesForPath(path: string): Promise<OCSShareData[]>
221
+ ```
222
+
223
+ ## Usage Examples
224
+
225
+ ### Example 1: Backup with Snapshot (Storage Box)
226
+
227
+ ```typescript
228
+ import { HetznerStorageClient } from 'hetzner-storage-sdk';
229
+
230
+ async function createBackup() {
231
+ const client = HetznerStorageClient.create({
232
+ type: 'box',
233
+ username: 'u123456',
234
+ password: 'your-password',
235
+ storageBoxId: '12345',
236
+ robotUsername: 'robot-user',
237
+ robotPassword: 'robot-pass'
238
+ });
239
+
240
+ // Upload backup files
241
+ await client.createDirectory('/backups');
242
+ await client.uploadFile('./database.sql', '/backups/database.sql');
243
+
244
+ // Create snapshot
245
+ const snapshot = await client.createSnapshot('Daily backup');
246
+ console.log('Snapshot created:', snapshot.name);
247
+
248
+ // List all snapshots
249
+ const snapshots = await client.listSnapshots();
250
+ console.log('Total snapshots:', snapshots.length);
251
+
252
+ await client.disconnect();
253
+ }
254
+ ```
255
+
256
+ ### Example 2: Share Files with Team (Storage Share)
257
+
258
+ ```typescript
259
+ import { HetznerStorageClient } from 'hetzner-storage-sdk';
260
+
261
+ async function shareWithTeam() {
262
+ const client = HetznerStorageClient.create({
263
+ type: 'share',
264
+ username: 'your-username',
265
+ password: 'your-password',
266
+ instance: 'u123456'
267
+ });
268
+
269
+ // Upload document
270
+ await client.uploadFile('./report.pdf', '/team/report.pdf');
271
+
272
+ // Create public share with password
273
+ const publicShare = await client.createPublicShare(
274
+ '/team/report.pdf',
275
+ 'secure-password',
276
+ '2024-12-31',
277
+ 1 // Read-only
278
+ );
279
+
280
+ console.log('Share link:', publicShare.url);
281
+ console.log('Password:', 'secure-password');
282
+
283
+ // Also share with specific team member
284
+ const userShare = await client.createUserShare(
285
+ '/team/report.pdf',
286
+ 'john.doe',
287
+ 31 // Full permissions
288
+ );
289
+
290
+ console.log('Shared with john.doe');
291
+
292
+ await client.disconnect();
293
+ }
294
+ ```
295
+
296
+ ### Example 3: Sync Local Directory (Storage Box)
297
+
298
+ ```typescript
299
+ import { HetznerStorageClient } from 'hetzner-storage-sdk';
300
+ import { readdir } from 'fs/promises';
301
+ import { join } from 'path';
302
+
303
+ async function syncDirectory(localDir: string, remoteDir: string) {
304
+ const client = HetznerStorageClient.create({
305
+ type: 'box',
306
+ username: 'u123456',
307
+ password: 'your-password'
308
+ });
309
+
310
+ // Create remote directory if it doesn't exist
311
+ if (!(await client.exists(remoteDir))) {
312
+ await client.createDirectory(remoteDir);
313
+ }
314
+
315
+ // Get local files
316
+ const localFiles = await readdir(localDir);
317
+
318
+ // Upload each file
319
+ for (const file of localFiles) {
320
+ const localPath = join(localDir, file);
321
+ const remotePath = `${remoteDir}/${file}`;
322
+
323
+ console.log(`Uploading ${file}...`);
324
+ await client.uploadFile(localPath, remotePath, { overwrite: true });
325
+ }
326
+
327
+ console.log(`Synced ${localFiles.length} files`);
328
+ await client.disconnect();
329
+ }
330
+ ```
331
+
332
+ ### Example 4: Download and Process Files
333
+
334
+ ```typescript
335
+ import { HetznerStorageClient } from 'hetzner-storage-sdk';
336
+
337
+ async function processFiles() {
338
+ const client = HetznerStorageClient.create({
339
+ type: 'box',
340
+ username: 'u123456',
341
+ password: 'your-password'
342
+ });
343
+
344
+ // List all CSV files
345
+ const files = await client.listFiles('/data');
346
+ const csvFiles = files.filter(f => f.filename.endsWith('.csv'));
347
+
348
+ for (const file of csvFiles) {
349
+ // Download as buffer (not to disk)
350
+ const buffer = await client.downloadFile(file.path) as Buffer;
351
+ const content = buffer.toString('utf-8');
352
+
353
+ // Process content
354
+ console.log(`Processing ${file.filename}`);
355
+ const lines = content.split('\n');
356
+ console.log(` - ${lines.length} lines`);
357
+
358
+ // Your processing logic here...
359
+ }
360
+
361
+ await client.disconnect();
362
+ }
363
+ ```
364
+
365
+ ### Example 5: Automated Backup Rotation
366
+
367
+ ```typescript
368
+ import { HetznerStorageClient } from 'hetzner-storage-sdk';
369
+
370
+ async function rotateBackups() {
371
+ const client = HetznerStorageClient.create({
372
+ type: 'box',
373
+ username: 'u123456',
374
+ password: 'your-password',
375
+ storageBoxId: '12345',
376
+ robotUsername: 'robot-user',
377
+ robotPassword: 'robot-pass'
378
+ });
379
+
380
+ const backupDir = '/backups';
381
+ const maxBackups = 7; // Keep last 7 backups
382
+
383
+ // Create new backup
384
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
385
+ const currentBackup = `${backupDir}/${timestamp}`;
386
+
387
+ await client.createDirectory(currentBackup);
388
+ await client.uploadFile('./data.tar.gz', `${currentBackup}/data.tar.gz`);
389
+
390
+ // Create snapshot
391
+ await client.createSnapshot(`Backup ${timestamp}`);
392
+
393
+ // Get all backups
394
+ const backups = await client.listFiles(backupDir);
395
+
396
+ // Delete old backups
397
+ if (backups.length > maxBackups) {
398
+ const sortedBackups = backups.sort((a, b) =>
399
+ a.lastModified.getTime() - b.lastModified.getTime()
400
+ );
401
+
402
+ const toDelete = sortedBackups.slice(0, backups.length - maxBackups);
403
+ for (const backup of toDelete) {
404
+ await client.deleteFile(backup.path);
405
+ console.log(`Deleted old backup: ${backup.filename}`);
406
+ }
407
+ }
408
+
409
+ await client.disconnect();
410
+ }
411
+ ```
412
+
413
+ ## Error Handling
414
+
415
+ ```typescript
416
+ import { HetznerStorageClient, StorageError } from 'hetzner-storage-sdk';
417
+
418
+ async function handleErrors() {
419
+ const client = HetznerStorageClient.create({
420
+ type: 'box',
421
+ username: 'u123456',
422
+ password: 'your-password'
423
+ });
424
+
425
+ try {
426
+ await client.uploadFile('./file.txt', '/remote/file.txt');
427
+ } catch (error) {
428
+ if (error instanceof Error) {
429
+ const storageError = error as StorageError;
430
+ console.error('Error:', storageError.message);
431
+ console.error('Code:', storageError.code);
432
+ console.error('Status:', storageError.statusCode);
433
+ }
434
+ } finally {
435
+ await client.disconnect();
436
+ }
437
+ }
438
+ ```
439
+
440
+ ## Architecture
441
+
442
+ The SDK follows a clean architecture with:
443
+
444
+ - **Factory Pattern**: `HetznerStorageClient` creates the appropriate provider
445
+ - **Provider Pattern**: Abstract `BaseStorageProvider` with concrete implementations
446
+ - **Separation of Concerns**:
447
+ - File operations via WebDAV
448
+ - Management via Robot API (Storage Box) or OCS API (Storage Share)
449
+
450
+ ```
451
+ ┌─────────────────────────────────┐
452
+ │ HetznerStorageClient │
453
+ │ (Factory) │
454
+ └─────────────┬───────────────────┘
455
+
456
+ ┌───────┴────────┐
457
+ │ │
458
+ ┌─────▼─────┐ ┌────▼──────┐
459
+ │ Storage │ │ Storage │
460
+ │ Box │ │ Share │
461
+ │ Provider │ │ Provider │
462
+ └─────┬─────┘ └────┬──────┘
463
+ │ │
464
+ ┌───┴───┐ ┌───┴───┐
465
+ │WebDAV │ │WebDAV │
466
+ │Robot │ │ OCS │
467
+ │ API │ │ API │
468
+ └───────┘ └───────┘
469
+ ```
470
+
471
+ ## Protocol Details
472
+
473
+ ### WebDAV Operations
474
+
475
+ Both Storage Box and Storage Share use WebDAV for file operations:
476
+
477
+ - **PROPFIND**: List directory contents
478
+ - **PUT**: Upload files
479
+ - **GET**: Download files
480
+ - **DELETE**: Remove files/directories
481
+ - **MKCOL**: Create directories
482
+ - **MOVE**: Move/rename files
483
+ - **COPY**: Copy files
484
+
485
+ ### Storage Box URL Format
486
+ ```
487
+ https://<username>.your-storagebox.de/
488
+ ```
489
+
490
+ ### Storage Share URL Format
491
+ ```
492
+ https://<instance>.your-storageshare.de/remote.php/dav/files/<username>/
493
+ ```
494
+
495
+ ## TypeScript Support
496
+
497
+ The SDK is written in TypeScript and provides full type definitions:
498
+
499
+ ```typescript
500
+ import {
501
+ HetznerStorageClient,
502
+ StorageBoxConfig,
503
+ StorageShareConfig,
504
+ FileMetadata,
505
+ StorageBoxDetails,
506
+ SnapshotInfo,
507
+ OCSShareData
508
+ } from 'hetzner-storage-sdk';
509
+
510
+ // Full autocomplete and type checking
511
+ const client = HetznerStorageClient.create({
512
+ type: 'box',
513
+ username: 'u123456',
514
+ password: 'pass'
515
+ });
516
+
517
+ const files: FileMetadata[] = await client.listFiles('/');
518
+ ```
519
+
520
+ ## Dependencies
521
+
522
+ - **axios**: HTTP client for API requests
523
+ - **webdav**: WebDAV client for file operations
524
+
525
+ ## License
526
+
527
+ MIT
528
+
529
+ ## Contributing
530
+
531
+ Contributions are welcome! Please feel free to submit a Pull Request.
532
+
533
+ ## Support
534
+
535
+ For issues or questions:
536
+ - Check the examples in the documentation
537
+ - Review the type definitions for detailed API information
538
+ - Open an issue on the project repository
539
+ - [Contact Me](mailto:work@sajankumarv.com)
@@ -0,0 +1,103 @@
1
+ import { BaseStorageProvider } from './providers/BaseStorageProvider';
2
+ import { StorageBoxProvider } from './providers/StorageBoxProvider';
3
+ import { StorageShareProvider } from './providers/StorageShareProvider';
4
+ import { ObjectStorageProvider } from './providers/ObjectStorageProvider';
5
+ import { StorageConfig, ObjectStorageConfig, StorageBoxConfig, StorageShareConfig } from './types';
6
+ /**
7
+ * Factory class for creating Hetzner Storage clients
8
+ *
9
+ * This class implements the Factory pattern to instantiate the appropriate
10
+ * storage provider based on the configuration.
11
+ *
12
+ * Configuration can be provided explicitly or loaded from environment variables.
13
+ * Explicit configuration takes precedence over environment variables.
14
+ *
15
+ * Environment Variables:
16
+ * - Storage Box:
17
+ * - HETZNER_STORAGE_BOX_USERNAME
18
+ * - HETZNER_STORAGE_BOX_PASSWORD
19
+ * - HETZNER_STORAGE_BOX_PROTOCOL (optional: webdav)
20
+ * - HETZNER_STORAGE_BOX_ID (optional)
21
+ * - HETZNER_ROBOT_USERNAME (optional)
22
+ * - HETZNER_ROBOT_PASSWORD (optional)
23
+ *
24
+ * - Storage Share:
25
+ * - HETZNER_STORAGE_SHARE_USERNAME
26
+ * - HETZNER_STORAGE_SHARE_PASSWORD
27
+ * - HETZNER_STORAGE_SHARE_INSTANCE
28
+ *
29
+ * - Object Storage:
30
+ * - HETZNER_OBJECT_STORAGE_ACCESS_KEY_ID
31
+ * - HETZNER_OBJECT_STORAGE_SECRET_ACCESS_KEY
32
+ * - HETZNER_OBJECT_STORAGE_REGION
33
+ * - HETZNER_OBJECT_STORAGE_BUCKET
34
+ *
35
+ * Usage:
36
+ * ```typescript
37
+ * // Using explicit configuration
38
+ * const client = HetznerStorageClient.create({
39
+ * type: 'box',
40
+ * username: 'u123456',
41
+ * password: 'your-password',
42
+ * });
43
+ *
44
+ * // Using environment variables (set env vars first)
45
+ * const client = HetznerStorageClient.create({ type: 'box' });
46
+ *
47
+ * // Mixed: override specific values while loading others from env
48
+ * const client = HetznerStorageClient.create({
49
+ * type: 'object',
50
+ * bucket: 'my-custom-bucket' // Other fields loaded from env
51
+ * });
52
+ * ```
53
+ */
54
+ export declare class HetznerStorageClient {
55
+ /**
56
+ * Create a new storage client based on the provided configuration
57
+ *
58
+ * Configuration values can be provided explicitly or loaded from environment variables.
59
+ * Explicitly provided values take precedence over environment variables.
60
+ *
61
+ * @param config - Storage configuration object (partial config allowed if env vars are set)
62
+ * @returns An instance of the appropriate storage provider
63
+ * @throws Error if the storage type is invalid or required configuration is missing
64
+ */
65
+ static create(config: Partial<StorageConfig> & {
66
+ type: StorageConfig['type'];
67
+ }): BaseStorageProvider;
68
+ /**
69
+ * Create a Storage Box client
70
+ * Convenience method for creating a Storage Box provider
71
+ *
72
+ * Configuration values can be provided explicitly or loaded from environment variables.
73
+ *
74
+ * @param config - Storage Box configuration (optional if env vars are set)
75
+ * @returns StorageBoxProvider instance
76
+ */
77
+ static createBoxClient(config?: Partial<Omit<StorageBoxConfig, 'type'>>): StorageBoxProvider;
78
+ /**
79
+ * Create a Storage Share client
80
+ * Convenience method for creating a Storage Share provider
81
+ *
82
+ * Configuration values can be provided explicitly or loaded from environment variables.
83
+ *
84
+ * @param config - Storage Share configuration (optional if env vars are set)
85
+ * @returns StorageShareProvider instance
86
+ */
87
+ static createShareClient(config?: Partial<Omit<StorageShareConfig, 'type'>>): StorageShareProvider;
88
+ /**
89
+ * Create an Object Storage client
90
+ * Convenience method for creating an Object Storage provider
91
+ *
92
+ * Configuration values can be provided explicitly or loaded from environment variables.
93
+ *
94
+ * @param config - Object Storage configuration (optional if env vars are set)
95
+ * @returns ObjectStorageProvider instance
96
+ */
97
+ static createObjectClient(config?: Partial<Omit<ObjectStorageConfig, 'type'>>): ObjectStorageProvider;
98
+ }
99
+ /**
100
+ * Re-export for convenience
101
+ */
102
+ export { BaseStorageProvider, StorageBoxProvider, StorageShareProvider, ObjectStorageProvider };
103
+ export * from './types';