[手摸手教你]用油猴(篡改猴)給寶塔證書排個序
喜歡用寶塔的朋友可能也有這個困擾,就是寶塔網站列表沒辦法給SSL證書過期時間這一欄排序
最近管理大量網站證書,苦於這個無法排序,沒辦法直觀檢視哪些證書已過期或者快過期,一頁頁翻的我想死啊
剛好之前用過一點油猴外掛,知道這個東西神通廣大,想實現這個功能應該還挺簡單,所以有了這個嘗試
廢話少說咱開搞 (ps.想省事的同學直接拉到最後看原始碼)
分析需求
可能有同學會說了,🤔不就是排個序嗎,用的著上油猴,控制檯指令碼分分鐘給你排好是
當然我剛開始也是這麼想的程式碼如下
// SSL過期排序
var tbody = document.getElementsByTagName('tbody')
var list = document.getElementsByTagName("tr")
var indexStatus = 8 // SSL證書欄位置 根據實際列表從左往右順序
console.info("%c ========================================","color:green;")
console.info("%c ========================================","color:green;")
console.info(`%c 確認SSL證書欄位置 第 ${indexStatus} 列`,"color:blue;font-weight:bold")
console.info("%c ========================================","color:green;")
var domListEnd = [] // 已過期狀態
var domListRest = [] // 剩餘狀態
var domListOther = [] // 其他狀態
for(let i=list.length-1;i>0;i--){
const item = list[i]
if(item&&item.childNodes.length>indexStatus){
const status = item.childNodes[indexStatus].textContent
if(status.match("已過期")){
domListEnd.push(item)
}else if(status.match(/剩餘\d+天/)){
item.exp = status.replace(/剩餘(\d+)天/,"$1")
domListRest.push(item)
}else{
domListOther.push(item)
}
}
}
// 剩餘天數排序
var domListRestOrder = domListRest.sort((a,b)=>{return a.exp - b.exp})
// 狀態排序
var orderList = domListEnd.concat(domListRestOrder).concat(domListOther)
// 重新顯示dom
tbody[0].innerHTML = null
orderList.forEach(function(item) {
tbody[0].append(item)
});
console.info("%c !!僅限檢視列表SSL過期順序,請勿點選詳情操作,詳情資料不對!!","color:red;font-weight:bold")
控制檯執行以上程式碼,即可對SSL證書進行排序
!注意:點選詳情頁面是錯誤的,並非所點選的站點
如果只需要看順序,我再送你一段程式碼
var pageSize = 500; // 分頁數量
var pageDom = document.createElement("option")
pageDom.setAttribute("value",pageSize);
pageDom.innerHTML= `${pageSize}條/頁`;
document.getElementsByClassName("page_select_number")[0].appendChild(pageDom)
執行後會新增一個分頁項
經過以上嘗試,修改頁面dom確實能排好頁面,如果你只需要檢視順序,配合分頁程式碼全部展示後再排序,完全沒有問題
但是如果你想點進去操作詳情,發現牛頭不對馬嘴,根本不是你點選的那個網站了,這個目前沒找到解決方案,所以最終還是請出大殺器油猴吧
油猴指令碼
油猴的安裝在此就不多說了,有需要的看另一篇文章油猴外掛安裝(Chrome)
進入寶塔網站頁面,點選油猴外掛新增新指令碼,新的指令碼會自動設定好當前頁面URL的匹配設定
在編輯指令碼頁面完成以下步驟
- 給指令碼起一個響亮的名字
- 設定指令碼生效的瀏覽器URL路徑,頁面新建的指令碼會自動設定好(也可設定正則匹配)
- 寫上測試程式碼
ctrl+s儲存指令碼
回到寶塔頁面,把油猴外掛固定在右上角,可以看見出現紅點表示有一條規則開啟,指令碼下拉選還可以快捷操作
正常開啟指令碼後,重新整理即可看見控制檯輸出了
經過嘗試,發現寶塔使用的是jquery的ajax請求,簡單攔截一下
const originFetch = window.$.ajax;
window.$.ajax = (...arg) => {
console.log('ajax ', arg);
return originFetch(...arg);
}
可在控制檯找到攔截的輸出
根據官方api https://www.bt.cn/data/api-doc.pdf 和網頁請求記錄可知
網站列表的請求地址url就是這裡的url
: /data?action=getData
請求引數就是這裡的 data
物件 { table: 'sites', limit: '10',... }
可能api與實際使用有出入,api中的表名是連結引數,實際上是data引數中
官方api截圖
繼續迭代,寫個正則實現精準攔截介面
const RegExp_SiteList = /\/data\?action=getData/g
const originFetch = window.$.ajax;
window.$.ajax = (...arg) => {
const url = arg[0].url;
const params = arg[0].data
// ==================
// 請求攔截
// ==================
// 請求網站列表
if (url.match(RegExp_SiteList) && params.table=="sites") {
console.log("請求網站列表")
console.log('ajax ', arg);
// 改變分頁引數
arg[0].data.limit = 5;
}
return originFetch(...arg);
}
這樣控制檯我們就只看見一條記錄了
注意:有可能出現偶爾控制檯不輸出的情況,重新整理頁面多試一下,機智的同學可能看出程式碼的問題了,這個問題我們稍後最佳化
繼續迭代,最終我們需要攔截ajax返回資料改變資料順序,也就是這裡的success
方法
檢視返回資料後,發現ssl是一個物件
根據不同狀態資料分析,需要對資料稍微處理一下再排序
const RegExp_SiteList = /\/data\?action=getData/g
const originFetch = window.$.ajax;
window.$.ajax = (...arg) => {
const url = arg[0].url;
const params = arg[0].data
// ==================
// 請求攔截
// ==================
// 請求網站列表
if (url.match(RegExp_SiteList) && params.table=="sites") {
console.log("請求網站列表")
console.log('ajax ', arg);
// 改變分頁引數
arg[0].data.limit = 5;
// ==================
// 返回資料攔截
// ==================
const originSuccess = arg[0].success;
arg[0].success = (...res)=>{
console.log(res)
res[0].data.map(item=>{
// 處理一下未部署的情況,排到最後
if(item.ssl==-1){
item.sslEndtime = 9999
}else{
// 其他按過期時間順序,最先過期的排前面
item.sslEndtime = item.ssl.endtime
}
})
res[0].data.sort((a,b)=>{
return a.sslEndtime - b.sslEndtime;
})
return originSuccess(...res)
}
}
return originFetch(...arg);
}
以後需要給證書過期排序的時候只需要開一下開關就可以了,不用了就關掉,舒服~😎
最佳化
指令碼的注入時間是透過@runat引數控制的
@run-at document-idle
指令碼將在 DOMContentLoaded 事件派發後注入。如果沒有給出 @run-at 標籤,則這是預設值。
@run-at document-start
指令碼將盡快注入。
@run-at document-body
如果 body 元素存在,則將注入指令碼。
@run-at document-end
指令碼將在 DOMContentLoaded 事件派發時或之後注入。
@run-at content-menu
如果在瀏覽器上下文選單中單擊指令碼,則將注入指令碼(僅限基於 Chrome 的桌面瀏覽器)。
因為預設的注入時間,導致有可能介面已經呼叫,但是指令碼還沒載入的情況,所以不能立即攔截到我們想要的介面
這裡我們需要將指令碼設定為儘快注入,並且做一個迴圈判斷,直到jq載入完
// ==UserScript==
...
// @run-at document-start
...
// 迴圈判斷
function waitForJs() {
// jq載入完判斷
if (typeof window.$ !== 'undefined') {
// 注入指令碼主邏輯
} else {
// 如果條件不滿足,則在10ms後再次嘗試
setTimeout(waitForJs, 10);
}
}
指令碼原始碼
// ==UserScript==
// @name 寶塔助手
// @namespace http://tampermonkey.net/
// @version 2024-06-07
// @description try to take over the world!
// @author ban
// @match [你的寶塔地址]
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
function waitForJs() {
// jq載入完判斷
if (typeof window.$ !== 'undefined') {
// 注入指令碼主邏輯
injectJqAjax();
} else {
// 如果條件不滿足,則在10ms後再次嘗試
setTimeout(waitForJs, 10);
}
}
waitForJs();
const RegExp_SiteList = /\/data\?action=getData/gi
function injectJqAjax(){
console.warn("[injectJqAjax] start")
const originFetch = window.$.ajax;
window.$.ajax = (...arg) => {
const url = arg[0].url;
const params = arg[0].data
// ==================
// 請求攔截
// ==================
// 請求網站列表
if (url.match(RegExp_SiteList) && params.table=="sites") {
// 改變分頁引數
arg[0].data.limit = 500;
// ==================
// 返回資料攔截
// ==================
const originSuccess = arg[0].success;
arg[0].success = (...res)=>{
res[0].data.map(item=>{
// 處理一下未部署的情況,排到最後
if(item.ssl==-1){
item.sslEndtime = 9999
}else{
// 其他按過期時間順序,最先過期的排前面
item.sslEndtime = item.ssl.endtime
}
})
res[0].data.sort((a,b)=>{
return a.sslEndtime - b.sslEndtime;
})
return originSuccess(...res)
}
}
return originFetch(...arg);
}
}
})();