pinme 1.1.6-alpha.4 → 1.2.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/README.md +294 -10
- package/dist/index.js +278 -134
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,10 +13,17 @@
|
|
|
13
13
|
|
|
14
14
|
# PinMe
|
|
15
15
|
|
|
16
|
-
[PinMe](https://pinme.eth.limo/) is a
|
|
16
|
+
[PinMe](https://pinme.eth.limo/) is a zero-config frontend deployment tool.
|
|
17
|
+
No servers. No accounts. No setup.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
Build a static site, generate a page with AI, or export your frontend — then deploy instantly with a single command.
|
|
19
20
|
|
|
21
|
+
PinMe publishes your site as verifiable content, making silent tampering and accidental breakage far harder than traditional hosting.
|
|
22
|
+
|
|
23
|
+
You don’t manage servers, regions, or uptime.
|
|
24
|
+
PinMe handles availability and persistence for you.
|
|
25
|
+
|
|
26
|
+
Website: [https://pinme.eth.limo/](https://pinme.eth.limo/)
|
|
20
27
|
|
|
21
28
|
## Installation
|
|
22
29
|
|
|
@@ -42,6 +49,10 @@ pinme upload
|
|
|
42
49
|
|
|
43
50
|
# Specify path directly
|
|
44
51
|
pinme upload /path/to/file-or-directory
|
|
52
|
+
|
|
53
|
+
# Upload and bind to a domain
|
|
54
|
+
pinme upload /path/to/file-or-directory --domain <name>
|
|
55
|
+
pinme upload /path/to/file-or-directory -d <name>
|
|
45
56
|
```
|
|
46
57
|
|
|
47
58
|
### Remove files from IPFS
|
|
@@ -70,6 +81,43 @@ pinme list -l 5
|
|
|
70
81
|
pinme list -c
|
|
71
82
|
```
|
|
72
83
|
|
|
84
|
+
### Set AppKey for authentication
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Interactive AppKey setup
|
|
88
|
+
pinme set-appkey
|
|
89
|
+
|
|
90
|
+
# Set AppKey directly
|
|
91
|
+
pinme set-appkey <AppKey>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### View AppKey information
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Show current AppKey (masked for security)
|
|
98
|
+
pinme show-appkey
|
|
99
|
+
|
|
100
|
+
# Or use the shorthand command
|
|
101
|
+
pinme appkey
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Log out
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Log out and clear authentication
|
|
108
|
+
pinme logout
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### View your domains
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# List all domains owned by current account
|
|
115
|
+
pinme my-domains
|
|
116
|
+
|
|
117
|
+
# Or use the shorthand command
|
|
118
|
+
pinme domain
|
|
119
|
+
```
|
|
120
|
+
|
|
73
121
|
### Get help
|
|
74
122
|
|
|
75
123
|
```bash
|
|
@@ -81,16 +129,19 @@ pinme help
|
|
|
81
129
|
|
|
82
130
|
### `upload`
|
|
83
131
|
|
|
84
|
-
Upload a file or directory to the IPFS network.
|
|
132
|
+
Upload a file or directory to the IPFS network. Supports binding to a Pinme subdomain after upload.
|
|
85
133
|
|
|
86
134
|
```bash
|
|
87
|
-
pinme upload [path]
|
|
135
|
+
pinme upload [path] [--domain <name>]
|
|
88
136
|
```
|
|
89
137
|
|
|
90
138
|
**Options:**
|
|
139
|
+
|
|
91
140
|
- `path`: Path to the file or directory to upload (optional, if not provided, interactive mode will be entered)
|
|
141
|
+
- `-d, --domain <name>`: Pinme subdomain to bind after upload (optional)
|
|
92
142
|
|
|
93
143
|
**Examples:**
|
|
144
|
+
|
|
94
145
|
```bash
|
|
95
146
|
# Interactive upload
|
|
96
147
|
pinme upload
|
|
@@ -100,6 +151,10 @@ pinme upload ./example.jpg
|
|
|
100
151
|
|
|
101
152
|
# Upload an entire directory
|
|
102
153
|
pinme upload ./my-website
|
|
154
|
+
|
|
155
|
+
# Upload and bind to a domain
|
|
156
|
+
pinme upload ./my-website --domain my-site
|
|
157
|
+
pinme upload ./my-website -d my-site
|
|
103
158
|
```
|
|
104
159
|
|
|
105
160
|
### `rm`
|
|
@@ -111,9 +166,11 @@ pinme rm [hash]
|
|
|
111
166
|
```
|
|
112
167
|
|
|
113
168
|
**Options:**
|
|
169
|
+
|
|
114
170
|
- `hash`: IPFS content hash to remove (optional, if not provided, interactive mode will be entered)
|
|
115
171
|
|
|
116
172
|
**Examples:**
|
|
173
|
+
|
|
117
174
|
```bash
|
|
118
175
|
# Interactive removal
|
|
119
176
|
pinme rm
|
|
@@ -134,10 +191,12 @@ pinme ls [options]
|
|
|
134
191
|
```
|
|
135
192
|
|
|
136
193
|
**Options:**
|
|
194
|
+
|
|
137
195
|
- `-l, --limit <number>`: Limit the number of records displayed
|
|
138
196
|
- `-c, --clear`: Clear all upload history
|
|
139
197
|
|
|
140
198
|
**Examples:**
|
|
199
|
+
|
|
141
200
|
```bash
|
|
142
201
|
# Show the last 10 records
|
|
143
202
|
pinme list
|
|
@@ -149,6 +208,105 @@ pinme ls -l 5
|
|
|
149
208
|
pinme list -c
|
|
150
209
|
```
|
|
151
210
|
|
|
211
|
+
### `set-appkey`
|
|
212
|
+
|
|
213
|
+
Set AppKey for authentication and automatically merge anonymous upload history to the current account.
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
pinme set-appkey [AppKey]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Options:**
|
|
220
|
+
|
|
221
|
+
- `AppKey`: Your AppKey for authentication (optional, if not provided, interactive mode will be entered)
|
|
222
|
+
|
|
223
|
+
**Examples:**
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Interactive AppKey setup
|
|
227
|
+
pinme set-appkey
|
|
228
|
+
|
|
229
|
+
# Set AppKey directly
|
|
230
|
+
pinme set-appkey your-app-key-here
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Note:** After setting the AppKey, your anonymous upload history will be automatically merged to your account.
|
|
234
|
+
|
|
235
|
+
### `show-appkey` / `appkey`
|
|
236
|
+
|
|
237
|
+
Display current AppKey information with masked sensitive data.
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
pinme show-appkey
|
|
241
|
+
pinme appkey
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Description:**
|
|
245
|
+
|
|
246
|
+
This command shows the current AppKey information including:
|
|
247
|
+
- Address (fully displayed)
|
|
248
|
+
- Token (masked for security)
|
|
249
|
+
- AppKey (masked for security)
|
|
250
|
+
|
|
251
|
+
**Examples:**
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# Show AppKey information
|
|
255
|
+
pinme show-appkey
|
|
256
|
+
|
|
257
|
+
# Shorthand command
|
|
258
|
+
pinme appkey
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Note:** Sensitive information (token and AppKey) will be masked to protect your credentials. Only the address is fully displayed.
|
|
262
|
+
|
|
263
|
+
### `logout`
|
|
264
|
+
|
|
265
|
+
Log out and clear authentication information from local storage.
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
pinme logout
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Description:**
|
|
272
|
+
|
|
273
|
+
This command logs out the current user and removes the authentication information from local storage. After logging out, you will need to set your AppKey again to use authenticated features.
|
|
274
|
+
|
|
275
|
+
**Examples:**
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
# Log out
|
|
279
|
+
pinme logout
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Note:** This action will remove your AppKey from local storage. You can set it again using `pinme set-appkey` command.
|
|
283
|
+
|
|
284
|
+
### `my-domains` / `domain`
|
|
285
|
+
|
|
286
|
+
List all domains owned by the current account.
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
pinme my-domains
|
|
290
|
+
pinme domain
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Examples:**
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
# List all domains
|
|
297
|
+
pinme my-domains
|
|
298
|
+
|
|
299
|
+
# Shorthand command
|
|
300
|
+
pinme domain
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
This command displays information about each domain including:
|
|
304
|
+
|
|
305
|
+
- Domain name
|
|
306
|
+
- Domain type
|
|
307
|
+
- Bind time
|
|
308
|
+
- Expire time
|
|
309
|
+
|
|
152
310
|
### `help`
|
|
153
311
|
|
|
154
312
|
Display help information.
|
|
@@ -158,9 +316,11 @@ pinme help [command]
|
|
|
158
316
|
```
|
|
159
317
|
|
|
160
318
|
**Options:**
|
|
319
|
+
|
|
161
320
|
- `command`: The specific command to view help for (optional)
|
|
162
321
|
|
|
163
322
|
**Examples:**
|
|
323
|
+
|
|
164
324
|
```bash
|
|
165
325
|
# Display general help
|
|
166
326
|
pinme help
|
|
@@ -168,8 +328,8 @@ pinme help
|
|
|
168
328
|
|
|
169
329
|
## Upload Limits
|
|
170
330
|
|
|
171
|
-
- Single file size limit:
|
|
172
|
-
- Total directory size limit:
|
|
331
|
+
- Single file size limit: 200MB (free plan)
|
|
332
|
+
- Total directory size limit: 1GB (free plan)
|
|
173
333
|
|
|
174
334
|
## File Storage
|
|
175
335
|
|
|
@@ -181,10 +341,10 @@ Uploaded files are stored on the IPFS network and accessible through the Glitter
|
|
|
181
341
|
### Log Locations
|
|
182
342
|
|
|
183
343
|
Logs and configuration files are stored in:
|
|
344
|
+
|
|
184
345
|
- Linux/macOS: `~/.pinme/`
|
|
185
346
|
- Windows: `%USERPROFILE%\.pinme\`
|
|
186
347
|
|
|
187
|
-
|
|
188
348
|
## License
|
|
189
349
|
|
|
190
350
|
MIT License - See the [LICENSE](LICENSE) file for details
|
|
@@ -200,16 +360,140 @@ When uploading projects built with Vite, please note:
|
|
|
200
360
|
```js
|
|
201
361
|
// vite.config.js
|
|
202
362
|
export default {
|
|
203
|
-
base:
|
|
363
|
+
base: './',
|
|
204
364
|
// other configurations...
|
|
205
|
-
}
|
|
365
|
+
};
|
|
206
366
|
```
|
|
207
367
|
|
|
368
|
+
## GitHub Actions Integration
|
|
369
|
+
|
|
370
|
+
PinMe can be integrated with GitHub Actions to automatically deploy your project when you push code to GitHub. This enables a fully automated CI/CD workflow.
|
|
371
|
+
|
|
372
|
+
### Quick Setup
|
|
373
|
+
|
|
374
|
+
1. **Add the workflow file** to your repository:
|
|
375
|
+
|
|
376
|
+
- Copy `.github/workflows/deploy.yml` from the PinMe repository to your project
|
|
377
|
+
- Or create `.github/workflows/deploy.yml` in your repository
|
|
378
|
+
|
|
379
|
+
2. **Configure GitHub Secrets**:
|
|
380
|
+
|
|
381
|
+
- Go to your repository → Settings → Secrets and variables → Actions
|
|
382
|
+
- Add a new secret named `PINME_APPKEY` with your PinMe AppKey
|
|
383
|
+
- (Optional) Add `PINME_DOMAIN` to specify a custom domain name
|
|
384
|
+
|
|
385
|
+
3. **Push to trigger deployment**:
|
|
386
|
+
- Push code to `main` or `master` branch to trigger automatic deployment
|
|
387
|
+
- Or manually trigger via Actions tab → "Deploy to PinMe" → Run workflow
|
|
388
|
+
|
|
389
|
+
### Workflow Features
|
|
390
|
+
|
|
391
|
+
The GitHub Actions workflow automatically:
|
|
392
|
+
|
|
393
|
+
- ✅ Detects and installs project dependencies
|
|
394
|
+
- ✅ Builds your project (if a build script exists)
|
|
395
|
+
- ✅ Installs PinMe CLI
|
|
396
|
+
- ✅ Sets up authentication using your AppKey
|
|
397
|
+
- ✅ Auto-detects build output directory (`dist`, `build`, `public`, or `out`)
|
|
398
|
+
- ✅ Uploads to IPFS and binds to your domain
|
|
399
|
+
- ✅ Provides deployment summary with access URL
|
|
400
|
+
|
|
401
|
+
### Configuration Options
|
|
402
|
+
|
|
403
|
+
#### Using GitHub Secrets
|
|
404
|
+
|
|
405
|
+
You can configure the following secrets in your repository:
|
|
406
|
+
|
|
407
|
+
- **`PINME_APPKEY`** (Required): Your PinMe AppKey for authentication
|
|
408
|
+
|
|
409
|
+
- Format: `<address>-<jwt>`
|
|
410
|
+
- Get your AppKey from [PinMe website](https://pinme.eth.limo/)
|
|
411
|
+
|
|
412
|
+
- **`PINME_DOMAIN`** (Optional): Default domain name to bind
|
|
413
|
+
- If not set, the workflow will generate a domain from your repository name
|
|
414
|
+
- Example: `my-awesome-project` → `https://my-awesome-project.pinit.eth.limo`
|
|
415
|
+
|
|
416
|
+
#### Manual Workflow Dispatch
|
|
417
|
+
|
|
418
|
+
You can also manually trigger the workflow with custom parameters:
|
|
419
|
+
|
|
420
|
+
1. Go to Actions tab in your repository
|
|
421
|
+
2. Select "Deploy to PinMe" workflow
|
|
422
|
+
3. Click "Run workflow"
|
|
423
|
+
4. Enter:
|
|
424
|
+
- **Domain**: Your desired PinMe domain name
|
|
425
|
+
- **Build Directory**: Your build output directory (default: `dist`)
|
|
426
|
+
|
|
427
|
+
### Example Workflow
|
|
428
|
+
|
|
429
|
+
```yaml
|
|
430
|
+
name: Deploy to PinMe
|
|
431
|
+
|
|
432
|
+
on:
|
|
433
|
+
push:
|
|
434
|
+
branches: [main, master]
|
|
435
|
+
workflow_dispatch:
|
|
436
|
+
inputs:
|
|
437
|
+
domain:
|
|
438
|
+
description: 'PinMe domain name'
|
|
439
|
+
required: true
|
|
440
|
+
build_dir:
|
|
441
|
+
description: 'Build directory'
|
|
442
|
+
default: 'dist'
|
|
443
|
+
|
|
444
|
+
jobs:
|
|
445
|
+
deploy:
|
|
446
|
+
runs-on: ubuntu-latest
|
|
447
|
+
steps:
|
|
448
|
+
- uses: actions/checkout@v4
|
|
449
|
+
- uses: actions/setup-node@v4
|
|
450
|
+
with:
|
|
451
|
+
node-version: '18'
|
|
452
|
+
- run: npm ci
|
|
453
|
+
- run: npm run build
|
|
454
|
+
- run: npm install -g pinme
|
|
455
|
+
- run: pinme set-appkey "${{ secrets.PINME_APPKEY }}"
|
|
456
|
+
- run: pinme upload dist --domain "${{ secrets.PINME_DOMAIN }}"
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Supported Build Tools
|
|
460
|
+
|
|
461
|
+
The workflow automatically detects and supports:
|
|
462
|
+
|
|
463
|
+
- **Vite**: Builds to `dist/`
|
|
464
|
+
- **Create React App**: Builds to `build/`
|
|
465
|
+
- **Next.js**: Builds to `out/` (with `output: 'export'`)
|
|
466
|
+
- **Vue CLI**: Builds to `dist/`
|
|
467
|
+
- **Angular**: Builds to `dist/`
|
|
468
|
+
- **Static sites**: Uses root directory or `public/`
|
|
469
|
+
|
|
470
|
+
### Troubleshooting
|
|
471
|
+
|
|
472
|
+
**Build directory not found:**
|
|
473
|
+
|
|
474
|
+
- Ensure your build script outputs to a standard directory (`dist`, `build`, `public`, or `out`)
|
|
475
|
+
- Or set `PINME_DOMAIN` secret and use manual workflow dispatch to specify custom directory
|
|
476
|
+
|
|
477
|
+
**Authentication failed:**
|
|
478
|
+
|
|
479
|
+
- Verify your `PINME_APPKEY` secret is correctly set
|
|
480
|
+
- Ensure the AppKey format is correct: `<address>-<jwt>`
|
|
481
|
+
|
|
482
|
+
**Domain binding failed:**
|
|
483
|
+
|
|
484
|
+
- Check if the domain name is available
|
|
485
|
+
- Ensure you have permission to bind the domain
|
|
486
|
+
- Try a different domain name
|
|
487
|
+
|
|
488
|
+
## Star History
|
|
489
|
+
|
|
490
|
+
[](https://star-history.com/#glitternetwork/pinme&Date)
|
|
491
|
+
|
|
208
492
|
## Contact Us
|
|
209
493
|
|
|
210
494
|
If you have questions or suggestions, please contact us through:
|
|
211
495
|
|
|
212
|
-
- GitHub Issues: [https://github.com/glitternetwork/pinme/issues](https://github.com/glitternetwork/pinme/
|
|
496
|
+
- GitHub Issues: [https://github.com/glitternetwork/pinme/issues](https://github.com/glitternetwork/pinme/issues)
|
|
213
497
|
- Email: [pinme@glitterprotocol.io](mailto:pinme@glitterprotocol.io)
|
|
214
498
|
|
|
215
499
|
---
|
package/dist/index.js
CHANGED
|
@@ -1519,11 +1519,11 @@ function checkNodeVersion() {
|
|
|
1519
1519
|
|
|
1520
1520
|
// bin/index.ts
|
|
1521
1521
|
var import_commander = require("commander");
|
|
1522
|
-
var
|
|
1522
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
1523
1523
|
var import_figlet3 = __toESM(require("figlet"));
|
|
1524
1524
|
|
|
1525
1525
|
// package.json
|
|
1526
|
-
var version = "1.1
|
|
1526
|
+
var version = "1.2.1";
|
|
1527
1527
|
|
|
1528
1528
|
// bin/upload.ts
|
|
1529
1529
|
var import_path6 = __toESM(require("path"));
|
|
@@ -4399,8 +4399,8 @@ var {
|
|
|
4399
4399
|
} = axios_default;
|
|
4400
4400
|
|
|
4401
4401
|
// bin/utils/uploadToIpfsSplit.ts
|
|
4402
|
-
var
|
|
4403
|
-
var
|
|
4402
|
+
var import_fs_extra4 = __toESM(require("fs-extra"));
|
|
4403
|
+
var import_path5 = __toESM(require("path"));
|
|
4404
4404
|
var import_form_data2 = __toESM(require("form-data"));
|
|
4405
4405
|
var import_ora = __toESM(require("ora"));
|
|
4406
4406
|
var crypto = __toESM(require("crypto"));
|
|
@@ -4540,23 +4540,91 @@ var clearUploadHistory = () => {
|
|
|
4540
4540
|
};
|
|
4541
4541
|
|
|
4542
4542
|
// bin/utils/getDeviceId.ts
|
|
4543
|
+
var import_fs_extra3 = __toESM(require("fs-extra"));
|
|
4544
|
+
var import_path4 = __toESM(require("path"));
|
|
4545
|
+
var import_os3 = __toESM(require("os"));
|
|
4546
|
+
var import_uuid = require("uuid");
|
|
4547
|
+
|
|
4548
|
+
// bin/utils/auth.ts
|
|
4543
4549
|
var import_fs_extra2 = __toESM(require("fs-extra"));
|
|
4544
|
-
var import_path3 = __toESM(require("path"));
|
|
4545
4550
|
var import_os2 = __toESM(require("os"));
|
|
4546
|
-
var
|
|
4551
|
+
var import_path3 = __toESM(require("path"));
|
|
4552
|
+
var CONFIG_DIR = import_path3.default.join(import_os2.default.homedir(), ".pinme");
|
|
4553
|
+
var AUTH_FILE = import_path3.default.join(CONFIG_DIR, "auth.json");
|
|
4554
|
+
function ensureConfigDir() {
|
|
4555
|
+
if (!import_fs_extra2.default.existsSync(CONFIG_DIR)) {
|
|
4556
|
+
import_fs_extra2.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
function parseCombinedToken(combined) {
|
|
4560
|
+
const firstDash = combined.indexOf("-");
|
|
4561
|
+
if (firstDash <= 0 || firstDash === combined.length - 1) {
|
|
4562
|
+
throw new Error('Invalid token format. Expected "<address>-<jwt>".');
|
|
4563
|
+
}
|
|
4564
|
+
const address = combined.slice(0, firstDash).trim();
|
|
4565
|
+
const token = combined.slice(firstDash + 1).trim();
|
|
4566
|
+
if (!address || !token) {
|
|
4567
|
+
throw new Error("Invalid token content. Address or token is empty.");
|
|
4568
|
+
}
|
|
4569
|
+
return { address, token };
|
|
4570
|
+
}
|
|
4571
|
+
function setAuthToken(combined) {
|
|
4572
|
+
ensureConfigDir();
|
|
4573
|
+
const auth = parseCombinedToken(combined);
|
|
4574
|
+
import_fs_extra2.default.writeJsonSync(AUTH_FILE, auth, { spaces: 2 });
|
|
4575
|
+
return auth;
|
|
4576
|
+
}
|
|
4577
|
+
function clearAuthToken() {
|
|
4578
|
+
try {
|
|
4579
|
+
if (import_fs_extra2.default.existsSync(AUTH_FILE)) {
|
|
4580
|
+
import_fs_extra2.default.removeSync(AUTH_FILE);
|
|
4581
|
+
}
|
|
4582
|
+
} catch (error) {
|
|
4583
|
+
console.error(`Failed to clear auth token: ${error}`);
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
function getAuthConfig() {
|
|
4587
|
+
try {
|
|
4588
|
+
if (!import_fs_extra2.default.existsSync(AUTH_FILE)) return null;
|
|
4589
|
+
const data = import_fs_extra2.default.readJsonSync(AUTH_FILE);
|
|
4590
|
+
if (!(data == null ? void 0 : data.address) || !(data == null ? void 0 : data.token)) return null;
|
|
4591
|
+
return data;
|
|
4592
|
+
} catch {
|
|
4593
|
+
return null;
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4596
|
+
function getAuthHeaders() {
|
|
4597
|
+
const conf = getAuthConfig();
|
|
4598
|
+
if (!conf) {
|
|
4599
|
+
throw new Error("Auth not set. Run: pinme set-appkey <AppKey>");
|
|
4600
|
+
}
|
|
4601
|
+
return {
|
|
4602
|
+
"token-address": conf.address,
|
|
4603
|
+
"authentication-tokens": conf.token
|
|
4604
|
+
};
|
|
4605
|
+
}
|
|
4606
|
+
|
|
4607
|
+
// bin/utils/getDeviceId.ts
|
|
4547
4608
|
function getDeviceId() {
|
|
4548
|
-
const configDir =
|
|
4549
|
-
const configFile =
|
|
4550
|
-
if (!
|
|
4551
|
-
|
|
4609
|
+
const configDir = import_path4.default.join(import_os3.default.homedir(), ".pinme");
|
|
4610
|
+
const configFile = import_path4.default.join(configDir, "device-id");
|
|
4611
|
+
if (!import_fs_extra3.default.existsSync(configDir)) {
|
|
4612
|
+
import_fs_extra3.default.mkdirSync(configDir, { recursive: true });
|
|
4552
4613
|
}
|
|
4553
|
-
if (
|
|
4554
|
-
return
|
|
4614
|
+
if (import_fs_extra3.default.existsSync(configFile)) {
|
|
4615
|
+
return import_fs_extra3.default.readFileSync(configFile, "utf8").trim();
|
|
4555
4616
|
}
|
|
4556
4617
|
const deviceId = (0, import_uuid.v4)();
|
|
4557
|
-
|
|
4618
|
+
import_fs_extra3.default.writeFileSync(configFile, deviceId);
|
|
4558
4619
|
return deviceId;
|
|
4559
4620
|
}
|
|
4621
|
+
function getUid() {
|
|
4622
|
+
const auth = getAuthConfig();
|
|
4623
|
+
if (auth == null ? void 0 : auth.address) {
|
|
4624
|
+
return auth.address;
|
|
4625
|
+
}
|
|
4626
|
+
return getDeviceId();
|
|
4627
|
+
}
|
|
4560
4628
|
|
|
4561
4629
|
// bin/utils/uploadToIpfsSplit.ts
|
|
4562
4630
|
var IPFS_API_URL = "https://pinme.dev/api/v3";
|
|
@@ -4592,12 +4660,12 @@ var StepProgressBar = class {
|
|
|
4592
4660
|
}
|
|
4593
4661
|
completeStep() {
|
|
4594
4662
|
}
|
|
4595
|
-
//
|
|
4663
|
+
// Start simulating progress to continue display after 90%
|
|
4596
4664
|
startSimulatingProgress() {
|
|
4597
4665
|
this.isSimulatingProgress = true;
|
|
4598
4666
|
this.simulationStartTime = Date.now();
|
|
4599
4667
|
}
|
|
4600
|
-
//
|
|
4668
|
+
// Stop simulating progress
|
|
4601
4669
|
stopSimulatingProgress() {
|
|
4602
4670
|
this.isSimulatingProgress = false;
|
|
4603
4671
|
}
|
|
@@ -4609,7 +4677,9 @@ var StepProgressBar = class {
|
|
|
4609
4677
|
this.stopProgress();
|
|
4610
4678
|
const totalTime = Math.floor((Date.now() - this.startTime) / 1e3);
|
|
4611
4679
|
const progressBar = this.createProgressBar(1);
|
|
4612
|
-
this.spinner.succeed(
|
|
4680
|
+
this.spinner.succeed(
|
|
4681
|
+
`Upload completed ${progressBar} 100% (${totalTime}s)`
|
|
4682
|
+
);
|
|
4613
4683
|
}
|
|
4614
4684
|
fail(error) {
|
|
4615
4685
|
this.stopProgress();
|
|
@@ -4639,7 +4709,10 @@ var StepProgressBar = class {
|
|
|
4639
4709
|
}
|
|
4640
4710
|
}
|
|
4641
4711
|
calculateProgress(elapsed) {
|
|
4642
|
-
return Math.min(
|
|
4712
|
+
return Math.min(
|
|
4713
|
+
elapsed / EXPECTED_UPLOAD_TIME * MAX_PROGRESS,
|
|
4714
|
+
MAX_PROGRESS
|
|
4715
|
+
);
|
|
4643
4716
|
}
|
|
4644
4717
|
createProgressBar(progress, width = 20) {
|
|
4645
4718
|
const percentage = Math.min(progress, 1);
|
|
@@ -4665,7 +4738,7 @@ var StepProgressBar = class {
|
|
|
4665
4738
|
async function calculateMD5(filePath) {
|
|
4666
4739
|
return new Promise((resolve, reject) => {
|
|
4667
4740
|
const hash = crypto.createHash("md5");
|
|
4668
|
-
const stream4 =
|
|
4741
|
+
const stream4 = import_fs_extra4.default.createReadStream(filePath);
|
|
4669
4742
|
stream4.on("data", hash.update.bind(hash));
|
|
4670
4743
|
stream4.on("end", () => resolve(hash.digest("hex")));
|
|
4671
4744
|
stream4.on("error", reject);
|
|
@@ -4674,20 +4747,20 @@ async function calculateMD5(filePath) {
|
|
|
4674
4747
|
async function compressDirectory(sourcePath) {
|
|
4675
4748
|
return new Promise((resolve, reject) => {
|
|
4676
4749
|
const tempDir = require("os").tmpdir();
|
|
4677
|
-
if (!
|
|
4678
|
-
|
|
4750
|
+
if (!import_fs_extra4.default.existsSync(tempDir)) {
|
|
4751
|
+
import_fs_extra4.default.mkdirSync(tempDir, { recursive: true });
|
|
4679
4752
|
}
|
|
4680
|
-
const outputPath =
|
|
4753
|
+
const outputPath = import_path5.default.join(
|
|
4681
4754
|
tempDir,
|
|
4682
|
-
`pinme_${
|
|
4755
|
+
`pinme_${import_path5.default.basename(sourcePath)}_${Date.now()}.zip`
|
|
4683
4756
|
);
|
|
4684
|
-
const output =
|
|
4757
|
+
const output = import_fs_extra4.default.createWriteStream(outputPath);
|
|
4685
4758
|
const zlib2 = require("zlib");
|
|
4686
4759
|
const gzip = zlib2.createGzip({ level: 9 });
|
|
4687
4760
|
output.on("close", () => resolve(outputPath));
|
|
4688
4761
|
gzip.on("error", reject);
|
|
4689
4762
|
gzip.pipe(output);
|
|
4690
|
-
const stats =
|
|
4763
|
+
const stats = import_fs_extra4.default.statSync(sourcePath);
|
|
4691
4764
|
if (stats.isDirectory()) {
|
|
4692
4765
|
const archive = require("archiver");
|
|
4693
4766
|
const archiveStream = archive("zip", { zlib: { level: 9 } });
|
|
@@ -4696,14 +4769,14 @@ async function compressDirectory(sourcePath) {
|
|
|
4696
4769
|
archiveStream.directory(sourcePath, false);
|
|
4697
4770
|
archiveStream.finalize();
|
|
4698
4771
|
} else {
|
|
4699
|
-
const fileStream =
|
|
4772
|
+
const fileStream = import_fs_extra4.default.createReadStream(sourcePath);
|
|
4700
4773
|
fileStream.pipe(gzip);
|
|
4701
4774
|
}
|
|
4702
4775
|
});
|
|
4703
4776
|
}
|
|
4704
4777
|
async function initChunkSession(filePath, deviceId, isDirectory = false) {
|
|
4705
|
-
const stats =
|
|
4706
|
-
const fileName =
|
|
4778
|
+
const stats = import_fs_extra4.default.statSync(filePath);
|
|
4779
|
+
const fileName = import_path5.default.basename(filePath);
|
|
4707
4780
|
const fileSize = stats.size;
|
|
4708
4781
|
const md5 = await calculateMD5(filePath);
|
|
4709
4782
|
try {
|
|
@@ -4804,7 +4877,7 @@ async function delayWithAbortCheck(delay, signal) {
|
|
|
4804
4877
|
});
|
|
4805
4878
|
}
|
|
4806
4879
|
async function uploadFileChunks(filePath, sessionId, totalChunks, chunkSize, deviceId, progressBar) {
|
|
4807
|
-
const fileData =
|
|
4880
|
+
const fileData = import_fs_extra4.default.readFileSync(filePath);
|
|
4808
4881
|
const abortController = new AbortController();
|
|
4809
4882
|
let completedCount = 0;
|
|
4810
4883
|
let hasFatalError = false;
|
|
@@ -4839,10 +4912,14 @@ async function uploadFileChunks(filePath, sessionId, totalChunks, chunkSize, dev
|
|
|
4839
4912
|
});
|
|
4840
4913
|
try {
|
|
4841
4914
|
const results = await Promise.allSettled(uploadTasks.map((task) => task()));
|
|
4842
|
-
const failedResults = results.filter(
|
|
4915
|
+
const failedResults = results.filter(
|
|
4916
|
+
(result) => result.status === "rejected"
|
|
4917
|
+
);
|
|
4843
4918
|
if (failedResults.length > 0) {
|
|
4844
4919
|
const firstFailure = failedResults[0];
|
|
4845
|
-
throw new Error(
|
|
4920
|
+
throw new Error(
|
|
4921
|
+
firstFailure.reason.message || "Error occurred during upload"
|
|
4922
|
+
);
|
|
4846
4923
|
}
|
|
4847
4924
|
if (hasFatalError) {
|
|
4848
4925
|
throw new Error(fatalError || "Unknown error occurred during upload");
|
|
@@ -4935,10 +5012,12 @@ async function uploadDirectoryInChunks(directoryPath, deviceId) {
|
|
|
4935
5012
|
const sizeCheck = checkDirectorySizeLimit(directoryPath);
|
|
4936
5013
|
if (sizeCheck.exceeds) {
|
|
4937
5014
|
throw new Error(
|
|
4938
|
-
`Directory ${directoryPath} exceeds size limit ${formatSize(
|
|
5015
|
+
`Directory ${directoryPath} exceeds size limit ${formatSize(
|
|
5016
|
+
sizeCheck.limit
|
|
5017
|
+
)} (size: ${formatSize(sizeCheck.size)})`
|
|
4939
5018
|
);
|
|
4940
5019
|
}
|
|
4941
|
-
const progressBar = new StepProgressBar(
|
|
5020
|
+
const progressBar = new StepProgressBar(import_path5.default.basename(directoryPath), true);
|
|
4942
5021
|
try {
|
|
4943
5022
|
progressBar.startStep(0, "Preparing compression");
|
|
4944
5023
|
const compressedPath = await compressDirectory(directoryPath);
|
|
@@ -4963,12 +5042,12 @@ async function uploadDirectoryInChunks(directoryPath, deviceId) {
|
|
|
4963
5042
|
const result = await monitorChunkProgress(traceId, deviceId, progressBar);
|
|
4964
5043
|
progressBar.completeStep();
|
|
4965
5044
|
try {
|
|
4966
|
-
|
|
5045
|
+
import_fs_extra4.default.unlinkSync(compressedPath);
|
|
4967
5046
|
} catch (error) {
|
|
4968
5047
|
}
|
|
4969
5048
|
const uploadData = {
|
|
4970
5049
|
path: directoryPath,
|
|
4971
|
-
filename:
|
|
5050
|
+
filename: import_path5.default.basename(directoryPath),
|
|
4972
5051
|
contentHash: (result == null ? void 0 : result.hash) || "unknown",
|
|
4973
5052
|
size: sizeCheck.size,
|
|
4974
5053
|
fileCount: 0,
|
|
@@ -4990,10 +5069,12 @@ async function uploadFileInChunks(filePath, deviceId) {
|
|
|
4990
5069
|
const sizeCheck = checkFileSizeLimit(filePath);
|
|
4991
5070
|
if (sizeCheck.exceeds) {
|
|
4992
5071
|
throw new Error(
|
|
4993
|
-
`File ${filePath} exceeds size limit ${formatSize(
|
|
5072
|
+
`File ${filePath} exceeds size limit ${formatSize(
|
|
5073
|
+
sizeCheck.limit
|
|
5074
|
+
)} (size: ${formatSize(sizeCheck.size)})`
|
|
4994
5075
|
);
|
|
4995
5076
|
}
|
|
4996
|
-
const fileName =
|
|
5077
|
+
const fileName = import_path5.default.basename(filePath);
|
|
4997
5078
|
const progressBar = new StepProgressBar(fileName, false);
|
|
4998
5079
|
try {
|
|
4999
5080
|
progressBar.startStep(0, "Initializing session");
|
|
@@ -5037,12 +5118,12 @@ async function uploadFileInChunks(filePath, deviceId) {
|
|
|
5037
5118
|
}
|
|
5038
5119
|
}
|
|
5039
5120
|
async function uploadToIpfsSplit_default(filePath) {
|
|
5040
|
-
const deviceId =
|
|
5121
|
+
const deviceId = getUid();
|
|
5041
5122
|
if (!deviceId) {
|
|
5042
5123
|
throw new Error("Device ID not found");
|
|
5043
5124
|
}
|
|
5044
5125
|
try {
|
|
5045
|
-
const isDirectory =
|
|
5126
|
+
const isDirectory = import_fs_extra4.default.statSync(filePath).isDirectory();
|
|
5046
5127
|
const result = isDirectory ? await uploadDirectoryInChunks(filePath, deviceId) : await uploadFileInChunks(filePath, deviceId);
|
|
5047
5128
|
if (result == null ? void 0 : result.hash) {
|
|
5048
5129
|
return {
|
|
@@ -5063,58 +5144,6 @@ var import_crypto_js = __toESM(require("crypto-js"));
|
|
|
5063
5144
|
|
|
5064
5145
|
// bin/utils/pinmeApi.ts
|
|
5065
5146
|
var import_chalk3 = __toESM(require("chalk"));
|
|
5066
|
-
|
|
5067
|
-
// bin/utils/auth.ts
|
|
5068
|
-
var import_fs_extra4 = __toESM(require("fs-extra"));
|
|
5069
|
-
var import_os3 = __toESM(require("os"));
|
|
5070
|
-
var import_path5 = __toESM(require("path"));
|
|
5071
|
-
var CONFIG_DIR = import_path5.default.join(import_os3.default.homedir(), ".pinme");
|
|
5072
|
-
var AUTH_FILE = import_path5.default.join(CONFIG_DIR, "auth.json");
|
|
5073
|
-
function ensureConfigDir() {
|
|
5074
|
-
if (!import_fs_extra4.default.existsSync(CONFIG_DIR)) {
|
|
5075
|
-
import_fs_extra4.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
5076
|
-
}
|
|
5077
|
-
}
|
|
5078
|
-
function parseCombinedToken(combined) {
|
|
5079
|
-
const firstDash = combined.indexOf("-");
|
|
5080
|
-
if (firstDash <= 0 || firstDash === combined.length - 1) {
|
|
5081
|
-
throw new Error('Invalid token format. Expected "<address>-<jwt>".');
|
|
5082
|
-
}
|
|
5083
|
-
const address = combined.slice(0, firstDash).trim();
|
|
5084
|
-
const token = combined.slice(firstDash + 1).trim();
|
|
5085
|
-
if (!address || !token) {
|
|
5086
|
-
throw new Error("Invalid token content. Address or token is empty.");
|
|
5087
|
-
}
|
|
5088
|
-
return { address, token };
|
|
5089
|
-
}
|
|
5090
|
-
function setAuthToken(combined) {
|
|
5091
|
-
ensureConfigDir();
|
|
5092
|
-
const auth = parseCombinedToken(combined);
|
|
5093
|
-
import_fs_extra4.default.writeJsonSync(AUTH_FILE, auth, { spaces: 2 });
|
|
5094
|
-
return auth;
|
|
5095
|
-
}
|
|
5096
|
-
function getAuthConfig() {
|
|
5097
|
-
try {
|
|
5098
|
-
if (!import_fs_extra4.default.existsSync(AUTH_FILE)) return null;
|
|
5099
|
-
const data = import_fs_extra4.default.readJsonSync(AUTH_FILE);
|
|
5100
|
-
if (!(data == null ? void 0 : data.address) || !(data == null ? void 0 : data.token)) return null;
|
|
5101
|
-
return data;
|
|
5102
|
-
} catch {
|
|
5103
|
-
return null;
|
|
5104
|
-
}
|
|
5105
|
-
}
|
|
5106
|
-
function getAuthHeaders() {
|
|
5107
|
-
const conf = getAuthConfig();
|
|
5108
|
-
if (!conf) {
|
|
5109
|
-
throw new Error("Auth not set. Run: pinme set-appkey <AppKey>");
|
|
5110
|
-
}
|
|
5111
|
-
return {
|
|
5112
|
-
"token-address": conf.address,
|
|
5113
|
-
"authentication-tokens": conf.token
|
|
5114
|
-
};
|
|
5115
|
-
}
|
|
5116
|
-
|
|
5117
|
-
// bin/utils/pinmeApi.ts
|
|
5118
5147
|
var DEFAULT_BASE = process.env.PINME_API_BASE || "http://ipfs-proxy.opena.chat/api/v4";
|
|
5119
5148
|
function createClient() {
|
|
5120
5149
|
const headers = getAuthHeaders();
|
|
@@ -5222,13 +5251,6 @@ function getDomainFromArgs() {
|
|
|
5222
5251
|
}
|
|
5223
5252
|
return null;
|
|
5224
5253
|
}
|
|
5225
|
-
function getUid() {
|
|
5226
|
-
const auth = getAuthConfig();
|
|
5227
|
-
if (auth == null ? void 0 : auth.address) {
|
|
5228
|
-
return auth.address;
|
|
5229
|
-
}
|
|
5230
|
-
return getDeviceId();
|
|
5231
|
-
}
|
|
5232
5254
|
var upload_default = async (options) => {
|
|
5233
5255
|
try {
|
|
5234
5256
|
console.log(
|
|
@@ -5251,7 +5273,11 @@ var upload_default = async (options) => {
|
|
|
5251
5273
|
if (domainArg) {
|
|
5252
5274
|
const check = await checkDomainAvailable(domainArg);
|
|
5253
5275
|
if (!check.is_valid) {
|
|
5254
|
-
console.log(
|
|
5276
|
+
console.log(
|
|
5277
|
+
import_chalk4.default.red(
|
|
5278
|
+
`Domain not available: ${check.error || "unknown reason"}`
|
|
5279
|
+
)
|
|
5280
|
+
);
|
|
5255
5281
|
return;
|
|
5256
5282
|
}
|
|
5257
5283
|
console.log(import_chalk4.default.green(`Domain available: ${domainArg}`));
|
|
@@ -5270,11 +5296,19 @@ var upload_default = async (options) => {
|
|
|
5270
5296
|
console.log(import_chalk4.default.cyan(`URL:`));
|
|
5271
5297
|
console.log(import_chalk4.default.cyan(`${URL2}${encryptedCID}`));
|
|
5272
5298
|
if (domainArg) {
|
|
5273
|
-
console.log(
|
|
5299
|
+
console.log(
|
|
5300
|
+
import_chalk4.default.blue(
|
|
5301
|
+
`Binding domain: ${domainArg} with CID: ${result.contentHash}`
|
|
5302
|
+
)
|
|
5303
|
+
);
|
|
5274
5304
|
const ok = await bindPinmeDomain(domainArg, result.contentHash);
|
|
5275
5305
|
if (ok) {
|
|
5276
5306
|
console.log(import_chalk4.default.green(`Bind success: ${domainArg}`));
|
|
5277
|
-
console.log(
|
|
5307
|
+
console.log(
|
|
5308
|
+
import_chalk4.default.white(
|
|
5309
|
+
`Visit (Pinme subdomain example): https://${domainArg}.pinit.eth.limo`
|
|
5310
|
+
)
|
|
5311
|
+
);
|
|
5278
5312
|
} else {
|
|
5279
5313
|
console.log(import_chalk4.default.red("Binding failed. Please try again later."));
|
|
5280
5314
|
}
|
|
@@ -5302,7 +5336,11 @@ var upload_default = async (options) => {
|
|
|
5302
5336
|
if (domainArg) {
|
|
5303
5337
|
const check = await checkDomainAvailable(domainArg);
|
|
5304
5338
|
if (!check.is_valid) {
|
|
5305
|
-
console.log(
|
|
5339
|
+
console.log(
|
|
5340
|
+
import_chalk4.default.red(
|
|
5341
|
+
`Domain not available: ${check.error || "unknown reason"}`
|
|
5342
|
+
)
|
|
5343
|
+
);
|
|
5306
5344
|
return;
|
|
5307
5345
|
}
|
|
5308
5346
|
console.log(import_chalk4.default.green(`Domain available: ${domainArg}`));
|
|
@@ -5321,11 +5359,19 @@ var upload_default = async (options) => {
|
|
|
5321
5359
|
console.log(import_chalk4.default.cyan(`URL:`));
|
|
5322
5360
|
console.log(import_chalk4.default.cyan(`${URL2}${encryptedCID}`));
|
|
5323
5361
|
if (domainArg) {
|
|
5324
|
-
console.log(
|
|
5362
|
+
console.log(
|
|
5363
|
+
import_chalk4.default.blue(
|
|
5364
|
+
`Binding domain: ${domainArg} with CID: ${result.contentHash}`
|
|
5365
|
+
)
|
|
5366
|
+
);
|
|
5325
5367
|
const ok = await bindPinmeDomain(domainArg, result.contentHash);
|
|
5326
5368
|
if (ok) {
|
|
5327
5369
|
console.log(import_chalk4.default.green(`Bind success: ${domainArg}`));
|
|
5328
|
-
console.log(
|
|
5370
|
+
console.log(
|
|
5371
|
+
import_chalk4.default.white(
|
|
5372
|
+
`Visit (Pinme subdomain example): https://${domainArg}.pinit.eth.limo`
|
|
5373
|
+
)
|
|
5374
|
+
);
|
|
5329
5375
|
} else {
|
|
5330
5376
|
console.log(import_chalk4.default.red("Binding failed. Please try again later."));
|
|
5331
5377
|
}
|
|
@@ -5353,7 +5399,7 @@ var import_chalk5 = __toESM(require("chalk"));
|
|
|
5353
5399
|
var ipfsApiUrl = "https://pinme.dev/api/v3";
|
|
5354
5400
|
async function removeFromIpfs(value, type = "hash") {
|
|
5355
5401
|
try {
|
|
5356
|
-
const uid =
|
|
5402
|
+
const uid = getUid();
|
|
5357
5403
|
console.log(import_chalk5.default.blue(`Removing content from IPFS: ${value}...`));
|
|
5358
5404
|
const queryParams = new URLSearchParams({
|
|
5359
5405
|
uid
|
|
@@ -5363,14 +5409,21 @@ async function removeFromIpfs(value, type = "hash") {
|
|
|
5363
5409
|
} else {
|
|
5364
5410
|
queryParams.append("arg", value);
|
|
5365
5411
|
}
|
|
5366
|
-
const response = await axios_default.post(
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5412
|
+
const response = await axios_default.post(
|
|
5413
|
+
`${ipfsApiUrl}/block/rm?${queryParams.toString()}`,
|
|
5414
|
+
{
|
|
5415
|
+
timeout: 3e4
|
|
5416
|
+
// 30 seconds timeout
|
|
5417
|
+
}
|
|
5418
|
+
);
|
|
5370
5419
|
const { code, msg, data } = response.data;
|
|
5371
5420
|
if (code === 200) {
|
|
5372
5421
|
console.log(import_chalk5.default.green("\u2713 Removal successful!"));
|
|
5373
|
-
console.log(
|
|
5422
|
+
console.log(
|
|
5423
|
+
import_chalk5.default.cyan(
|
|
5424
|
+
`Content ${type}: ${value} has been removed from IPFS network`
|
|
5425
|
+
)
|
|
5426
|
+
);
|
|
5374
5427
|
return true;
|
|
5375
5428
|
} else {
|
|
5376
5429
|
console.log(import_chalk5.default.red("\u2717 Removal failed"));
|
|
@@ -5381,17 +5434,31 @@ async function removeFromIpfs(value, type = "hash") {
|
|
|
5381
5434
|
console.log(import_chalk5.default.red("\u2717 Removal failed", error));
|
|
5382
5435
|
if (error.response) {
|
|
5383
5436
|
const { status, data } = error.response;
|
|
5384
|
-
console.log(
|
|
5437
|
+
console.log(
|
|
5438
|
+
import_chalk5.default.red(`HTTP Error ${status}: ${(data == null ? void 0 : data.msg) || "Server error"}`)
|
|
5439
|
+
);
|
|
5385
5440
|
if (status === 404) {
|
|
5386
|
-
console.log(
|
|
5441
|
+
console.log(
|
|
5442
|
+
import_chalk5.default.yellow("Content not found on the network or already removed")
|
|
5443
|
+
);
|
|
5387
5444
|
} else if (status === 403) {
|
|
5388
|
-
console.log(
|
|
5445
|
+
console.log(
|
|
5446
|
+
import_chalk5.default.yellow(
|
|
5447
|
+
"Permission denied - you may not have access to remove this content"
|
|
5448
|
+
)
|
|
5449
|
+
);
|
|
5389
5450
|
} else if (status === 500) {
|
|
5390
|
-
console.log(
|
|
5451
|
+
console.log(
|
|
5452
|
+
import_chalk5.default.yellow("Server internal error - please try again later")
|
|
5453
|
+
);
|
|
5391
5454
|
}
|
|
5392
5455
|
} else if (error.request) {
|
|
5393
|
-
console.log(
|
|
5394
|
-
|
|
5456
|
+
console.log(
|
|
5457
|
+
import_chalk5.default.red("Network error: Unable to connect to IPFS service")
|
|
5458
|
+
);
|
|
5459
|
+
console.log(
|
|
5460
|
+
import_chalk5.default.yellow("Please check your internet connection and try again")
|
|
5461
|
+
);
|
|
5395
5462
|
} else {
|
|
5396
5463
|
console.log(import_chalk5.default.red(`Error: ${error.message}`));
|
|
5397
5464
|
}
|
|
@@ -5579,32 +5646,93 @@ async function setAppKeyCmd() {
|
|
|
5579
5646
|
}
|
|
5580
5647
|
}
|
|
5581
5648
|
|
|
5582
|
-
// bin/
|
|
5649
|
+
// bin/logout.ts
|
|
5583
5650
|
var import_chalk8 = __toESM(require("chalk"));
|
|
5651
|
+
var import_inquirer4 = __toESM(require("inquirer"));
|
|
5652
|
+
async function logoutCmd() {
|
|
5653
|
+
try {
|
|
5654
|
+
const auth = getAuthConfig();
|
|
5655
|
+
if (!auth) {
|
|
5656
|
+
console.log(import_chalk8.default.yellow("No active session found. You are already logged out."));
|
|
5657
|
+
return;
|
|
5658
|
+
}
|
|
5659
|
+
const answer = await import_inquirer4.default.prompt([
|
|
5660
|
+
{
|
|
5661
|
+
type: "confirm",
|
|
5662
|
+
name: "confirm",
|
|
5663
|
+
message: `Are you sure you want to log out? (Current address: ${auth.address})`,
|
|
5664
|
+
default: false
|
|
5665
|
+
}
|
|
5666
|
+
]);
|
|
5667
|
+
if (!answer.confirm) {
|
|
5668
|
+
console.log(import_chalk8.default.blue("Logout cancelled."));
|
|
5669
|
+
return;
|
|
5670
|
+
}
|
|
5671
|
+
clearAuthToken();
|
|
5672
|
+
console.log(import_chalk8.default.green("Successfully logged out."));
|
|
5673
|
+
console.log(import_chalk8.default.gray(`Address ${auth.address} has been removed from local storage.`));
|
|
5674
|
+
} catch (e) {
|
|
5675
|
+
console.log(import_chalk8.default.red(`Failed to logout: ${(e == null ? void 0 : e.message) || e}`));
|
|
5676
|
+
}
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
// bin/show-appkey.ts
|
|
5680
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
5681
|
+
function showAppKeyCmd() {
|
|
5682
|
+
try {
|
|
5683
|
+
const auth = getAuthConfig();
|
|
5684
|
+
if (!auth) {
|
|
5685
|
+
console.log(import_chalk9.default.yellow("No AppKey found. Please set your AppKey first."));
|
|
5686
|
+
console.log(import_chalk9.default.gray("Run: pinme set-appkey <AppKey>"));
|
|
5687
|
+
return;
|
|
5688
|
+
}
|
|
5689
|
+
console.log(import_chalk9.default.green("Current AppKey Information:"));
|
|
5690
|
+
console.log(import_chalk9.default.cyan(` Address: ${auth.address}`));
|
|
5691
|
+
const token = auth.token;
|
|
5692
|
+
if (token.length > 12) {
|
|
5693
|
+
const maskedToken = `${token.substring(0, 8)}${"*".repeat(token.length - 12)}${token.substring(token.length - 4)}`;
|
|
5694
|
+
console.log(import_chalk9.default.cyan(` Token: ${maskedToken}`));
|
|
5695
|
+
} else {
|
|
5696
|
+
console.log(import_chalk9.default.cyan(` Token: ${"*".repeat(token.length)}`));
|
|
5697
|
+
}
|
|
5698
|
+
const combined = `${auth.address}-${auth.token}`;
|
|
5699
|
+
if (combined.length > 20) {
|
|
5700
|
+
const maskedAppKey = `${combined.substring(0, 12)}${"*".repeat(combined.length - 16)}${combined.substring(combined.length - 4)}`;
|
|
5701
|
+
console.log(import_chalk9.default.cyan(` AppKey: ${maskedAppKey}`));
|
|
5702
|
+
} else {
|
|
5703
|
+
console.log(import_chalk9.default.cyan(` AppKey: ${"*".repeat(combined.length)}`));
|
|
5704
|
+
}
|
|
5705
|
+
} catch (e) {
|
|
5706
|
+
console.log(import_chalk9.default.red(`Failed to show AppKey: ${(e == null ? void 0 : e.message) || e}`));
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
|
|
5710
|
+
// bin/my-domains.ts
|
|
5711
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
5584
5712
|
var import_dayjs2 = __toESM(require("dayjs"));
|
|
5585
5713
|
async function myDomainsCmd() {
|
|
5586
5714
|
try {
|
|
5587
5715
|
const list = await getMyDomains();
|
|
5588
5716
|
if (!list.length) {
|
|
5589
|
-
console.log(
|
|
5717
|
+
console.log(import_chalk10.default.yellow("No bound domains found."));
|
|
5590
5718
|
return;
|
|
5591
5719
|
}
|
|
5592
|
-
console.log(
|
|
5593
|
-
console.log(
|
|
5720
|
+
console.log(import_chalk10.default.cyan("My domains:"));
|
|
5721
|
+
console.log(import_chalk10.default.cyan("-".repeat(80)));
|
|
5594
5722
|
list.forEach((item, i) => {
|
|
5595
|
-
console.log(
|
|
5596
|
-
console.log(
|
|
5723
|
+
console.log(import_chalk10.default.green(`${i + 1}. ${item.domain_name}`));
|
|
5724
|
+
console.log(import_chalk10.default.white(` Type: ${item.domain_type}`));
|
|
5597
5725
|
if (item.bind_time) {
|
|
5598
|
-
console.log(
|
|
5726
|
+
console.log(import_chalk10.default.white(` Bind time: ${(0, import_dayjs2.default)(item.bind_time * 1e3).format("YYYY-MM-DD HH:mm:ss")}`));
|
|
5599
5727
|
}
|
|
5600
5728
|
if (typeof item.expire_time === "number") {
|
|
5601
5729
|
const label = item.expire_time === 0 ? "Never" : (0, import_dayjs2.default)(item.expire_time * 1e3).format("YYYY-MM-DD HH:mm:ss");
|
|
5602
|
-
console.log(
|
|
5730
|
+
console.log(import_chalk10.default.white(` Expire time: ${label}`));
|
|
5603
5731
|
}
|
|
5604
|
-
console.log(
|
|
5732
|
+
console.log(import_chalk10.default.cyan("-".repeat(80)));
|
|
5605
5733
|
});
|
|
5606
5734
|
} catch (e) {
|
|
5607
|
-
console.log(
|
|
5735
|
+
console.log(import_chalk10.default.red(`Failed to fetch domains: ${(e == null ? void 0 : e.message) || e}`));
|
|
5608
5736
|
}
|
|
5609
5737
|
}
|
|
5610
5738
|
|
|
@@ -5613,27 +5741,39 @@ import_dotenv.default.config();
|
|
|
5613
5741
|
checkNodeVersion();
|
|
5614
5742
|
function showBanner() {
|
|
5615
5743
|
console.log(
|
|
5616
|
-
|
|
5617
|
-
import_figlet3.default.textSync("Pinme", { horizontalLayout: "full" })
|
|
5618
|
-
)
|
|
5744
|
+
import_chalk11.default.cyan(import_figlet3.default.textSync("Pinme", { horizontalLayout: "full" }))
|
|
5619
5745
|
);
|
|
5620
|
-
console.log(
|
|
5746
|
+
console.log(import_chalk11.default.cyan("A command-line tool for uploading files to IPFS\n"));
|
|
5621
5747
|
}
|
|
5622
5748
|
var program = new import_commander.Command();
|
|
5623
5749
|
program.name("pinme").version(version).option("-v, --version", "output the current version");
|
|
5624
|
-
program.command("upload").description(
|
|
5750
|
+
program.command("upload").description(
|
|
5751
|
+
"upload a file or directory to IPFS. Supports --domain to bind after upload"
|
|
5752
|
+
).option("-d, --domain <name>", "Pinme subdomain").action(() => upload_default());
|
|
5625
5753
|
program.command("rm").description("remove a file from IPFS network").action(() => remove_default());
|
|
5626
|
-
program.command("set-appkey").description(
|
|
5754
|
+
program.command("set-appkey").description(
|
|
5755
|
+
"Set AppKey for authentication, and auto-merge anonymous history"
|
|
5756
|
+
).action(() => setAppKeyCmd());
|
|
5757
|
+
program.command("logout").description("log out and clear authentication").action(() => logoutCmd());
|
|
5758
|
+
program.command("show-appkey").alias("appkey").description("show current AppKey information (masked)").action(() => showAppKeyCmd());
|
|
5627
5759
|
program.command("my-domains").alias("domain").description("List domains owned by current account").action(() => myDomainsCmd());
|
|
5628
5760
|
program.command("domain").description("Alias for 'my-domains' command").action(() => myDomainsCmd());
|
|
5629
|
-
program.command("list").description("show upload history").option(
|
|
5761
|
+
program.command("list").description("show upload history").option(
|
|
5762
|
+
"-l, --limit <number>",
|
|
5763
|
+
"limit the number of records to show",
|
|
5764
|
+
parseInt
|
|
5765
|
+
).option("-c, --clear", "clear all upload history").action((options) => {
|
|
5630
5766
|
if (options.clear) {
|
|
5631
5767
|
clearUploadHistory();
|
|
5632
5768
|
} else {
|
|
5633
5769
|
displayUploadHistory(options.limit || 10);
|
|
5634
5770
|
}
|
|
5635
5771
|
});
|
|
5636
|
-
program.command("ls").description("alias for 'list' command").option(
|
|
5772
|
+
program.command("ls").description("alias for 'list' command").option(
|
|
5773
|
+
"-l, --limit <number>",
|
|
5774
|
+
"limit the number of records to show",
|
|
5775
|
+
parseInt
|
|
5776
|
+
).option("-c, --clear", "clear all upload history").action((options) => {
|
|
5637
5777
|
if (options.clear) {
|
|
5638
5778
|
clearUploadHistory();
|
|
5639
5779
|
} else {
|
|
@@ -5651,13 +5791,17 @@ program.on("--help", () => {
|
|
|
5651
5791
|
console.log(" $ pinme upload <path> --domain <name>");
|
|
5652
5792
|
console.log(" $ pinme rm <hash>");
|
|
5653
5793
|
console.log(" $ pinme set-appkey <AppKey>");
|
|
5794
|
+
console.log(" $ pinme show-appkey");
|
|
5795
|
+
console.log(" $ pinme logout");
|
|
5654
5796
|
console.log(" $ pinme my-domains");
|
|
5655
5797
|
console.log(" $ pinme domain");
|
|
5656
5798
|
console.log(" $ pinme list -l 5");
|
|
5657
5799
|
console.log(" $ pinme ls");
|
|
5658
5800
|
console.log(" $ pinme help");
|
|
5659
5801
|
console.log("");
|
|
5660
|
-
console.log(
|
|
5802
|
+
console.log(
|
|
5803
|
+
"For more information, visit: https://github.com/glitternetwork/pinme"
|
|
5804
|
+
);
|
|
5661
5805
|
});
|
|
5662
5806
|
program.parse(process.argv);
|
|
5663
5807
|
if (process.argv.length === 2) {
|