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 +21 -0
- package/README.md +539 -0
- package/dist/HetznerStorageClient.d.ts +103 -0
- package/dist/HetznerStorageClient.js +202 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +56 -0
- package/dist/providers/BaseStorageProvider.d.ts +73 -0
- package/dist/providers/BaseStorageProvider.js +13 -0
- package/dist/providers/ObjectStorageProvider.d.ts +114 -0
- package/dist/providers/ObjectStorageProvider.js +285 -0
- package/dist/providers/StorageBoxProvider.d.ts +96 -0
- package/dist/providers/StorageBoxProvider.js +312 -0
- package/dist/providers/StorageShareProvider.d.ts +112 -0
- package/dist/providers/StorageShareProvider.js +253 -0
- package/dist/types/index.d.ts +247 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/OcsApiClient.d.ts +41 -0
- package/dist/utils/OcsApiClient.js +144 -0
- package/dist/utils/RobotApiClient.d.ts +48 -0
- package/dist/utils/RobotApiClient.js +98 -0
- package/dist/utils/S3ApiClient.d.ts +28 -0
- package/dist/utils/S3ApiClient.js +326 -0
- package/dist/utils/env-config.d.ts +45 -0
- package/dist/utils/env-config.js +112 -0
- package/package.json +47 -0
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';
|