@xuanqikai/one-click-upload
Version:
A CLI tool for one-click file upload to cloud storage services (OSS, TOS)
570 lines • 24.8 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigCommand = void 0;
const chalk_1 = __importDefault(require("chalk"));
const commander_1 = require("commander");
const inquirer_1 = __importDefault(require("inquirer"));
const ConfigManager_1 = require("../config/ConfigManager");
const UploaderFactory_1 = require("../uploaders/UploaderFactory");
class ConfigCommand {
constructor() {
this.configManager = new ConfigManager_1.ConfigManager();
}
getCommand() {
const configCmd = new commander_1.Command('config')
.description('Manage service configurations');
// 添加服务配置
configCmd
.command('add')
.description('Add a new service configuration')
.option('-n, --name <name>', 'Service name')
.option('-t, --type <type>', 'Service type (oss, tos)')
.option('--interactive', 'Use interactive mode', true)
.action(async (options) => {
await this.addService(options);
});
// 列出所有服务配置
configCmd
.command('list')
.alias('ls')
.description('List all service configurations')
.action(async () => {
await this.listServices();
});
// 显示服务配置详情
configCmd
.command('show <name>')
.description('Show service configuration details')
.action(async (name) => {
await this.showService(name);
});
// 更新服务配置
configCmd
.command('update <name>')
.description('Update service configuration')
.action(async (name) => {
await this.updateService(name);
});
// 删除服务配置
configCmd
.command('remove <name>')
.alias('rm')
.description('Remove service configuration')
.action(async (name) => {
await this.removeService(name);
});
// 设置默认服务
configCmd
.command('default <name>')
.description('Set default service')
.action(async (name) => {
await this.setDefaultService(name);
});
// 测试服务配置
configCmd
.command('test <name>')
.description('Test service configuration')
.action(async (name) => {
await this.testService(name);
});
// 显示配置文件路径
configCmd
.command('path')
.description('Show configuration file path')
.action(async () => {
await this.showConfigPath();
});
return configCmd;
}
/**
* 添加服务配置
*/
async addService(options) {
try {
let serviceName = options.name;
let serviceType = options.type;
// 交互式输入服务基本信息
if (!serviceName || !serviceType) {
const basicInfo = await inquirer_1.default.prompt([
{
type: 'input',
name: 'name',
message: 'Service name:',
when: !serviceName,
validate: (input) => input.trim() ? true : 'Service name is required'
},
{
type: 'list',
name: 'type',
message: 'Service type:',
when: !serviceType,
choices: UploaderFactory_1.UploaderFactory.getSupportedTypes().map(type => ({
name: UploaderFactory_1.UploaderFactory.getTypeDisplayName(type),
value: type
}))
}
]);
serviceName = serviceName || basicInfo.name;
serviceType = serviceType || basicInfo.type;
}
// 检查服务名是否已存在
if (this.configManager.getService(serviceName)) {
console.error(chalk_1.default.red(`Service '${serviceName}' already exists`));
return;
}
// 获取服务类型的配置字段
const configFields = UploaderFactory_1.UploaderFactory.getConfigFields(serviceType);
// 交互式输入配置信息
const configAnswers = await inquirer_1.default.prompt(configFields.map(field => ({
type: field.type === 'password' ? 'password' : 'input',
name: field.key,
message: `${field.label}:`,
validate: (input) => {
if (field.required && !input.trim()) {
return `${field.label} is required`;
}
return true;
}
})));
// 询问是否启用 CDN 功能
let cdnConfig = {};
if (serviceType === 'oss' || serviceType === 'tos') {
const { enableCDN } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'enableCDN',
message: `Enable CDN ${serviceType === 'oss' ? 'auto refresh' : 'preload'} after upload?`,
default: false
}
]);
if (enableCDN) {
if (serviceType === 'oss') {
// OSS CDN 配置
const cdnAnswers = await inquirer_1.default.prompt([
{
type: 'input',
name: 'cdnAccessKeyId',
message: 'CDN Access Key ID:',
validate: (input) => input.trim() ? true : 'CDN Access Key ID is required'
},
{
type: 'password',
name: 'cdnAccessKeySecret',
message: 'CDN Access Key Secret:',
validate: (input) => input.trim() ? true : 'CDN Access Key Secret is required'
},
{
type: 'input',
name: 'cdnDomain',
message: 'CDN Domain (e.g., https://cdn.example.com):',
validate: (input) => {
if (!input.trim())
return 'CDN Domain is required';
if (!input.startsWith('http://') && !input.startsWith('https://')) {
return 'CDN Domain must start with http:// or https://';
}
return true;
}
}
]);
cdnConfig = {
accessKeyId: cdnAnswers.cdnAccessKeyId,
accessKeySecret: cdnAnswers.cdnAccessKeySecret,
cdnDomain: cdnAnswers.cdnDomain,
autoRefresh: true
};
}
else if (serviceType === 'tos') {
// TOS CDN 配置(简化版,只需要CDN地址)
const cdnAnswers = await inquirer_1.default.prompt([
{
type: 'input',
name: 'cdnDomain',
message: 'CDN Domain (e.g., cdn.example.com):',
validate: (input) => {
if (!input.trim())
return 'CDN Domain is required';
// 允许不带协议前缀的域名
return true;
}
}
]);
cdnConfig = {
cdnDomain: cdnAnswers.cdnDomain
};
}
}
}
// 询问是否设为默认服务
const { isDefault } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'isDefault',
message: 'Set as default service?',
default: this.configManager.getAllServices().length === 0
}
]);
// 创建服务配置
const finalConfig = {
...configAnswers,
...(Object.keys(cdnConfig).length > 0 && { cdn: cdnConfig })
};
const serviceConfig = {
name: serviceName,
type: serviceType,
config: finalConfig,
isDefault
};
// 验证配置
const validation = UploaderFactory_1.UploaderFactory.validateConfig(serviceConfig);
if (!validation.valid) {
console.error(chalk_1.default.red('Configuration validation failed:'));
validation.errors.forEach(error => console.error(chalk_1.default.red(` - ${error}`)));
return;
}
// 保存配置
await this.configManager.addService(serviceConfig);
console.log(chalk_1.default.green(`✓ Service '${serviceName}' added successfully`));
// 询问是否测试配置
const { testConfig } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'testConfig',
message: 'Test the configuration now?',
default: true
}
]);
if (testConfig) {
await this.testService(serviceName);
}
}
catch (error) {
console.error(chalk_1.default.red('Failed to add service:'), error.message || 'Unknown error');
}
}
/**
* 列出所有服务配置
*/
async listServices() {
const services = this.configManager.getAllServices();
if (services.length === 0) {
console.log(chalk_1.default.yellow('No services configured. Use "one-upload config add" to add a service.'));
return;
}
console.log(chalk_1.default.bold('\nConfigured Services:'));
console.log('─'.repeat(60));
services.forEach(service => {
const defaultMark = service.isDefault ? chalk_1.default.green(' (default)') : '';
const typeName = UploaderFactory_1.UploaderFactory.getTypeDisplayName(service.type);
console.log(`${chalk_1.default.cyan(service.name)}${defaultMark}`);
console.log(` Type: ${typeName}`);
console.log(` Bucket: ${service.config.bucket}`);
console.log(` Region: ${service.config.region}`);
console.log('');
});
}
/**
* 显示服务配置详情
*/
async showService(name) {
const service = this.configManager.getService(name);
if (!service) {
console.error(chalk_1.default.red(`Service '${name}' not found`));
return;
}
const typeName = UploaderFactory_1.UploaderFactory.getTypeDisplayName(service.type);
const defaultMark = service.isDefault ? chalk_1.default.green(' (default)') : '';
console.log(chalk_1.default.bold(`\nService: ${chalk_1.default.cyan(service.name)}${defaultMark}`));
console.log('─'.repeat(40));
console.log(`Type: ${typeName}`);
// 显示配置信息(隐藏敏感信息)
const config = service.config;
Object.keys(config).forEach(key => {
if (key === 'cdn') {
// 特殊处理 CDN 配置
const cdnConfig = config[key];
if (cdnConfig && Object.keys(cdnConfig).length > 0) {
console.log(`${key}:`);
console.log(` accessKeyId: ${cdnConfig.accessKeyId ? '***' + cdnConfig.accessKeyId.slice(-4) : 'Not set'}`);
if (service.type === 'oss') {
console.log(` cdnDomain: ${cdnConfig.cdnDomain || 'Not set'}`);
console.log(` autoRefresh: ${cdnConfig.autoRefresh ? 'Enabled' : 'Disabled'}`);
}
else if (service.type === 'tos') {
console.log(` cdnDomain: ${cdnConfig.cdnDomain || 'Not set'}`);
console.log(` preload: Enabled`);
}
}
else {
console.log(`${key}: Not configured`);
}
}
else {
const value = config[key];
if (key.toLowerCase().includes('secret') || key.toLowerCase().includes('password')) {
console.log(`${key}: ${'*'.repeat(8)}`);
}
else {
console.log(`${key}: ${value}`);
}
}
});
}
/**
* 更新服务配置
*/
async updateService(name) {
const service = this.configManager.getService(name);
if (!service) {
console.error(chalk_1.default.red(`Service '${name}' not found`));
return;
}
console.log(chalk_1.default.bold(`Updating service: ${chalk_1.default.cyan(name)}`));
// 获取配置字段
const configFields = UploaderFactory_1.UploaderFactory.getConfigFields(service.type);
// 交互式更新配置
const updates = await inquirer_1.default.prompt([
{
type: 'input',
name: 'name',
message: 'Service name:',
default: service.name
},
...configFields.map(field => ({
type: field.type === 'password' ? 'password' : 'input',
name: field.key,
message: `${field.label}:`,
default: field.type === 'password' ? undefined : service.config[field.key]
})),
{
type: 'confirm',
name: 'isDefault',
message: 'Set as default service?',
default: service.isDefault
}
]);
// 询问是否更新 CDN 配置
let cdnConfig = {};
if (service.type === 'oss' || service.type === 'tos') {
const currentCDN = service.config.cdn;
const hasCDN = currentCDN && Object.keys(currentCDN).length > 0;
const { updateCDN } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'updateCDN',
message: `Update CDN configuration? ${hasCDN ? '(Current: Enabled)' : '(Current: Disabled)'}`,
default: hasCDN
}
]);
if (updateCDN) {
const { enableCDN } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'enableCDN',
message: `Enable CDN ${service.type === 'oss' ? 'auto refresh' : 'preload'} after upload?`,
default: hasCDN ? (service.type === 'oss' ? currentCDN.autoRefresh : true) : false
}
]);
if (enableCDN) {
if (service.type === 'oss') {
// OSS CDN 配置
const cdnAnswers = await inquirer_1.default.prompt([
{
type: 'input',
name: 'cdnAccessKeyId',
message: 'CDN Access Key ID:',
default: hasCDN ? currentCDN.accessKeyId : undefined,
validate: (input) => input.trim() ? true : 'CDN Access Key ID is required'
},
{
type: 'password',
name: 'cdnAccessKeySecret',
message: 'CDN Access Key Secret:',
default: hasCDN ? currentCDN.accessKeySecret : undefined,
validate: (input) => input.trim() ? true : 'CDN Access Key Secret is required'
},
{
type: 'input',
name: 'cdnDomain',
message: 'CDN Domain (e.g., https://cdn.example.com):',
default: hasCDN ? currentCDN.cdnDomain : undefined,
validate: (input) => {
if (!input.trim())
return 'CDN Domain is required';
if (!input.startsWith('http://') && !input.startsWith('https://')) {
return 'CDN Domain must start with http:// or https://';
}
return true;
}
}
]);
cdnConfig = {
accessKeyId: cdnAnswers.cdnAccessKeyId,
accessKeySecret: cdnAnswers.cdnAccessKeySecret,
cdnDomain: cdnAnswers.cdnDomain,
autoRefresh: true
};
}
else if (service.type === 'tos') {
// TOS CDN 配置(简化版,只需要CDN地址)
const cdnAnswers = await inquirer_1.default.prompt([
{
type: 'input',
name: 'cdnDomain',
message: 'CDN Domain (e.g., cdn.example.com):',
default: hasCDN ? currentCDN.cdnDomain : undefined,
validate: (input) => {
if (!input.trim())
return 'CDN Domain is required';
return true;
}
}
]);
cdnConfig = {
cdnDomain: cdnAnswers.cdnDomain
};
}
}
else if (hasCDN) {
// 如果之前有 CDN 配置但现在要禁用,清空 CDN 配置
cdnConfig = null;
}
}
else if (hasCDN) {
// 保持现有 CDN 配置不变
cdnConfig = currentCDN;
}
}
try {
// 构建更新的配置
const updatedConfig = { ...updates };
const configData = { ...updatedConfig };
delete configData.name;
delete configData.isDefault;
// 处理 CDN 配置
if (Object.keys(cdnConfig).length > 0) {
if (cdnConfig === null) {
// 清空 CDN 配置
delete configData.cdn;
}
else {
// 更新或添加 CDN 配置
configData.cdn = cdnConfig;
}
}
await this.configManager.updateService(name, {
name: updates.name,
config: configData,
isDefault: updates.isDefault
});
console.log(chalk_1.default.green(`✓ Service '${name}' updated successfully`));
}
catch (error) {
console.error(chalk_1.default.red('Failed to update service:'), error.message || 'Unknown error');
}
}
/**
* 删除服务配置
*/
async removeService(name) {
const service = this.configManager.getService(name);
if (!service) {
console.error(chalk_1.default.red(`Service '${name}' not found`));
return;
}
const { confirm } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'confirm',
message: `Are you sure you want to remove service '${name}'?`,
default: false
}
]);
if (!confirm) {
console.log('Operation cancelled');
return;
}
try {
await this.configManager.removeService(name);
console.log(chalk_1.default.green(`✓ Service '${name}' removed successfully`));
}
catch (error) {
console.error(chalk_1.default.red('Failed to remove service:'), error.message || 'Unknown error');
}
}
/**
* 设置默认服务
*/
async setDefaultService(name) {
try {
await this.configManager.setDefaultService(name);
console.log(chalk_1.default.green(`✓ Service '${name}' set as default`));
}
catch (error) {
console.error(chalk_1.default.red('Failed to set default service:'), error.message || 'Unknown error');
}
}
/**
* 测试服务配置
*/
async testService(name) {
const service = this.configManager.getService(name);
if (!service) {
console.error(chalk_1.default.red(`Service '${name}' not found`));
return;
}
console.log(chalk_1.default.blue(`Testing service '${name}'...`));
try {
const uploader = UploaderFactory_1.UploaderFactory.createUploader(service);
const isValid = await uploader.validateConfig();
if (isValid) {
console.log(chalk_1.default.green(`✓ Service '${name}' configuration is valid`));
}
else {
console.log(chalk_1.default.red(`✗ Service '${name}' configuration is invalid`));
}
}
catch (error) {
console.error(chalk_1.default.red(`✗ Service '${name}' test failed:`), error.message || 'Unknown error');
}
}
/**
* 显示配置文件路径
*/
async showConfigPath() {
const configPath = this.configManager.getConfigPath();
const configExists = await this.configManager.configExists();
console.log(chalk_1.default.bold('\nConfiguration File Information:'));
console.log('─'.repeat(50));
console.log(`Path: ${chalk_1.default.cyan(configPath)}`);
console.log(`Exists: ${configExists ? chalk_1.default.green('Yes') : chalk_1.default.yellow('No')}`);
if (configExists) {
try {
const stats = await require('fs-extra').stat(configPath);
console.log(`Size: ${stats.size} bytes`);
console.log(`Modified: ${stats.mtime.toLocaleString()}`);
const services = this.configManager.getAllServices();
console.log(`Services: ${services.length}`);
if (services.length > 0) {
console.log('\nConfigured services:');
services.forEach(service => {
const defaultMark = service.isDefault ? chalk_1.default.green(' (default)') : '';
console.log(` • ${chalk_1.default.cyan(service.name)}${defaultMark} - ${UploaderFactory_1.UploaderFactory.getTypeDisplayName(service.type)}`);
});
}
}
catch (error) {
console.log(chalk_1.default.red(`Error reading config file: ${error.message}`));
}
}
else {
console.log(chalk_1.default.yellow('\nTo create a configuration file, run:'));
console.log(chalk_1.default.cyan(' one-upload config add'));
}
}
}
exports.ConfigCommand = ConfigCommand;
//# sourceMappingURL=ConfigCommand.js.map