shogun-relay-sdk 1.2.11 → 1.4.0

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 ADDED
@@ -0,0 +1,408 @@
1
+ # Shogun Relay SDK
2
+
3
+ TypeScript/JavaScript SDK for interacting with Shogun Relay API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @shogun/relay-sdk
9
+ # or
10
+ yarn add @shogun/relay-sdk
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import ShogunRelaySDK from '@shogun/relay-sdk';
17
+ import { generateWalletSignature } from '@shogun/relay-sdk/utils/wallet';
18
+ import { ethers } from 'ethers';
19
+
20
+ // Initialize SDK
21
+ const sdk = new ShogunRelaySDK({
22
+ baseURL: 'https://shogun-relay.scobrudot.dev',
23
+ token: 'your-admin-token' // Optional: for admin operations
24
+ });
25
+
26
+ // Or set token later
27
+ sdk.setToken('your-admin-token');
28
+ ```
29
+
30
+ ## Authentication
31
+
32
+ ### Admin Authentication
33
+
34
+ For admin operations, use the admin token:
35
+
36
+ ```typescript
37
+ const sdk = new ShogunRelaySDK({
38
+ baseURL: 'https://shogun-relay.scobrudot.dev',
39
+ token: 'your-admin-token'
40
+ });
41
+
42
+ // Admin upload (no signature required)
43
+ const result = await sdk.ipfs.uploadFile(
44
+ fileBuffer,
45
+ 'example.txt',
46
+ 'text/plain'
47
+ );
48
+ ```
49
+
50
+ ### Wallet Signature Authentication
51
+
52
+ For user operations, you need to sign a message with your wallet:
53
+
54
+ ```typescript
55
+ import { generateWalletSignature } from '@shogun/relay-sdk/utils/wallet';
56
+ import { ethers } from 'ethers';
57
+
58
+ // Connect to wallet
59
+ const provider = new ethers.BrowserProvider(window.ethereum);
60
+ const signer = await provider.getSigner();
61
+ const address = await signer.getAddress();
62
+
63
+ // Generate signature
64
+ const signature = await generateWalletSignature(signer);
65
+
66
+ // Upload with wallet signature
67
+ const result = await sdk.ipfs.uploadFile(
68
+ fileBuffer,
69
+ 'example.txt',
70
+ 'text/plain',
71
+ {
72
+ userAddress: address,
73
+ walletSignature: signature,
74
+ isDealUpload: true // For deal uploads (no subscription required)
75
+ }
76
+ );
77
+ ```
78
+
79
+ ## API Keys
80
+
81
+ Manage API keys for programmatic access to all relay services:
82
+
83
+ ```typescript
84
+ // List all API keys
85
+ const keys = await sdk.apiKeys.list();
86
+
87
+ // Create a new API key
88
+ const newKey = await sdk.apiKeys.create('My App Key', 30); // 30 days expiration
89
+ console.log('API Key:', newKey.token); // Save this, it's only shown once!
90
+
91
+ // Use the API key for authentication
92
+ sdk.apiKeys.useApiKey(newKey.token);
93
+
94
+ // Or use it directly
95
+ sdk.setToken(newKey.token);
96
+
97
+ // Revoke an API key
98
+ await sdk.apiKeys.revoke(newKey.keyId);
99
+ ```
100
+
101
+ **Note**: API keys work across all relay services (Drive, IPFS, etc.) and use the prefix `shogun-api-`.
102
+
103
+ ## Drive Operations
104
+
105
+ The Drive module provides file system operations for the admin drive:
106
+
107
+ ### List Files
108
+
109
+ ```typescript
110
+ // List root directory
111
+ const files = await sdk.drive.list();
112
+
113
+ // List specific directory
114
+ const files = await sdk.drive.list('folder/subfolder');
115
+ ```
116
+
117
+ ### Upload Files
118
+
119
+ ```typescript
120
+ // Upload single file
121
+ const result = await sdk.drive.uploadFile(
122
+ fileBuffer,
123
+ 'example.txt',
124
+ 'folder' // optional path
125
+ );
126
+
127
+ // Upload multiple files
128
+ const result = await sdk.drive.uploadFiles([
129
+ { file: fileBuffer1, filename: 'file1.txt' },
130
+ { file: fileBuffer2, filename: 'file2.txt' }
131
+ ], 'folder');
132
+ ```
133
+
134
+ ### Download Files
135
+
136
+ ```typescript
137
+ const fileBuffer = await sdk.drive.download('path/to/file.txt');
138
+ ```
139
+
140
+ ### Directory Operations
141
+
142
+ ```typescript
143
+ // Create directory
144
+ await sdk.drive.createDirectory('new-folder', 'parent-folder');
145
+
146
+ // Rename file/directory
147
+ await sdk.drive.rename('old-name.txt', 'new-name.txt');
148
+
149
+ // Move file/directory
150
+ await sdk.drive.move('source.txt', 'destination/folder/source.txt');
151
+
152
+ // Delete file/directory
153
+ await sdk.drive.delete('path/to/item');
154
+ ```
155
+
156
+ ### Storage Statistics
157
+
158
+ ```typescript
159
+ const stats = await sdk.drive.getStats();
160
+ console.log(`Total: ${stats.stats.totalSizeMB} MB`);
161
+ console.log(`Files: ${stats.stats.fileCount}`);
162
+ ```
163
+
164
+ ### Public Links
165
+
166
+ ```typescript
167
+ // Create a public sharing link
168
+ const link = await sdk.drive.createPublicLink('document.pdf', 7); // 7 days expiration
169
+ console.log('Public URL:', link.publicUrl);
170
+
171
+ // List all public links
172
+ const links = await sdk.drive.listPublicLinks();
173
+
174
+ // Revoke a link
175
+ await sdk.drive.revokePublicLink(link.linkId);
176
+
177
+ // Get public file URL (for direct access)
178
+ const publicUrl = sdk.drive.getPublicFileUrl(link.linkId, 'https://shogun-relay.scobrudot.dev');
179
+ ```
180
+
181
+ ## IPFS Operations
182
+
183
+ ### Upload Single File
184
+
185
+ ```typescript
186
+ // Admin upload
187
+ const result = await sdk.ipfs.uploadFile(
188
+ fileBuffer,
189
+ 'example.txt',
190
+ 'text/plain'
191
+ );
192
+
193
+ // User upload with subscription
194
+ const result = await sdk.ipfs.uploadFile(
195
+ fileBuffer,
196
+ 'example.txt',
197
+ 'text/plain',
198
+ {
199
+ userAddress: address,
200
+ walletSignature: signature
201
+ }
202
+ );
203
+
204
+ // Deal upload (no subscription required)
205
+ const result = await sdk.ipfs.uploadFile(
206
+ fileBuffer,
207
+ 'example.txt',
208
+ 'text/plain',
209
+ {
210
+ userAddress: address,
211
+ walletSignature: signature,
212
+ isDealUpload: true
213
+ }
214
+ );
215
+
216
+ // Encrypted upload
217
+ const encryptedResult = await sdk.ipfs.uploadFile(
218
+ fileBuffer,
219
+ 'example.txt',
220
+ 'text/plain',
221
+ {
222
+ userAddress: address,
223
+ walletSignature: signature,
224
+ encrypted: true,
225
+ encryptionToken: signature // Use same signature for encryption
226
+ }
227
+ );
228
+ ```
229
+
230
+ ### Upload Directory
231
+
232
+ ```typescript
233
+ const files = [
234
+ {
235
+ buffer: indexHtmlBuffer,
236
+ filename: 'index.html',
237
+ path: 'index.html',
238
+ contentType: 'text/html'
239
+ },
240
+ {
241
+ buffer: styleCssBuffer,
242
+ filename: 'style.css',
243
+ path: 'css/style.css',
244
+ contentType: 'text/css'
245
+ }
246
+ ];
247
+
248
+ const result = await sdk.ipfs.uploadDirectory(files, {
249
+ userAddress: address,
250
+ walletSignature: signature,
251
+ isDealUpload: true
252
+ });
253
+ ```
254
+
255
+ ### Browser Upload
256
+
257
+ ```typescript
258
+ // Single file
259
+ const fileInput = document.querySelector('input[type="file"]');
260
+ const file = fileInput.files[0];
261
+
262
+ const result = await sdk.ipfs.uploadFileBrowser(file, {
263
+ userAddress: address,
264
+ walletSignature: signature,
265
+ isDealUpload: true
266
+ });
267
+
268
+ // Directory upload
269
+ const directoryInput = document.querySelector('input[type="file"][webkitdirectory]');
270
+ const files = Array.from(directoryInput.files);
271
+
272
+ const result = await sdk.ipfs.uploadDirectoryBrowser(files, {
273
+ userAddress: address,
274
+ walletSignature: signature
275
+ });
276
+ ```
277
+
278
+ ### Retrieve File
279
+
280
+ ```typescript
281
+ // Get file content
282
+ const content = await sdk.ipfs.cat('QmHash...');
283
+
284
+ // Get encrypted file (decrypted)
285
+ const decryptedContent = await sdk.ipfs.catDecrypt(
286
+ 'QmHash...',
287
+ signature, // Encryption token
288
+ address // Optional: for signature verification
289
+ );
290
+ ```
291
+
292
+ ## Storage Deals
293
+
294
+ ### Upload for Deal
295
+
296
+ ```typescript
297
+ const signature = await generateWalletSignature(signer);
298
+
299
+ const result = await sdk.deals.uploadForDeal(
300
+ fileBuffer,
301
+ 'example.txt',
302
+ 'text/plain',
303
+ address,
304
+ signature // Required: wallet signature
305
+ );
306
+ ```
307
+
308
+ ### Create Deal
309
+
310
+ ```typescript
311
+ const deal = await sdk.deals.createDeal({
312
+ cid: 'QmHash...',
313
+ clientAddress: address,
314
+ sizeMB: 10,
315
+ durationDays: 30,
316
+ tier: 'standard'
317
+ });
318
+ ```
319
+
320
+ ## Wallet Utilities
321
+
322
+ The SDK includes utility functions for wallet operations:
323
+
324
+ ```typescript
325
+ import {
326
+ generateWalletSignature,
327
+ verifyWalletSignature,
328
+ getAddressFromSignature,
329
+ WALLET_AUTH_MESSAGE
330
+ } from '@shogun/relay-sdk/utils/wallet';
331
+
332
+ // Generate signature
333
+ const signature = await generateWalletSignature(signer);
334
+
335
+ // Verify signature
336
+ const isValid = await verifyWalletSignature(address, signature);
337
+
338
+ // Get address from signature
339
+ const recoveredAddress = await getAddressFromSignature(signature);
340
+
341
+ // The message that must be signed
342
+ console.log(WALLET_AUTH_MESSAGE); // "I Love Shogun"
343
+ ```
344
+
345
+ ## Complete Example
346
+
347
+ ```typescript
348
+ import ShogunRelaySDK from '@shogun/relay-sdk';
349
+ import { generateWalletSignature } from '@shogun/relay-sdk/utils/wallet';
350
+ import { ethers } from 'ethers';
351
+ import fs from 'fs';
352
+
353
+ async function uploadFileExample() {
354
+ // Initialize SDK
355
+ const sdk = new ShogunRelaySDK({
356
+ baseURL: 'https://shogun-relay.scobrudot.dev'
357
+ });
358
+
359
+ // Connect wallet
360
+ const provider = new ethers.BrowserProvider(window.ethereum);
361
+ const signer = await provider.getSigner();
362
+ const address = await signer.getAddress();
363
+
364
+ // Generate signature for authentication
365
+ const signature = await generateWalletSignature(signer);
366
+
367
+ // Read file
368
+ const fileBuffer = fs.readFileSync('example.txt');
369
+
370
+ // Upload as deal (no subscription required)
371
+ const result = await sdk.ipfs.uploadFile(
372
+ fileBuffer,
373
+ 'example.txt',
374
+ 'text/plain',
375
+ {
376
+ userAddress: address,
377
+ walletSignature: signature,
378
+ isDealUpload: true
379
+ }
380
+ );
381
+
382
+ console.log('Uploaded! CID:', result.cid);
383
+ return result;
384
+ }
385
+ ```
386
+
387
+ ## API Reference
388
+
389
+ See the main [API Documentation](../docs/API.md) for complete endpoint reference.
390
+
391
+ ## Modules
392
+
393
+ - **System**: Health checks, stats, system information
394
+ - **IPFS**: File uploads, directory uploads, content retrieval, pinning
395
+ - **Drive**: Admin drive file system operations, public link sharing
396
+ - **API Keys**: API key management for programmatic access
397
+ - **Deals**: Storage deal creation, activation, management
398
+ - **Registry**: On-chain relay registry operations
399
+ - **Network**: Network federation, reputation, relay discovery
400
+ - **X402**: Subscription management, storage limits
401
+ - **Bridge**: L2 bridge operations, deposits, withdrawals
402
+ - **Uploads**: User upload metadata management
403
+ - **AnnasArchive**: Torrent and archive management
404
+
405
+ ## License
406
+
407
+ MIT
408
+
package/dist/index.d.ts CHANGED
@@ -8,8 +8,13 @@ import { RegistryModule } from "./modules/registry";
8
8
  import { UploadsModule } from "./modules/uploads";
9
9
  import { BridgeModule } from "./modules/bridge";
10
10
  import { AnnasArchiveModule } from "./modules/annas-archive";
11
+ import { DriveModule } from "./modules/drive";
12
+ import { ApiKeysModule } from "./modules/api-keys";
11
13
  export * from "./types";
12
14
  export * from "./modules/annas-archive";
15
+ export * from "./modules/drive";
16
+ export * from "./modules/api-keys";
17
+ export * from "./utils/wallet";
13
18
  export declare class ShogunRelaySDK {
14
19
  private client;
15
20
  system: SystemModule;
@@ -21,6 +26,8 @@ export declare class ShogunRelaySDK {
21
26
  uploads: UploadsModule;
22
27
  bridge: BridgeModule;
23
28
  annasArchive: AnnasArchiveModule;
29
+ drive: DriveModule;
30
+ apiKeys: ApiKeysModule;
24
31
  constructor(config: ApiClientConfig);
25
32
  setToken(token: string): void;
26
33
  }
package/dist/index.js CHANGED
@@ -25,9 +25,15 @@ const registry_1 = require("./modules/registry");
25
25
  const uploads_1 = require("./modules/uploads");
26
26
  const bridge_1 = require("./modules/bridge");
27
27
  const annas_archive_1 = require("./modules/annas-archive");
28
+ const drive_1 = require("./modules/drive");
29
+ const api_keys_1 = require("./modules/api-keys");
28
30
  // Export types
29
31
  __exportStar(require("./types"), exports);
30
32
  __exportStar(require("./modules/annas-archive"), exports);
33
+ __exportStar(require("./modules/drive"), exports);
34
+ __exportStar(require("./modules/api-keys"), exports);
35
+ // Export wallet utilities
36
+ __exportStar(require("./utils/wallet"), exports);
31
37
  class ShogunRelaySDK {
32
38
  constructor(config) {
33
39
  this.client = new client_1.ApiClient(config);
@@ -40,6 +46,8 @@ class ShogunRelaySDK {
40
46
  this.uploads = new uploads_1.UploadsModule(this.client);
41
47
  this.bridge = new bridge_1.BridgeModule(this.client);
42
48
  this.annasArchive = new annas_archive_1.AnnasArchiveModule(this.client);
49
+ this.drive = new drive_1.DriveModule(this.client);
50
+ this.apiKeys = new api_keys_1.ApiKeysModule(this.client);
43
51
  }
44
52
  setToken(token) {
45
53
  this.client.setToken(token);
@@ -0,0 +1,44 @@
1
+ import { ApiClient } from "../client";
2
+ export interface ApiKey {
3
+ keyId: string;
4
+ name: string;
5
+ createdAt: number;
6
+ lastUsedAt: number | null;
7
+ expiresAt: number | null;
8
+ }
9
+ export interface ApiKeyCreateResponse {
10
+ success: boolean;
11
+ keyId: string;
12
+ token: string;
13
+ name: string;
14
+ createdAt: number;
15
+ expiresAt: number | null;
16
+ message: string;
17
+ }
18
+ export interface ApiKeysListResponse {
19
+ success: boolean;
20
+ keys: ApiKey[];
21
+ }
22
+ export declare class ApiKeysModule {
23
+ private client;
24
+ constructor(client: ApiClient);
25
+ /**
26
+ * List all API keys
27
+ */
28
+ list(): Promise<ApiKeysListResponse>;
29
+ /**
30
+ * Create a new API key
31
+ */
32
+ create(name: string, expiresInDays?: number): Promise<ApiKeyCreateResponse>;
33
+ /**
34
+ * Revoke an API key
35
+ */
36
+ revoke(keyId: string): Promise<{
37
+ success: boolean;
38
+ message: string;
39
+ }>;
40
+ /**
41
+ * Set the API key as the authentication token for future requests
42
+ */
43
+ useApiKey(apiKeyToken: string): void;
44
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiKeysModule = void 0;
4
+ class ApiKeysModule {
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+ /**
9
+ * List all API keys
10
+ */
11
+ async list() {
12
+ return this.client.get("/api/v1/api-keys");
13
+ }
14
+ /**
15
+ * Create a new API key
16
+ */
17
+ async create(name, expiresInDays) {
18
+ return this.client.post("/api/v1/api-keys", {
19
+ name,
20
+ expiresInDays,
21
+ });
22
+ }
23
+ /**
24
+ * Revoke an API key
25
+ */
26
+ async revoke(keyId) {
27
+ return this.client.delete(`/api/v1/api-keys/${keyId}`);
28
+ }
29
+ /**
30
+ * Set the API key as the authentication token for future requests
31
+ */
32
+ useApiKey(apiKeyToken) {
33
+ this.client.setToken(apiKeyToken);
34
+ }
35
+ }
36
+ exports.ApiKeysModule = ApiKeysModule;
@@ -3,7 +3,7 @@ export declare class DealsModule {
3
3
  private client;
4
4
  constructor(client: ApiClient);
5
5
  getPricing(sizeMB?: number, durationDays?: number, tier?: string): Promise<any>;
6
- uploadForDeal(fileBuffer: Buffer, filename: string, contentType: string, walletAddress: string): Promise<any>;
6
+ uploadForDeal(fileBuffer: Buffer, filename: string, contentType: string, walletAddress: string, walletSignature: string): Promise<any>;
7
7
  createDeal(dealParams: {
8
8
  cid: string;
9
9
  clientAddress: string;
@@ -19,7 +19,7 @@ class DealsModule {
19
19
  params.tier = tier;
20
20
  return this.client.get("/api/v1/deals/pricing", { params });
21
21
  }
22
- async uploadForDeal(fileBuffer, filename, contentType, walletAddress) {
22
+ async uploadForDeal(fileBuffer, filename, contentType, walletAddress, walletSignature) {
23
23
  const form = new form_data_1.default();
24
24
  form.append("file", fileBuffer, {
25
25
  filename: filename,
@@ -28,7 +28,9 @@ class DealsModule {
28
28
  return this.client.post("/api/v1/deals/upload", form, {
29
29
  headers: {
30
30
  ...form.getHeaders(),
31
- "x-wallet-address": walletAddress,
31
+ "x-user-address": walletAddress,
32
+ "x-wallet-signature": walletSignature,
33
+ "x-deal-upload": "true",
32
34
  },
33
35
  });
34
36
  }
@@ -0,0 +1,135 @@
1
+ import { ApiClient } from "../client";
2
+ export interface DriveFileItem {
3
+ name: string;
4
+ path: string;
5
+ type: "file" | "directory";
6
+ size: number;
7
+ modified: number;
8
+ }
9
+ export interface DriveListResponse {
10
+ success: boolean;
11
+ items: DriveFileItem[];
12
+ path: string;
13
+ }
14
+ export interface DriveStatsResponse {
15
+ success: boolean;
16
+ stats: {
17
+ totalBytes: number;
18
+ totalSizeMB: string;
19
+ totalSizeGB: string;
20
+ fileCount: number;
21
+ dirCount: number;
22
+ };
23
+ }
24
+ export interface PublicLink {
25
+ linkId: string;
26
+ filePath: string;
27
+ createdAt: number;
28
+ expiresAt: number | null;
29
+ accessCount: number;
30
+ lastAccessedAt: number | null;
31
+ }
32
+ export interface PublicLinkResponse {
33
+ success: boolean;
34
+ linkId: string;
35
+ filePath: string;
36
+ publicUrl: string;
37
+ createdAt: number;
38
+ expiresAt: number | null;
39
+ }
40
+ export interface PublicLinksListResponse {
41
+ success: boolean;
42
+ links: PublicLink[];
43
+ }
44
+ export declare class DriveModule {
45
+ private client;
46
+ constructor(client: ApiClient);
47
+ /**
48
+ * List files and folders in the specified directory
49
+ */
50
+ list(path?: string): Promise<DriveListResponse>;
51
+ /**
52
+ * Upload a single file
53
+ */
54
+ uploadFile(file: Buffer | Blob, filename: string, path?: string): Promise<{
55
+ success: boolean;
56
+ message: string;
57
+ files: Array<{
58
+ name: string;
59
+ path: string;
60
+ size: number;
61
+ }>;
62
+ }>;
63
+ /**
64
+ * Upload multiple files
65
+ */
66
+ uploadFiles(files: Array<{
67
+ file: Buffer | Blob;
68
+ filename: string;
69
+ }>, path?: string): Promise<{
70
+ success: boolean;
71
+ message: string;
72
+ files: Array<{
73
+ name: string;
74
+ path: string;
75
+ size: number;
76
+ }>;
77
+ }>;
78
+ /**
79
+ * Download a file
80
+ */
81
+ download(path: string): Promise<Buffer>;
82
+ /**
83
+ * Delete a file or directory
84
+ */
85
+ delete(path: string): Promise<{
86
+ success: boolean;
87
+ message: string;
88
+ }>;
89
+ /**
90
+ * Create a directory
91
+ */
92
+ createDirectory(name: string, path?: string): Promise<{
93
+ success: boolean;
94
+ message: string;
95
+ path: string;
96
+ }>;
97
+ /**
98
+ * Rename a file or directory
99
+ */
100
+ rename(oldPath: string, newName: string): Promise<{
101
+ success: boolean;
102
+ message: string;
103
+ }>;
104
+ /**
105
+ * Move a file or directory
106
+ */
107
+ move(sourcePath: string, destPath: string): Promise<{
108
+ success: boolean;
109
+ message: string;
110
+ }>;
111
+ /**
112
+ * Get storage statistics
113
+ */
114
+ getStats(): Promise<DriveStatsResponse>;
115
+ /**
116
+ * Create a public sharing link for a file
117
+ */
118
+ createPublicLink(filePath: string, expiresInDays?: number): Promise<PublicLinkResponse>;
119
+ /**
120
+ * List all public links
121
+ */
122
+ listPublicLinks(): Promise<PublicLinksListResponse>;
123
+ /**
124
+ * Revoke a public link
125
+ */
126
+ revokePublicLink(linkId: string): Promise<{
127
+ success: boolean;
128
+ message: string;
129
+ }>;
130
+ /**
131
+ * Get public file URL (for direct access without authentication)
132
+ * Note: This requires the baseURL from the SDK configuration to construct the full URL
133
+ */
134
+ getPublicFileUrl(linkId: string, baseURL?: string): string;
135
+ }
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DriveModule = void 0;
7
+ const form_data_1 = __importDefault(require("form-data"));
8
+ class DriveModule {
9
+ constructor(client) {
10
+ this.client = client;
11
+ }
12
+ /**
13
+ * List files and folders in the specified directory
14
+ */
15
+ async list(path) {
16
+ const url = path ? `/api/v1/drive/list/${encodeURIComponent(path)}` : "/api/v1/drive/list";
17
+ return this.client.get(url);
18
+ }
19
+ /**
20
+ * Upload a single file
21
+ */
22
+ async uploadFile(file, filename, path) {
23
+ const formData = new form_data_1.default();
24
+ formData.append("file", file, filename);
25
+ const url = path ? `/api/v1/drive/upload/${encodeURIComponent(path)}` : "/api/v1/drive/upload";
26
+ return this.client.post(url, formData, {
27
+ headers: formData.getHeaders(),
28
+ });
29
+ }
30
+ /**
31
+ * Upload multiple files
32
+ */
33
+ async uploadFiles(files, path) {
34
+ const formData = new form_data_1.default();
35
+ files.forEach(({ file, filename }) => {
36
+ formData.append("files", file, filename);
37
+ });
38
+ const url = path ? `/api/v1/drive/upload/${encodeURIComponent(path)}` : "/api/v1/drive/upload";
39
+ return this.client.post(url, formData, {
40
+ headers: formData.getHeaders(),
41
+ });
42
+ }
43
+ /**
44
+ * Download a file
45
+ */
46
+ async download(path) {
47
+ const url = `/api/v1/drive/download/${encodeURIComponent(path)}`;
48
+ // Use the internal axios client to handle arraybuffer response
49
+ const response = await this.client.client.get(url, {
50
+ responseType: "arraybuffer",
51
+ });
52
+ return Buffer.from(response.data);
53
+ }
54
+ /**
55
+ * Delete a file or directory
56
+ */
57
+ async delete(path) {
58
+ const url = `/api/v1/drive/delete/${encodeURIComponent(path)}`;
59
+ return this.client.delete(url);
60
+ }
61
+ /**
62
+ * Create a directory
63
+ */
64
+ async createDirectory(name, path) {
65
+ const url = path ? `/api/v1/drive/mkdir/${encodeURIComponent(path)}` : "/api/v1/drive/mkdir";
66
+ return this.client.post(url, { name });
67
+ }
68
+ /**
69
+ * Rename a file or directory
70
+ */
71
+ async rename(oldPath, newName) {
72
+ return this.client.post("/api/v1/drive/rename", {
73
+ oldPath,
74
+ newName,
75
+ });
76
+ }
77
+ /**
78
+ * Move a file or directory
79
+ */
80
+ async move(sourcePath, destPath) {
81
+ return this.client.post("/api/v1/drive/move", {
82
+ sourcePath,
83
+ destPath,
84
+ });
85
+ }
86
+ /**
87
+ * Get storage statistics
88
+ */
89
+ async getStats() {
90
+ return this.client.get("/api/v1/drive/stats");
91
+ }
92
+ /**
93
+ * Create a public sharing link for a file
94
+ */
95
+ async createPublicLink(filePath, expiresInDays) {
96
+ return this.client.post("/api/v1/drive/links", {
97
+ filePath,
98
+ expiresInDays,
99
+ });
100
+ }
101
+ /**
102
+ * List all public links
103
+ */
104
+ async listPublicLinks() {
105
+ return this.client.get("/api/v1/drive/links");
106
+ }
107
+ /**
108
+ * Revoke a public link
109
+ */
110
+ async revokePublicLink(linkId) {
111
+ return this.client.delete(`/api/v1/drive/links/${linkId}`);
112
+ }
113
+ /**
114
+ * Get public file URL (for direct access without authentication)
115
+ * Note: This requires the baseURL from the SDK configuration to construct the full URL
116
+ */
117
+ getPublicFileUrl(linkId, baseURL) {
118
+ if (baseURL) {
119
+ return `${baseURL}/api/v1/drive/public/${linkId}`;
120
+ }
121
+ // If baseURL is not provided, return relative path
122
+ return `/api/v1/drive/public/${linkId}`;
123
+ }
124
+ }
125
+ exports.DriveModule = DriveModule;
@@ -3,7 +3,13 @@ export declare class IpfsModule {
3
3
  private readonly client;
4
4
  constructor(client: ApiClient);
5
5
  getStatus(): Promise<any>;
6
- uploadFile(fileBuffer: Buffer, filename: string, contentType: string, userAddress?: string): Promise<any>;
6
+ uploadFile(fileBuffer: Buffer, filename: string, contentType: string, options?: {
7
+ userAddress?: string;
8
+ walletSignature?: string;
9
+ isDealUpload?: boolean;
10
+ encrypted?: boolean;
11
+ encryptionToken?: string;
12
+ }): Promise<any>;
7
13
  /**
8
14
  * Upload multiple files as a directory to IPFS
9
15
  * Maintains directory structure using relative paths
@@ -17,7 +23,11 @@ export declare class IpfsModule {
17
23
  filename: string;
18
24
  path: string;
19
25
  contentType?: string;
20
- }>, userAddress?: string): Promise<any>;
26
+ }>, options?: {
27
+ userAddress?: string;
28
+ walletSignature?: string;
29
+ isDealUpload?: boolean;
30
+ }): Promise<any>;
21
31
  cat(cid: string): Promise<Buffer>;
22
32
  /**
23
33
  * Cat a file from an IPFS directory using a relative path
@@ -37,19 +47,29 @@ export declare class IpfsModule {
37
47
  /**
38
48
  * Upload a file using browser FormData (for browser environments)
39
49
  * @param file File object from browser File API
40
- * @param userAddress Optional user address for authentication
50
+ * @param options Upload options including authentication
41
51
  * @returns Promise with upload result
42
52
  */
43
- uploadFileBrowser(file: File, userAddress?: string): Promise<any>;
53
+ uploadFileBrowser(file: File, options?: {
54
+ userAddress?: string;
55
+ walletSignature?: string;
56
+ isDealUpload?: boolean;
57
+ encrypted?: boolean;
58
+ encryptionToken?: string;
59
+ }): Promise<any>;
44
60
  /**
45
61
  * Upload multiple files as a directory using browser FormData (for browser environments)
46
62
  * Maintains directory structure using relative paths from File.webkitRelativePath or file.name
47
63
  *
48
64
  * @param files Array of File objects from browser File API
49
- * @param userAddress Optional user address for authentication
65
+ * @param options Upload options including authentication
50
66
  * @returns Promise with directory CID and file information
51
67
  */
52
- uploadDirectoryBrowser(files: File[], userAddress?: string): Promise<any>;
68
+ uploadDirectoryBrowser(files: File[], options?: {
69
+ userAddress?: string;
70
+ walletSignature?: string;
71
+ isDealUpload?: boolean;
72
+ }): Promise<any>;
53
73
  /**
54
74
  * Cat a file and return as Blob (for browser environments)
55
75
  * @param cid The CID of the file
@@ -12,17 +12,31 @@ class IpfsModule {
12
12
  async getStatus() {
13
13
  return this.client.get("/api/v1/ipfs/status");
14
14
  }
15
- async uploadFile(fileBuffer, filename, contentType, userAddress) {
15
+ async uploadFile(fileBuffer, filename, contentType, options) {
16
16
  const form = new form_data_1.default();
17
17
  form.append("file", fileBuffer, {
18
18
  filename: filename,
19
19
  contentType: contentType,
20
20
  });
21
21
  const headers = form.getHeaders();
22
- if (userAddress) {
23
- headers["x-user-address"] = userAddress;
22
+ if (options?.userAddress) {
23
+ headers["x-user-address"] = options.userAddress;
24
+ }
25
+ if (options?.walletSignature) {
26
+ headers["x-wallet-signature"] = options.walletSignature;
27
+ }
28
+ if (options?.isDealUpload) {
29
+ headers["x-deal-upload"] = "true";
30
+ }
31
+ if (options?.encrypted) {
32
+ form.append("encrypted", "true");
33
+ form.append("encryptionMethod", "SEA");
34
+ if (options.encryptionToken) {
35
+ form.append("encryptionToken", options.encryptionToken);
36
+ }
24
37
  }
25
- return this.client.post("/api/v1/ipfs/upload", form, {
38
+ const queryParams = options?.isDealUpload ? "?deal=true" : "";
39
+ return this.client.post(`/api/v1/ipfs/upload${queryParams}`, form, {
26
40
  headers: headers,
27
41
  });
28
42
  }
@@ -34,7 +48,7 @@ class IpfsModule {
34
48
  * @param userAddress Optional user address for authentication
35
49
  * @returns Promise with directory CID and file information
36
50
  */
37
- async uploadDirectory(files, userAddress) {
51
+ async uploadDirectory(files, options) {
38
52
  if (!files || files.length === 0) {
39
53
  throw new Error("At least one file is required for directory upload");
40
54
  }
@@ -47,8 +61,14 @@ class IpfsModule {
47
61
  });
48
62
  });
49
63
  const headers = form.getHeaders();
50
- if (userAddress) {
51
- headers["x-user-address"] = userAddress;
64
+ if (options?.userAddress) {
65
+ headers["x-user-address"] = options.userAddress;
66
+ }
67
+ if (options?.walletSignature) {
68
+ headers["x-wallet-signature"] = options.walletSignature;
69
+ }
70
+ if (options?.isDealUpload) {
71
+ headers["x-deal-upload"] = "true";
52
72
  }
53
73
  return this.client.post("/api/v1/ipfs/upload-directory", form, {
54
74
  headers: headers,
@@ -124,18 +144,32 @@ class IpfsModule {
124
144
  /**
125
145
  * Upload a file using browser FormData (for browser environments)
126
146
  * @param file File object from browser File API
127
- * @param userAddress Optional user address for authentication
147
+ * @param options Upload options including authentication
128
148
  * @returns Promise with upload result
129
149
  */
130
- async uploadFileBrowser(file, userAddress) {
150
+ async uploadFileBrowser(file, options) {
131
151
  const formData = new form_data_1.default();
132
152
  formData.append("file", file, file.name);
133
153
  const headers = {};
134
- if (userAddress) {
135
- headers["x-user-address"] = userAddress;
154
+ if (options?.userAddress) {
155
+ headers["x-user-address"] = options.userAddress;
156
+ }
157
+ if (options?.walletSignature) {
158
+ headers["x-wallet-signature"] = options.walletSignature;
159
+ }
160
+ if (options?.isDealUpload) {
161
+ headers["x-deal-upload"] = "true";
162
+ }
163
+ if (options?.encrypted) {
164
+ formData.append("encrypted", "true");
165
+ formData.append("encryptionMethod", "SEA");
166
+ if (options.encryptionToken) {
167
+ formData.append("encryptionToken", options.encryptionToken);
168
+ }
136
169
  }
170
+ const queryParams = options?.isDealUpload ? "?deal=true" : "";
137
171
  // Explicitly don't set Content-Type - let browser set it with boundary for FormData
138
- return this.client.post("/api/v1/ipfs/upload", formData, {
172
+ return this.client.post(`/api/v1/ipfs/upload${queryParams}`, formData, {
139
173
  headers: headers,
140
174
  // Ensure axios doesn't serialize FormData as JSON
141
175
  transformRequest: [(data) => {
@@ -152,10 +186,10 @@ class IpfsModule {
152
186
  * Maintains directory structure using relative paths from File.webkitRelativePath or file.name
153
187
  *
154
188
  * @param files Array of File objects from browser File API
155
- * @param userAddress Optional user address for authentication
189
+ * @param options Upload options including authentication
156
190
  * @returns Promise with directory CID and file information
157
191
  */
158
- async uploadDirectoryBrowser(files, userAddress) {
192
+ async uploadDirectoryBrowser(files, options) {
159
193
  if (!files || files.length === 0) {
160
194
  throw new Error("At least one file is required for directory upload");
161
195
  }
@@ -167,8 +201,14 @@ class IpfsModule {
167
201
  formData.append("files", file, relativePath);
168
202
  });
169
203
  const headers = {};
170
- if (userAddress) {
171
- headers["x-user-address"] = userAddress;
204
+ if (options?.userAddress) {
205
+ headers["x-user-address"] = options.userAddress;
206
+ }
207
+ if (options?.walletSignature) {
208
+ headers["x-wallet-signature"] = options.walletSignature;
209
+ }
210
+ if (options?.isDealUpload) {
211
+ headers["x-deal-upload"] = "true";
172
212
  }
173
213
  // Explicitly don't set Content-Type - let browser set it with boundary for FormData
174
214
  return this.client.post("/api/v1/ipfs/upload-directory", formData, {
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Wallet utility functions for Shogun Relay SDK
3
+ */
4
+ /**
5
+ * Message that must be signed for wallet authentication
6
+ */
7
+ export declare const WALLET_AUTH_MESSAGE = "I Love Shogun";
8
+ /**
9
+ * Generate wallet signature for authentication
10
+ *
11
+ * @param signer - Ethers.js Signer instance or similar that implements signMessage
12
+ * @returns Promise<string> - EIP-191 signature of the auth message
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { ethers } from 'ethers';
17
+ * import { generateWalletSignature } from '@shogun/relay-sdk/utils/wallet';
18
+ *
19
+ * const provider = new ethers.BrowserProvider(window.ethereum);
20
+ * const signer = await provider.getSigner();
21
+ * const signature = await generateWalletSignature(signer);
22
+ * ```
23
+ */
24
+ export declare function generateWalletSignature(signer: {
25
+ signMessage: (message: string) => Promise<string>;
26
+ }): Promise<string>;
27
+ /**
28
+ * Verify wallet signature
29
+ *
30
+ * @param address - Ethereum wallet address
31
+ * @param signature - EIP-191 signature
32
+ * @returns Promise<boolean> - True if signature is valid for the address
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * import { ethers } from 'ethers';
37
+ * import { verifyWalletSignature } from '@shogun/relay-sdk/utils/wallet';
38
+ *
39
+ * const isValid = await verifyWalletSignature(address, signature);
40
+ * ```
41
+ */
42
+ export declare function verifyWalletSignature(address: string, signature: string): Promise<boolean>;
43
+ /**
44
+ * Get wallet address from signature
45
+ *
46
+ * @param signature - EIP-191 signature
47
+ * @returns Promise<string | null> - Recovered wallet address or null if invalid
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * import { getAddressFromSignature } from '@shogun/relay-sdk/utils/wallet';
52
+ *
53
+ * const address = await getAddressFromSignature(signature);
54
+ * ```
55
+ */
56
+ export declare function getAddressFromSignature(signature: string): Promise<string | null>;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ /**
3
+ * Wallet utility functions for Shogun Relay SDK
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.WALLET_AUTH_MESSAGE = void 0;
40
+ exports.generateWalletSignature = generateWalletSignature;
41
+ exports.verifyWalletSignature = verifyWalletSignature;
42
+ exports.getAddressFromSignature = getAddressFromSignature;
43
+ /**
44
+ * Message that must be signed for wallet authentication
45
+ */
46
+ exports.WALLET_AUTH_MESSAGE = "I Love Shogun";
47
+ /**
48
+ * Generate wallet signature for authentication
49
+ *
50
+ * @param signer - Ethers.js Signer instance or similar that implements signMessage
51
+ * @returns Promise<string> - EIP-191 signature of the auth message
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import { ethers } from 'ethers';
56
+ * import { generateWalletSignature } from '@shogun/relay-sdk/utils/wallet';
57
+ *
58
+ * const provider = new ethers.BrowserProvider(window.ethereum);
59
+ * const signer = await provider.getSigner();
60
+ * const signature = await generateWalletSignature(signer);
61
+ * ```
62
+ */
63
+ async function generateWalletSignature(signer) {
64
+ return await signer.signMessage(exports.WALLET_AUTH_MESSAGE);
65
+ }
66
+ /**
67
+ * Verify wallet signature
68
+ *
69
+ * @param address - Ethereum wallet address
70
+ * @param signature - EIP-191 signature
71
+ * @returns Promise<boolean> - True if signature is valid for the address
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * import { ethers } from 'ethers';
76
+ * import { verifyWalletSignature } from '@shogun/relay-sdk/utils/wallet';
77
+ *
78
+ * const isValid = await verifyWalletSignature(address, signature);
79
+ * ```
80
+ */
81
+ async function verifyWalletSignature(address, signature) {
82
+ try {
83
+ const { ethers } = await Promise.resolve().then(() => __importStar(require("ethers")));
84
+ const recoveredAddress = ethers.verifyMessage(exports.WALLET_AUTH_MESSAGE, signature);
85
+ return recoveredAddress.toLowerCase() === address.toLowerCase();
86
+ }
87
+ catch (error) {
88
+ return false;
89
+ }
90
+ }
91
+ /**
92
+ * Get wallet address from signature
93
+ *
94
+ * @param signature - EIP-191 signature
95
+ * @returns Promise<string | null> - Recovered wallet address or null if invalid
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * import { getAddressFromSignature } from '@shogun/relay-sdk/utils/wallet';
100
+ *
101
+ * const address = await getAddressFromSignature(signature);
102
+ * ```
103
+ */
104
+ async function getAddressFromSignature(signature) {
105
+ try {
106
+ const { ethers } = await Promise.resolve().then(() => __importStar(require("ethers")));
107
+ return ethers.verifyMessage(exports.WALLET_AUTH_MESSAGE, signature);
108
+ }
109
+ catch (error) {
110
+ return null;
111
+ }
112
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shogun-relay-sdk",
3
- "version": "1.2.11",
3
+ "version": "1.4.0",
4
4
  "description": "SDK for interacting with Shogun Relay API - HTTP client for IPFS, deals, registry, and network operations",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",