初始提交: 通用数据同步服务

- 基于OpenResty的RESTful API服务
- 支持Cookie/Token等数据类型的存储和管理
- 登录认证保护
- Web管理界面
- 数据过期管理
This commit is contained in:
2026-08-14 09:53:32 +08:00
commit edf4dfcd52
22 changed files with 4389 additions and 0 deletions
+223
View File
@@ -0,0 +1,223 @@
"""
通用数据同步服务 - Python 客户端示例
演示如何在 Python 脚本中使用同步服务
"""
import sys
sys.path.append('/home/hermes/projects/data-sync-service/scripts')
from utils import (
upload_data,
get_data,
delete_data,
list_all_tags,
load_cookies_from_sync,
load_token_from_sync
)
def example_upload_cookies():
"""示例:上传 Cookie 数据"""
# 模拟 Cookie 数据
cookies_data = {
"cookies": [
{
"name": "session_id",
"value": "abc123xyz789",
"domain": ".example.com",
"path": "/"
},
{
"name": "auth_token",
"value": "token_value_here",
"domain": ".example.com",
"path": "/"
}
],
"raw_cookie": "session_id=abc123xyz789; auth_token=token_value_here",
"user_agent": "Mozilla/5.0 ...",
"url": "https://example.com"
}
success = upload_data(
tag="example:user@example.com",
data_type="cookie",
data=cookies_data,
expires_in=604800, # 7天
note="示例账号",
login_url="https://example.com/login"
)
if success:
print("✅ Cookie 上传成功")
else:
print("❌ Cookie 上传失败")
def example_upload_token():
"""示例:上传 Token 数据"""
token_data = {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"api_endpoint": "https://api.example.com/v1"
}
success = upload_data(
tag="api:service_account",
data_type="token",
data=token_data,
expires_in=2592000, # 30天
note="API 服务账号",
login_url="https://console.example.com"
)
if success:
print("✅ Token 上传成功")
else:
print("❌ Token 上传失败")
def example_get_data():
"""示例:获取数据"""
data = get_data("example:user@example.com")
if data:
print("✅ 数据获取成功:")
print(f" Tag: {data.get('tag')}")
print(f" Type: {data.get('type')}")
print(f" Created: {data.get('metadata', {}).get('created_at')}")
print(f" Expires: {data.get('metadata', {}).get('expires_at')}")
else:
print("❌ 数据不存在或获取失败")
def example_load_cookies():
"""示例:加载 Cookie 数据(便捷方法)"""
cookies = load_cookies_from_sync("tingwu:ykaayk@qq.com")
if cookies:
print("✅ Cookie 加载成功:")
for cookie in cookies:
print(f" - {cookie['name']}: {cookie['value'][:20]}...")
else:
print("❌ Cookie 加载失败")
def example_list_all():
"""示例:列出所有 tag"""
tags = list_all_tags()
print(f"📋 共有 {len(tags)} 个数据:")
for item in tags:
status_icon = {
"valid": "",
"expiring_soon": "⚠️",
"expired": "",
"no_expiry": "♾️"
}.get(item.get("status", "unknown"), "")
print(f" {status_icon} {item['tag']} ({item['type']})")
def example_delete():
"""示例:删除数据"""
success = delete_data("example:user@example.com")
if success:
print("✅ 数据删除成功")
else:
print("❌ 数据删除失败")
def example_tingwu_script():
"""
示例:在通义听悟脚本中使用同步服务
替代原有的本地文件读取方式
"""
# 尝试从同步服务加载 Cookie
cookies = load_cookies_from_sync("tingwu:ykaayk@qq.com")
if cookies:
print("✅ 从同步服务加载 Cookie 成功")
# 转换为 requests 可用的格式
cookie_dict = {c['name']: c['value'] for c in cookies}
# 使用 Cookie 发起请求
import requests
try:
response = requests.get(
"https://tingwu.aliyun.com/api/some-endpoint",
cookies=cookie_dict,
timeout=10
)
print(f" API 调用成功: {response.status_code}")
except Exception as e:
print(f" API 调用失败: {e}")
else:
print("❌ Cookie 加载失败,请检查同步服务")
def example_with_fallback():
"""
示例:带降级方案的使用方式
优先从同步服务加载,失败时使用本地文件
"""
# 尝试从同步服务加载
data = get_data("tingwu:ykaayk@qq.com")
if data:
print("✅ 从同步服务加载成功")
cookies = data.get("data", {}).get("cookies", [])
else:
print("⚠️ 同步服务不可用,使用本地文件")
# 降级到本地文件
import json
try:
with open("/path/to/local/cookies.json", 'r') as f:
local_data = json.load(f)
cookies = local_data.get("cookies", [])
except:
print("❌ 本地文件也加载失败")
cookies = []
return cookies
if __name__ == "__main__":
print("=" * 50)
print("通用数据同步服务 - Python 客户端示例")
print("=" * 50)
# 运行示例
print("\n1️⃣ 上传 Cookie 数据")
example_upload_cookies()
print("\n2️⃣ 上传 Token 数据")
example_upload_token()
print("\n3️⃣ 获取数据")
example_get_data()
print("\n4️⃣ 列出所有 tag")
example_list_all()
print("\n5️⃣ 加载 Cookie(便捷方法)")
example_load_cookies()
print("\n6️⃣ 通义听悟脚本示例")
example_tingwu_script()
# 删除示例数据(取消注释以执行)
# print("\n7️⃣ 删除数据")
# example_delete()
print("\n" + "=" * 50)
+405
View File
@@ -0,0 +1,405 @@
// ==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();
}
})();