// ==UserScript==
// @name 魔数大仓专属talos1.0插件
// @namespace Violentmonkey Scripts
// @version 1.1.7
// @description 一起快乐发布吧!!!
// @author MYH
// @match https://talos.sankuai.com/
// @icon https://www.google.com/s2/favicons?sz=64&domain=sankuai.com
// @grant none
// @license MIT
// ==/UserScript==
(function () {
// 展示发布应用信息等
const showApp = () => {
document.querySelector(".data-list .logs")?.childNodes?.forEach((child) => {
const cardEl = child?.childNodes[0].childNodes[1];
const cradFlowId = cardEl
?.getElementsByTagName("a")[0]
["href"].split("/")
.pop();
let idTag = cardEl.getElementsByClassName("custom-flowid");
if (idTag.length === 0) {
idTag = document.createElement("span");
idTag.className = "custom-flowid";
idTag.style.position = "absolute";
idTag.style.right = 0;
idTag.style.bottom = 0;
cardEl.appendChild(idTag);
}
window
.fetch(`//talos-api.sankuai.com/sub_app/751/publish/${cradFlowId}`, {
credentials: "include",
})
.then((res) => res.json())
.then((res) => {
const { data } = res;
const appValue =
data.flow?.publishConfig?.envs?.find(
(item) => item.key === "MULTI_SUBAPP_LIST"
)?.value || "";
if (!appValue) return;
const swimlane =
data.flow?.target === "newtest"
? data.flow?.publishConfig?.envs?.find(
(item) => item.key === "SWIMLANE"
)?.value || ""
: "";
const isGraypublish =
data.flow?.publishConfig.hasOwnProperty("canaryType"); // 存在此字段则为灰度发布
let showText = `${appValue}`;
if (swimlane) {
showText = `${cradFlowId} -- (泳道:${swimlane}) -- ${showText}`;
} else if (isGraypublish) {
const grayCell =
data.flow?.publishConfig.canaryConfig.canaryOnline.split(
"cell="
)[1];
showText = `${cradFlowId} --(灰度cell:${grayCell}) -- ${showText}`;
} else {
showText = `${cradFlowId} -- ${showText}`;
}
(idTag[0] || idTag).textContent = showText;
});
});
};
const getButton = () => {
const appRecords = document.querySelector(".sub-app-records");
if (!appRecords.getElementsByClassName("show-app-button")?.length) {
appRecords.style.position = "relative";
const showAppButton = document.createElement("button");
showAppButton.className = "show-app-button";
showAppButton.innerText = "展示所选app";
showAppButton.style.position = "absolute";
showAppButton.style.right = 0;
showAppButton.style.top = 0;
showAppButton.addEventListener("click", () => {
showApp();
});
appRecords.appendChild(showAppButton);
}
};
// 灰度发布,重置流量比例
const getPublishType = () => {
if (!document.querySelector('input[value="canaryPublish"]')?.checked)
return "";
const grayConfigCheckedList = document.querySelectorAll(
".canary-config-checkbox .el-checkbox.is-checked"
);
// 线上配置不展示时, 说明不是灰度热更新
// !!document.querySelector('.canary-config-onlineCanary').style?.cssText?.includes("display: none")
const isGrayHotPublishEl = Array.from(grayConfigCheckedList).find(
(node) => {
return node.innerText.includes("灰度热更新");
}
);
if (isGrayHotPublishEl) return "grayRefresh";
return "gray";
};
const resetCanaryRangeLimit = () => {
const canaryLimitEl = document.querySelector(
".canary-config-range-limit input"
);
if (!canaryLimitEl) return;
canaryLimitEl.value = 1;
canaryLimitEl.dispatchEvent(new Event("change"));
};
// 灰度热更新
const desc = Object.getOwnPropertyDescriptor(
HTMLInputElement.prototype,
"value"
);
const getApplistEl = () => {
// 获取属性为for="pluginsConfig"的label元素
const label = document.querySelector('label[for="pluginsConfig"]');
// 判断其名称是否为“插件配置”
if (label && label.textContent.trim() === "插件配置") {
// 获取它的兄弟元素
const sibling = label.nextElementSibling;
// 从表单中查找
const formItemNodeList =
sibling.querySelectorAll(".env-row.el-row") || [];
const nodeListArray = Array.from(formItemNodeList);
const appSelecItemNode = nodeListArray.find((element) => {
return element.innerText.includes("魔数批量发布应用选择器");
});
// 再次判断是不是应用配置,若是,取下拉元素
const input = appSelecItemNode.querySelector("input");
if (input.value === "MULTI_SUBAPP_LIST") {
return appSelecItemNode.querySelector(".env-row.el-row .el-select");
}
}
return null;
};
const getAppListValue = (envs) => {
try {
const applistValue = envs.find(
(env) => env.key === "MULTI_SUBAPP_LIST"
).value;
return JSON.parse(applistValue);
} catch (error) {
return null;
}
};
const handleReplace = async (flowId) => {
const res = await window
.fetch(`//talos-api.sankuai.com/sub_app/751/publish/${flowId}`, {
credentials: "include",
})
.then((res) => res.json());
const { data } = res;
const { flow } = data;
const { publishConfig } = flow;
const { branch, canaryConfig, envs } = publishConfig;
const { canaryOnline, canaryUpdate, canaryOffline } = canaryConfig;
const openConfEl = document.querySelector(
".deploy-panel .canary-config .canary-config-item"
).parentElement;
if (!Array.from(openConfEl.classList).includes("is-active")) {
await openConfEl.click();
}
const branchEl = document.querySelector(
".deploy-branch input[role=textbox]"
);
branchEl.value = branch;
branchEl.dispatchEvent(new Event("input"));
//const canaryLimitEl = document.querySelector('.canary-config-range-limit input')
//canaryLimitEl.value = 1
//canaryLimitEl.dispatchEvent(new Event('change'))
resetCanaryRangeLimit();
const confNodes = Array.from(
document.querySelector(
".deploy-panel .canary-config [role=tabpanel] .el-collapse-item__content"
).childNodes
);
const canaryOnlineEl = confNodes
.find((item) => item.innerHTML.includes("Canary Online"))
.querySelector("input");
const canaryUpdateEl = confNodes
.find((item) => item.innerHTML.includes("Canary Update"))
.querySelector("input");
const canaryOfflineEl = confNodes
.find((item) => item.innerHTML.includes("Canary Offline"))
.querySelector("input");
const applistEl = getApplistEl();
const appListValue = getAppListValue(envs);
if (appListValue && applistEl) {
applistEl.__vue__.$emit("input", appListValue);
}
canaryOnlineEl.value = canaryOnline;
canaryOnlineEl.dispatchEvent(new Event("input"));
canaryUpdateEl.value = canaryUpdate;
canaryUpdateEl.dispatchEvent(new Event("input"));
canaryOfflineEl.value = canaryOnline;
canaryOfflineEl.dispatchEvent(new Event("input"));
};
// 插入灰度cookie 复制按钮
let isGrayButtonCreated = false;
const createdGrayButton = () => {
if (isGrayButtonCreated) return;
const element = document.querySelector(".el-dialog .canary-whitelist-item");
const talosVersion =
document.querySelectorAll(".op-info-tag.el-tag--success")[1]?.outerText ||
"";
if (!element || !talosVersion) {
return;
}
// 创建一个新的div元素
const div = document.createElement("div");
// 创建一个按钮
const generateButton = document.createElement("button");
generateButton.textContent = "获取talosHash";
generateButton.addEventListener("click", () => {
navigator.clipboard.writeText(talosVersion);
});
const addBtn = document.getElementsByClassName(
"el-button btn-add-whitelist"
)?.[0];
const list = [
{ label: "分析板", value: "analysis_talos_version" },
{ label: "分析板分享", value: "analysis-display_talos_version" },
{ label: "dashboard", value: "dashboard_talos_version" },
{ label: "仪表板分享", value: "dashboard-display_talos_version" },
{ label: "仪表板2.0图表编辑页", value:"dashboard-chart-edit_talos_version"},
{ label: "ai", value: "ai_talos_version" },
{ label: "SQL-V2", value: "sqlv2_talos_version" },
{ label: "数据集-V2", value: "datasetv2_talos_version" },
{ label: "SQL", value: "sql_talos_version" },
{ label: "Portal", value: "portal_talos_version" },
{ label: "工作空间", value: "space_talos_version" },
{ label: "监控预警", value: "monitor_talos_version" },
{ label: "数据集", value: "dataset_talos_version" },
{ label: "取数工具", value: "wormhole_talos_version" },
{ label: "首页", value: "home_talos_version" },
{ label: "资源管理", value: "source-manager_talos_version" },
];
div.appendChild(generateButton);
// 创建其他按钮并添加到div中
list.forEach((item) => {
const button = document.createElement("button");
button.textContent = item.label;
button.style.marginLeft = "10px";
button.style.background = "#fff";
button.style.cursor = "pointer";
button.addEventListener("click", () => {
// navigator.clipboard.writeText(item.value)
if (addBtn) {
addBtn.click();
setTimeout(() => {
const canaryItemList = document.querySelectorAll(
".canary-whitelist-item"
);
const canaryItem = canaryItemList[canaryItemList.length - 1];
const keyInputEl = canaryItem.querySelector(
'.canary-config-whitelistKey.el-input input[placeholder="key"]'
);
if (keyInputEl) {
keyInputEl.value = item.value;
keyInputEl.dispatchEvent(new Event("input"));
}
const valueInputEl = canaryItem.querySelector(
".canary-config-whitelistVal input"
);
if (valueInputEl) {
valueInputEl.value = talosVersion;
valueInputEl.dispatchEvent(new Event("input"));
}
}, 100);
}
});
div.appendChild(button);
});
element.insertAdjacentElement("beforebegin", div);
isGrayButtonCreated = true;
};
// 检查当前线上是否已有该灰度cell
const grayCellClassName = "gray-cell-check-button";
const checkGray = () => {
const env = document
.querySelector('div[class="publish"]')
?.querySelector('div[class="el-tabs__item is-left is-active"]')
?.innerText?.replace(/(.*)\((.*)\)/, "$2")
?.trim();
const cellDoms = document.querySelectorAll(
'div[class="el-collapse-item__content"]'
);
let currentCell = "";
Array.from(cellDoms).forEach((dom) => {
currentCell = dom
.querySelector("input")
?.value?.replace(/.*cell=(.*)/, "$1")
?.trim();
});
if (!currentCell) {
return;
}
const btn = document.getElementsByClassName(grayCellClassName)?.[0];
if (btn) {
btn.innerText = "检测中...";
}
window
.fetch(
`https://talos-api.sankuai.com/sub_app/751/publish?id=10329&target=${env}&page_num=1&page_size=20&type=canary&status=pending`,
{
credentials: "include",
}
)
.then((res) => res.json())
.then((res) => {
if (btn) {
btn.innerText = "检测灰度cell(最近20条)";
}
const { data } = res || {};
const { list } = data || {};
for (let i = 0; i < list.length; i++) {
const { publish_config, id, op } = list[i] || {};
const { canaryType, canaryConfig } = publish_config || {};
const { canaryOnline } = canaryConfig || {};
if (canaryType && typeof canaryOnline === "string") {
// 灰度发布 & 正在进行中灰度
const cell = canaryOnline.replace(/.*cell=(.*)/, "$1")?.trim();
if (cell === currentCell) {
navigator.clipboard.writeText(
`https://talos.sankuai.com/#/project/10329/sub-app/751/log/${id}`
);
window.alert(
`已存在相同cell: ${cell} \n发布人:${op}\n冲突版本的发布日志链接已复制到剪切板, 可新开一页粘贴查看`
);
return;
}
}
}
window.confirm(
`${env}环境不存在相同cell: ${currentCell},可放心灰度~`
);
});
};
const insertGrayCheckBtn = () => {
Array.from(document.getElementsByTagName("button")).forEach((btn) => {
if (btn.innerText === "发布") {
if (document.getElementsByClassName(grayCellClassName).length > 0) {
return;
}
const checkGrayBtn = document.createElement("button");
checkGrayBtn.className = grayCellClassName;
checkGrayBtn.innerText = "检测灰度cell(最近20条)";
checkGrayBtn.style.position = "absolute";
checkGrayBtn.style.left = "-200px";
checkGrayBtn.style.top = 0;
checkGrayBtn.addEventListener("click", (e) => {
checkGray();
e.preventDefault();
});
btn.parentElement.appendChild(checkGrayBtn);
}
});
};
const checkGrayCellExist = () => {
if (document.querySelector('input[value="canaryPublish"]')?.checked) {
insertGrayCheckBtn();
}
};
const removeGrayCheckBtn = () => {
document.getElementsByClassName(grayCellClassName)?.[0]?.remove();
};
const observeTab = () => {
let activeTab = null;
let grayEl = null;
const observer = new MutationObserver(() => {
if (
window.location.hash.includes("#/project/10329/sub-app/751/records")
) {
activeTab = document.querySelector(".el-menu-item.is-active");
activeTab?.childNodes[1].innerText === "发布记录" && getButton();
}
// 灰度发布
if (
window.location.hash.includes("#/project/10329/sub-app/751/publish")
) {
const publishType = getPublishType();
// 灰度热更新
if (publishType === "grayRefresh") {
removeGrayCheckBtn();
grayEl = document.querySelector(".canary-config-onlineCanary input");
if (grayEl) {
Object.defineProperty(grayEl, "value", {
...desc,
set(v) {
handleReplace(v.replace(/\D+/, ""));
desc.set.call(this, v);
},
});
}
}
// 灰度发布
else if (publishType === "gray") {
// 重置默认流量比例, 延迟触发,确保元素能获取到
checkGrayCellExist();
setTimeout(() => {
resetCanaryRangeLimit();
}, 300);
} else {
removeGrayCheckBtn();
}
}
if (window.location.hash.includes("#/project/10329/sub-app/751/log/")) {
createdGrayButton();
} else {
// 重置按钮添加标识
isGrayButtonCreated = false;
}
});
observer.observe(document.body, { childList: true, subtree: true });
};
observeTab();
})();