CookieCloud

CookieCloud的tampermonkey版本,目前仅支持上传cookie。

目前為 2024-09-25 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         CookieCloud
// @namespace    http://tampermonkey.net/
// @version      2024-09-24
// @description  CookieCloud的tampermonkey版本,目前仅支持上传cookie。
// @author       tomato
// @icon         https://store-images.s-microsoft.com/image/apps.63473.a0ccb631-d5e7-422b-bcc7-c0405274114b.be044f83-1292-4e84-a65d-e0527d895863.05fc1666-519a-4d36-8b67-8110c70b45cc?mode=scale&h=64&q=90&w=64
// @match        *://*/*
// @grant        GM_cookie
// @grant        GM_xmlhttpRequest
// @connect *
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js
// @run-at       document-end
// @license MIT
// ==/UserScript==

/* global $, jQuery, CryptoJS */


(function() {
    'use strict';

    const configStoreKey = '_cookieCloudConfig';

    async function init() {
        const btnContainer = document.createElement('section');
        const asyncBtn = document.createElement('button');
        const asyncConfigBtn = document.createElement('button');
        asyncBtn.innerText = '上传';
        asyncConfigBtn.innerText = '配置';
        const btnContainerStyles = {
            position: 'fixed',
            bottom: '200px',
            left: '20px',
            fontSize: '14px',
            zIndex: 1000,
        };

        const btnStyles = {
            display: 'block',
            width: '50px',
            height: '50px',
            borderRadius: '50%',
            backgroundColor: '#87CEEB',
            border: 'none',
            color: '#fff',
            marginBottom: '10px',
            boxShadow: '0 4px 8px rgba(0, 0, 0, 0.3)'
        }
        Object.assign(btnContainer.style, btnContainerStyles);
        Object.assign(asyncBtn.style, btnStyles);
        Object.assign(asyncConfigBtn.style, btnStyles);

        btnContainer.appendChild(asyncBtn);
        btnContainer.appendChild(asyncConfigBtn);
        document.body.appendChild(btnContainer);

        asyncConfigBtn.onclick = function() {
            const modal = initConfigForm();
            document.body.appendChild(modal);
        }
        // 为按钮添加点击事件
        asyncBtn.onclick = async function(event) {
            event.stopPropagation();

            const config = localStorage.getItem(configStoreKey);
            if (!config) {
                alert('请填写配置');
                return;
            };
            const {url, uuid, password, domain = location.host} = JSON.parse(config);
            if (!url) {
                alert('请填写服务器地址');
                return;
            };
            if (!uuid) {
                alert('请填写uuid');
                return;
            };
            if (!password) {
                alert('请填写密码');
                return;
            };

            const cookies = await getCookie(domain);
            const encryptCookies = cookie_encrypt(uuid, password, cookies);

            const payload = {
                uuid,
                encrypted: encryptCookies
            };

            const res = await syncCookie(url, payload);
            try {
                const resData = JSON.parse(res.response)
                console.log('resData:', resData);
                if (resData.action === 'done') {
                    alert('同步成功')
                } else {
                    throw('错误')
                }
            } catch(e) {
                alert(String(e))
            }
        };
    }

    function initConfigForm() {
        // 创建遮罩层
        const overlay = document.createElement('div');
        overlay.style.position = 'fixed';
        overlay.style.top = '0';
        overlay.style.left = '0';
        overlay.style.width = '100%';
        overlay.style.height = '100%';
        overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
        overlay.style.zIndex = '1001';

        // 创建弹框(Modal)容器
        const modal = document.createElement('div');
        const modalStyles = {
            position: 'fixed',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)',
            width: '400px',
            backgroundColor: '#fff',
            padding: '20px',
            boxShadow: '0 4px 10px rgba(0, 0, 0, 0.3)',
            zIndex: '1000',
            borderRadius: '8px',
        };
        Object.assign(modal.style, modalStyles);

        // 创建表单
        const form = document.createElement('form');
        const inputStyles = {
            width: '100%',
            padding: '8px',
            boxSizing: 'border-box',
            marginBottom: '10px'
        };

        // 创建同步域名关键词·默认当前域名
        const domainEle = document.createElement('input');
        domainEle.type = 'text';
        domainEle.placeholder = '同步域名关键词·默认当前域名';
        Object.assign(domainEle.style, inputStyles);

        // 创建输入框 服务器地址
        const urlEle = document.createElement('input');
        urlEle.type = 'text';
        urlEle.placeholder = '服务器地址';
        Object.assign(urlEle.style, inputStyles);

        // 创建输入框 端对端加密密码
        const pwdEle = document.createElement('input');
        pwdEle.type = 'text';
        pwdEle.placeholder = '输入框 端对端加密密码';
        Object.assign(pwdEle.style, inputStyles);

        // 创建输入框 用户KEY · UUID
        const uuieEle = document.createElement('input');
        uuieEle.type = 'text';
        uuieEle.placeholder = '用户KEY · UUID';
        Object.assign(uuieEle.style, inputStyles);

        // 创建保存按钮
        const saveButton = document.createElement('button');
        saveButton.type = 'submit';
        saveButton.innerText = '保存';
        saveButton.style.width = '100%';
        saveButton.style.padding = '10px';
        saveButton.style.backgroundColor = '#87CEEB';
        saveButton.style.border = 'none';
        saveButton.style.color = '#fff';
        saveButton.style.cursor = 'pointer';
        saveButton.style.fontSize = '16px';
        saveButton.style.borderRadius = '4px';

        const config = localStorage.getItem(configStoreKey);
        if (config) {
            const {url, uuid, password, domain = ''} = JSON.parse(config);
            urlEle.value = url;
            pwdEle.value = password;
            uuieEle.value = uuid;
            domainEle.value = domain;
        };

        saveButton.onclick = function () {
            const configStr = JSON.stringify({
                url: urlEle.value,
                password: pwdEle.value,
                uuid: uuieEle.value,
                domain: domainEle.value
            });
            localStorage.setItem(configStoreKey, configStr);
            overlay.remove();
        }
        modal.onclick = function (event) {
            event.stopPropagation();
        }
        overlay.onclick = function () {
            overlay.remove();
        }
        // 将输入框和保存按钮添加到表单
        form.appendChild(domainEle);
        form.appendChild(urlEle);
        form.appendChild(pwdEle);
        form.appendChild(uuieEle);
        form.appendChild(saveButton);

        // 将表单添加到弹框中
        modal.appendChild(form);
        overlay.appendChild(modal);
        return overlay;
    }

    // 用aes对cookie进行加密
    function cookie_encrypt( uuid, password, cookies ) {
        const the_key = CryptoJS.MD5(uuid+'-'+password).toString().substring(0,16);
        const data_to_encrypt = JSON.stringify({"cookie_data":cookies,"update_time":new Date()});
        return CryptoJS.AES.encrypt(data_to_encrypt, the_key).toString();
    }

    async function getCookie(domain) {
        return new Promise((res, rej) => {
            GM_cookie.list({}, function(cookies, error) {
                if (!error) {
                    const ret_cookies = {};
                    for( const cookie of cookies ) {
                        if( cookie.domain?.includes(domain) ) {
                            if (!ret_cookies[domain]) {
                                ret_cookies[domain] = [cookie];
                            } else {
                                ret_cookies[domain].push(cookie);
                            }
                        }
                    }
                    res(ret_cookies);
                } else {
                    console.error(error);
                    rej(error)
                }
            });
        })
    }
    // 上传cookie
    async function syncCookie(url, body) {
        return new Promise((res, rej) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: url+'/update',
                data: JSON.stringify(body),
                headers: {
                    'Content-Type': 'application/json',
                },
                onload: function(response) {
                    console.log('Response:', response.responseText);
                    res(response);
                },
                onerror: function(error) {
                    console.error('Error:', error);
                    rej(error);
                }
            });
        })
    }

    init();
})();