UNPKG

hexo-safego-thun888

Version:

✨Hexo plugin for safe external link redirection with base64 encoding, domain whitelisting, and support for a custom redirect page.

523 lines (464 loc) 16.6 kB
<!DOCTYPE html> <html lang="zh"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="robots" content="noindex, nofollow" /> <title>{{title}}</title> <style type="text/css"> :root { --theme-color: {{theme_color}}; --theme-color-darker: {{theme_color}}e6; /* 90% 透明度 */ --theme-color-hover: {{theme_color}}cc; /* 80% 透明度 */ } body { display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif; overflow: hidden; flex-direction: column; background-color: #f9fafb; } .avatar-container { margin-bottom: 16px; text-align: center; } .avatar { width: 48px; height: 48px; border-radius: 50%; display: none; border: 1px solid #eee; } .avatar-placeholder { width: 48px; height: 48px; border-radius: 50%; background-color: #eaeaea; display: inline-block; } .header { margin-bottom: 8px; text-align: center; } .logo { width: 64px; height: 30px; } .description { font-size: 18px; color: #333; margin-bottom: 4px; text-align: center; } .subtitle { font-size: 12px; margin-bottom: 20px; color: #8590a6; text-align: center; } .card { text-align: left; padding: 24px; border-radius: 2px; background-color: #fff; box-shadow: 0 1px 3px rgba(18, 18, 18, 0.1); width: 400px; max-width: 90%; } .title { font-size: 18px; font-weight: 600; color: #121212; margin-bottom: 8px; } .message { font-size: 14px; color: #646464; line-height: 1.6; margin-bottom: 24px; } .link-container { margin-bottom: 20px; padding: 12px 12px; background-color: #f6f6f6; border-radius: 2px; word-break: break-all; } .link { font-size: 14px; color: #121212; text-decoration: none; } .countdown-container { font-size: 13px; color: #8590a6; margin-bottom: 16px; text-align: center; } .progress-bar { width: 100%; height: 3px; background-color: #f0f2f5; border-radius: 2px; overflow: hidden; margin-bottom: 24px; } .progress { height: 100%; background-color: var(--theme-color); width: 100%; transition: width 1s linear; } .divider { height: 1px; background-color: #EBEBEB; margin: 16px 0; } .button-container { display: flex; justify-content: space-between; gap: 12px; } .button { display: inline-block; padding: 8px 0; border-radius: 3px; border: none; cursor: pointer; font-size: 14px; text-align: center; flex: 1; } .cancel-button { background-color: #f7f8fa; color: #444; border: 1px solid #edeeef; } .confirm-button { background-color: var(--theme-color); color: white; } .confirm-button:hover { background-color: var(--theme-color-hover); } .site-info { display: flex; align-items: center; padding: 12px; background-color: #f9f9f9; border-radius: 4px; margin-bottom: 20px; position: relative; cursor: pointer; } .site-favicon { width: 16px; height: 16px; margin-right: 8px; border-radius: 2px; } .site-title { font-size: 14px; color: #333; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .site-info-loading { display: flex; align-items: center; justify-content: center; height: 40px; font-size: 13px; color: #8590a6; } .site-desc { font-size: 12px; color: #333; margin-top: 8px; padding: 12px; display: none; margin-bottom: 20px; background-color: #f9f9f9; border-radius: 4px; } /* 深色模式 */ @media (prefers-color-scheme: dark) { body { background-color: #1A1A1A; } .card { background-color: #2A2A2A; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); } .avatar-placeholder { background-color: #3a3a3a; } .avatar { border-color: #444; } .description { color: #d3d3d3; } .subtitle { color: #999; } .title { color: #F6F6F6; } .message { color: #ACACAC; } .link-container { background-color: #3A3A3A; } .link { color: #F6F6F6; } .countdown-container { color: #999; } .progress-bar { background-color: #444; } .divider { background-color: #3A3A3A; } .cancel-button { background-color: #3a3a3a; color: #d3d3d3; border-color: #444; } .site-info { background-color: #3A3A3A; } .site-title { color: #F6F6F6; } .site-info-loading { color: #999; } } </style> {% if siteinfo_enable and siteinfo_api %} <script type="text/javascript"> // 网站信息API获取脚本 async function fetchSiteInfo(url) { try { const response = await fetch('{{siteinfo_api}}'.replace('{href}', encodeURIComponent(url))); return await response.json(); } catch (error) { console.error('获取网站信息失败:', error); return null; } } function getFaviconUrl(url) { try { const urlObj = new URL(url); return `${urlObj.protocol}//${urlObj.hostname}/favicon.ico`; } catch (error) { return ''; } } function displaySiteInfo(data, url) { if (!data || !data.title) { document.getElementById('site-info-container').style.display = 'none'; return; } const siteTitle = document.getElementById('site-title'); const siteFavicon = document.getElementById('site-favicon'); const siteInfo = document.getElementById('site-info'); const siteInfoLoading = document.getElementById('site-info-loading'); const siteDesc = document.getElementById('site-desc'); // 设置网站标题 siteTitle.textContent = data.title; // 如果有描述信息,添加为title属性实现悬浮提示 if (data.desc) { siteInfo.title = data.desc; // 同时添加到描述区域 siteDesc.textContent = data.desc; // 添加点击事件,点击时切换显示/隐藏描述 siteInfo.addEventListener('click', function() { if (siteDesc.style.display === 'block') { siteDesc.style.display = 'none'; } else { siteDesc.style.display = 'block'; } }); } // 设置网站图标 if (data.icon) { siteFavicon.src = data.icon; } else { siteFavicon.src = getFaviconUrl(url); siteFavicon.onerror = function() { siteFavicon.style.display = 'none'; }; } // 显示网站信息,隐藏加载提示 siteInfoLoading.style.display = 'none'; siteInfo.style.display = 'flex'; } function initSiteInfo(url) { fetchSiteInfo(url).then(data => { displaySiteInfo(data, url); }).catch(() => { document.getElementById('site-info-container').style.display = 'none'; }); } </script> {% endif %} <script type="text/javascript"> const host = window.location.host; function GetQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); var r = window.location.search.substr(1).match(reg); return r ? decodeURI(r[2]) : null } function base64Decode(input) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; let str = input.replace(/=+$/, ''); let output = ''; if (str.length % 4 == 1) { throw new Error("'atob' failed: The string to be decoded is not correctly encoded."); } for (let bc = 0, bs, buffer, idx = 0; buffer = str.charAt(idx++); ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0) { buffer = chars.indexOf(buffer) } return output } let countdown = {{countdown}}; let jump_url = GetQueryString('{{url_param_name}}'); {% if enable_base64_encode %} jump_url = base64Decode(jump_url); {% endif %} const UrlReg = '^((http|https|thunder|qqdl|ed2k|Flashget|qbrowser|ftp|rtsp|mms)://)'; if (!jump_url || !jump_url.match(UrlReg)) { document.title = '参数错误,正在返回首页...'; jump_url = location.origin } let progressElement; let countdownElement; function updateCountdown() { if (countdown < 0) { return; } countdownElement.textContent = countdown; progressElement.style.width = (countdown / {{countdown}} * 100) + '%'; {% if auto_redirect %} if (countdown === 0) { jump(); } else { countdown--; setTimeout(updateCountdown, 1000); } {% else %} // 非自动跳转模式下,不继续倒计时 {% endif %} } function jump() { location.href = jump_url; } function closeWindow() { window.opener = null; window.close(); } function loadAvatar() { const avatarImg = document.querySelector('.avatar'); const placeholder = document.querySelector('.avatar-placeholder'); const img = new Image(); img.src = '{{avatar}}'; img.onload = function() { avatarImg.src = img.src; avatarImg.style.display = 'block'; placeholder.style.display = 'none'; } } window.addEventListener('load', function() { loadAvatar(); progressElement = document.getElementById('progress'); countdownElement = document.getElementById('countdown'); const jumpUrlElement = document.getElementById('jump-url'); jumpUrlElement.textContent = jump_url; jumpUrlElement.href = jump_url; {% if siteinfo_enable and siteinfo_api %} // 初始化网站信息 initSiteInfo(jump_url); {% else %} // 不显示网站信息容器 const siteInfoContainer = document.getElementById('site-info-container'); if (siteInfoContainer) { siteInfoContainer.style.display = 'none'; } {% endif %} }); </script> </head> <body> <!-- <div class="avatar-container"> <div class="avatar-placeholder"></div> <img src="" alt="头像" class="avatar"> </div> <div class="description">{{title}}</div> <div class="subtitle">{{subtitle}}</div> --> <div class="card"> <div class="title"> <img src="{{avatar}}" alt="网站图标" style="width: 20px; height: 20px; border-radius: 4px; vertical-align: middle; margin-right: 8px;"> <span>{{title}}</span> </div> <div class="message"> {% if auto_redirect %} 您即将离开「{{subtitle}}」,系统将自动为您跳转。<br>请注意您的账号和财产安全。 {% else %} 您即将离开「{{subtitle}}」,请点击下方按钮前往。<br>注意保护您的账号和财产安全。 {% endif %} </div> {% if siteinfo_enable and siteinfo_api %} <div id="site-info-container"> <div id="site-info-loading" class="site-info-loading"> 正在加载网站信息... </div> <div id="site-info" class="site-info" style="display: none;"> <img id="site-favicon" class="site-favicon" src="" alt="网站图标"> <div id="site-title" class="site-title"></div> </div> <div id="site-desc" class="site-desc"></div> </div> {% endif %} <div class="link-container"> <a class="link" id="jump-url" target="_blank"></a> </div> {% if auto_redirect %} <div class="countdown-container"> 将在 <span id="countdown"></span> 秒后自动跳转 </div> <div class="progress-bar"> <div class="progress" id="progress"></div> </div> {% else %} <div class="countdown-container" style="display: none;"> 将在 <span id="countdown"></span> 秒后自动跳转 </div> <div class="progress-bar" style="display: none;"> <div class="progress" id="progress"></div> </div> {% endif %} <div class="button-container"> <button class="button cancel-button" onclick="closeWindow()">取消</button> <button class="button confirm-button" onclick="jump()"> {% if auto_redirect %} 立即前往 {% else %} 前往链接 {% endif %} </button> </div> </div> </body> </html>