UNPKG

@xuanqikai/one-click-upload

Version:

A CLI tool for one-click file upload to cloud storage services (OSS, TOS)

305 lines 13.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UploadCommand = void 0; const chalk_1 = __importDefault(require("chalk")); const commander_1 = require("commander"); const inquirer_1 = __importDefault(require("inquirer")); const ora_1 = __importDefault(require("ora")); const path = __importStar(require("path")); const ConfigManager_1 = require("../config/ConfigManager"); const UploaderFactory_1 = require("../uploaders/UploaderFactory"); const fileUtils_1 = require("../utils/fileUtils"); class UploadCommand { constructor() { this.configManager = new ConfigManager_1.ConfigManager(); } getCommand() { const uploadCmd = new commander_1.Command('upload') .description('Upload files or directories to cloud storage'); uploadCmd .argument('<local-path>', 'Local file or directory path to upload') .argument('[remote-path]', 'Remote path in the storage service') .option('-s, --service <name>', 'Service name to use for upload') .option('-o, --overwrite', 'Overwrite existing files', false) .option('-r, --resume', 'Enable resume upload for large files', true) .option('--max-retries <number>', 'Maximum retry attempts', '3') .option('--chunk-size <size>', 'Chunk size for multipart upload (in MB)', '1') .option('--contents-only', 'Upload folder contents only (without folder name)') .option('--cache-control <value>', 'Custom cache control header (e.g., "no-cache")') .action(async (localPath, remotePath, options) => { await this.directUpload(localPath, remotePath, options); }); return uploadCmd; } /** * 交互式上传 */ async interactiveUpload() { try { // 检查是否有配置的服务 const services = this.configManager.getAllServices(); if (services.length === 0) { console.log(chalk_1.default.yellow('No services configured. Please add a service first:')); console.log(chalk_1.default.cyan(' one-upload config add')); return; } // 交互式输入上传参数 const answers = await inquirer_1.default.prompt([ { type: 'input', name: 'localPath', message: 'Local file or directory path:', validate: async (input) => { if (!input.trim()) { return 'Local path is required'; } if (!(await (0, fileUtils_1.pathExists)(input.trim()))) { return 'Local path does not exist'; } return true; } }, { type: 'input', name: 'remotePath', message: 'Remote path (optional):', default: '' }, { type: 'list', name: 'serviceName', message: 'Select service:', choices: services.map(service => ({ name: `${service.name}${service.isDefault ? ' (default)' : ''} - ${UploaderFactory_1.UploaderFactory.getTypeDisplayName(service.type)}`, value: service.name })), default: this.configManager.getDefaultService()?.name }, { type: 'confirm', name: 'overwrite', message: 'Overwrite existing files?', default: false }, { type: 'confirm', name: 'enableResume', message: 'Enable resume upload for large files?', default: true } ]); const options = { serviceName: answers.serviceName, localPath: answers.localPath.trim(), remotePath: answers.remotePath.trim() || path.basename(answers.localPath.trim()), overwrite: !!answers.overwrite, enableResume: answers.enableResume, maxRetries: 3, chunkSize: 1024 * 1024 // 1MB }; await this.performUpload(options); } catch (error) { console.error(chalk_1.default.red('Interactive upload failed:'), error.message || 'Unknown error'); } } /** * 直接上传 */ async directUpload(localPath, remotePath, cliOptions) { try { // 检查命令行参数中的 overwrite 选项 const hasOverwriteFlag = process.argv.includes('--overwrite') || process.argv.includes('-o'); // 检查本地路径是否存在 if (!(await (0, fileUtils_1.pathExists)(localPath))) { console.error(chalk_1.default.red(`Local path does not exist: ${localPath}`)); return; } // 确定使用的服务 let serviceName = cliOptions?.service; if (!serviceName) { const defaultService = this.configManager.getDefaultService(); if (defaultService) { serviceName = defaultService.name; } else { console.error(chalk_1.default.red('No service specified and no default service configured')); console.log(chalk_1.default.cyan('Use --service option or set a default service with: one-upload config default <name>')); return; } } // 检查服务是否存在 const service = this.configManager.getService(serviceName); if (!service) { console.error(chalk_1.default.red(`Service '${serviceName}' not found`)); return; } // 设置默认远程路径 if (!remotePath) { remotePath = path.basename(localPath); } const options = { serviceName, localPath, remotePath, overwrite: hasOverwriteFlag, enableResume: cliOptions?.resume !== false, maxRetries: parseInt(cliOptions?.maxRetries) || 3, chunkSize: (parseInt(cliOptions?.chunkSize) || 1) * 1024 * 1024, // Convert MB to bytes customCacheControl: cliOptions?.cacheControl }; await this.performUpload(options); } catch (error) { console.error(chalk_1.default.red('Direct upload failed:'), error.message || 'Unknown error'); } } /** * 执行上传操作 */ async performUpload(options) { const service = this.configManager.getService(options.serviceName); if (!service) { console.error(chalk_1.default.red(`Service '${options.serviceName}' not found`)); return; } console.log(chalk_1.default.blue(`\nUploading to ${UploaderFactory_1.UploaderFactory.getTypeDisplayName(service.type)} service: ${chalk_1.default.cyan(service.name)}`)); console.log(chalk_1.default.gray(`Local: ${options.localPath}`)); console.log(chalk_1.default.gray(`Remote: ${options.remotePath}`)); console.log(''); const spinner = (0, ora_1.default)('Initializing upload...').start(); try { // 创建上传器 const uploader = UploaderFactory_1.UploaderFactory.createUploader(service); // 设置进度回调 const onProgress = (progress) => { const percentage = progress.percentage.toFixed(1); const loaded = (0, fileUtils_1.formatFileSize)(progress.loaded); const total = (0, fileUtils_1.formatFileSize)(progress.total); spinner.text = `Uploading ${progress.fileName}: ${percentage}% (${loaded}/${total})`; }; // 执行上传 const result = await uploader.upload(options.localPath, options.remotePath, { overwrite: options.overwrite, enableResume: options.enableResume, maxRetries: options.maxRetries, chunkSize: options.chunkSize, onProgress }); spinner.stop(); // 显示上传结果 this.displayUploadResult(result); } catch (error) { spinner.fail('Upload failed'); console.error(chalk_1.default.red('Error:'), error.message || 'Unknown error'); } } /** * 显示上传结果 */ displayUploadResult(result) { console.log(''); if ('results' in result) { // 批量上传结果 this.displayBatchResult(result); } else { // 单个文件上传结果 this.displaySingleResult(result); } } /** * 显示单个文件上传结果 */ displaySingleResult(result) { if (result.success) { console.log(chalk_1.default.green('✓ Upload completed successfully')); console.log(chalk_1.default.gray(`File: ${result.fileName}`)); if (result.size) { console.log(chalk_1.default.gray(`Size: ${(0, fileUtils_1.formatFileSize)(result.size)}`)); } if (result.url) { console.log(chalk_1.default.gray(`URL: ${result.url}`)); } } else { console.log(chalk_1.default.red('✗ Upload failed')); console.log(chalk_1.default.gray(`File: ${result.fileName}`)); if (result.error) { console.log(chalk_1.default.red(`Error: ${result.error}`)); } } } /** * 显示批量上传结果 */ displayBatchResult(result) { const { totalFiles, successCount, failedCount, totalSize, duration } = result; console.log(chalk_1.default.bold('Upload Summary:')); console.log('─'.repeat(40)); console.log(`Total files: ${totalFiles}`); console.log(`Successful: ${chalk_1.default.green(successCount)}`); console.log(`Failed: ${failedCount > 0 ? chalk_1.default.red(failedCount) : failedCount}`); console.log(`Total size: ${(0, fileUtils_1.formatFileSize)(totalSize)}`); console.log(`Duration: ${(0, fileUtils_1.formatDuration)(duration)}`); if (failedCount > 0) { console.log('\nFailed files:'); result.results .filter(r => !r.success) .forEach(r => { console.log(chalk_1.default.red(` ✗ ${r.fileName}: ${r.error}`)); }); } if (successCount > 0) { console.log('\nSuccessful uploads:'); result.results .filter(r => r.success) .slice(0, 5) // 只显示前5个成功的文件 .forEach(r => { console.log(chalk_1.default.green(` ✓ ${r.fileName}`)); }); if (successCount > 5) { console.log(chalk_1.default.gray(` ... and ${successCount - 5} more files`)); } } } } exports.UploadCommand = UploadCommand; //# sourceMappingURL=UploadCommand.js.map