Merge branch 'feat-cli-login' of github.com:glitternetwork/pinme into feat-cli-login

This commit is contained in:
codergma
2026-03-22 16:00:00 +08:00
13 changed files with 1937 additions and 613 deletions
-1
View File
@@ -121,7 +121,6 @@ export default async function bindCmd(): Promise<void> {
// Auto-detect domain type if not explicitly specified
const isDns = dns || isDnsDomain(domain);
console.log(isDns,'isDns')
const displayDomain = domain.replace(/^https?:\/\//, '').replace(/\/$/, '');
// Validate DNS domain format
+5 -1
View File
@@ -281,6 +281,10 @@ export default async function createCmd(options: CreateOptions): Promise<void> {
execSync('pinme upload ./dist', {
cwd: frontendDir,
stdio: 'inherit',
env: {
...process.env,
PINME_PROJECT_NAME: workerData.project_name,
},
});
console.log(chalk.green(' Frontend uploaded to IPFS'));
} catch (error: any) {
@@ -294,7 +298,7 @@ export default async function createCmd(options: CreateOptions): Promise<void> {
console.log(chalk.gray(` Project Name: ${workerData.project_name}`));
console.log(chalk.gray(`\nNext steps:`));
console.log(chalk.gray(` cd ${projectName}`));
console.log(chalk.gray(` pinme save # 首次部署后端 + 前端`));
console.log(chalk.gray(` pinme save`));
process.exit(0);
} catch (error: any) {
+92 -98
View File
@@ -8,119 +8,113 @@ import { getAuthHeaders } from './utils/webLogin';
const API_BASE = process.env.PINME_API_BASE || '';
interface DeleteOptions {
name?: string;
force?: boolean;
name?: string;
force?: boolean;
}
// 从 pinme.toml 获取项目名
function getProjectName(): string | null {
const configPath = path.join(process.cwd(), 'pinme.toml');
if (!fs.existsSync(configPath)) {
return null;
}
const config = fs.readFileSync(configPath, 'utf-8');
const match = config.match(/project_name\s*=\s*"([^"]+)"/);
return match?.[1] || null;
const configPath = path.join(process.cwd(), 'pinme.toml');
if (!fs.existsSync(configPath)) {
return null;
}
const config = fs.readFileSync(configPath, 'utf-8');
const match = config.match(/project_name\s*=\s*"([^"]+)"/);
return match?.[1] || null;
}
/**
* Delete a project: removes Worker domain binding, Worker script, D1 database
*/
export default async function deleteCmd(options: DeleteOptions): Promise<void> {
try {
// Check if user is logged in
const headers = getAuthHeaders();
if (!headers['authentication-tokens'] || !headers['token-address']) {
console.log(chalk.yellow('\n⚠️ You are not logged in.'));
console.log(chalk.gray('Please run: pinme login'));
process.exit(1);
}
try {
// Check if user is logged in
const headers = getAuthHeaders();
if (!headers['authentication-tokens'] || !headers['token-address']) {
console.log(chalk.yellow('\n⚠️ You are not logged in.'));
console.log(chalk.gray('Please run: pinme login'));
process.exit(1);
}
console.log(chalk.blue('Deleting project...\n'));
console.log(chalk.blue('Deleting project...\n'));
// Get project name from pinme.toml or options
let projectName = options.name || getProjectName();
// Get project name from pinme.toml or options
let projectName = options.name || getProjectName();
if (!projectName) {
console.log(chalk.red('\n❌ Error: Cannot find project name.'));
console.log(chalk.yellow(' Please make sure you are in the project directory.'));
console.log(chalk.gray(' The project directory should contain a pinme.toml file.'));
console.log(chalk.gray('\n Or specify the project name:'));
console.log(chalk.gray(' cd /path/to/your-project'));
console.log(chalk.gray(' pinme delete'));
process.exit(1);
}
if (!projectName) {
console.log(chalk.red('\n❌ Error: Cannot find project name.'));
console.log(chalk.yellow(' Please make sure you are in the project directory.'));
console.log(chalk.gray(' The project directory should contain a pinme.toml file.'));
console.log(chalk.gray('\n Or specify the project name:'));
console.log(chalk.gray(' cd /path/to/your-project'));
console.log(chalk.gray(' pinme delete'));
process.exit(1);
}
console.log(chalk.gray(`Project: ${projectName}`));
console.log(chalk.gray(`Directory: ${process.cwd()}`));
console.log(chalk.gray(`Project: ${projectName}`));
console.log(chalk.gray(`Directory: ${process.cwd()}`));
// Confirm deletion
if (!options.force) {
const answers = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: `Are you sure you want to delete project "${projectName}"? This will remove Worker, domain binding, and D1 database.`,
default: false,
},
]);
if (!answers.confirm) {
console.log(chalk.gray('Cancelled.'));
process.exit(0);
}
}
// Call API to delete project
console.log(chalk.blue('Deleting project on platform...'));
const apiUrl = `${API_BASE}/delete_project`;
console.log(chalk.gray(`API URL: ${apiUrl}`));
console.log(chalk.gray(`Project name: ${projectName}`));
const response = await axios.post(apiUrl, {
project_name: projectName,
}, {
headers: {
...headers,
'Content-Type': 'application/json',
},
}).catch((error) => {
if (error.response) {
console.log(chalk.red(` Response status: ${error.response?.status}`));
console.log(chalk.red(` Response data: ${JSON.stringify(error.response?.data)}`));
} else {
console.log(chalk.red('No Response'))
}
throw error;
});
const data = response.data;
if (data.code === 200) {
console.log(chalk.green('\n✅ Project deleted successfully!'));
console.log(chalk.gray(`\nProject: ${data.data.project_name}`));
console.log(chalk.gray(` Domain deleted: ${data.data.domain_deleted ? '✅' : '❌'}`));
console.log(chalk.gray(` Worker deleted: ${data.data.worker_deleted ? '✅' : '❌'}`));
console.log(chalk.gray(` Database deleted: ${data.data.database_deleted ? '✅' : '❌'}`));
console.log(chalk.gray('\nLocal files are kept unchanged.'));
} else {
const errorMsg = data?.msg || 'Failed to delete project';
throw new Error(errorMsg);
}
// Confirm deletion
if (!options.force) {
const answers = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: `Are you sure you want to delete project "${projectName}"? This will remove Worker, domain binding, and D1 database.`,
default: false,
},
]);
if (!answers.confirm) {
console.log(chalk.gray('Cancelled.'));
process.exit(0);
}
} catch (error: any) {
console.log(chalk.red(error));
const errorMsg = error.response?.data?.msg
|| error.message
|| 'Failed to delete project';
console.error(chalk.red(`\n❌ Error: ${errorMsg}`));
process.exit(1);
}
// Call API to delete project
console.log(chalk.blue('Deleting project on platform...'));
const apiUrl = `${API_BASE}/delete_project`;
console.log(chalk.gray(`API URL: ${apiUrl}`));
console.log(chalk.gray(`Project name: ${projectName}`));
const response = await axios.post(apiUrl, {
project_name: projectName,
}, {
headers: {
...headers,
'Content-Type': 'application/json',
},
}).catch((error) => {
console.log(chalk.red(` Response status: ${error.response?.status}`));
console.log(chalk.red(` Response data: ${JSON.stringify(error.response?.data)}`));
throw error;
});
const data = response.data;
if (data.code === 200) {
console.log(chalk.green('\n✅ Project deleted successfully!'));
console.log(chalk.gray(`\nProject: ${data.data.project_name}`));
console.log(chalk.gray(` Domain deleted: ${data.data.domain_deleted ? '✅' : '❌'}`));
console.log(chalk.gray(` Worker deleted: ${data.data.worker_deleted ? '✅' : '❌'}`));
console.log(chalk.gray(` Database deleted: ${data.data.database_deleted ? '✅' : '❌'}`));
// 删除本地项目目录
const projectDir = process.cwd();
if (fs.existsSync(projectDir)) {
console.log(chalk.blue('\nDeleting local project directory...'));
// 先切换到父目录,避免在已删除的目录中
const parentDir = path.dirname(projectDir);
process.chdir(parentDir);
await fs.remove(projectDir);
console.log(chalk.green(`✅ Local directory deleted: ${projectDir}`));
}
} else {
const errorMsg = data?.msg || 'Failed to delete project';
throw new Error(errorMsg);
}
process.exit(0);
} catch (error: any) {
console.log(chalk.red(error));
const errorMsg = error.response?.data?.msg
|| error.message
|| 'Failed to delete project';
console.error(chalk.red(`\n❌ Error: ${errorMsg}`));
process.exit(1);
}
}
+20 -12
View File
@@ -56,7 +56,7 @@ function buildWorker() {
function installDependencies() {
console.log(chalk.blue('Installing dependencies...'));
// 安装根目录依赖
try {
execSync('npm install', {
@@ -67,7 +67,7 @@ function installDependencies() {
} catch (error: any) {
throw new Error(`Root dependencies install failed: ${error.message}`);
}
// 安装后端依赖
const backendDir = path.join(PROJECT_DIR, 'backend');
if (fs.existsSync(path.join(backendDir, 'package.json'))) {
@@ -81,7 +81,7 @@ function installDependencies() {
throw new Error(`Backend dependencies install failed: ${error.message}`);
}
}
// 安装前端依赖
const frontendDir = path.join(PROJECT_DIR, 'frontend');
if (fs.existsSync(path.join(frontendDir, 'package.json'))) {
@@ -136,11 +136,11 @@ function getSqlFiles(): string[] {
async function saveWorker(workerJsPath: string, modulePaths: string[], sqlFiles: string[], metadata: any, projectName: string) {
console.log(chalk.blue('Saving worker to platform...'));
console.log(chalk.gray(`Project: ${projectName}`));
console.log(chalk.gray(`workerJsPath: ${workerJsPath}`));
console.log(chalk.gray(`modulePaths: ${modulePaths}`));
console.log(chalk.gray(`sqlFiles: ${sqlFiles}`));
console.log(chalk.gray(`metadata: ${metadata}`));
console.log(chalk.gray(`Project: ${projectName}`));
console.log(chalk.gray(`workerJsPath: ${workerJsPath}`));
console.log(chalk.gray(`modulePaths: ${modulePaths}`));
console.log(chalk.gray(`sqlFiles: ${sqlFiles}`));
console.log(chalk.gray(`metadata: ${metadata}`));
const apiUrl = `${API_BASE}/save_worker?project_name=${encodeURIComponent(projectName)}`;
const headers = getAuthHeaders();
console.log(chalk.gray(`API URL: ${apiUrl}`));
@@ -192,8 +192,12 @@ async function saveWorker(workerJsPath: string, modulePaths: string[], sqlFiles:
throw new Error(response.data?.errors?.[0]?.message || 'Failed to save worker');
}
} catch (error: any) {
console.log(chalk.red(` Response status: ${error.response?.status}`));
console.log(chalk.red(` Response data: ${JSON.stringify(error.response?.data)}`));
if (error.response) {
console.log(chalk.red(` Response status: ${error.response?.status}`));
console.log(chalk.red(` Response data: ${JSON.stringify(error.response?.data)}`));
} else {
console.log(chalk.red('No Response'))
}
const errorMsg = error.response?.data?.errors?.[0]?.message
|| error.response?.data?.error
|| error.message
@@ -217,12 +221,16 @@ function buildFrontend() {
}
}
function deployFrontend() {
function deployFrontend(projectName: string) {
console.log(chalk.blue('Deploying frontend to IPFS...'));
try {
execSync('pinme upload ./frontend/dist', {
cwd: PROJECT_DIR,
stdio: 'inherit',
env: {
...process.env,
PINME_PROJECT_NAME: projectName,
},
});
console.log(chalk.green('Frontend deployed to IPFS'));
} catch (error: any) {
@@ -285,7 +293,7 @@ export default async function saveCmd(options: SaveOptions): Promise<void> {
// Frontend: build + deploy
console.log(chalk.blue('\n--- Frontend ---'));
buildFrontend();
deployFrontend();
deployFrontend(projectName);
console.log(chalk.green('\n✅ Deployment complete!'));
process.exit(0);
+6 -2
View File
@@ -107,8 +107,12 @@ async function updateDb(sqlFiles: string[], projectName: string) {
throw new Error(errorMsg);
}
} catch (error: any) {
console.log(chalk.red(` Response status: ${error.response?.status}`));
console.log(chalk.red(` Response data: ${JSON.stringify(error.response?.data)}`));
if (error.response) {
console.log(chalk.red(` Response status: ${error.response?.status}`));
console.log(chalk.red(` Response data: ${JSON.stringify(error.response?.data)}`));
} else {
console.log(chalk.red('No Response'))
}
const errorMsg = error.response?.data?.errors?.[0]?.message
|| error.response?.data?.error
|| error.response?.data?.msg
+6 -2
View File
@@ -42,12 +42,16 @@ function buildFrontend() {
}
}
function deployFrontend() {
function deployFrontend(projectName: string) {
console.log(chalk.blue('Deploying frontend to IPFS...'));
try {
execSync('pinme upload ./frontend/dist', {
cwd: PROJECT_DIR,
stdio: 'inherit',
env: {
...process.env,
PINME_PROJECT_NAME: projectName,
},
});
console.log(chalk.green('Frontend deployed to IPFS'));
} catch (error: any) {
@@ -94,7 +98,7 @@ export default async function updateWebCmd(options?: UpdateWebOptions): Promise<
// Frontend: build + deploy
console.log(chalk.blue('\n--- Frontend Update ---'));
buildFrontend();
deployFrontend();
deployFrontend(projectName);
console.log(chalk.green('\n✅ Web update complete!'));
process.exit(0);
+6 -2
View File
@@ -152,8 +152,12 @@ async function updateWorker(workerJsPath: string, modulePaths: string[], metadat
throw new Error(response.data?.errors?.[0]?.message || 'Failed to update worker');
}
} catch (error: any) {
console.log(chalk.red(` Response status: ${error.response?.status}`));
console.log(chalk.red(` Response data: ${JSON.stringify(error.response?.data)}`));
if (error.response) {
console.log(chalk.red(` Response status: ${error.response?.status}`));
console.log(chalk.red(` Response data: ${JSON.stringify(error.response?.data)}`));
} else {
console.log(chalk.red('No Response'))
}
const errorMsg = error.response?.data?.errors?.[0]?.message
|| error.response?.data?.error
|| error.message
+94 -61
View File
@@ -103,6 +103,34 @@ interface UploadOptions {
[key: string]: any;
}
function formatEnsUrl(shortUrl?: string): string {
if (!shortUrl) return '';
const normalized = shortUrl.trim();
if (!normalized) return '';
if (/^https?:\/\//.test(normalized)) return normalized;
if (normalized.includes('.')) return `https://${normalized}`;
return `https://${normalized}.pinit.eth.limo`;
}
function printUploadUrls(contentHash: string, shortUrl?: string): void {
const uid = getUid();
const encryptedCID = encryptHash(contentHash, secretKey, uid);
const previewUrl = `${URL}${encryptedCID}`;
const projectName = process.env.PINME_PROJECT_NAME?.trim();
if (projectName) {
const ensUrl = formatEnsUrl(shortUrl);
console.log(chalk.cyan(`URL:`));
console.log(chalk.cyan(ensUrl || previewUrl));
console.log(chalk.cyan(`Management page:`));
console.log(chalk.cyan(previewUrl));
return;
}
console.log(chalk.cyan(`URL:`));
console.log(chalk.cyan(previewUrl));
}
function getDomainFromArgs(): string | null {
const args = process.argv.slice(2);
const dIdx = args.findIndex((a) => a === '--domain' || a === '-d');
@@ -257,44 +285,47 @@ export default async (options?: UploadOptions): Promise<void> => {
}
console.log(chalk.blue(`uploading ${absolutePath} to ipfs...`));
let result;
try {
const result = await upload(absolutePath);
if (result) {
const uid = getUid();
const encryptedCID = encryptHash(result.contentHash, secretKey, uid);
console.log(
chalk.cyan(
figlet.textSync('Successful', { horizontalLayout: 'full' }),
),
);
console.log(chalk.cyan(`URL:`));
console.log(chalk.cyan(`${URL}${encryptedCID}`));
// optional: bind domain after upload
if (domainArg) {
console.log(
chalk.blue(
`Binding domain: ${displayDomain} with CID: ${result.contentHash}`,
),
);
try {
await bindDomain(domainArg, result.contentHash, isDns, authConfig);
} catch (e: any) {
if (e.message === 'Token expired') {
process.exit(1);
}
throw e;
}
}
console.log(chalk.green('\n🎉 upload successful, program exit'));
}
result = await upload(absolutePath);
} catch (error: any) {
console.error(chalk.red(`Error: ${error.message}`));
console.error(chalk.red(`Upload error: ${error.message}`));
process.exit(1);
}
if (!result) {
console.error(chalk.red('Upload failed: no result returned'));
process.exit(1);
}
console.log(
chalk.cyan(
figlet.textSync('Successful', { horizontalLayout: 'full' }),
),
);
printUploadUrls(result.contentHash, result.shortUrl);
// optional: bind domain after upload
if (domainArg) {
console.log(
chalk.blue(
`Binding domain: ${displayDomain} with CID: ${result.contentHash}`,
),
);
try {
await bindDomain(domainArg, result.contentHash, isDns, authConfig);
} catch (e: any) {
if (e.message === 'Token expired') {
process.exit(1);
}
throw e;
}
}
console.log(chalk.green('\n🎉 upload successful, program exit'));
process.exit(0);
}
// No path argument provided, use interactive mode
const answer = await inquirer.prompt([
{
type: 'input',
@@ -362,40 +393,42 @@ export default async (options?: UploadOptions): Promise<void> => {
}
console.log(chalk.blue(`uploading ${absolutePath} to ipfs...`));
let result;
try {
const result = await upload(absolutePath);
if (result) {
const uid = getUid();
const encryptedCID = encryptHash(result.contentHash, secretKey, uid);
console.log(
chalk.cyan(
figlet.textSync('Successful', { horizontalLayout: 'full' }),
),
);
console.log(chalk.cyan(`URL:`));
console.log(chalk.cyan(`${URL}${encryptedCID}`));
if (domainArg) {
console.log(
chalk.blue(
`Binding domain: ${displayDomain} with CID: ${result.contentHash}`,
),
);
try {
await bindDomain(domainArg, result.contentHash, isDns, authConfig);
} catch (e: any) {
if (e.message === 'Token expired') {
process.exit(1);
}
throw e;
}
}
console.log(chalk.green('\n🎉 upload successful, program exit'));
}
result = await upload(absolutePath);
} catch (error: any) {
console.error(chalk.red(`Error: ${error.message}`));
console.error(chalk.red(`Upload error: ${error.message}`));
process.exit(1);
}
if (!result) {
console.error(chalk.red('Upload failed: no result returned'));
process.exit(1);
}
console.log(
chalk.cyan(
figlet.textSync('Successful', { horizontalLayout: 'full' }),
),
);
printUploadUrls(result.contentHash, result.shortUrl);
if (domainArg) {
console.log(
chalk.blue(
`Binding domain: ${displayDomain} with CID: ${result.contentHash}`,
),
);
try {
await bindDomain(domainArg, result.contentHash, isDns, authConfig);
} catch (e: any) {
if (e.message === 'Token expired') {
process.exit(1);
}
throw e;
}
}
console.log(chalk.green('\n🎉 upload successful, program exit'));
process.exit(0);
}
} catch (error: any) {
+1 -1
View File
@@ -254,7 +254,7 @@ export async function isVip(
// CAR Export API
const CAR_API_BASE =
process.env.CAR_API_BASE ||
process.env.PINME_API_BASE ||
process.env.IPFS_API_URL ||
'http://ipfs-proxy.opena.chat/api/v3';
function createCarClient(): AxiosInstance {
+25 -5
View File
@@ -61,6 +61,7 @@ interface UploadStatusResponse {
ShortUrl: string;
};
is_ready: boolean;
domain?: string;
};
}
@@ -74,10 +75,21 @@ async function pollUploadStatus(
let consecutiveErrors = 0;
let stopProgressUpdates = false;
const projectName = process.env.PINME_PROJECT_NAME?.trim();
while (Date.now() - startTime < maxPollTime) {
try {
// Build query params
const queryParams = new URLSearchParams({
trace_id: traceId,
uid: deviceId,
});
if (projectName) {
queryParams.append('project_name', projectName);
console.log(chalk.gray(`[up_status] project_name: ${projectName}`));
}
const response = await axios.get<UploadStatusResponse>(
`${ipfsApiUrl}/up_status?trace_id=${traceId}&uid=${deviceId}`,
`${ipfsApiUrl}/up_status?${queryParams.toString()}`,
{
timeout: pollTimeout,
headers: {
@@ -399,6 +411,10 @@ async function uploadDirectory(
const directoryItem = uploadResult.upload_rst;
if (directoryItem) {
const fileCount = countFilesInDirectory(directoryPath);
// Use domain from backend to construct full URL
const shortUrl = directoryItem.ShortUrl;
const domain = uploadResult.domain;
const fullShortUrl = shortUrl && domain ? `${shortUrl}.${domain}` : shortUrl;
const uploadData = {
path: directoryPath,
filename: path.basename(directoryPath),
@@ -407,14 +423,14 @@ async function uploadDirectory(
size: sizeCheck.size,
fileCount: fileCount,
isDirectory: true,
shortUrl: directoryItem.ShortUrl || null,
shortUrl: fullShortUrl || null,
};
saveUploadHistory(uploadData);
clearInterval(timeInterval);
return {
hash: directoryItem.Hash,
shortUrl: directoryItem.ShortUrl,
shortUrl: fullShortUrl,
};
}
@@ -585,6 +601,10 @@ async function uploadFile(
const fileItem = uploadResult.upload_rst;
if (fileItem) {
// Use domain from backend to construct full URL
const shortUrl = fileItem.ShortUrl;
const domain = uploadResult.domain;
const fullShortUrl = shortUrl && domain ? `${shortUrl}.${domain}` : shortUrl;
const uploadData = {
path: filePath,
filename: fileName,
@@ -593,7 +613,7 @@ async function uploadFile(
size: sizeCheck.size,
fileCount: 1,
isDirectory: false,
shortUrl: fileItem.ShortUrl || null,
shortUrl: fullShortUrl || null,
};
saveUploadHistory(uploadData);
@@ -602,7 +622,7 @@ async function uploadFile(
return {
hash: fileItem.Hash,
shortUrl: fileItem.ShortUrl,
shortUrl: fullShortUrl,
};
}
+35 -15
View File
@@ -11,6 +11,7 @@ import {
} from './uploadLimits';
import { saveUploadHistory } from './history';
import { getUid } from './getDeviceId';
import { getAuthHeaders } from './webLogin';
// Configuration constants
const IPFS_API_URL =
@@ -65,6 +66,7 @@ interface ChunkStatusResponse {
Hash?: string;
ShortUrl?: string;
};
domain?: string;
};
}
@@ -153,9 +155,8 @@ class StepProgressBar {
const duration = this.formatDuration(Math.floor(elapsed / 1000));
const progressBar = this.createProgressBar(progress);
this.spinner.text = `Uploading ${
this.fileName
}... ${progressBar} ${Math.round(progress * 100)}% (${duration})`;
this.spinner.text = `Uploading ${this.fileName
}... ${progressBar} ${Math.round(progress * 100)}% (${duration})`;
}, PROGRESS_UPDATE_INTERVAL);
}
@@ -341,8 +342,7 @@ async function uploadChunkWithAbort(
}
throw new Error(
`Chunk ${chunkIndex + 1} upload failed after ${MAX_RETRIES} retries: ${
error.message
`Chunk ${chunkIndex + 1} upload failed after ${MAX_RETRIES} retries: ${error.message
}`,
);
}
@@ -418,9 +418,8 @@ async function uploadFileChunks(
}
hasFatalError = true;
fatalError = `Chunk ${chunkIndex + 1}/${totalChunks} upload failed: ${
error.message
}`;
fatalError = `Chunk ${chunkIndex + 1}/${totalChunks} upload failed: ${error.message
}`;
abortController.abort();
throw new Error(fatalError);
}
@@ -455,15 +454,24 @@ async function completeChunkUpload(
): Promise<string> {
try {
const requestBody: any = { session_id: sessionId, uid: deviceId };
const projectName = process.env.PINME_PROJECT_NAME?.trim();
let authHeaders: Record<string, string> = {};
if (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`,
requestBody,
{
timeout: TIMEOUT,
headers: { 'Content-Type': 'application/json' },
headers: {
'Content-Type': 'application/json',
...authHeaders,
},
},
);
@@ -485,10 +493,18 @@ async function getChunkStatus(
deviceId: string,
): Promise<ChunkStatusResponse['data']> {
try {
const projectName = process.env.PINME_PROJECT_NAME?.trim();
const queryParams = new URLSearchParams({
trace_id: sessionId,
uid: deviceId,
});
if (projectName) {
queryParams.append('project_name', projectName);
}
const response = await axios.get<ChunkStatusResponse>(
`${IPFS_API_URL}/up_status`,
`${IPFS_API_URL}/up_status?${queryParams.toString()}`,
{
params: { trace_id: sessionId, uid: deviceId },
timeout: TIMEOUT,
headers: { 'Content-Type': 'application/json' },
},
@@ -531,9 +547,13 @@ async function monitorChunkProgress(
if (progressBar) {
progressBar.stopSimulatingProgress();
}
// Use domain from backend to construct full URL
const shortUrl = status.upload_rst.ShortUrl;
const domain = status.domain;
const fullShortUrl = shortUrl && domain ? `${shortUrl}.${domain}` : shortUrl;
return {
hash: status.upload_rst.Hash,
shortUrl: status.upload_rst.ShortUrl,
shortUrl: fullShortUrl,
};
}
} catch (error: any) {
@@ -711,7 +731,7 @@ export default async function (filePath: string, importAsCar: boolean = false):
try {
const isDirectory = fs.statSync(filePath).isDirectory();
const result = isDirectory
const result = isDirectory
? await uploadDirectoryInChunks(filePath, deviceId, importAsCar)
: await uploadFileInChunks(filePath, deviceId, importAsCar);
@@ -722,8 +742,8 @@ export default async function (filePath: string, importAsCar: boolean = false):
shortUrl: result.shortUrl,
};
}
return null;
throw new Error('Upload failed: no hash returned');
} catch (error: any) {
return null;
throw error;
}
}
+1646 -412
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "pinme",
"version": "2.0.0-beta.8",
"version": "2.0.0-beta.15",
"publishConfig": {
"access": "public"
},