edgeone-cli 1.0.13 → 1.0.14
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/dist/api/client.js +1 -210
- package/dist/commands/config.js +1 -312
- package/dist/commands/edgeone.js +1 -414
- package/dist/constants/index.js +1 -60
- package/dist/index.js +1 -37
- package/dist/repl/config-menu.js +1 -264
- package/dist/repl/index.js +1 -95
- package/dist/repl/prefetch-menu.js +1 -192
- package/dist/repl/purge-menu.js +1 -228
- package/dist/repl/ui.js +1 -29
- package/dist/repl/zone-menu.js +1 -219
- package/dist/types/index.js +1 -5
- package/dist/utils/crypto.js +1 -85
- package/dist/utils/date.js +1 -52
- package/dist/utils/display.js +1 -72
- package/dist/utils/signature.js +1 -49
- package/package.json +1 -1
package/dist/api/client.js
CHANGED
|
@@ -1,210 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { toISOStringWithTimezone, getOneWeekAgo, getOneMonthAgo } from "../utils/date.js";
|
|
3
|
-
export class EdgeOneClient {
|
|
4
|
-
secretId;
|
|
5
|
-
secretKey;
|
|
6
|
-
endpoint;
|
|
7
|
-
region;
|
|
8
|
-
mode;
|
|
9
|
-
constructor(secretId, secretKey, endpoint, region = "ap-guangzhou", mode = "production") {
|
|
10
|
-
this.secretId = secretId;
|
|
11
|
-
this.secretKey = secretKey;
|
|
12
|
-
// 确保 endpoint 有效,防止空值或 undefined
|
|
13
|
-
this.endpoint = endpoint || "teo.tencentcloudapi.com";
|
|
14
|
-
this.region = region;
|
|
15
|
-
this.mode = mode;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* 发送请求
|
|
19
|
-
*/
|
|
20
|
-
async request(action, version, params) {
|
|
21
|
-
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
22
|
-
// 请求体直接使用传入的参数
|
|
23
|
-
const requestBody = JSON.stringify(params);
|
|
24
|
-
// 计算签名
|
|
25
|
-
const authorization = calculateSignature({
|
|
26
|
-
secretId: this.secretId,
|
|
27
|
-
secretKey: this.secretKey,
|
|
28
|
-
endpoint: this.endpoint,
|
|
29
|
-
action,
|
|
30
|
-
requestBody,
|
|
31
|
-
timestamp,
|
|
32
|
-
});
|
|
33
|
-
const requestHeaders = {
|
|
34
|
-
Authorization: authorization,
|
|
35
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
36
|
-
"X-TC-Action": action,
|
|
37
|
-
"X-TC-Version": version,
|
|
38
|
-
"X-TC-Timestamp": timestamp,
|
|
39
|
-
"X-TC-Language": "zh-CN",
|
|
40
|
-
};
|
|
41
|
-
// 仅在调试模式下显示请求信息
|
|
42
|
-
if (this.mode === "debug") {
|
|
43
|
-
console.log("\n请求信息:");
|
|
44
|
-
console.log(` URL: https://${this.endpoint}`);
|
|
45
|
-
console.log(` Headers: ${JSON.stringify({ ...requestHeaders, Authorization: "***" }, null, 2)}`);
|
|
46
|
-
console.log(` Body: ${requestBody}\n`);
|
|
47
|
-
}
|
|
48
|
-
// 发送请求
|
|
49
|
-
try {
|
|
50
|
-
const response = await fetch(`https://${this.endpoint}`, {
|
|
51
|
-
method: "POST",
|
|
52
|
-
headers: requestHeaders,
|
|
53
|
-
body: requestBody,
|
|
54
|
-
});
|
|
55
|
-
const result = (await response.json());
|
|
56
|
-
// 仅在调试模式下显示响应结果
|
|
57
|
-
if (this.mode === "debug") {
|
|
58
|
-
console.log("响应结果:");
|
|
59
|
-
console.log(JSON.stringify(result, null, 2));
|
|
60
|
-
}
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
throw error;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* 创建清除缓存任务
|
|
69
|
-
*/
|
|
70
|
-
async createPurgeTask(options) {
|
|
71
|
-
const params = {
|
|
72
|
-
ZoneId: options.zoneId || "*",
|
|
73
|
-
Type: options.type,
|
|
74
|
-
Method: "delete",
|
|
75
|
-
Targets: options.targets || [],
|
|
76
|
-
};
|
|
77
|
-
return (await this.request("CreatePurgeTask", "2022-09-01", params));
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* 查询清除缓存任务
|
|
81
|
-
*/
|
|
82
|
-
async describePurgeTasks(zoneIds) {
|
|
83
|
-
const baseParams = {
|
|
84
|
-
StartTime: toISOStringWithTimezone(getOneWeekAgo()),
|
|
85
|
-
EndTime: toISOStringWithTimezone(new Date()),
|
|
86
|
-
};
|
|
87
|
-
// 如果没有指定 zoneIds,直接查询
|
|
88
|
-
if (!zoneIds || zoneIds.length === 0) {
|
|
89
|
-
return (await this.request("DescribePurgeTasks", "2022-09-01", baseParams));
|
|
90
|
-
}
|
|
91
|
-
// 如果只有一个 zone,直接查询
|
|
92
|
-
if (zoneIds.length === 1) {
|
|
93
|
-
baseParams.ZoneId = zoneIds[0];
|
|
94
|
-
return (await this.request("DescribePurgeTasks", "2022-09-01", baseParams));
|
|
95
|
-
}
|
|
96
|
-
// 多个 zone,并发查询并合并结果
|
|
97
|
-
const requests = zoneIds.map(zoneId => {
|
|
98
|
-
const params = { ...baseParams, ZoneId: zoneId };
|
|
99
|
-
return this.request("DescribePurgeTasks", "2022-09-01", params);
|
|
100
|
-
});
|
|
101
|
-
const results = await Promise.all(requests);
|
|
102
|
-
// 合并结果
|
|
103
|
-
const allTasks = [];
|
|
104
|
-
for (const result of results) {
|
|
105
|
-
const response = result.Response;
|
|
106
|
-
if ("Error" in response) {
|
|
107
|
-
// 如果任何一个请求失败,返回第一个错误
|
|
108
|
-
return result;
|
|
109
|
-
}
|
|
110
|
-
if ("Tasks" in response && response.Tasks) {
|
|
111
|
-
allTasks.push(...response.Tasks);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// 合并成功响应
|
|
115
|
-
const merged = {
|
|
116
|
-
Response: {
|
|
117
|
-
...results[0].Response,
|
|
118
|
-
Tasks: allTasks,
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
return merged;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* 创建预热任务
|
|
125
|
-
*/
|
|
126
|
-
async createPrefetchTask(options) {
|
|
127
|
-
const params = {
|
|
128
|
-
ZoneId: options.zoneId || "*",
|
|
129
|
-
Targets: options.urls,
|
|
130
|
-
};
|
|
131
|
-
return (await this.request("CreatePrefetchTask", "2022-09-01", params));
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* 查询预热任务
|
|
135
|
-
*/
|
|
136
|
-
async describePrefetchTasks(zoneIds) {
|
|
137
|
-
const baseParams = {
|
|
138
|
-
StartTime: toISOStringWithTimezone(getOneMonthAgo()),
|
|
139
|
-
EndTime: toISOStringWithTimezone(new Date()),
|
|
140
|
-
};
|
|
141
|
-
// 如果没有指定 zoneIds,直接查询
|
|
142
|
-
if (!zoneIds || zoneIds.length === 0) {
|
|
143
|
-
return (await this.request("DescribePrefetchTasks", "2022-09-01", baseParams));
|
|
144
|
-
}
|
|
145
|
-
// 如果只有一个 zone,直接查询
|
|
146
|
-
if (zoneIds.length === 1) {
|
|
147
|
-
baseParams.ZoneId = zoneIds[0];
|
|
148
|
-
return (await this.request("DescribePrefetchTasks", "2022-09-01", baseParams));
|
|
149
|
-
}
|
|
150
|
-
// 多个 zone,并发查询并合并结果
|
|
151
|
-
const requests = zoneIds.map(zoneId => {
|
|
152
|
-
const params = { ...baseParams, ZoneId: zoneId };
|
|
153
|
-
return this.request("DescribePrefetchTasks", "2022-09-01", params);
|
|
154
|
-
});
|
|
155
|
-
const results = await Promise.all(requests);
|
|
156
|
-
// 合并结果
|
|
157
|
-
const allTasks = [];
|
|
158
|
-
for (const result of results) {
|
|
159
|
-
const response = result.Response;
|
|
160
|
-
if ("Error" in response) {
|
|
161
|
-
// 如果任何一个请求失败,返回第一个错误
|
|
162
|
-
return result;
|
|
163
|
-
}
|
|
164
|
-
if ("PrefetchTasks" in response && response.PrefetchTasks) {
|
|
165
|
-
allTasks.push(...response.PrefetchTasks);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
// 合并成功响应
|
|
169
|
-
const merged = {
|
|
170
|
-
Response: {
|
|
171
|
-
...results[0].Response,
|
|
172
|
-
PrefetchTasks: allTasks,
|
|
173
|
-
},
|
|
174
|
-
};
|
|
175
|
-
return merged;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* 修改站点状态
|
|
179
|
-
*/
|
|
180
|
-
async modifyZoneStatus(options) {
|
|
181
|
-
const params = {
|
|
182
|
-
ZoneId: options.zoneId,
|
|
183
|
-
Paused: options.paused,
|
|
184
|
-
};
|
|
185
|
-
return (await this.request("ModifyZoneStatus", "2022-09-01", params));
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* 查询站点状态
|
|
189
|
-
*/
|
|
190
|
-
async describeZoneStatus(options) {
|
|
191
|
-
const params = {
|
|
192
|
-
Filters: [
|
|
193
|
-
{
|
|
194
|
-
Name: "zone-id",
|
|
195
|
-
Values: [options.zoneId],
|
|
196
|
-
},
|
|
197
|
-
],
|
|
198
|
-
};
|
|
199
|
-
return (await this.request("DescribeZones", "2022-01-06", params));
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* 查询站点域名列表
|
|
203
|
-
*/
|
|
204
|
-
async describeHostSettings(options) {
|
|
205
|
-
const params = {
|
|
206
|
-
ZoneId: options.zoneId,
|
|
207
|
-
};
|
|
208
|
-
return (await this.request("DescribeHostSettings", "2024-06-04", params));
|
|
209
|
-
}
|
|
210
|
-
}
|
|
1
|
+
import{calculateSignature}from"../utils/signature.js";import{toISOStringWithTimezone,getOneWeekAgo,getOneMonthAgo}from"../utils/date.js";export class EdgeOneClient{secretId;secretKey;endpoint;region;mode;constructor(secretId,secretKey,endpoint,region="ap-guangzhou",mode="production"){this.secretId=secretId,this.secretKey=secretKey,this.endpoint=endpoint||"teo.tencentcloudapi.com",this.region=region,this.mode=mode}async request(action,version,params){const timestamp=Math.floor(Date.now()/1e3).toString(),requestBody=JSON.stringify(params),requestHeaders={Authorization:calculateSignature({secretId:this.secretId,secretKey:this.secretKey,endpoint:this.endpoint,action,requestBody,timestamp}),"Content-Type":"application/json; charset=utf-8","X-TC-Action":action,"X-TC-Version":version,"X-TC-Timestamp":timestamp,"X-TC-Language":"zh-CN"};"debug"===this.mode&&(console.log("\n请求信息:"),console.log(` URL: https://${this.endpoint}`),console.log(` Headers: ${JSON.stringify({...requestHeaders,Authorization:"***"},null,2)}`),console.log(` Body: ${requestBody}\n`));try{const response=await fetch(`https://${this.endpoint}`,{method:"POST",headers:requestHeaders,body:requestBody}),result=await response.json();return"debug"===this.mode&&(console.log("响应结果:"),console.log(JSON.stringify(result,null,2))),result}catch(error){throw error}}async createPurgeTask(options){const params={ZoneId:options.zoneId||"*",Type:options.type,Method:"delete",Targets:options.targets||[]};return await this.request("CreatePurgeTask","2022-09-01",params)}async describePurgeTasks(zoneIds){const baseParams={StartTime:toISOStringWithTimezone(getOneWeekAgo()),EndTime:toISOStringWithTimezone(new Date)};if(!zoneIds||0===zoneIds.length)return await this.request("DescribePurgeTasks","2022-09-01",baseParams);if(1===zoneIds.length)return baseParams.ZoneId=zoneIds[0],await this.request("DescribePurgeTasks","2022-09-01",baseParams);const requests=zoneIds.map(zoneId=>{const params={...baseParams,ZoneId:zoneId};return this.request("DescribePurgeTasks","2022-09-01",params)}),results=await Promise.all(requests),allTasks=[];for(const result of results){const response=result.Response;if("Error"in response)return result;"Tasks"in response&&response.Tasks&&allTasks.push(...response.Tasks)}return{Response:{...results[0].Response,Tasks:allTasks}}}async createPrefetchTask(options){const params={ZoneId:options.zoneId||"*",Targets:options.urls};return await this.request("CreatePrefetchTask","2022-09-01",params)}async describePrefetchTasks(zoneIds){const baseParams={StartTime:toISOStringWithTimezone(getOneMonthAgo()),EndTime:toISOStringWithTimezone(new Date)};if(!zoneIds||0===zoneIds.length)return await this.request("DescribePrefetchTasks","2022-09-01",baseParams);if(1===zoneIds.length)return baseParams.ZoneId=zoneIds[0],await this.request("DescribePrefetchTasks","2022-09-01",baseParams);const requests=zoneIds.map(zoneId=>{const params={...baseParams,ZoneId:zoneId};return this.request("DescribePrefetchTasks","2022-09-01",params)}),results=await Promise.all(requests),allTasks=[];for(const result of results){const response=result.Response;if("Error"in response)return result;"PrefetchTasks"in response&&response.PrefetchTasks&&allTasks.push(...response.PrefetchTasks)}return{Response:{...results[0].Response,PrefetchTasks:allTasks}}}async modifyZoneStatus(options){const params={ZoneId:options.zoneId,Paused:options.paused};return await this.request("ModifyZoneStatus","2022-09-01",params)}async describeZoneStatus(options){const params={Filters:[{Name:"zone-id",Values:[options.zoneId]}]};return await this.request("DescribeZones","2022-01-06",params)}async describeHostSettings(options){const params={ZoneId:options.zoneId};return await this.request("DescribeHostSettings","2024-06-04",params)}}
|
package/dist/commands/config.js
CHANGED
|
@@ -1,312 +1 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { Command } from "commander";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import inquirer from "inquirer";
|
|
5
|
-
import os from "os";
|
|
6
|
-
import path from "path";
|
|
7
|
-
import { decrypt, encrypt, isEncrypted } from "../utils/crypto.js";
|
|
8
|
-
const CONFIG_DIR = path.join(os.homedir(), ".edgeone-cli");
|
|
9
|
-
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
10
|
-
/**
|
|
11
|
-
* 确保配置目录存在
|
|
12
|
-
*/
|
|
13
|
-
export function ensureConfigDir() {
|
|
14
|
-
if (!fs.existsSync(CONFIG_DIR)) {
|
|
15
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* 读取配置(自动解密,自动迁移旧格式)
|
|
20
|
-
*/
|
|
21
|
-
export function readConfig() {
|
|
22
|
-
if (fs.existsSync(CONFIG_FILE)) {
|
|
23
|
-
const content = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
24
|
-
const stored = JSON.parse(content);
|
|
25
|
-
let secretKey = stored.secretKey;
|
|
26
|
-
let needsMigration = !stored.encrypted;
|
|
27
|
-
// 如果是加密格式或检测到是加密数据,则解密
|
|
28
|
-
if (stored.encrypted || isEncrypted(stored.secretKey)) {
|
|
29
|
-
try {
|
|
30
|
-
secretKey = decrypt(stored.secretKey);
|
|
31
|
-
needsMigration = false;
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
// 解密失败,可能是旧格式明文
|
|
35
|
-
needsMigration = false;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// 迁移 domainIdList:如果是字符串数组,转换为 ZoneConfig 数组
|
|
39
|
-
let domainIdList = [];
|
|
40
|
-
if (stored.domainIdList && stored.domainIdList.length > 0) {
|
|
41
|
-
if (typeof stored.domainIdList[0] === "string") {
|
|
42
|
-
// 旧格式:字符串数组,转换为 ZoneConfig
|
|
43
|
-
domainIdList = stored.domainIdList.map((id) => ({ id }));
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
// 新格式:ZoneConfig 数组
|
|
47
|
-
domainIdList = stored.domainIdList;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const config = {
|
|
51
|
-
secretId: stored.secretId,
|
|
52
|
-
secretKey: secretKey,
|
|
53
|
-
endpoint: stored.endpoint || "teo.tencentcloudapi.com", // 默认值
|
|
54
|
-
mode: stored.mode || "production", // 默认为生产模式
|
|
55
|
-
domainIdList: domainIdList, // 默认为空数组
|
|
56
|
-
};
|
|
57
|
-
// 自动迁移:检测到旧格式明文配置,自动转为加密格式
|
|
58
|
-
if (needsMigration) {
|
|
59
|
-
writeConfig(config);
|
|
60
|
-
console.log(chalk.gray("💡 配置已自动升级为加密格式"));
|
|
61
|
-
}
|
|
62
|
-
return config;
|
|
63
|
-
}
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* 写入配置(自动加密 SecretKey)
|
|
68
|
-
*/
|
|
69
|
-
export function writeConfig(config) {
|
|
70
|
-
ensureConfigDir();
|
|
71
|
-
// 加密 secretKey
|
|
72
|
-
const encryptedSecretKey = encrypt(config.secretKey);
|
|
73
|
-
const stored = {
|
|
74
|
-
secretId: config.secretId,
|
|
75
|
-
secretKey: encryptedSecretKey,
|
|
76
|
-
endpoint: config.endpoint,
|
|
77
|
-
mode: config.mode,
|
|
78
|
-
domainIdList: config.domainIdList,
|
|
79
|
-
encrypted: true,
|
|
80
|
-
};
|
|
81
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(stored, null, 2), "utf-8");
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* 遮盖字符串中间部分
|
|
85
|
-
*/
|
|
86
|
-
function maskString(str, showLength = 4) {
|
|
87
|
-
if (!str)
|
|
88
|
-
return "N/A";
|
|
89
|
-
if (str.length <= showLength * 2)
|
|
90
|
-
return str;
|
|
91
|
-
return str.slice(0, showLength) + "****" + str.slice(-showLength);
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* 显示配置
|
|
95
|
-
*/
|
|
96
|
-
function showConfig() {
|
|
97
|
-
const config = readConfig();
|
|
98
|
-
if (!config) {
|
|
99
|
-
console.log(chalk.yellow('⚠ 未找到配置文件,请先运行 "edgeone-cli config init"'));
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
const modeText = config.mode === "debug" ? "调试模式" : "生产模式";
|
|
103
|
-
const modeColor = config.mode === "debug" ? chalk.yellow : chalk.green;
|
|
104
|
-
console.log(chalk.cyan("\n📋 当前配置:\n"));
|
|
105
|
-
console.log(chalk.gray("─".repeat(40)));
|
|
106
|
-
console.log(` SecretId: ${chalk.green(maskString(config.secretId))}`);
|
|
107
|
-
console.log(` SecretKey: ${chalk.green(maskString(config.secretKey))}`);
|
|
108
|
-
console.log(` 接口请求域名: ${chalk.yellow(config.endpoint || "teo.tencentcloudapi.com")}`);
|
|
109
|
-
console.log(` 运行模式: ${modeColor(modeText)}`);
|
|
110
|
-
if (config.domainIdList && config.domainIdList.length > 0) {
|
|
111
|
-
console.log(` 站点列表:`);
|
|
112
|
-
config.domainIdList.forEach((zone, idx) => {
|
|
113
|
-
const display = zone.name ? `${zone.name} (${zone.id})` : zone.id;
|
|
114
|
-
console.log(` ${idx + 1}. ${chalk.yellow(display)}`);
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
console.log(chalk.gray("─".repeat(40)));
|
|
118
|
-
}
|
|
119
|
-
// 创建配置命令
|
|
120
|
-
const configCmd = new Command("config").description("管理 EdgeOne 配置");
|
|
121
|
-
// 初始化配置
|
|
122
|
-
configCmd
|
|
123
|
-
.command("init")
|
|
124
|
-
.description("初始化配置")
|
|
125
|
-
.action(async () => {
|
|
126
|
-
const existingConfig = readConfig();
|
|
127
|
-
// 如果已有配置,提示用户确认
|
|
128
|
-
if (existingConfig) {
|
|
129
|
-
console.log(chalk.yellow("\n⚠ 检测到已存在的配置:\n"));
|
|
130
|
-
console.log(chalk.gray("─".repeat(40)));
|
|
131
|
-
console.log(` SecretId: ${chalk.green(maskString(existingConfig.secretId))}`);
|
|
132
|
-
console.log(` SecretKey: ${chalk.green(maskString(existingConfig.secretKey))}`);
|
|
133
|
-
console.log(` 接口请求域名: ${chalk.yellow(existingConfig.endpoint || "teo.tencentcloudapi.com")}`);
|
|
134
|
-
console.log(` 运行模式: ${existingConfig.mode === "debug" ? chalk.yellow("调试模式") : chalk.green("生产模式")}`);
|
|
135
|
-
console.log(chalk.gray("─".repeat(40)));
|
|
136
|
-
const { confirm } = await inquirer.prompt([
|
|
137
|
-
{
|
|
138
|
-
type: "confirm",
|
|
139
|
-
name: "confirm",
|
|
140
|
-
message: "已有配置将被覆盖,是否继续?",
|
|
141
|
-
default: false,
|
|
142
|
-
},
|
|
143
|
-
]);
|
|
144
|
-
if (!confirm) {
|
|
145
|
-
console.log(chalk.gray("❌️ 已取消"));
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
console.log(chalk.cyan("\n🔧 开始初始化 EdgeOne 配置\n"));
|
|
150
|
-
const answers = await inquirer.prompt([
|
|
151
|
-
{
|
|
152
|
-
type: "input",
|
|
153
|
-
name: "secretId",
|
|
154
|
-
message: "请输入 SecretId:",
|
|
155
|
-
validate: (input) => input.trim() !== "" || "SecretId 不能为空",
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
type: "password",
|
|
159
|
-
name: "secretKey",
|
|
160
|
-
message: "请输入 SecretKey:",
|
|
161
|
-
mask: "*",
|
|
162
|
-
validate: (input) => input.trim() !== "" || "SecretKey 不能为空",
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
type: "input",
|
|
166
|
-
name: "endpoint",
|
|
167
|
-
message: "请输入接口请求域名:",
|
|
168
|
-
default: "teo.tencentcloudapi.com",
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
type: "list",
|
|
172
|
-
name: "mode",
|
|
173
|
-
message: "请选择运行模式:",
|
|
174
|
-
choices: [
|
|
175
|
-
{ name: "生产模式 (不显示调试信息)", value: "production" },
|
|
176
|
-
{ name: "调试模式 (显示详细请求信息)", value: "debug" },
|
|
177
|
-
],
|
|
178
|
-
default: "production",
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
type: "input",
|
|
182
|
-
name: "domainIdList",
|
|
183
|
-
message: "请输入站点列表,格式: id:备注(多个用逗号分隔,留空跳过):",
|
|
184
|
-
default: "",
|
|
185
|
-
},
|
|
186
|
-
]);
|
|
187
|
-
// 处理站点列表,支持 id:name 格式
|
|
188
|
-
let domainIdList = [];
|
|
189
|
-
const domainIdInput = answers.domainIdList;
|
|
190
|
-
if (domainIdInput && domainIdInput.trim()) {
|
|
191
|
-
domainIdList = domainIdInput
|
|
192
|
-
.split(",")
|
|
193
|
-
.map((item) => {
|
|
194
|
-
item = item.trim();
|
|
195
|
-
if (item.includes(":")) {
|
|
196
|
-
const [id, name] = item.split(":");
|
|
197
|
-
return { id: id.trim(), name: name.trim() };
|
|
198
|
-
}
|
|
199
|
-
return { id: item };
|
|
200
|
-
})
|
|
201
|
-
.filter((zone) => zone.id !== "");
|
|
202
|
-
}
|
|
203
|
-
writeConfig({
|
|
204
|
-
secretId: answers.secretId,
|
|
205
|
-
secretKey: answers.secretKey,
|
|
206
|
-
endpoint: answers.endpoint || "teo.tencentcloudapi.com",
|
|
207
|
-
mode: answers.mode || "production",
|
|
208
|
-
domainIdList: domainIdList,
|
|
209
|
-
});
|
|
210
|
-
console.log(chalk.green("\n✅ 配置已加密保存到: " + CONFIG_FILE));
|
|
211
|
-
});
|
|
212
|
-
// 显示配置
|
|
213
|
-
configCmd.command("show").description("显示当前配置").action(showConfig);
|
|
214
|
-
// 删除配置
|
|
215
|
-
configCmd
|
|
216
|
-
.command("remove")
|
|
217
|
-
.description("删除配置文件")
|
|
218
|
-
.action(async () => {
|
|
219
|
-
if (!fs.existsSync(CONFIG_FILE)) {
|
|
220
|
-
console.log(chalk.yellow("⚠ 配置文件不存在"));
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
const { confirm } = await inquirer.prompt([
|
|
224
|
-
{
|
|
225
|
-
type: "confirm",
|
|
226
|
-
name: "confirm",
|
|
227
|
-
message: "确定要删除配置文件吗?",
|
|
228
|
-
default: false,
|
|
229
|
-
},
|
|
230
|
-
]);
|
|
231
|
-
if (confirm) {
|
|
232
|
-
fs.unlinkSync(CONFIG_FILE);
|
|
233
|
-
console.log(chalk.green("✅ 配置文件已删除"));
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
console.log(chalk.gray("❌️ 已取消"));
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
// 编辑配置
|
|
240
|
-
configCmd
|
|
241
|
-
.command("edit")
|
|
242
|
-
.description("编辑配置项")
|
|
243
|
-
.action(async () => {
|
|
244
|
-
const config = readConfig();
|
|
245
|
-
if (!config) {
|
|
246
|
-
console.log(chalk.yellow('⚠ 未找到配置文件,请先运行 "edgeone-cli config init"'));
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const answers = await inquirer.prompt([
|
|
250
|
-
{
|
|
251
|
-
type: "list",
|
|
252
|
-
name: "field",
|
|
253
|
-
message: "选择要修改的配置项:",
|
|
254
|
-
choices: [
|
|
255
|
-
{ name: "SecretId", value: "secretId" },
|
|
256
|
-
{ name: "SecretKey", value: "secretKey" },
|
|
257
|
-
{ name: "接口请求域名", value: "endpoint" },
|
|
258
|
-
{ name: "运行模式", value: "mode" },
|
|
259
|
-
{ name: "站点列表", value: "domainIdList" },
|
|
260
|
-
],
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
type: "input",
|
|
264
|
-
name: "value",
|
|
265
|
-
message: (answers) => `请输入新的 ${answers.field}:`,
|
|
266
|
-
when: (answers) => answers.field !== "secretKey" && answers.field !== "mode" && answers.field !== "domainIdList",
|
|
267
|
-
validate: (input) => input.trim() !== "" || "不能为空",
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
type: "password",
|
|
271
|
-
name: "value",
|
|
272
|
-
message: "请输入新的 SecretKey:",
|
|
273
|
-
mask: "*",
|
|
274
|
-
when: (answers) => answers.field === "secretKey",
|
|
275
|
-
validate: (input) => input.trim() !== "" || "不能为空",
|
|
276
|
-
},
|
|
277
|
-
{
|
|
278
|
-
type: "list",
|
|
279
|
-
name: "value",
|
|
280
|
-
message: "请选择运行模式:",
|
|
281
|
-
when: (answers) => answers.field === "mode",
|
|
282
|
-
choices: [
|
|
283
|
-
{ name: "生产模式 (不显示调试信息)", value: "production" },
|
|
284
|
-
{ name: "调试模式 (显示详细请求信息)", value: "debug" },
|
|
285
|
-
],
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
type: "input",
|
|
289
|
-
name: "value",
|
|
290
|
-
message: "请输入站点列表(多个用逗号分隔):",
|
|
291
|
-
when: (answers) => answers.field === "domainIdList",
|
|
292
|
-
default: "",
|
|
293
|
-
},
|
|
294
|
-
]);
|
|
295
|
-
if (answers.field === "domainIdList") {
|
|
296
|
-
const domainIdInput = answers.value;
|
|
297
|
-
const domainIds = domainIdInput && domainIdInput.trim()
|
|
298
|
-
? domainIdInput
|
|
299
|
-
.split(",")
|
|
300
|
-
.map(id => id.trim())
|
|
301
|
-
.filter(id => id !== "")
|
|
302
|
-
: [];
|
|
303
|
-
config[answers.field] = domainIds;
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
config[answers.field] = answers.value;
|
|
307
|
-
}
|
|
308
|
-
writeConfig(config);
|
|
309
|
-
console.log(chalk.green("✅ 配置已更新"));
|
|
310
|
-
});
|
|
311
|
-
export default configCmd;
|
|
312
|
-
export { CONFIG_FILE };
|
|
1
|
+
import chalk from"chalk";import{Command}from"commander";import fs from"fs";import inquirer from"inquirer";import os from"os";import path from"path";import{decrypt,encrypt,isEncrypted}from"../utils/crypto.js";const CONFIG_DIR=path.join(os.homedir(),".edgeone-cli"),CONFIG_FILE=path.join(CONFIG_DIR,"config.json");export function ensureConfigDir(){fs.existsSync(CONFIG_DIR)||fs.mkdirSync(CONFIG_DIR,{recursive:!0})}export function readConfig(){if(fs.existsSync(CONFIG_FILE)){const content=fs.readFileSync(CONFIG_FILE,"utf-8"),stored=JSON.parse(content);let secretKey=stored.secretKey,needsMigration=!stored.encrypted;if(stored.encrypted||isEncrypted(stored.secretKey))try{secretKey=decrypt(stored.secretKey),needsMigration=!1}catch{needsMigration=!1}let domainIdList=[];stored.domainIdList&&stored.domainIdList.length>0&&(domainIdList="string"==typeof stored.domainIdList[0]?stored.domainIdList.map(id=>({id})):stored.domainIdList);const config={secretId:stored.secretId,secretKey,endpoint:stored.endpoint||"teo.tencentcloudapi.com",mode:stored.mode||"production",domainIdList};return needsMigration&&(writeConfig(config),console.log(chalk.gray("💡 配置已自动升级为加密格式"))),config}return null}export function writeConfig(config){ensureConfigDir();const encryptedSecretKey=encrypt(config.secretKey),stored={secretId:config.secretId,secretKey:encryptedSecretKey,endpoint:config.endpoint,mode:config.mode,domainIdList:config.domainIdList,encrypted:!0};fs.writeFileSync(CONFIG_FILE,JSON.stringify(stored,null,2),"utf-8")}function maskString(str,showLength=4){return str?str.length<=2*showLength?str:str.slice(0,showLength)+"****"+str.slice(-showLength):"N/A"}const configCmd=new Command("config").description("管理 EdgeOne 配置");configCmd.command("init").description("初始化配置").action(async()=>{const existingConfig=readConfig();if(existingConfig){console.log(chalk.yellow("\n⚠ 检测到已存在的配置:\n")),console.log(chalk.gray("─".repeat(40))),console.log(` SecretId: ${chalk.green(maskString(existingConfig.secretId))}`),console.log(` SecretKey: ${chalk.green(maskString(existingConfig.secretKey))}`),console.log(` 接口请求域名: ${chalk.yellow(existingConfig.endpoint||"teo.tencentcloudapi.com")}`),console.log(` 运行模式: ${"debug"===existingConfig.mode?chalk.yellow("调试模式"):chalk.green("生产模式")}`),console.log(chalk.gray("─".repeat(40)));const{confirm}=await inquirer.prompt([{type:"confirm",name:"confirm",message:"已有配置将被覆盖,是否继续?",default:!1}]);if(!confirm)return void console.log(chalk.gray("❌️ 已取消"))}console.log(chalk.cyan("\n🔧 开始初始化 EdgeOne 配置\n"));const answers=await inquirer.prompt([{type:"input",name:"secretId",message:"请输入 SecretId:",validate:input=>""!==input.trim()||"SecretId 不能为空"},{type:"password",name:"secretKey",message:"请输入 SecretKey:",mask:"*",validate:input=>""!==input.trim()||"SecretKey 不能为空"},{type:"input",name:"endpoint",message:"请输入接口请求域名:",default:"teo.tencentcloudapi.com"},{type:"list",name:"mode",message:"请选择运行模式:",choices:[{name:"生产模式 (不显示调试信息)",value:"production"},{name:"调试模式 (显示详细请求信息)",value:"debug"}],default:"production"},{type:"input",name:"domainIdList",message:"请输入站点列表,格式: id:备注(多个用逗号分隔,留空跳过):",default:""}]);let domainIdList=[];const domainIdInput=answers.domainIdList;domainIdInput&&domainIdInput.trim()&&(domainIdList=domainIdInput.split(",").map(item=>{if((item=item.trim()).includes(":")){const[id,name]=item.split(":");return{id:id.trim(),name:name.trim()}}return{id:item}}).filter(zone=>""!==zone.id)),writeConfig({secretId:answers.secretId,secretKey:answers.secretKey,endpoint:answers.endpoint||"teo.tencentcloudapi.com",mode:answers.mode||"production",domainIdList}),console.log(chalk.green("\n✅ 配置已加密保存到: "+CONFIG_FILE))}),configCmd.command("show").description("显示当前配置").action(function(){const config=readConfig();if(!config)return void console.log(chalk.yellow('⚠ 未找到配置文件,请先运行 "edgeone-cli config init"'));const modeText="debug"===config.mode?"调试模式":"生产模式",modeColor="debug"===config.mode?chalk.yellow:chalk.green;console.log(chalk.cyan("\n📋 当前配置:\n")),console.log(chalk.gray("─".repeat(40))),console.log(` SecretId: ${chalk.green(maskString(config.secretId))}`),console.log(` SecretKey: ${chalk.green(maskString(config.secretKey))}`),console.log(` 接口请求域名: ${chalk.yellow(config.endpoint||"teo.tencentcloudapi.com")}`),console.log(` 运行模式: ${modeColor(modeText)}`),config.domainIdList&&config.domainIdList.length>0&&(console.log(" 站点列表:"),config.domainIdList.forEach((zone,idx)=>{const display=zone.name?`${zone.name} (${zone.id})`:zone.id;console.log(` ${idx+1}. ${chalk.yellow(display)}`)})),console.log(chalk.gray("─".repeat(40)))}),configCmd.command("remove").description("删除配置文件").action(async()=>{if(!fs.existsSync(CONFIG_FILE))return void console.log(chalk.yellow("⚠ 配置文件不存在"));const{confirm}=await inquirer.prompt([{type:"confirm",name:"confirm",message:"确定要删除配置文件吗?",default:!1}]);confirm?(fs.unlinkSync(CONFIG_FILE),console.log(chalk.green("✅ 配置文件已删除"))):console.log(chalk.gray("❌️ 已取消"))}),configCmd.command("edit").description("编辑配置项").action(async()=>{const config=readConfig();if(!config)return void console.log(chalk.yellow('⚠ 未找到配置文件,请先运行 "edgeone-cli config init"'));const answers=await inquirer.prompt([{type:"list",name:"field",message:"选择要修改的配置项:",choices:[{name:"SecretId",value:"secretId"},{name:"SecretKey",value:"secretKey"},{name:"接口请求域名",value:"endpoint"},{name:"运行模式",value:"mode"},{name:"站点列表",value:"domainIdList"}]},{type:"input",name:"value",message:answers=>`请输入新的 ${answers.field}:`,when:answers=>"secretKey"!==answers.field&&"mode"!==answers.field&&"domainIdList"!==answers.field,validate:input=>""!==input.trim()||"不能为空"},{type:"password",name:"value",message:"请输入新的 SecretKey:",mask:"*",when:answers=>"secretKey"===answers.field,validate:input=>""!==input.trim()||"不能为空"},{type:"list",name:"value",message:"请选择运行模式:",when:answers=>"mode"===answers.field,choices:[{name:"生产模式 (不显示调试信息)",value:"production"},{name:"调试模式 (显示详细请求信息)",value:"debug"}]},{type:"input",name:"value",message:"请输入站点列表(多个用逗号分隔):",when:answers=>"domainIdList"===answers.field,default:""}]);if("domainIdList"===answers.field){const domainIdInput=answers.value,domainIds=domainIdInput&&domainIdInput.trim()?domainIdInput.split(",").map(id=>id.trim()).filter(id=>""!==id):[];config[answers.field]=domainIds}else config[answers.field]=answers.value;writeConfig(config),console.log(chalk.green("✅ 配置已更新"))});export default configCmd;export{CONFIG_FILE};
|