tencentcloud-edgeone-migration-nodejs-v2
Version:
tencentcloud cdn config copy to edgeone
1,046 lines (964 loc) • 31.4 kB
JavaScript
const _ = require("lodash");
const utils = require("./utils");
const transfer = require("./transfer");
const logger = require("./logger");
const ObjectsToCsv = require("objects-to-csv");
const isStandard = require("../src/getPlansUtils");
const { t } = require("../i18n/trans");
const { reportTitle, ZONE_GLOBAL_ENTITY, USE_DEFAULT } = require("./const");
const { AppId, isPrivate, isDev } = require("../config.json");
const genLog = require("../logGenerator")
const capi = require("./api")
const Aegis = require("../lib/@tencent/aegis-node-sdk");
const aegis = new Aegis({
id: "lJ7dDFybL1wqa79Po5", // 项目上报ID
...(!isPrivate && {
selector: {
type: "host",
},
}),
});
// 新版源站组接口未上线,暂时屏蔽后两个配置
const defaultConfigs = [
"IpFilter",
"StatusCodeCache",
"Compression",
"BandwidthAlert",
"RangeOriginPull",
"FollowRedirect",
"ErrorPage",
"RequestHeader",
"ResponseHeader",
"CacheKey",
"Cache",
"Authentication",
"ForceRedirect",
"Referer",
"MaxAge",
"UrlRedirect",
"UserAgentFilter",
"OfflineCache",
"PostMaxSize",
"Quic",
"WebSocket",
"PathBasedOrigin",
"PathRules",
"OriginPullTimeout",
"HostHeaderRewrite"
];
const ruleConfigs = [
"StatusCodeCache",
"Compression",
"RangeOriginPull",
"FollowRedirect",
"ErrorPage",
"RequestHeader",
"ResponseHeader",
"CacheKey",
"Cache",
"Authentication",
"MaxAge",
"UrlRedirect",
"OfflineCache",
"PostMaxSize",
"WebSocket",
"PathBasedOrigin",
"PathRules",
"OriginPullTimeout",
"HostHeaderRewrite"
];
const otherConfigs = ["IpFilterRefererUserAgentFilter", "BandwidthAlert"];
const httpsConfigs = [
"ForceRedirect",
"Quic",
"Http2",
"OcspStapling",
"Hsts",
"TlsVersion",
];
// SCDN 迁移名单
const SCDNDefaultConfigs = ["Acl", "Bot", "CC"];
const SCDN2EO_MAP = {
Acl: "AclConfig",
CC: "RateLimitConfig",
Bot: "BotConfig",
};
/**
* 任务入参
* @param {Object} options
* @param {Array} options.DomainZoneMaps CDN域名跟EO站点的映射关系数组,必须
* @param {Array} options.DomainZoneMaps[i].Domains CDN域名,可批量传入,要求跟站点匹配,必须
* @param {String} options.DomainZoneMaps[i].ZoneId EO站点唯一ID,必须
* @param {Array} options.Configs 指定要迁移的配置,默认值见readme说明,非必须
* @param {Boolean} options.NeedCreateDomain 是否需要创建域名,不传默认true,传false的话,只迁移配置,不创建域名,非必须
*/
async function runTasks(options) {
const reportDatas = [];
try {
const domainMaps = options.DomainZoneMaps;
const needCreateDomain =
options.NeedCreateDomain === undefined ? true : options.NeedCreateDomain;
let needConfigsKeys =
options.Configs && options.Configs.length > 0
? options.Configs.filter((config) => {
return defaultConfigs.includes(config);
})
: defaultConfigs;
if (needConfigsKeys.length === 0) {
needConfigsKeys = defaultConfigs;
}
if (!domainMaps || domainMaps.length === 0) {
genLog.throwError(t("参数 DomainZoneMaps 不存在或为空数组"));
}
const tasks = [];
domainMaps.forEach((map) => {
if (
!map.Domains ||
!map.Domains.length ||
map.Domains.length === 0 ||
!map.ZoneId
) {
return;
}
map.Domains.forEach((domain) => {
tasks.push({
domain,
zoneId: map.ZoneId,
});
});
});
if (tasks.length === 0) {
genLog.throwError(t("不存在有效的任务,请检查参数"));
}
genLog.defaultLog(t("待执行的任务数:{{taskLength}}", { taskLength: tasks.length }));
for (let task of tasks) {
const report = {
Domain: task.domain,
ZoneId: task.zoneId,
AppId,
Details: [],
};
genLog.defaultLog(t("开始任务,Domain: {{taskDomain}},ZoneId: {{taskZoneId}}", {
taskDomain: task.domain || null,
taskZoneId: task.zoneId || null,
}));
const res = await singleTask(
task.domain,
task.zoneId,
needConfigsKeys,
report,
needCreateDomain
);
if(!isDev) {
try {
// 日志上报
aegis.report(report);
} catch (e) {
logger.error(e);
}
}
report.Details.forEach((log) => {
reportDatas.push({
[reportTitle.DOMAIN]: report.Domain,
[reportTitle.ZONEID]: report.ZoneId,
[reportTitle.CONFIG]: log.config,
[reportTitle.RESULT]: log.result,
[reportTitle.DETAIL]: log.detail,
});
});
// 一个域名空一行
reportDatas.push({
[reportTitle.DOMAIN]: "",
[reportTitle.ZONEID]: "",
[reportTitle.CONFIG]: "",
[reportTitle.RESULT]: "",
[reportTitle.DETAIL]: "",
});
}
genLog.successLog(t("所有任务迁移完成"))
const csv = new ObjectsToCsv(reportDatas);
await csv.toDisk(`./report-${new Date().getTime()}.csv`);
logger.success(t("已生成迁移报告"));
} catch (e) {
logger.error(e);
} finally {
genLog.writeLogFile(`${new Date().toISOString()}-${AppId}-task`)
utils.exitTerminal();
}
}
/**
* 任务入参
* @param {Object} options
* @param {Array} options.DomainZoneMaps SCDN域名跟EO站点的映射关系数组,必须
* @param {Array} options.DomainZoneMaps[i].Domains SCDN域名唯一ID,可批量传入,要求跟站点匹配,必须
* @param {String} options.DomainZoneMaps[i].ZoneId EO站点唯一ID,必须
* @param {Array} options.Configs 指定要迁移的配置,默认值见readme说明,非必须
*/
async function runScdnTasks(options) {
const reportDatas = [];
try {
const domainMaps = options.DomainZoneMaps;
if (!domainMaps || domainMaps.length === 0) {
genLog.throwError(t("参数 DomainZoneMaps 不存在或为空数组"))
}
let tasks = [];
domainMaps.forEach((map) => {
if (
!map.Domains ||
!map.Domains.length ||
map.Domains.length === 0 ||
!map.ZoneId
) {
return;
}
map.Domains.forEach((domain) => {
tasks.push({
domain,
zoneId: map.ZoneId,
});
});
});
if (tasks.length === 0) {
genLog.throwError(t(`不存在有效的任务,请检查参数`))
}
const successDomainsInfo = await checkDomains(options, false);
const successDomains = tasks.filter((task) => {
return successDomainsInfo.some((successDomain) => {
return task.domain === successDomain.DomainName;
});
});
const unSuccessDomains = tasks.filter((task) => {
return !successDomains.some((successDomain) => {
return task.domain === successDomain.domain;
});
});
if (unSuccessDomains.length > 0) {
const regex = /^\[\{|\{|\}|\}|(\])$/g;
const unSuccessMessage = JSON.stringify(unSuccessDomains).replace(
regex,
""
);
genLog.warnLog(t("以下站点下域名未部署成功:{{unSuccessMessage}}", {unSuccessMessage}))
}
tasks = successDomains;
if (tasks.length === 0) {
unSuccessDomains.forEach((item) => {
reportDatas.push({
Domain: item.domain,
ZoneId: item.zoneId,
Detail: t("以上站点迁移失败"),
});
});
}
genLog.defaultLog(t("待迁移的任务数:{{taskLength}}", { taskLength: tasks.length }))
for (let task of tasks) {
const report = {
Domain: task.domain,
ZoneId: task.zoneId,
AppId,
Details: [],
};
genLog.defaultLog(t(
"开始SCDN配置迁移任务,ResourceId: {{taskDomain}},ZoneId: {{taskZoneId}}",
{ taskDomain: task.domain || null, taskZoneId: task.zoneId || null }
))
const res = await singleScdnTask(
task.domain,
task.zoneId,
report,
options
);
if(!isDev) {
try {
// 日志上报
aegis.report(report);
} catch (e) {
logger.error(e);
}
}
report.Details.forEach((log) => {
reportDatas.push({
[reportTitle.DOMAIN]: report.Domain,
[reportTitle.ZONEID]: report.ZoneId,
[reportTitle.CONFIG]: log.config,
[reportTitle.RESULT]: log.result,
[reportTitle.DETAIL]: log.detail,
});
});
if (tasks.length > 1) {
// 一个域名空一行
reportDatas.push({
[reportTitle.DOMAIN]: "",
[reportTitle.ZONEID]: "",
[reportTitle.CONFIG]: "",
[reportTitle.RESULT]: "",
[reportTitle.DETAIL]: "",
});
}
}
genLog.successLog(t(`所有任务迁移完成`))
const csv = new ObjectsToCsv(reportDatas);
await csv.toDisk(`./report-scdn-${new Date().getTime()}.csv`);
logger.success(t(`已生成迁移报告`));
} catch (e) {
logger.error(e);
} finally {
utils.exitTerminal();
genLog.writeLogFile(`${new Date().toISOString()}-${AppId}-scdnTask`)
}
}
/**
* 任务入参
* @param {Object} options
* @param {String} options.DomainZoneMaps[i].ZoneId EO站点唯一ID
*/
async function checkDomains(options, isExitProcess = true) {
const successDomains = [];
try {
for (let task of options.DomainZoneMaps) {
const limit = 20; // 单次最多查询20个域名
const taskDomains = _.uniq(task.Domains);
const fetchTimes = Math.ceil(taskDomains.length / limit);
const getDomains = [];
for (let i = 0; i < fetchTimes; i++) {
const domains = taskDomains.slice(i * limit, (i + 1) * limit);
const { AccelerationDomains } = await capi("DescribeAccelerationDomains", utils.TEO, {
ZoneId: task.ZoneId,
Filters: [{ Name: "domain-name", Values: domains }],
})
getDomains.push(...AccelerationDomains);
}
if (getDomains.length > 0) {
if (getDomains.length < task.Domains.length) {
const notExistDomains = task.Domains.filter(
(taskDomain) =>
!getDomains.some(
(existDomain) => existDomain.DomainName === taskDomain
)
);
genLog.warnLog(t("以下域名不存在于站点{{zoneId}}中:\n{{domains}}", {
zoneId: task.ZoneId,
domains: notExistDomains.join("\n"),
}))
}
const check = getDomains.filter(
(item) => item.DomainStatus !== "online"
);
const successDomain = getDomains.filter(
(item) => item.DomainStatus === "online"
);
successDomains.push(...successDomain);
if (check.length > 0) {
genLog.warnLog(t("站点ID:{{task}}以下域名未部署成功:", { task: task.ZoneId }))
check.forEach((item) => genLog.defaultLog(item.DomainName));
} else {
genLog.successLog(t("所有域名均已成功部署"))
}
} else {
genLog.warnLog(t("该站点(站点ID:{{task}})下未查询到域名,请核对站点ID是否正确", {
task: task.ZoneId,
}))
}
}
return successDomains;
} catch (e) {
genLog.errorLog(e)
} finally {
isExitProcess && utils.exitTerminal();
isExitProcess && genLog.writeLogFile(`${new Date().toISOString()}-${AppId}-checkDomains`)
}
}
/**
* 任务入参
* @param {Object} options
* @param {String} options.DomainZoneMaps[i].ZoneId EO站点唯一ID
*/
async function accessTask(options) {
let tasks = [];
const reportDatas = [];
const domainMaps = options.DomainZoneMaps;
if (!domainMaps || domainMaps.length === 0) {
genLog.throwError(t(`参数 DomainZoneMaps 不存在或为空数组`))
}
domainMaps.forEach((map) => {
if (
!map.Domains ||
!map.Domains.length ||
map.Domains.length === 0 ||
!map.ZoneId
) {
return;
}
map.Domains.forEach((domain) => {
tasks.push({
domain,
zoneId: map.ZoneId,
});
});
});
if (tasks.length === 0) {
genLog.throwError(t(`不存在有效的任务,请检查参数`))
}
const successDomainsInfo = await checkDomains(options, false);
const successDomains = tasks.filter((task) => {
return successDomainsInfo.some((successDomain) => {
return task.domain === successDomain.DomainName;
});
});
const unSuccessDomains = tasks.filter((task) => {
return !successDomains.some((successDomain) => {
return task.domain === successDomain.domain;
});
});
if (unSuccessDomains.length > 0) {
const regex = /^\[\{|\{|\}|\}|(\])$/g;
const unSuccessMessage = JSON.stringify(unSuccessDomains).replace(
regex,
""
);
genLog.warnLog(t("以下站点下域名未部署成功:{{unSuccessMessage}}", { unSuccessMessage }))
}
tasks = successDomains;
if (tasks.length === 0) {
unSuccessDomains.forEach((item) => {
reportDatas.push({
Domain: item.domain,
ZoneId: item.zoneId,
Detail: t("以上站点迁移失败"),
});
});
}
genLog.defaultLog(t("待迁移的任务数:{{taskLength}}", { taskLength: tasks.length }))
try {
for (let task of tasks) {
const report = {
Domain: task.domain,
ZoneId: task.zoneId,
AppId,
Details: [],
};
await accessSingleTask(task.domain, task.zoneId, report, options);
if(!isDev) {
try {
// 日志上报
aegis.report(report);
} catch (e) {
logger.error(e);
}
}
report.Details.forEach((log) => {
reportDatas.push({
[reportTitle.DOMAIN]: report.Domain,
[reportTitle.ZONEID]: report.ZoneId,
[reportTitle.CONFIG]: log.config,
[reportTitle.RESULT]: log.result,
[reportTitle.DETAIL]: log.detail,
});
});
if (tasks.length > 1) {
// 一个域名空一行
reportDatas.push({
[reportTitle.DOMAIN]: "",
[reportTitle.ZONEID]: "",
[reportTitle.CONFIG]: "",
[reportTitle.RESULT]: "",
[reportTitle.DETAIL]: "",
});
}
}
genLog.successLog(t(`所有任务迁移完成`))
const csv = new ObjectsToCsv(reportDatas);
await csv.toDisk(`./report-access-${new Date().getTime()}.csv`);
logger.success(t(`已生成迁移报告`));
} catch (e) {
genLog.errorLog(e)
} finally {
utils.exitTerminal();
genLog.writeLogFile(`${new Date().toISOString()}-${AppId}-access`)
}
}
async function accessSingleTask(domain, zoneId, report) {
// 创建其他配置
// 创建IpFilter,RefererUser,AgentFilter配置
try {
genLog.defaultLog(t(`获取加速域名配置...`));
const {
Domains: [domainConfig],
} = await capi("DescribeDomainsConfig", utils.CDN, {
Filters: [
{
Name: "domain",
Value: [domain],
},
],
})
await transfer.otherTransfer["IpFilterRefererUserAgentFilter"](
zoneId,
domain,
domainConfig,
report
);
} catch (e) {
report.Details.push({
config: t(
"安全配置创建(IP黑白名单:IpFilter, 防盗链:Referer, UA黑白名单:UserAgentFilter)"
),
result: t("失败"),
detail: `${e.toString()}`,
});
logger.error(e);
}
// 创建IBandwidthAlert配置,CreateUsageCappingStrategy接口未更新到云API的SDK,暂时屏蔽
// await transfer.otherTransfer['BandwidthAlert'](zoneId, domainName, domainConfig);
genLog.successLog(t("{{domain}} 访问控制迁移完成", { domain }))
}
async function singleTask(
domain,
zoneId,
needConfigsKeys,
report,
needCreateDomain
) {
if (!domain) {
const emptyDomainMsg = t(`参数 Domain 为空,跳过任务`)
genLog.errorLog(emptyDomainMsg)
report.Details.push({
config: t("所有配置"),
result: t("失败"),
detail: emptyDomainMsg,
});
return;
}
if (!zoneId) {
const emptyZoneIdMsg = t(`参数 zoneId 为空,跳过任务`);
genLog.errorLog(emptyZoneIdMsg)
report.Details.push({
config: t("所有配置"),
result: t("失败"),
detail: emptyZoneIdMsg,
});
return;
}
genLog.defaultLog(t(`获取 CDN 域名配置...`))
try {
const {
Domains: [domainConfig],
} = await capi("DescribeDomainsConfig", utils.CDN, {
Filters: [
{
Name: "domain",
Value: [domain],
},
],
})
if (!domainConfig) {
genLog.throwError(t(`Domain: {{domain}} 对应的域名不存在,跳过任务`, { domain }))
}
genLog.successLog(t("配置获取成功"));
// EO创建域名
const domainName = domainConfig.Domain;
const origins = domainConfig.Origin.Origins;
const originType = domainConfig.Origin.OriginType;
const ipv6Status = domainConfig.Ipv6Access.Switch || "follow";
let groupId = "";
let originPort = null;
if (domainConfig.Origin?.BackupOrigins?.length) {
logger.warn(
t(
"暂不支持迁移热备源站, 请前往 EdgeOne 负载均衡(需加白申请)进行配置。"
)
);
}
if (domainConfig?.IpFreqLimit?.Switch === "on") {
logger.warn(
t(
"暂不支持迁移 IP 访问限频配置, 请前往 EdgeOne web 防护速率限制进行设置。"
)
);
}
if (domainConfig?.RemoteAuthentication?.Switch === "on") {
logger.warn(t("暂不支持迁移远程鉴权, 请前往 EdgeOne 边缘函数进行配置。"));
}
let originInfo = {
// 非 'cos', 'image', 'igtm', 'third_party' 则认为是自有源站,此时不需要填写 HostHeader
...(!['cos', 'image', 'igtm', 'third_party'].includes(originType) && {HostHeader: domainConfig.Origin.ServerName}),
};
if (needCreateDomain) {
if (originType === "igtm") {
originInfo["OriginType"] = "IP_DOMAIN";
originInfo["Origin"] = origins[0];
} else if (originType === "cos") {
originInfo["OriginType"] = "COS";
originInfo["PrivateAccess"] = domainConfig.Origin.CosPrivateAccess;
originInfo["Origin"] = origins[0];
} else if (["domain", "domainv6", "ip", "ipv6", "ip_domain"].includes(originType)) {
if (
origins.length > 1 ||
utils.testIpAndWeight(origins[0]) ||
utils.testIpAndPortAndWeight(origins[0]) ||
utils.testDomainAndWeight(origins[0]) ||
utils.testDomainAndPortAndWeight(origins[0])
) {
const validResult = utils.validateOriginsPort(origins);
if (!validResult[0]) {
throw new Error(validResult[1]);
}
originPort = +validResult[1];
const originGroupName = domainName.replaceAll(".", "_");
groupId = await createOriginGroup({
zoneId,
origins: origins,
name: `CDN_migrate_domain_${originGroupName}`,
});
originInfo["OriginType"] = "ORIGIN_GROUP";
originInfo["Origin"] = groupId;
delete originInfo["HostHeader"]; // 自有源站 + 源站组 不需要填写 HostHeader
} else {
const [ip, port] = origins[0].split(":");
originInfo["OriginType"] = "IP_DOMAIN";
originInfo["Origin"] = ip;
if (port) {
originPort = +port;
}
}
} else if (
["third_party"].includes(originType) &&
domainConfig.Origin.OriginCompany === "aws_s3"
) {
originInfo["OriginType"] = "IP_DOMAIN";
originInfo["Origin"] = origins[0];
originInfo["PrivateAccess"] =
domainConfig.AwsPrivateAccess && domainConfig.AwsPrivateAccess.Switch
? domainConfig.AwsPrivateAccess.Switch
: "off";
if (originInfo["PrivateAccess"] === "off") {
originInfo["PrivateParameters"] = [
{
Name: "AccessKeyId",
Value: "",
},
{
Name: "SecretAccessKey",
Value: "",
},
{
Name: "SignatureVersion",
Value: "v4",
},
{
Name: "Region",
Value: "",
},
];
} else {
originInfo["PrivateParameters"] = [
{
Name: "AccessKeyId",
Value: domainConfig.AwsPrivateAccess.AccessKey,
},
{
Name: "SecretAccessKey",
Value: domainConfig.AwsPrivateAccess.SecretKey,
},
{
Name: "SignatureVersion",
Value: domainConfig.AwsPrivateAccess.Bucket,
},
{
Name: "Region",
Value: domainConfig.AwsPrivateAccess.Region,
},
];
}
} else {
genLog.throwError(t("暂不支持该源站类型的域名迁移"));
}
}
if (needCreateDomain) {
logger.info(t(`调用 CreateAccelerationDomain 创建 EO 域名...`));
const param = {
ZoneId: zoneId,
DomainName: domainName,
OriginInfo: originInfo,
IPv6Status: ipv6Status,
};
if (domainConfig.Origin.OriginPullProtocol) {
param["OriginProtocol"] =
domainConfig.Origin.OriginPullProtocol.toUpperCase();
}
if (originPort) {
param["HttpOriginPort"] = originPort;
param["HttpsOriginPort"] = originPort;
}
await capi("CreateAccelerationDomain", utils.TEO, param)
genLog.successLog(t(`域名创建成功`));
}
// 分类配置,EO的配置主要分三部分接口组成,1.添加域名时源站配置,2.规则引擎配置,3.安全/用量封顶等其他配置
// 规则引擎配置
let ruleConfigKeys = needConfigsKeys.filter((key) => {
return ruleConfigs.includes(key);
});
// 判断是否需要迁移证书配置
if (
domainConfig.Https &&
domainConfig.Https.Switch === "on" &&
domainConfig.Https.CertInfo &&
domainConfig.Https.CertInfo.CertId
) {
const expireTime = new Date(
domainConfig.Https.CertInfo.ExpireTime.replace(/-/g, "/")
).getTime();
const now = new Date().getTime();
if (now > expireTime) {
const expireMsg = t("证书已过期,无法迁移 HTTPS 相关配置");
genLog.errorLog(expireMsg);
report.Details.push({
config: t("HTTPS配置(Https)"),
result: t("失败"),
detail: expireMsg,
});
} else {
// 获取ssl 证书列表
try {
const { TotalCount, Certificates } = await capi("DescribeCertificates", utils.SSL, {
Offset: 0,
Limit: 1000,
SearchKey: domainConfig.Https.CertInfo.CertId,
})
if (TotalCount === 0) {
genLog.throwError(t(`获取不到相关证书信息,无法迁移 HTTPS 相关配置`))
}
// 创建证书配置
await capi("ModifyHostsCertificate", utils.TEO, {
ZoneId: zoneId,
Hosts: [domainName],
Mode: 'sslcert',
ServerCertInfo: [
{
CertId: domainConfig.Https.CertInfo.CertId,
},
],
})
// 证书创建完毕后,把证书相关配置加入到规则引擎中
ruleConfigKeys = [].concat(ruleConfigKeys, httpsConfigs);
} catch (e) {
genLog.errorLog(`${t("HTTPS配置(Https)")} ${t("失败")}: ${JSON.stringify(e)}`)
report.Details.push({
config: t("HTTPS配置(Https)"),
result: t("失败"),
detail: `${e.toString()}`,
});
}
}
} else {
report.Details.push({
config: t("HTTPS配置(Https)"),
result: t("未配置"),
detail: "",
});
}
const cdnRuleConfigs = {};
const eoRuleConfigs = {};
genLog.defaultLog(t(`迁移 CDN 配置到规则引擎...`))
// CDN配置转成规则引擎配置
if (ruleConfigKeys.length > 0) {
// 记录每个配置转成规则的日志
let ruleTransferLog = [];
ruleConfigKeys.forEach((key) => {
cdnRuleConfigs[key] = domainConfig[key];
eoRuleConfigs[key] = transfer.ruleTransfer[key](domainName, domainConfig, ruleTransferLog);
});
// 规则引擎配置拼接
const {Cache, ...res} = eoRuleConfigs
const rules = utils.rulesGenerator(domainName, res);
const groupByRules = utils.copyConditionToOtherRule(rules);
const requestMap = groupByRules.map((rule, index) => {
return async () => {
return capi("CreateRule", utils.TEO, {
ZoneId: zoneId,
RuleName: t(`[CDN迁移EO] {{domainName}}`, { domainName: `${domainName}_${index}` }),
Status: 'enable',
Rules: [rule],
});
};
});
for (const request of requestMap) {
try {
await request();
// 规则创建成功,则将日志写到报告里
report.Details = report.Details.concat(ruleTransferLog);
genLog.successLog(t(`规则创建成功`))
} catch (e) {
report.Details.push({
config: t("规则引擎相关配置"),
result: t("失败"),
detail: `${e.toString()}`,
});
genLog.errorLog(`${t("规则引擎相关配置")} ${t("失败")}: ${JSON.stringify(e)}`)
}
}
if (Cache) {
const configRules = utils.rulesGenerator(domainName, { Cache });
const groupByTTLRules = utils.copyConditionToOtherRule(configRules);
const requestTTLMap = groupByTTLRules.map((ttlRule, index) => {
return async () => {
return capi("CreateRule", utils.TEO, {
ZoneId: zoneId,
RuleName: t(`[CDN迁移EO-节点缓存 TTL] {{domainName}}`, { domainName: `${domainName}_${index}` }),
Status: 'enable',
Rules: [ttlRule],
});
};
});
for (const request of requestTTLMap) {
try {
await request();
// 规则创建成功,则将日志写到报告里
report.Details = report.Details.concat(ruleTransferLog);
genLog.successLog(t(`节点缓存 TTL 配置迁移成功`));
} catch (e) {
report.Details.push({
config: t('节点缓存 TTL'),
result: t('失败'),
detail: `${e.toString()}`
});
genLog.errorLog(`${t('节点缓存 TTL')} ${t('失败')}: ${JSON.stringify(e)}`);
}
}
}
}
else {
genLog.warnLog(t(`无可迁移配置,跳过步骤`))
}
genLog.successLog(t(`{{domainName}} 迁移完成`, { domainName }))
} catch (e) {
report.Details.push({
config: t("所有配置"),
result: t("失败"),
detail: `${e?.stack || e}`,
});
genLog.errorLog(`${t("所有配置")} ${t("失败")}`);
genLog.errorLog(e?.stack || e);
}
}
async function singleScdnTask(domain, zoneId, report, options) {
if (!domain) {
const emptyDomainMsg = t(`参数 Domain 为空,跳过任务`);
genLog.errorLog(emptyDomainMsg);
report.Details.push({
config: t("所有配置"),
result: t("失败"),
detail: emptyDomainMsg,
});
return;
}
if (!zoneId) {
const emptyZoneIdMsg = t(`参数 ZoneId 为空,跳过任务`);
genLog.errorLog(emptyZoneIdMsg);
report.Details.push({
config: t("所有配置"),
result: t("失败"),
detail: emptyZoneIdMsg,
});
return;
}
genLog.defaultLog(t(`获取 SCDN 加速域名安全配置...`));
try {
const scdnConfig = await capi("DescribeScdnConfig", utils.CDN, {
Domain: domain,
})
if (!scdnConfig) {
genLog.defaultLog(t("当前域名未配置安全防护,条过此步骤"));
return;
}
const { Zones } = await capi("DescribeZones", utils.TEO, {
Filters: [
{
Name: "zone-id",
Values: [zoneId],
},
],
})
const eoPlanType = isStandard(Zones[0].Resources || [], Zones[0]);
genLog.successLog(t(`SCDN 加速域名安全配置获取成功`));
genLog.defaultLog(t(`迁移 SCDN 加速域名安全配置到安全防护...`));
// 1、获取 scdnConfig 中需要导出的属性,判断当前拉取配置项是否在迁移名单 SCDNDefaultConfigs 中
const filterKeys = Object.keys(scdnConfig);
const whiteList = filterKeys.filter((item) => {
return SCDNDefaultConfigs.includes(item);
});
// 2、数据结构转换
const eoSecurityConfig = {};
// 迁移日志
let ruleTransferLog = [];
whiteList.map((key) => {
eoSecurityConfig[SCDN2EO_MAP[key]] = transfer.scdnTransfer[
SCDN2EO_MAP[key]
](domain, scdnConfig[key], eoPlanType, ruleTransferLog);
});
// 规则创建成功,则将日志写到报告里
report.Details = report.Details.concat(ruleTransferLog);
// 新站点,新增域名修改安全配置步骤:解绑到空模板
try {
await capi("BindSecurityTemplateToEntity", utils.TEO, {
Entities: [domain],
Operate: USE_DEFAULT,
OverWrite: true,
TemplateId: ZONE_GLOBAL_ENTITY,
ZoneId: zoneId,
})
} catch (e) {
genLog.defaultLog(e.message);
}
// 3、创建步骤
await capi("ModifySecurityPolicy", utils.TEO, {
ZoneId: zoneId,
Entity: domain,
SecurityConfig: eoSecurityConfig,
TemplateId: "",
})
genLog.successLog(t(`{{domain}} 迁移完成`, { domain }))
} catch (e) {
report.Details.push({
config: t("所有配置"),
result: t("失败"),
detail: `${e.toString()}`,
});
genLog.errorLog(`${t("所有配置")} ${t("失败")}`);
genLog.errorLog(e);
}
}
async function createOriginGroup(params) {
const { zoneId, origins, name } = params;
const records = [];
origins.forEach((origin) => {
const [ipDomain, port, weight] = origin.split(":");
let record = {
Record: ipDomain,
Type: "IP_DOMAIN",
Weight: Number(weight),
};
if (weight === "" || weight === undefined) {
delete record.Weight;
}
records.push(record);
});
const { OriginGroupId } = await capi("CreateOriginGroup", utils.TEO, {
ZoneId: zoneId,
Name: name,
Type: "GENERAL",
Records: records,
})
return OriginGroupId;
}
const API_MAP = {
runTasks: runTasks,
runScdnTasks: runScdnTasks,
checkDomains: checkDomains,
accessTask: accessTask,
};
module.exports.init = function (CDN2EO, task) {
_.each(API_MAP, function (fn, apiName) {
CDN2EO.prototype[apiName] = fn;
});
};