CookieCloud

CookieCloud的tampermonkey版本,目前仅支持上传cookie,兼容移动端gear浏览器;

Od 26.09.2024.. Pogledajte najnovija verzija.

// ==UserScript==
// @name         CookieCloud
// @namespace    http://tampermonkey.net/
// @version      v0.13
// @description  CookieCloud的tampermonkey版本,目前仅支持上传cookie,兼容移动端gear浏览器;
// @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
// @grant        GM_notification
// @connect *
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js
// @run-at       document-start
// @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) {
                msg('请填写配置');
                return;
            };
            const {url, uuid, password, domain = location.host} = JSON.parse(config);
            if (!url) {
                msg('请填写服务器地址');
                return;
            };
            if (!uuid) {
                msg('请填写uuid');
                return;
            };
            if (!password) {
                msg('请填写密码');
                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') {
                    msg('同步成功')
                } else {
                    throw('错误')
                }
            } catch(e) {
                msg(String(e))
            }
        };
    }

    const msg = (function () {
        const originalAlert = window.alert;
        return (title) => {
            originalAlert(title);
        }
    })();

    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: '95%',
            maxWidth: '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',
            border: '1px solid #ddd',
            outline: 'none',
            borderRadius: '8px',
            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);
                }
            });
        })
    }

    window.addEventListener('load', init);
})();