功能: 优化油猴脚本 v3.0
主要改进: 1. 修复核心Bug - 正确检测API响应状态(检查success字段) 2. 配置界面化 - 使用GM_setValue/GM_getValue持久化存储 3. 添加可视化配置面板 - 无需修改代码 4. 增强错误处理 - 显示详细的错误信息 5. API兼容性验证 - 与后端完全兼容 提供两个版本: - clients/userscript-template.js (v3.0增强版, 608行) - client/userscript-template.js (v2.0简化版, 431行) 新增文档: - USERSCRIPT_CHANGELOG.md - 更新日志 - USERSCRIPT_FIX_SUMMARY.md - 修复总结 - USERSCRIPT_USAGE.md - 使用指南 由Claude Code完成。
This commit is contained in:
+234
-31
@@ -1,37 +1,62 @@
|
||||
// ==UserScript==
|
||||
// @name 通用数据同步脚本
|
||||
// @namespace http://tampermonkey.net/
|
||||
// @version 2.0
|
||||
// @description 通用数据同步服务客户端,支持自动检测项目和账号
|
||||
// @version 3.0
|
||||
// @description 通用数据同步服务客户端,支持自动检测项目和账号,可视化配置管理
|
||||
// @author Hermes
|
||||
// @match *://*/*
|
||||
// @grant GM_xmlhttpRequest
|
||||
// @grant GM_setClipboard
|
||||
// @grant GM_notification
|
||||
// @grant GM_setValue
|
||||
// @grant GM_getValue
|
||||
// @connect 47.122.126.244
|
||||
// @connect *
|
||||
// ==/UserScript==
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// ==================== 配置区 ====================
|
||||
const CONFIG = {
|
||||
// ==================== 配置管理 ====================
|
||||
const DEFAULT_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 表示不定时同步)
|
||||
};
|
||||
|
||||
// 加载配置
|
||||
function loadConfig() {
|
||||
const saved = GM_getValue('sync_config', null);
|
||||
if (saved) {
|
||||
try {
|
||||
return {...DEFAULT_CONFIG, ...JSON.parse(saved)};
|
||||
} catch (e) {
|
||||
console.error('配置解析失败,使用默认配置', e);
|
||||
}
|
||||
}
|
||||
return {...DEFAULT_CONFIG};
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
function saveConfig(config) {
|
||||
GM_setValue('sync_config', JSON.stringify(config));
|
||||
console.log('✅ 配置已保存');
|
||||
}
|
||||
|
||||
// 全局配置对象
|
||||
let CONFIG = loadConfig();
|
||||
|
||||
// ==================== 项目检测规则 ====================
|
||||
const PROJECT_RULES = [
|
||||
{
|
||||
@@ -134,19 +159,34 @@
|
||||
},
|
||||
data: JSON.stringify(payload),
|
||||
onload: function(response) {
|
||||
if (response.status === 200) {
|
||||
const result = JSON.parse(response.responseText);
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(response.responseText);
|
||||
} catch (e) {
|
||||
console.error('❌ 响应解析失败:', response.responseText);
|
||||
GM_notification({
|
||||
title: '同步失败',
|
||||
text: '服务器响应格式错误',
|
||||
timeout: 5000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否成功:HTTP 200 且响应体包含 success: true
|
||||
if (response.status === 200 && result.success === true) {
|
||||
console.log('✅ 数据同步成功:', result);
|
||||
GM_notification({
|
||||
title: '数据同步成功',
|
||||
text: `Tag: ${tag}\n时间: ${result.timestamp}`,
|
||||
text: `Tag: ${tag}\n时间: ${result.timestamp || '未知'}`,
|
||||
timeout: 3000
|
||||
});
|
||||
} else {
|
||||
console.error('❌ 数据同步失败:', response.responseText);
|
||||
// 失败:可能是 HTTP 错误码,或响应包含 error 字段
|
||||
const errorMsg = result.error || result.message || response.statusText || '未知错误';
|
||||
console.error('❌ 数据同步失败:', errorMsg, response);
|
||||
GM_notification({
|
||||
title: '数据同步失败',
|
||||
text: `状态码: ${response.status}`,
|
||||
text: `状态码: ${response.status}\n错误: ${errorMsg}`,
|
||||
timeout: 5000
|
||||
});
|
||||
}
|
||||
@@ -275,32 +315,42 @@
|
||||
*/
|
||||
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 style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.2); max-width: 450px;">
|
||||
<h3 style="margin: 0 0 15px 0; color: #333;">数据同步控制面板</h3>
|
||||
|
||||
<div style="margin-bottom: 15px; padding: 10px; background: #f5f5f5; border-radius: 4px;">
|
||||
<div style="margin-bottom: 8px;">
|
||||
<strong>项目:</strong> ${detected.project}
|
||||
</div>
|
||||
<div style="margin-bottom: 8px;">
|
||||
<strong>账号:</strong> ${detected.account}
|
||||
</div>
|
||||
<div style="margin-bottom: 8px;">
|
||||
<strong>Tag:</strong> ${detected.tag}
|
||||
</div>
|
||||
<div>
|
||||
<strong>类型:</strong> ${CONFIG.type}
|
||||
</div>
|
||||
</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 style="display: flex; gap: 10px; margin-bottom: 15px;">
|
||||
<button id="sync-now-btn" style="flex: 1; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||||
🔄 立即同步
|
||||
</button>
|
||||
<button id="copy-config-btn" style="flex: 1; padding: 10px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||||
📋 复制配置
|
||||
</button>
|
||||
</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 id="settings-btn" style="flex: 1; padding: 10px; background: #FF9800; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||||
⚙️ 配置设置
|
||||
</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 id="close-panel-btn" style="padding: 10px 20px; background: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
@@ -340,10 +390,163 @@
|
||||
copyConfig();
|
||||
});
|
||||
|
||||
document.getElementById('settings-btn').addEventListener('click', () => {
|
||||
closePanel();
|
||||
showSettingsPanel();
|
||||
});
|
||||
|
||||
document.getElementById('close-panel-btn').addEventListener('click', closePanel);
|
||||
overlay.addEventListener('click', closePanel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示设置面板
|
||||
*/
|
||||
function showSettingsPanel() {
|
||||
const panel = document.createElement('div');
|
||||
panel.id = 'data-sync-settings-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: 500px; max-height: 80vh; overflow-y: auto;">
|
||||
<h3 style="margin: 0 0 15px 0; color: #333;">配置设置</h3>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold; color: #555;">服务器地址</label>
|
||||
<input type="text" id="setting-serverUrl" value="${CONFIG.serverUrl}"
|
||||
style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold; color: #555;">密码</label>
|
||||
<input type="password" id="setting-password" value="${CONFIG.password}"
|
||||
style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold; color: #555;">Tag(留空自动检测)</label>
|
||||
<input type="text" id="setting-tag" value="${CONFIG.tag === 'auto' ? '' : CONFIG.tag}" placeholder="auto"
|
||||
style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
||||
<small style="color: #888;">格式:项目名:账号,如 tingwu:user@email.com</small>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold; color: #555;">数据类型</label>
|
||||
<select id="setting-type" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
||||
<option value="cookie" ${CONFIG.type === 'cookie' ? 'selected' : ''}>Cookie</option>
|
||||
<option value="token" ${CONFIG.type === 'token' ? 'selected' : ''}>Token</option>
|
||||
<option value="credential" ${CONFIG.type === 'credential' ? 'selected' : ''}>Credential</option>
|
||||
<option value="session" ${CONFIG.type === 'session' ? 'selected' : ''}>Session</option>
|
||||
<option value="custom" ${CONFIG.type === 'custom' ? 'selected' : ''}>Custom</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold; color: #555;">过期时间(秒)</label>
|
||||
<input type="number" id="setting-expiresIn" value="${CONFIG.expiresIn}"
|
||||
style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
||||
<small style="color: #888;">默认 604800(7天)</small>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: flex; align-items: center; cursor: pointer;">
|
||||
<input type="checkbox" id="setting-autoDetect" ${CONFIG.autoDetect ? 'checked' : ''}
|
||||
style="margin-right: 8px;">
|
||||
<span style="font-weight: bold; color: #555;">自动检测项目和账号</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: flex; align-items: center; cursor: pointer;">
|
||||
<input type="checkbox" id="setting-autoSync" ${CONFIG.autoSync ? 'checked' : ''}
|
||||
style="margin-right: 8px;">
|
||||
<span style="font-weight: bold; color: #555;">页面加载后自动同步</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold; color: #555;">定时同步间隔(秒,0=禁用)</label>
|
||||
<input type="number" id="setting-syncInterval" value="${CONFIG.syncInterval}"
|
||||
style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<button id="save-settings-btn" style="flex: 1; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||||
✅ 保存配置
|
||||
</button>
|
||||
<button id="reset-settings-btn" style="flex: 1; padding: 10px; background: #FF9800; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||||
🔄 恢复默认
|
||||
</button>
|
||||
<button id="close-settings-btn" style="padding: 10px 20px; background: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||||
关闭
|
||||
</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('save-settings-btn').addEventListener('click', () => {
|
||||
const newConfig = {
|
||||
serverUrl: document.getElementById('setting-serverUrl').value.trim(),
|
||||
password: document.getElementById('setting-password').value,
|
||||
tag: document.getElementById('setting-tag').value.trim() || 'auto',
|
||||
type: document.getElementById('setting-type').value,
|
||||
expiresIn: parseInt(document.getElementById('setting-expiresIn').value) || 604800,
|
||||
autoDetect: document.getElementById('setting-autoDetect').checked,
|
||||
autoSync: document.getElementById('setting-autoSync').checked,
|
||||
syncInterval: parseInt(document.getElementById('setting-syncInterval').value) || 0,
|
||||
};
|
||||
|
||||
CONFIG = newConfig;
|
||||
saveConfig(newConfig);
|
||||
|
||||
GM_notification({
|
||||
title: '配置已保存',
|
||||
text: '设置将在下次页面加载时生效',
|
||||
timeout: 3000
|
||||
});
|
||||
|
||||
closePanel();
|
||||
});
|
||||
|
||||
document.getElementById('reset-settings-btn').addEventListener('click', () => {
|
||||
if (confirm('确定要恢复默认配置吗?')) {
|
||||
CONFIG = {...DEFAULT_CONFIG};
|
||||
saveConfig(CONFIG);
|
||||
GM_notification({
|
||||
title: '配置已重置',
|
||||
text: '已恢复为默认配置',
|
||||
timeout: 3000
|
||||
});
|
||||
closePanel();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('close-settings-btn').addEventListener('click', closePanel);
|
||||
overlay.addEventListener('click', closePanel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭面板
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user