tencentcloud-edgeone-migration-nodejs-v2
Version:
tencentcloud cdn config copy to edgeone
444 lines (401 loc) • 11.2 kB
JavaScript
// 工具方法,包含对配置进行组装,判断优先级,条件去重,参数合法性校验等功能
const _ = require("lodash");
const { t } = require("../i18n/trans");
function testIp(value) {
let $reg_is_ip =
/^((([0-9]?[0-9])|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\.){3}(([0-9]?[0-9])|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))$/;
return $reg_is_ip.test(value);
}
function testIpv6(value) {
let $reg_is_ipv6 =
/^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$/;
return $reg_is_ipv6.test(value);
}
function testDomain(value) {
var $reg_is_domain =
/^(\*\.)?([a-zA-Z0-9_]([a-zA-Z0-9-_]{0,61}[a-zA-Z0-9_])?\.){1,}[a-zA-Z]{2,}$/;
return $reg_is_domain.test(value);
}
function testIpAndPort(value) {
if (!value) {
return false;
}
var ref = value.split(":");
if (ref.length > 2) {
return false;
}
var host = ref[0];
var port = ref[1];
if (!testIp(host)) {
return false;
}
if (port === undefined) {
return false;
}
if (port === "" || !/^\d+$/.test(port) || +port > 65535 || +port == 0) {
return false;
}
return true;
}
function testIpAndWeight(value) {
var ref = value.split("::");
var host = ref[0];
var weight = ref[1];
if (!testIp(host)) {
return false;
}
if (weight === undefined) {
return false;
}
if (weight === "" || !/^\d+$/.test(weight)) {
return false;
}
return true;
}
function testIpAndPortAndWeight(value) {
var ref = value.split(":");
if (ref.length > 3) return false;
var host = ref[0];
var port = ref[1];
var weight = ref[2];
if (!testIp(host)) {
return false;
}
if (port === undefined) {
return false;
}
if (port === "" || !/^\d+$/.test(port) || +port > 65535 || +port == 0) {
return false;
}
if (weight === undefined) {
return false;
}
if (weight === "" || !/^\d+$/.test(weight)) {
return false;
}
return true;
}
function testDomainAndPort(value) {
if (!value) {
return false;
}
var ref = value.split(":");
var host = ref[0];
var port = ref[1];
if (!testDomain(host)) {
return false;
}
if (port === undefined) {
return false;
}
if (port === "" || !/^\d+$/.test(port) || +port > 65535 || +port == 0) {
return false;
}
return true;
}
function testDomainAndWeight(value) {
var ref = value.split("::");
var host = ref[0];
var weight = ref[1];
if (!testDomain(host)) {
return false;
}
if (weight === undefined) {
return false;
}
if (weight === "" || !/^\d+$/.test(weight)) {
return false;
}
return true;
}
function testDomainAndPortAndWeight(value) {
var ref = value.split(":");
var host = ref[0];
var port = ref[1];
var weight = ref[2];
if (!testDomain(host)) {
return false;
}
if (port === undefined) {
return false;
}
if (port === "" || !/^\d+$/.test(port) || +port > 65535 || +port == 0) {
return false;
}
if (weight === undefined) {
return false;
}
if (weight === "" || !/^\d+$/.test(weight)) {
return false;
}
return true;
}
function formatWildcardOriginHandler(illegalConfig) {
return illegalConfig.map((item) => {
const curOrigin = item.Values[0].split(',')[0]
const formatOrigin = curOrigin.replace(/https?:\/\//g, "");
return {
Rules: [
{
Conditions: [
{
Conditions: [
{
Operator: "equal",
Target: "request_header",
IgnoreCase: false,
Name: "origin",
Values: [formatOrigin],
},
],
},
],
Actions: [
{
RewriteAction: {
Action: "ResponseHeader",
Parameters: [
{
Action: "set",
Name: "Access-Control-Allow-Origin",
Values: ['${http.request.headers["origin"]}'],
},
],
},
},
],
},
],
Tags: [],
}
})
}
function copyConditionToOtherRule(rule) {
const result = [];
rule.forEach(item => {
if (item.SubRules.length > 0) {
for (let i = 0; i < item.SubRules.length; i += 20) {
const newSubRules = item.SubRules.slice(i, i + 20);
const newItem = {
Conditions: item.Conditions,
Actions: item?.Actions,
SubRules: newSubRules
};
result.push(newItem);
}
}
});
return result
}
function rulesGenerator(domain, eoRuleConfigs) {
let baseRule = {
Conditions: [
{
Conditions: [
{
Operator: "equal",
Target: "host",
IgnoreCase: false,
Values: [domain],
},
],
},
],
Actions: [],
SubRules: [],
};
Object.keys(eoRuleConfigs).map((key) => {
if (!eoRuleConfigs[key]) {
return;
}
// 优衣库节点缓存适配
if (["Cache", "CacheKey", "MaxAge", "UrlRedirect"].includes(key)) {
baseRule.SubRules.push(
...eoRuleConfigs[key].Rules.map((item) => {
return {
Rules: [item],
Tags: []
};
})
);
}else if (eoRuleConfigs[key].Rules) {
baseRule.SubRules.push(eoRuleConfigs[key]);
} else if (eoRuleConfigs[key].length > 0) {
baseRule.SubRules = [].concat(baseRule.SubRules, eoRuleConfigs[key]);
} else {
if(key === 'ResponseHeader') {
const { RewriteAction } = eoRuleConfigs[key];
const legalValue = []
const illegalValue = []
RewriteAction.Parameters.forEach(item => {
if([item.Name === 'Access-Control-Allow-Origin', item.Values[0]?.length > 1, item.Values[0]?.includes('*')].every((v) => !!v)) {
illegalValue.push(item)
} else {
legalValue.push(item)
}
})
const defaultTemplate = {
RewriteAction: {
Action: 'ResponseHeader',
Parameters: legalValue
}
}
baseRule.Actions.push(defaultTemplate)
if(illegalValue.length) {
const formatWildcardOrigin = formatWildcardOriginHandler(illegalValue)
baseRule.SubRules.push(...formatWildcardOrigin)
}
} else {
baseRule.Actions.push(eoRuleConfigs[key]);
}
}
});
if (baseRule.SubRules.length === 0) {
delete baseRule.SubRules;
}
if (_.flatten(baseRule.Actions).length === 0) {
delete baseRule.Actions;
}
return [baseRule];
}
function getTarget(type) {
switch (type) {
case "file":
return "extension";
case "directory":
return "url";
case "path":
return "full_url";
case "all":
return "host";
case "index":
return "url";
case "regex":
return "full_url";
}
}
// 校验源站可否迁移,并返回端口号
function validateOriginsPort(origins) {
const portList = [];
origins.forEach((origin) => {
const [ipDomain, port, weight] = origin.split(":");
if (port) {
portList.push(port);
}
});
if (portList.length === 0) {
return [true, null];
} else if (portList.length > 0 && portList.length !== origins.length) {
return [
false,
t(
"部分源站有端口配置,部分没有端口配置,无法迁移,必须全有(且端口一致)或全无"
),
];
}
const uniqPortList = _.uniq(portList);
if (uniqPortList.length > 1) {
return [false, t("源站端口配置不一致,无法迁移")];
}
return [true, uniqPortList[0]];
}
// --------------------- 相同 CIDR 网段校验 ---------------------
function cidrToSubnetMask(prefixLength) {
const mask = [];
for (let i = 0; i < 4; i++) {
const n = Math.min(prefixLength, 8);
mask.push(256 - 2 ** (8 - n));
prefixLength -= n;
}
return mask.join(".");
}
function ipToInt(ip) {
return ip
.split(".")
.reduce((int, octet) => (int << 8) + parseInt(octet, 10), 0);
}
function isSameCIDR(cidr1, cidr2) {
const [ip1, prefixLength1] = cidr1.split("/");
const [ip2, prefixLength2] = cidr2.split("/");
if (prefixLength1 !== prefixLength2) {
return false;
}
const subnetMask = cidrToSubnetMask(parseInt(prefixLength1, 10));
const ip1Int = ipToInt(ip1);
const ip2Int = ipToInt(ip2);
const subnetMaskInt = ipToInt(subnetMask);
return (ip1Int & subnetMaskInt) === (ip2Int & subnetMaskInt);
}
function checkIsCIDR(ips) {
const CIDRArr = ips.filter((item) => item.split("/").length > 1);
const uniqueCIDRs = CIDRArr.filter((cidr, index, arr) => {
for (let i = 0; i < index; i++) {
if (isSameCIDR(cidr, arr[i])) {
return false;
}
}
return true;
});
const uniqIps = ips.filter((item) => item.split("/").length === 1);
return [...uniqIps, ...uniqueCIDRs];
}
// --------------------- 相同 CIDR 网段校验 ---------------------
function exitTerminal() {
setTimeout(() => {
process.exit();
}, 3e3);
}
const genWildString = (str) => {
if (!str.endsWith('/*')) {
if (str.endsWith('/')) {
return str + '*';
} else {
return str + '/*';
}
} else {
return str;
}
}
const deepSplit = (arr) => {
return arr.flatMap(item => {
if (Array.isArray(item)) {
return deepSplit(item);
}
return typeof item === 'string' ? item.split(',') : [];
});
};
const genOriginValue = (rule, needSplit = false) => {
if (rule.RuleType === 'directory') {
return rule.RulePaths.map((path) => {
return genWildString(path);
});
}
if(rule.RuleType === 'index') {
return ['/*']
}
return needSplit ? deepSplit(rule.RulePaths || []) : rule.RulePaths;
}
const TEO = 'eoClient'
const CDN = 'cdnClient'
const SSL = 'sslClient'
const utils = {
testIp,
testDomain,
testIpAndPort,
testIpAndWeight,
testIpAndPortAndWeight,
testDomainAndPort,
testDomainAndWeight,
testDomainAndPortAndWeight,
rulesGenerator,
getTarget,
validateOriginsPort,
checkIsCIDR,
exitTerminal,
TEO,
CDN,
SSL,
genOriginValue,
copyConditionToOtherRule
};
module.exports = utils;