mirror of
https://github.com/glitternetwork/pinme.git
synced 2026-08-28 17:42:38 +08:00
feat: upload require auth and send auth headers on upload APIs
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
# PinMe
|
||||
|
||||
[PinMe](https://pinme.eth.limo/) is a zero-config frontend deployment tool.
|
||||
No servers. No accounts. No setup.
|
||||
No servers to manage. Minimal setup.
|
||||
|
||||
Build a static site, generate a page with AI, export your frontend, or import CAR files — then deploy instantly with a single command.
|
||||
|
||||
@@ -55,14 +55,23 @@ Website: [https://pinme.eth.limo/](https://pinme.eth.limo/)
|
||||
node --version
|
||||
```
|
||||
|
||||
### Three Steps to Deploy
|
||||
### Four Steps to Deploy
|
||||
|
||||
1. **Install PinMe**
|
||||
```bash
|
||||
npm install -g pinme
|
||||
```
|
||||
|
||||
2. **Build your project** (if not already built)
|
||||
2. **Login or set AppKey**
|
||||
```bash
|
||||
# Recommended: browser login
|
||||
pinme login
|
||||
|
||||
# Or use an AppKey in CLI/CI environments
|
||||
pinme set-appkey <AppKey>
|
||||
```
|
||||
|
||||
3. **Build your project** (if not already built)
|
||||
```bash
|
||||
# For Vite/React/Vue projects
|
||||
npm run build
|
||||
@@ -71,7 +80,7 @@ npm run build
|
||||
npm run build
|
||||
```
|
||||
|
||||
3. **Upload to PinMe**
|
||||
4. **Upload to PinMe**
|
||||
```bash
|
||||
# Upload the dist directory (most common)
|
||||
pinme upload dist
|
||||
@@ -105,7 +114,16 @@ This section provides AI-specific instructions for deploying websites using PinM
|
||||
npm install -g pinme
|
||||
```
|
||||
|
||||
3. **Identify the Static File Directory**
|
||||
3. **Authenticate before upload**
|
||||
```bash
|
||||
# Recommended
|
||||
pinme login
|
||||
|
||||
# Or for automation / non-browser environments
|
||||
pinme set-appkey <AppKey>
|
||||
```
|
||||
|
||||
4. **Identify the Static File Directory**
|
||||
Priority order to check:
|
||||
- `dist/` - Default output directory for Vue/React/Vite projects
|
||||
- `build/` - Output directory for Create React App
|
||||
@@ -125,12 +143,12 @@ This section provides AI-specific instructions for deploying websites using PinM
|
||||
- For React: use `HashRouter` instead of `BrowserRouter`
|
||||
- For Vue: use `createHashHistory()` instead of `createWebHistory()`
|
||||
|
||||
4. **Execute Deployment Command**
|
||||
5. **Execute Deployment Command**
|
||||
```bash
|
||||
pinme upload <folder_path>
|
||||
```
|
||||
|
||||
5. **Return Result**
|
||||
6. **Return Result**
|
||||
- Only return the final URL printed by PinMe
|
||||
- URL priority is: DNS domain > Pinme subdomain > short URL > hash preview URL
|
||||
- Hash fallback format: `https://pinme.eth.limo/#/preview/*`
|
||||
@@ -154,6 +172,9 @@ For easy AI tool parsing:
|
||||
"node_version": ">=16.13.0"
|
||||
},
|
||||
"install": "npm install -g pinme",
|
||||
"login": "pinme login",
|
||||
"set_appkey": "pinme set-appkey <AppKey>",
|
||||
"auth_required_for_upload": true,
|
||||
"upload": "pinme upload {{directory}}",
|
||||
"upload_with_domain": "pinme upload {{directory}} --domain {{domain}}",
|
||||
"upload_with_dns": "pinme upload {{directory}} --domain {{domain}} --dns",
|
||||
@@ -171,7 +192,6 @@ For easy AI tool parsing:
|
||||
"dns_domain_format": "https://<your-domain>",
|
||||
"other_commands": {
|
||||
"version": "pinme --version",
|
||||
"login": "pinme login",
|
||||
"list": "pinme list",
|
||||
"import": "pinme import <car-file>",
|
||||
"export": "pinme export <cid>",
|
||||
@@ -201,9 +221,10 @@ For easy AI tool parsing:
|
||||
> **Operation Steps:**
|
||||
> 1. Check Node.js version (requires 16.13.0+)
|
||||
> 2. Check if pinme is installed, install if not
|
||||
> 3. Identify the static file directory for the website to deploy
|
||||
> 4. Execute deployment command
|
||||
> 5. Return the final public URL printed by PinMe. If no custom URL is available, return the preview page link: `https://pinme.eth.limo/#/preview/*`
|
||||
> 3. Authenticate with `pinme login` or `pinme set-appkey <AppKey>`
|
||||
> 4. Identify the static file directory for the website to deploy
|
||||
> 5. Execute deployment command
|
||||
> 6. Return the final public URL printed by PinMe. If no custom URL is available, return the preview page link: `https://pinme.eth.limo/#/preview/*`
|
||||
|
||||
---
|
||||
|
||||
@@ -235,6 +256,9 @@ pinme --version
|
||||
### Upload Files or Directories
|
||||
|
||||
```bash
|
||||
# Login is required before upload
|
||||
pinme login
|
||||
|
||||
# Interactive upload
|
||||
pinme upload
|
||||
|
||||
@@ -242,6 +266,8 @@ pinme upload
|
||||
pinme upload /path/to/file-or-directory
|
||||
```
|
||||
|
||||
**Authentication requirement:** `pinme upload` and `pinme import` require a valid login session or AppKey. Use `pinme login` for browser login, or `pinme set-appkey <AppKey>` for CLI/CI environments.
|
||||
|
||||
### Bind Domain (requires wallet balance)
|
||||
|
||||
```bash
|
||||
@@ -311,6 +337,9 @@ pinme delete my-app --force
|
||||
### Import CAR files
|
||||
|
||||
```bash
|
||||
# Login is required before import
|
||||
pinme login
|
||||
|
||||
# Interactive CAR import
|
||||
pinme import
|
||||
|
||||
@@ -471,6 +500,7 @@ pinme upload ./dist --domain my-site --dns
|
||||
- Use `--dns` or `-D` flag to force DNS domain mode when needed
|
||||
|
||||
**Requirements:**
|
||||
- Login or AppKey authentication is required before upload/bind
|
||||
- Sufficient wallet balance is required for domain binding
|
||||
- Valid AppKey must be set (run: `pinme set-appkey <AppKey>`)
|
||||
- For DNS domains, you must own the domain
|
||||
@@ -495,6 +525,8 @@ pinme upload [path] [--domain <name>]
|
||||
- `-d, --domain <name>`: Pinme subdomain or DNS domain to bind after upload (optional, requires wallet balance)
|
||||
- `--dns`, `-D`: Force DNS domain mode
|
||||
|
||||
**Authentication:** This command requires login. Run `pinme login` first, or configure `pinme set-appkey <AppKey>`.
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Upload dist directory
|
||||
@@ -529,6 +561,8 @@ pinme import [path] [--domain <name>]
|
||||
- `path`: Path to the CAR file to import (optional, if not provided, interactive mode will be entered)
|
||||
- `-d, --domain <name>`: Pinme subdomain to bind after import (optional)
|
||||
|
||||
**Authentication:** This command requires login. Run `pinme login` first, or configure `pinme set-appkey <AppKey>`.
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Interactive CAR import
|
||||
@@ -816,7 +850,7 @@ Uploaded files are stored on the IPFS network and accessible through the Glitter
|
||||
- Use a custom DNS domain: `https://<your-domain>`
|
||||
|
||||
### Login and Management
|
||||
- Support user login via AppKey
|
||||
- Support browser login and AppKey-based authentication
|
||||
- View historical upload records
|
||||
- Manage uploaded files
|
||||
|
||||
@@ -945,6 +979,8 @@ jobs:
|
||||
- run: pinme upload dist --domain "${{ secrets.PINME_DOMAIN }}"
|
||||
```
|
||||
|
||||
`pinme set-appkey` satisfies the authentication requirement for `pinme upload` in CI.
|
||||
|
||||
### Supported Build Tools
|
||||
|
||||
- **Vite**: Builds to `dist/`
|
||||
|
||||
+8
-3
@@ -6,7 +6,6 @@ import fs from 'fs';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { checkDomainAvailable, bindPinmeDomain, getRootDomain } from './utils/pinmeApi';
|
||||
import { getAuthConfig } from './utils/webLogin';
|
||||
import { getDeviceId } from './utils/getDeviceId';
|
||||
import { APP_CONFIG } from './utils/config';
|
||||
import { uploadPath } from './services/uploadService';
|
||||
// get from environment variables
|
||||
@@ -64,13 +63,13 @@ function getDomainFromArgs(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get uid: use address from auth if logged in, otherwise use deviceId
|
||||
// Upload/import now requires login. Use the authenticated address as uid.
|
||||
function getUid(): string {
|
||||
const auth = getAuthConfig();
|
||||
if (auth?.address) {
|
||||
return auth.address;
|
||||
}
|
||||
return getDeviceId();
|
||||
throw new Error('Please login first. Run: pinme login');
|
||||
}
|
||||
|
||||
export default async (options?: ImportOptions): Promise<void> => {
|
||||
@@ -85,6 +84,12 @@ export default async (options?: ImportOptions): Promise<void> => {
|
||||
}),
|
||||
);
|
||||
|
||||
const auth = getAuthConfig();
|
||||
if (!auth) {
|
||||
console.log(chalk.red('Please login first. Run: pinme login'));
|
||||
return;
|
||||
}
|
||||
|
||||
// if the parameter is passed, import directly, pinme import /path/to/dir
|
||||
const argPath = process.argv[3];
|
||||
const domainArg = getDomainFromArgs();
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
import chalk from 'chalk';
|
||||
import { getAuthConfig, WebLoginManager } from './utils/webLogin';
|
||||
import { WebLoginManager } from './utils/webLogin';
|
||||
import { getDeviceId } from './utils/getDeviceId';
|
||||
import { bindAnonymousDevice } from './utils/pinmeApi';
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface EnvOption {
|
||||
|
||||
const ENV_URLS: Record<string, string> = {
|
||||
dev: 'http://localhost:5173',
|
||||
test: "http://test-pinme.pinit.eth.limo",
|
||||
test: 'http://test-pinme.pinit.eth.limo',
|
||||
prod: 'https://pinme.eth.limo',
|
||||
};
|
||||
|
||||
@@ -23,7 +23,11 @@ export default async function loginCmd(options: EnvOption = {}): Promise<void> {
|
||||
webBaseUrl = ENV_URLS[env];
|
||||
console.log(chalk.blue(`Using ${env} environment: ${webBaseUrl}`));
|
||||
} else {
|
||||
console.log(chalk.yellow(`Unknown environment: ${options.env}. Using default prod.`));
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Unknown environment: ${options.env}. Using default prod.`,
|
||||
),
|
||||
);
|
||||
webBaseUrl = ENV_URLS.prod;
|
||||
console.log(chalk.blue(`Using prod environment: ${webBaseUrl}`));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { getUid } from '../utils/getDeviceId';
|
||||
import uploadToIpfsSplit from '../utils/uploadToIpfsSplit';
|
||||
import { APP_CONFIG } from '../utils/config';
|
||||
import { getRootDomain } from '../utils/pinmeApi';
|
||||
import { getAuthConfig } from '../utils/webLogin';
|
||||
|
||||
export interface UploadServiceOptions {
|
||||
importAsCar?: boolean;
|
||||
@@ -111,10 +112,15 @@ export async function uploadPath(
|
||||
targetPath: string,
|
||||
options: UploadServiceOptions = {},
|
||||
): Promise<UploadServiceResult> {
|
||||
const authConfig = getAuthConfig();
|
||||
if (!authConfig) {
|
||||
throw new Error('Please login first. Run: pinme login');
|
||||
}
|
||||
|
||||
const result = await uploadToIpfsSplit(targetPath, {
|
||||
importAsCar: options.importAsCar,
|
||||
projectName: options.projectName,
|
||||
uid: options.uid,
|
||||
uid: options.uid || authConfig.address,
|
||||
});
|
||||
|
||||
if (!result?.contentHash) {
|
||||
|
||||
+6
-8
@@ -140,17 +140,15 @@ export default async (options?: UploadOptions): Promise<void> => {
|
||||
}),
|
||||
);
|
||||
|
||||
const authConfig = getAuthConfig();
|
||||
if (!authConfig) {
|
||||
console.log(chalk.red('Please login first. Run: pinme login'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if domain/dns options are provided
|
||||
const domainArg = getDomainFromArgs();
|
||||
const dnsArg = getDnsFromArgs();
|
||||
const needsAuth = !!domainArg || dnsArg;
|
||||
|
||||
// Check auth only when domain/dns options are provided
|
||||
const authConfig = getAuthConfig();
|
||||
if (needsAuth && !authConfig) {
|
||||
console.log(chalk.red('Please login first. Run: pinme set-appkey <AppKey>'));
|
||||
return;
|
||||
}
|
||||
|
||||
// if the parameter is passed, upload directly, pinme upload /path/to/dir
|
||||
const argPath = process.argv[3];
|
||||
|
||||
@@ -84,6 +84,10 @@ interface UploadExecutionOptions {
|
||||
uid?: string;
|
||||
}
|
||||
|
||||
function getUploadAuthHeaders(): Record<string, string> {
|
||||
return getAuthHeaders();
|
||||
}
|
||||
|
||||
function extractAxiosErrorMessage(error: any): string {
|
||||
const responseData = error?.response?.data;
|
||||
|
||||
@@ -334,7 +338,7 @@ async function initChunkSession(
|
||||
|
||||
try {
|
||||
const projectName = options.projectName?.trim();
|
||||
let authHeaders: Record<string, string> = {};
|
||||
const authHeaders = getUploadAuthHeaders();
|
||||
const requestBody: any = {
|
||||
file_name: fileName,
|
||||
file_size: fileSize,
|
||||
@@ -345,7 +349,6 @@ async function initChunkSession(
|
||||
|
||||
if (projectName) {
|
||||
requestBody.project_name = projectName;
|
||||
authHeaders = getAuthHeaders();
|
||||
}
|
||||
|
||||
const response = await axios.post<ChunkSessionResponse>(
|
||||
@@ -400,7 +403,10 @@ async function uploadChunkWithAbort(
|
||||
`${IPFS_API_URL}/chunk/upload`,
|
||||
form,
|
||||
{
|
||||
headers: { ...form.getHeaders() },
|
||||
headers: {
|
||||
...form.getHeaders(),
|
||||
...getUploadAuthHeaders(),
|
||||
},
|
||||
timeout: TIMEOUT,
|
||||
signal,
|
||||
},
|
||||
@@ -554,13 +560,12 @@ async function completeChunkUpload(
|
||||
try {
|
||||
const requestBody: any = { session_id: sessionId, uid: deviceId };
|
||||
const projectName = options.projectName?.trim();
|
||||
let authHeaders: Record<string, string> = {};
|
||||
const authHeaders = getUploadAuthHeaders();
|
||||
if (options.importAsCar) {
|
||||
requestBody.import_as_car = true;
|
||||
}
|
||||
if (projectName) {
|
||||
requestBody.project_name = projectName;
|
||||
authHeaders = getAuthHeaders();
|
||||
}
|
||||
const response = await axios.post<ChunkCompleteResponse>(
|
||||
`${IPFS_API_URL}/chunk/complete`,
|
||||
@@ -595,6 +600,7 @@ async function getChunkStatus(
|
||||
): Promise<ChunkStatusResponse['data']> {
|
||||
try {
|
||||
const projectName = options.projectName?.trim();
|
||||
const authHeaders = getUploadAuthHeaders();
|
||||
const queryParams = new URLSearchParams({
|
||||
trace_id: sessionId,
|
||||
uid: deviceId,
|
||||
@@ -607,7 +613,10 @@ async function getChunkStatus(
|
||||
`${IPFS_API_URL}/up_status?${queryParams.toString()}`,
|
||||
{
|
||||
timeout: TIMEOUT,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...authHeaders,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+28
-15
@@ -1,10 +1,10 @@
|
||||
# PinMe — llms.txt
|
||||
Last updated: 2026-04-23
|
||||
Last updated: 2026-04-26
|
||||
Canonical repo: https://github.com/glitternetwork/pinme
|
||||
Website: https://pinme.eth.limo/
|
||||
|
||||
## Purpose
|
||||
PinMe is a zero-config frontend deployment tool: no servers, no accounts, no setup.
|
||||
PinMe is a zero-config frontend deployment tool: no servers to manage, minimal setup.
|
||||
It publishes static sites as verifiable content (content-addressed), making silent tampering and accidental breakage harder than traditional hosting.
|
||||
|
||||
This file is written for LLMs/AI agents to:
|
||||
@@ -38,11 +38,15 @@ Prerequisite: Node.js >= 16.13.0
|
||||
1) Install:
|
||||
npm install -g pinme
|
||||
|
||||
2) Build the project (if not already built):
|
||||
2) Authenticate:
|
||||
pinme login
|
||||
# or: pinme set-appkey <AppKey>
|
||||
|
||||
3) Build the project (if not already built):
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
3) Upload the build output:
|
||||
4) Upload the build output:
|
||||
pinme upload dist
|
||||
# or: pinme upload build
|
||||
# or: pinme upload out
|
||||
@@ -63,12 +67,18 @@ Goal: Upload static files to PinMe and return ONLY the final public URL printed
|
||||
- If missing:
|
||||
npm install -g pinme
|
||||
|
||||
2) Always ensure a static build exists:
|
||||
2) Authenticate before upload:
|
||||
- Preferred:
|
||||
pinme login
|
||||
- For automation/CI:
|
||||
pinme set-appkey <AppKey>
|
||||
|
||||
3) Always ensure a static build exists:
|
||||
- If the repo looks like a Node frontend project:
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
3) Identify the static output directory (priority order):
|
||||
4) Identify the static output directory (priority order):
|
||||
1) dist/ (Vite/React/Vue/Astro common)
|
||||
2) build/ (Create React App common)
|
||||
3) out/ (Next.js static export common)
|
||||
@@ -82,10 +92,10 @@ Validation rules:
|
||||
If no suitable folder is found:
|
||||
- Ask the user to specify the correct build output path.
|
||||
|
||||
4) Deploy:
|
||||
5) Deploy:
|
||||
pinme upload <folder_path>
|
||||
|
||||
5) Return result:
|
||||
6) Return result:
|
||||
- Return ONLY the final URL printed by PinMe.
|
||||
- URL priority is:
|
||||
1) DNS domain
|
||||
@@ -107,6 +117,9 @@ If no suitable folder is found:
|
||||
"node_version": ">=16.13.0"
|
||||
},
|
||||
"install": "npm install -g pinme",
|
||||
"login": "pinme login",
|
||||
"set_appkey": "pinme set-appkey <AppKey>",
|
||||
"auth_required_for_upload": true,
|
||||
"upload": "pinme upload {{directory}}",
|
||||
"upload_with_domain": "pinme upload {{directory}} --domain {{domain}}",
|
||||
"upload_with_dns": "pinme upload {{directory}} --domain {{domain}} --dns",
|
||||
@@ -124,7 +137,6 @@ If no suitable folder is found:
|
||||
"dns_domain_format": "https://<your-domain>",
|
||||
"other_commands": {
|
||||
"version": "pinme --version",
|
||||
"login": "pinme login",
|
||||
"list": "pinme list",
|
||||
"import": "pinme import <car-file>",
|
||||
"export": "pinme export <cid>",
|
||||
@@ -146,7 +158,7 @@ If no suitable folder is found:
|
||||
|
||||
## CLI reference (high-level)
|
||||
- pinme upload <dir> [--domain <name>]
|
||||
Upload a static directory. Optionally bind to a Pinme subdomain or DNS domain.
|
||||
Upload a static directory. Login or AppKey authentication is required. Optionally bind to a Pinme subdomain or DNS domain.
|
||||
|
||||
- pinme upload <dir> --domain <domain> --dns
|
||||
Force DNS domain mode. Otherwise domains containing a dot are auto-detected as DNS domains.
|
||||
@@ -158,7 +170,7 @@ If no suitable folder is found:
|
||||
Show upload history (supports limiting and clearing per README).
|
||||
|
||||
- pinme import [path] [--domain <name>]
|
||||
Import a CAR file (interactive if no path). Optionally bind to a domain.
|
||||
Import a CAR file (interactive if no path). Login or AppKey authentication is required. Optionally bind to a domain.
|
||||
|
||||
- pinme export <CID> [--output <path>]
|
||||
Export IPFS content as a CAR file (defaults to Downloads if no output path).
|
||||
@@ -225,7 +237,8 @@ Deployment Request:
|
||||
Steps:
|
||||
1) node --version (must be >=16.13.0)
|
||||
2) npm install -g pinme (if needed)
|
||||
3) npm install && npm run build
|
||||
4) Identify output folder (dist/build/out/public) containing index.html
|
||||
5) pinme upload <folder>
|
||||
6) Return ONLY the final URL printed by PinMe. If no custom URL is available, return the preview URL: https://pinme.eth.limo/#/preview/*
|
||||
3) Authenticate with `pinme login` or `pinme set-appkey <AppKey>`
|
||||
4) npm install && npm run build
|
||||
5) Identify output folder (dist/build/out/public) containing index.html
|
||||
6) pinme upload <folder>
|
||||
7) Return ONLY the final URL printed by PinMe. If no custom URL is available, return the preview URL: https://pinme.eth.limo/#/preview/*
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pinme",
|
||||
"version": "2.0.2-beta.10",
|
||||
"version": "2.0.2-beta.11",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
||||
+15
-7
@@ -24,16 +24,18 @@ digraph pinme_decision {
|
||||
|
||||
## Path 1: Upload Files / Static Sites
|
||||
|
||||
> No login required.
|
||||
> Login required. Use `pinme login` or `pinme set-appkey <AppKey>` before `pinme upload` or `pinme import`.
|
||||
|
||||
```dot
|
||||
digraph upload_flow {
|
||||
"Install/update pinme to latest" [shape=box];
|
||||
"Authenticate" [shape=box];
|
||||
"Determine build artifacts" [shape=box];
|
||||
"pinme upload <path>" [shape=box];
|
||||
"Return preview URL" [shape=doublecircle];
|
||||
|
||||
"Install/update pinme to latest" -> "Determine build artifacts";
|
||||
"Install/update pinme to latest" -> "Authenticate";
|
||||
"Authenticate" -> "Determine build artifacts";
|
||||
"Determine build artifacts" -> "pinme upload <path>";
|
||||
"pinme upload <path>" -> "Return preview URL";
|
||||
}
|
||||
@@ -46,19 +48,25 @@ LATEST=$(npm view pinme version)
|
||||
[ "$LOCAL" != "$LATEST" ] && npm install -g pinme@latest || echo "pinme is up to date ($LOCAL)"
|
||||
```
|
||||
|
||||
**2. Determine upload target** (priority order):
|
||||
**2. Authenticate:**
|
||||
```bash
|
||||
pinme login
|
||||
# or: pinme set-appkey <AppKey>
|
||||
```
|
||||
|
||||
**3. Determine upload target** (priority order):
|
||||
1. `dist/` — Vite / Vue / React
|
||||
2. `build/` — Create React App
|
||||
3. `out/` — Next.js static export
|
||||
4. `public/` — Plain static files
|
||||
|
||||
**3. Upload:**
|
||||
**4. Upload:**
|
||||
```bash
|
||||
pinme upload <path>
|
||||
pinme upload ./dist --domain my-site # Optional: bind subdomain (VIP required)
|
||||
pinme upload ./dist --domain my-site # Optional: bind subdomain (wallet balance required)
|
||||
```
|
||||
|
||||
**4. Return** the preview URL (`https://pinme.eth.limo/#/preview/*`) to the user. Note: return the **full URL** including all hash characters — do not truncate.
|
||||
**5. Return** the final URL printed by PinMe to the user. URL priority is: DNS domain > PinMe subdomain > short URL > preview URL. If it falls back to preview, return the **full URL** including all hash characters — do not truncate.
|
||||
|
||||
### Common Examples
|
||||
|
||||
@@ -68,7 +76,7 @@ pinme upload ./my-folder # Folder
|
||||
pinme upload dist # Vite/Vue build artifacts
|
||||
pinme upload build # CRA build artifacts
|
||||
pinme upload out # Next.js static export
|
||||
pinme upload ./dist --domain my-site # Bind PinMe subdomain (VIP required)
|
||||
pinme upload ./dist --domain my-site # Bind PinMe subdomain (wallet balance required)
|
||||
pinme import ./my-archive.car # Import CAR file
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user