- 基于OpenResty的RESTful API服务 - 支持Cookie/Token等数据类型的存储和管理 - 登录认证保护 - Web管理界面 - 数据过期管理
406 lines
12 KiB
JavaScript
406 lines
12 KiB
JavaScript
// ==UserScript==
|
|
// @name 通用数据同步脚本
|
|
// @namespace http://tampermonkey.net/
|
|
// @version 2.0
|
|
// @description 通用数据同步服务客户端,支持自动检测项目和账号
|
|
// @author Hermes
|
|
// @match *://*/*
|
|
// @grant GM_xmlhttpRequest
|
|
// @grant GM_setClipboard
|
|
// @grant GM_notification
|
|
// @connect 47.122.126.244
|
|
// ==/UserScript==
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// ==================== 配置区 ====================
|
|
const CONFIG = {
|
|
serverUrl: 'http://47.122.126.244:5001',
|
|
password: 'admin123123123',
|
|
|
|
// 自动检测配置
|
|
autoDetect: true, // 是否自动检测项目名和账号
|
|
tag: 'auto', // 'auto' 表示自动检测,或手动指定如 'tingwu:ykaayk@qq.com'
|
|
type: 'cookie', // cookie/token/credential/session/custom
|
|
|
|
// 过期配置
|
|
expiresIn: 604800, // 7天(秒)
|
|
|
|
// 其他配置
|
|
autoSync: false, // 是否自动同步(页面加载后自动上传)
|
|
syncInterval: 0, // 定时同步间隔(秒,0 表示不定时同步)
|
|
};
|
|
|
|
// ==================== 项目检测规则 ====================
|
|
const PROJECT_RULES = [
|
|
{
|
|
name: 'tingwu',
|
|
match: (url) => url.includes('tingwu.aliyun.com'),
|
|
getAccount: () => {
|
|
// 从页面提取账号信息
|
|
const emailElement = document.querySelector('[data-account]');
|
|
if (emailElement) return emailElement.getAttribute('data-account');
|
|
|
|
// 从 localStorage 提取
|
|
try {
|
|
const userData = JSON.parse(localStorage.getItem('user_data') || '{}');
|
|
return userData.email || userData.username || 'unknown';
|
|
} catch {}
|
|
|
|
return 'unknown';
|
|
},
|
|
loginUrl: 'https://tingwu.aliyun.com/'
|
|
},
|
|
{
|
|
name: 'wechat',
|
|
match: (url) => url.includes('mp.weixin.qq.com'),
|
|
getAccount: () => {
|
|
const accountElement = document.querySelector('.account_info_text');
|
|
return accountElement ? accountElement.textContent.trim() : 'admin';
|
|
},
|
|
loginUrl: 'https://mp.weixin.qq.com/'
|
|
},
|
|
// 添加更多项目...
|
|
];
|
|
|
|
// ==================== 核心功能 ====================
|
|
|
|
/**
|
|
* 自动检测项目和账号
|
|
*/
|
|
function autoDetectTag() {
|
|
const url = window.location.href;
|
|
|
|
for (const rule of PROJECT_RULES) {
|
|
if (rule.match(url)) {
|
|
const account = rule.getAccount();
|
|
return {
|
|
tag: `${rule.name}:${account}`,
|
|
loginUrl: rule.loginUrl,
|
|
project: rule.name,
|
|
account: account
|
|
};
|
|
}
|
|
}
|
|
|
|
return {
|
|
tag: 'unknown:unknown',
|
|
loginUrl: window.location.origin,
|
|
project: 'unknown',
|
|
account: 'unknown'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 获取当前页面的 Cookie 数据
|
|
*/
|
|
function getCookieData() {
|
|
const cookies = document.cookie.split(';').map(c => {
|
|
const [name, ...valueParts] = c.trim().split('=');
|
|
return {
|
|
name: name,
|
|
value: valueParts.join('='),
|
|
domain: window.location.hostname,
|
|
path: '/'
|
|
};
|
|
}).filter(c => c.name);
|
|
|
|
return {
|
|
cookies: cookies,
|
|
raw_cookie: document.cookie,
|
|
user_agent: navigator.userAgent,
|
|
url: window.location.href
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 上传数据到服务器
|
|
*/
|
|
function uploadData(tag, type, data, metadata) {
|
|
const payload = {
|
|
password: CONFIG.password,
|
|
tag: tag,
|
|
type: type,
|
|
data: data,
|
|
metadata: metadata
|
|
};
|
|
|
|
GM_xmlhttpRequest({
|
|
method: 'POST',
|
|
url: `${CONFIG.serverUrl}/api/data`,
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
data: JSON.stringify(payload),
|
|
onload: function(response) {
|
|
if (response.status === 200) {
|
|
const result = JSON.parse(response.responseText);
|
|
console.log('✅ 数据同步成功:', result);
|
|
GM_notification({
|
|
title: '数据同步成功',
|
|
text: `Tag: ${tag}\n时间: ${result.timestamp}`,
|
|
timeout: 3000
|
|
});
|
|
} else {
|
|
console.error('❌ 数据同步失败:', response.responseText);
|
|
GM_notification({
|
|
title: '数据同步失败',
|
|
text: `状态码: ${response.status}`,
|
|
timeout: 5000
|
|
});
|
|
}
|
|
},
|
|
onerror: function(error) {
|
|
console.error('❌ 网络错误:', error);
|
|
GM_notification({
|
|
title: '网络错误',
|
|
text: '无法连接到同步服务器',
|
|
timeout: 5000
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 同步当前页面数据
|
|
*/
|
|
function syncData() {
|
|
let tag = CONFIG.tag;
|
|
let loginUrl = window.location.origin;
|
|
|
|
// 自动检测
|
|
if (CONFIG.autoDetect || tag === 'auto') {
|
|
const detected = autoDetectTag();
|
|
tag = detected.tag;
|
|
loginUrl = detected.loginUrl;
|
|
|
|
console.log('🔍 自动检测结果:', detected);
|
|
}
|
|
|
|
// 获取数据
|
|
let data;
|
|
if (CONFIG.type === 'cookie') {
|
|
data = getCookieData();
|
|
} else {
|
|
console.error('❌ 不支持的数据类型:', CONFIG.type);
|
|
return;
|
|
}
|
|
|
|
// 构建元数据
|
|
const metadata = {
|
|
expires_in: CONFIG.expiresIn,
|
|
login_url: loginUrl,
|
|
note: `自动同步 - ${new Date().toLocaleString()}`
|
|
};
|
|
|
|
// 上传
|
|
uploadData(tag, CONFIG.type, data, metadata);
|
|
}
|
|
|
|
/**
|
|
* 复制配置到剪贴板
|
|
*/
|
|
function copyConfig() {
|
|
let tag = CONFIG.tag;
|
|
|
|
if (CONFIG.autoDetect || tag === 'auto') {
|
|
const detected = autoDetectTag();
|
|
tag = detected.tag;
|
|
}
|
|
|
|
const config = {
|
|
serverUrl: CONFIG.serverUrl,
|
|
password: CONFIG.password,
|
|
tag: tag,
|
|
type: CONFIG.type
|
|
};
|
|
|
|
const configText = JSON.stringify(config, null, 2);
|
|
GM_setClipboard(configText);
|
|
|
|
console.log('📋 配置已复制到剪贴板:', config);
|
|
GM_notification({
|
|
title: '配置已复制',
|
|
text: `Tag: ${tag}`,
|
|
timeout: 2000
|
|
});
|
|
}
|
|
|
|
// ==================== UI 界面 ====================
|
|
|
|
/**
|
|
* 创建浮动按钮
|
|
*/
|
|
function createFloatingButton() {
|
|
const button = document.createElement('div');
|
|
button.id = 'data-sync-button';
|
|
button.innerHTML = '🔄';
|
|
button.style.cssText = `
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
width: 50px;
|
|
height: 50px;
|
|
background: #4CAF50;
|
|
color: white;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
font-size: 24px;
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
|
|
z-index: 10000;
|
|
transition: all 0.3s;
|
|
`;
|
|
|
|
button.addEventListener('mouseenter', () => {
|
|
button.style.transform = 'scale(1.1)';
|
|
});
|
|
|
|
button.addEventListener('mouseleave', () => {
|
|
button.style.transform = 'scale(1)';
|
|
});
|
|
|
|
button.addEventListener('click', () => {
|
|
showSyncPanel();
|
|
});
|
|
|
|
document.body.appendChild(button);
|
|
}
|
|
|
|
/**
|
|
* 显示同步面板
|
|
*/
|
|
function showSyncPanel() {
|
|
const detected = autoDetectTag();
|
|
|
|
const panel = document.createElement('div');
|
|
panel.id = 'data-sync-panel';
|
|
panel.innerHTML = `
|
|
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.2); max-width: 400px;">
|
|
<h3 style="margin: 0 0 15px 0;">数据同步</h3>
|
|
<div style="margin-bottom: 10px;">
|
|
<strong>项目:</strong> ${detected.project}
|
|
</div>
|
|
<div style="margin-bottom: 10px;">
|
|
<strong>账号:</strong> ${detected.account}
|
|
</div>
|
|
<div style="margin-bottom: 10px;">
|
|
<strong>Tag:</strong> ${detected.tag}
|
|
</div>
|
|
<div style="margin-bottom: 15px;">
|
|
<strong>类型:</strong> ${CONFIG.type}
|
|
</div>
|
|
<div style="display: flex; gap: 10px;">
|
|
<button id="sync-now-btn" style="flex: 1; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
立即同步
|
|
</button>
|
|
<button id="copy-config-btn" style="flex: 1; padding: 10px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
复制配置
|
|
</button>
|
|
<button id="close-panel-btn" style="padding: 10px; background: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
关闭
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
panel.style.cssText = `
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
z-index: 10001;
|
|
`;
|
|
|
|
// 添加背景遮罩
|
|
const overlay = document.createElement('div');
|
|
overlay.id = 'data-sync-overlay';
|
|
overlay.style.cssText = `
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
z-index: 10000;
|
|
`;
|
|
|
|
document.body.appendChild(overlay);
|
|
document.body.appendChild(panel);
|
|
|
|
// 绑定事件
|
|
document.getElementById('sync-now-btn').addEventListener('click', () => {
|
|
syncData();
|
|
closePanel();
|
|
});
|
|
|
|
document.getElementById('copy-config-btn').addEventListener('click', () => {
|
|
copyConfig();
|
|
});
|
|
|
|
document.getElementById('close-panel-btn').addEventListener('click', closePanel);
|
|
overlay.addEventListener('click', closePanel);
|
|
}
|
|
|
|
/**
|
|
* 关闭面板
|
|
*/
|
|
function closePanel() {
|
|
const panel = document.getElementById('data-sync-panel');
|
|
const overlay = document.getElementById('data-sync-overlay');
|
|
if (panel) panel.remove();
|
|
if (overlay) overlay.remove();
|
|
}
|
|
|
|
// ==================== 初始化 ====================
|
|
|
|
function init() {
|
|
console.log('🚀 通用数据同步脚本已加载');
|
|
|
|
// 创建浮动按钮
|
|
createFloatingButton();
|
|
|
|
// 自动同步
|
|
if (CONFIG.autoSync) {
|
|
setTimeout(() => {
|
|
console.log('⏰ 执行自动同步...');
|
|
syncData();
|
|
}, 3000);
|
|
}
|
|
|
|
// 定时同步
|
|
if (CONFIG.syncInterval > 0) {
|
|
setInterval(() => {
|
|
console.log('⏰ 执行定时同步...');
|
|
syncData();
|
|
}, CONFIG.syncInterval * 1000);
|
|
}
|
|
|
|
// 快捷键 Ctrl+Shift+S
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.ctrlKey && e.shiftKey && e.key === 'S') {
|
|
e.preventDefault();
|
|
syncData();
|
|
}
|
|
});
|
|
|
|
// 快捷键 Ctrl+Shift+C
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.ctrlKey && e.shiftKey && e.key === 'C') {
|
|
e.preventDefault();
|
|
copyConfig();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 等待 DOM 加载完成
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
|
|
})();
|