初始提交: 通用数据同步服务
- 基于OpenResty的RESTful API服务 - 支持Cookie/Token等数据类型的存储和管理 - 登录认证保护 - Web管理界面 - 数据过期管理
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
// ==UserScript==
|
||||
// @name 通用数据同步助手
|
||||
// @namespace http://tampermonkey.net/
|
||||
// @version 1.0.0
|
||||
// @description 一键同步 Cookie/Token 到服务器(通用版)
|
||||
// @author K-hermes
|
||||
// @match https://tingwu.aliyun.com/*
|
||||
// @match https://mp.weixin.qq.com/*
|
||||
// @grant GM_xmlhttpRequest
|
||||
// @grant GM_setClipboard
|
||||
// ==/UserScript==
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// ==================== 配置 ====================
|
||||
const CONFIG = {
|
||||
serverUrl: 'http://47.122.126.244:5001',
|
||||
password: 'admin123123123',
|
||||
autoDetect: true // 自动检测项目名和账号
|
||||
};
|
||||
|
||||
// ==================== 项目检测 ====================
|
||||
const PROJECT_PATTERNS = {
|
||||
'tingwu.aliyun.com': {
|
||||
project: 'tingwu',
|
||||
accountSelector: '.user-email, [class*="email"], [class*="account"]',
|
||||
loginUrl: 'https://tingwu.aliyun.com/'
|
||||
},
|
||||
'mp.weixin.qq.com': {
|
||||
project: 'wechat',
|
||||
accountSelector: '.account__nickname, .weui-desktop-account__name',
|
||||
loginUrl: 'https://mp.weixin.qq.com/'
|
||||
}
|
||||
};
|
||||
|
||||
function detectProject() {
|
||||
const host = window.location.hostname;
|
||||
for (const [pattern, config] of Object.entries(PROJECT_PATTERNS)) {
|
||||
if (host.includes(pattern)) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectAccount(selector) {
|
||||
if (!selector) return 'auto';
|
||||
|
||||
const element = document.querySelector(selector);
|
||||
if (element) {
|
||||
return element.innerText.trim() || 'auto';
|
||||
}
|
||||
|
||||
// 尝试从 localStorage/sessionStorage 读取
|
||||
const storageKeys = ['userEmail', 'account', 'username', 'user_id'];
|
||||
for (const key of storageKeys) {
|
||||
const value = localStorage.getItem(key) || sessionStorage.getItem(key);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return 'auto';
|
||||
}
|
||||
|
||||
// ==================== Cookie 提取 ====================
|
||||
function getAllCookies() {
|
||||
const cookies = document.cookie.split(';').map(cookie => {
|
||||
const [name, value] = cookie.trim().split('=');
|
||||
return {
|
||||
name: name,
|
||||
value: value || '',
|
||||
domain: window.location.hostname,
|
||||
path: '/',
|
||||
secure: window.location.protocol === 'https:',
|
||||
httpOnly: false
|
||||
};
|
||||
});
|
||||
|
||||
return cookies.filter(c => c.name);
|
||||
}
|
||||
|
||||
// ==================== 同步功能 ====================
|
||||
function syncData() {
|
||||
const projectConfig = detectProject();
|
||||
if (!projectConfig) {
|
||||
showMessage('⚠️ 当前网站不支持自动同步', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const account = detectAccount(projectConfig.accountSelector);
|
||||
const tag = `${projectConfig.project}:${account}`;
|
||||
const cookies = getAllCookies();
|
||||
|
||||
if (cookies.length === 0) {
|
||||
showMessage('⚠️ 未找到 Cookie', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
password: CONFIG.password,
|
||||
tag: tag,
|
||||
type: 'cookie',
|
||||
data: {
|
||||
cookies: cookies,
|
||||
raw_cookie: document.cookie,
|
||||
user_agent: navigator.userAgent
|
||||
},
|
||||
metadata: {
|
||||
expires_in: 7 * 24 * 3600, // 7天
|
||||
note: `${projectConfig.project} 账号同步`,
|
||||
login_url: projectConfig.loginUrl
|
||||
}
|
||||
};
|
||||
|
||||
showMessage('🔄 正在同步...', 'info');
|
||||
|
||||
GM_xmlhttpRequest({
|
||||
method: 'POST',
|
||||
url: `${CONFIG.serverUrl}/api/data`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(data),
|
||||
onload: function(response) {
|
||||
if (response.status === 200) {
|
||||
const result = JSON.parse(response.responseText);
|
||||
showMessage(`✅ 同步成功!\n\nTag: ${tag}\n过期时间: ${result.expires_at}`, 'success');
|
||||
} else {
|
||||
showMessage(`❌ 同步失败: ${response.statusText}`, 'error');
|
||||
}
|
||||
},
|
||||
onerror: function(error) {
|
||||
showMessage(`❌ 网络错误: ${error.error}`, 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 复制配置 ====================
|
||||
function copyConfig() {
|
||||
const projectConfig = detectProject();
|
||||
if (!projectConfig) {
|
||||
showMessage('⚠️ 当前网站不支持', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const account = detectAccount(projectConfig.accountSelector);
|
||||
const tag = `${projectConfig.project}:${account}`;
|
||||
|
||||
const config = {
|
||||
serverUrl: CONFIG.serverUrl,
|
||||
password: CONFIG.password,
|
||||
tag: tag,
|
||||
type: 'cookie'
|
||||
};
|
||||
|
||||
const configText = JSON.stringify(config, null, 2);
|
||||
GM_setClipboard(configText);
|
||||
showMessage('✅ 配置已复制到剪贴板', 'success');
|
||||
}
|
||||
|
||||
// ==================== UI ====================
|
||||
function createButton() {
|
||||
const button = document.createElement('div');
|
||||
button.innerHTML = `
|
||||
<style>
|
||||
#sync-helper-btn {
|
||||
position: fixed;
|
||||
top: 100px;
|
||||
right: 20px;
|
||||
z-index: 99999;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
#sync-helper-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
|
||||
}
|
||||
#sync-helper-menu {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 150px;
|
||||
right: 20px;
|
||||
z-index: 99998;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||
overflow: hidden;
|
||||
}
|
||||
.sync-menu-item {
|
||||
padding: 12px 20px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.sync-menu-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.sync-menu-item:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
<div id="sync-helper-btn">
|
||||
🔄 同步数据
|
||||
</div>
|
||||
<div id="sync-helper-menu">
|
||||
<div class="sync-menu-item" id="sync-now">🔄 立即同步</div>
|
||||
<div class="sync-menu-item" id="copy-config">📋 复制配置</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(button);
|
||||
|
||||
// 事件绑定
|
||||
const btn = document.getElementById('sync-helper-btn');
|
||||
const menu = document.getElementById('sync-helper-menu');
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
|
||||
});
|
||||
|
||||
document.getElementById('sync-now').addEventListener('click', () => {
|
||||
menu.style.display = 'none';
|
||||
syncData();
|
||||
});
|
||||
|
||||
document.getElementById('copy-config').addEventListener('click', () => {
|
||||
menu.style.display = 'none';
|
||||
copyConfig();
|
||||
});
|
||||
|
||||
// 点击外部关闭菜单
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!btn.contains(e.target) && !menu.contains(e.target)) {
|
||||
menu.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showMessage(text, type) {
|
||||
const colors = {
|
||||
success: '#4CAF50',
|
||||
error: '#f44336',
|
||||
warning: '#ff9800',
|
||||
info: '#2196F3'
|
||||
};
|
||||
|
||||
const msg = document.createElement('div');
|
||||
msg.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 999999;
|
||||
background: ${colors[type] || colors.info};
|
||||
color: white;
|
||||
padding: 15px 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
|
||||
font-size: 14px;
|
||||
white-space: pre-line;
|
||||
max-width: 400px;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
`;
|
||||
msg.textContent = text;
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes slideIn {
|
||||
from { transform: translateX(400px); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
document.body.appendChild(msg);
|
||||
|
||||
setTimeout(() => {
|
||||
msg.style.animation = 'slideOut 0.3s ease-in';
|
||||
setTimeout(() => msg.remove(), 300);
|
||||
}, 3000);
|
||||
|
||||
const styleOut = document.createElement('style');
|
||||
styleOut.textContent = `
|
||||
@keyframes slideOut {
|
||||
from { transform: translateX(0); opacity: 1; }
|
||||
to { transform: translateX(400px); opacity: 0; }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(styleOut);
|
||||
}
|
||||
|
||||
// ==================== 初始化 ====================
|
||||
window.addEventListener('load', () => {
|
||||
setTimeout(() => {
|
||||
createButton();
|
||||
console.log('[同步助手] 已加载');
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user