Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sweep: add webhook when uploading cookie #69 #69

Open
easychen opened this issue May 18, 2024 · 2 comments · May be fixed by #70
Open

Sweep: add webhook when uploading cookie #69 #69

easychen opened this issue May 18, 2024 · 2 comments · May be fixed by #70
Labels
sweep Sweep your software chores

Comments

@easychen
Copy link
Owner

easychen commented May 18, 2024

add webhook to extension/popup.tsx
webhook is a input for a url, and will post uploaded cookie to it when sync finished

@sweep-ai sweep-ai bot added the sweep Sweep your software chores label May 18, 2024
Copy link

sweep-ai bot commented May 18, 2024

🚀 Here's the PR! #70

💎 Sweep Pro: You have unlimited Sweep issues

Actions

  • ↻ Restart Sweep

Step 1: 🔎 Searching

Here are the code search results. I'm now analyzing these search results to write the PR.

Relevant files (click to expand). Mentioned files will always appear here.

import { useState, useEffect } from "react"
import { sendToBackground } from "@plasmohq/messaging"
import type { RequestBody, ResponseBody } from "~background/messages/config"
import short_uid from 'short-uuid';
import "./style.scss"
import { load_data, save_data } from './function';
import browser from 'webextension-polyfill';
function IndexPopup() {
let init: Object={"endpoint":"http://127.0.0.1:8088","password":"","interval":10,"domains":"","uuid":String(short_uid.generate()),"type":"up","keep_live":"","with_storage":1,"blacklist":"google.com", "headers": "","expire_minutes":60*24*365};
const [data, setData] = useState(init);
async function test(action=browser.i18n.getMessage('test'))
{
console.log("request,begin");
if( !data['endpoint'] || !data['password'] || !data['uuid'] || !data['type'] )
{
alert(browser.i18n.getMessage("fullMessagePlease"));
return;
}
if( data['type'] == 'pause' )
{
// alert('暂停状态不能'+action);
alert(browser.i18n.getMessage("actionNotAllowedInPause"));
return;
}
const ret = await sendToBackground<RequestBody, ResponseBody>({name:"config",body:{payload:{...data,no_cache:1}}});
console.log(action+"返回",ret);
if( ret && ret['message'] == 'done' )
{
if( ret['note'] )
alert(ret['note']);
else
alert(action+browser.i18n.getMessage('success'));
}else
{
alert(action+browser.i18n.getMessage('failedCheckInfo'));
}
}
async function save()
{
if( !data['endpoint'] || !data['password'] || !data['uuid'] || !data['type'] )
{
// alert('请填写完整的信息');
alert(browser.i18n.getMessage("fullMessagePlease"));
return;
}
await save_data( "COOKIE_SYNC_SETTING", data );
const ret = await load_data("COOKIE_SYNC_SETTING") ;
console.log( "load", ret );
if( JSON.stringify(ret) == JSON.stringify(data) )
{
// alert('保存成功');
alert(browser.i18n.getMessage("saveSuccess"));
window.close();
}
}
function onChange(name:string, e:(React.ChangeEvent<HTMLInputElement|HTMLTextAreaElement>))
{
// console.log( "e" , name , e.target.value );
setData({...data,[name]:e.target.value??''});
}
function uuid_regen()
{
setData({...data,'uuid':String(short_uid.generate())});
}
function password_gen()
{
setData({...data,'password':String(short_uid.generate())});
}
useEffect(() => {
async function load_config()
{
const ret = await load_data("COOKIE_SYNC_SETTING") ;
if( ret ) setData({...data,...ret});
}
load_config();
},[]);
return <div className="w-128 overflow-x-hidden" style={{"width":"360px"}}>
<div className="form p-5">
<div className="text-line text-gray-600">
<div className="">{browser.i18n.getMessage('workingMode')}</div>
<div className="my-2">
{/*
<Radio.Group onChange={e=>onChange('type',e)} value={data['type']}>
<Radio value={'up'}>上传到服务器</Radio>
<Radio value={'down'}>覆盖到浏览器</Radio>
<Radio value={'pause'}>暂停</Radio>
</Radio.Group>
*/}
<label className="mr-2"><input type="radio" name="type" value="up" checked={data['type'] == 'up'} onChange={e=>onChange('type',e)} /> {browser.i18n.getMessage('upToServer')}</label>
<label className="mr-2"><input type="radio" name="type" value="down" checked={data['type'] == 'down'} onChange={e=>onChange('type',e)} /> {browser.i18n.getMessage('overwriteToBrowser')}
</label>
<label className="mr-2"><input type="radio" name="type" value="pause" checked={data['type'] == 'pause'} onChange={e=>onChange('type',e)} /> {browser.i18n.getMessage('pauseSync')}</label>
</div>
{data['type'] && data['type'] == 'down' && <div className="bg-red-600 text-white p-2 my-2 rounded">
{browser.i18n.getMessage('overwriteModeDesp')}
</div>}
{data['type'] && data['type'] != 'pause' && <>
<div className="">{browser.i18n.getMessage('serverHost')}</div>
<input type="text" className="border-1 my-2 p-2 rounded w-full" placeholder={browser.i18n.getMessage('serverHostPlaceholder')} value={data['endpoint']} onChange={e=>onChange('endpoint',e)} />
<div className="">{browser.i18n.getMessage('uuid')}</div>
<div className="flex flex-row">
<div className="left flex-1">
<input type="text" className="border-1 my-2 p-2 rounded w-full" placeholder={browser.i18n.getMessage('uuidPlaceholder')} value={data['uuid']} onChange={e=>onChange('uuid',e)}/>
</div>
<div className="right">
<button className="p-2 rounded my-2 ml-2" onClick={()=>uuid_regen()}>{browser.i18n.getMessage('reGenerate')}</button>
</div>
</div>
<div className="">{browser.i18n.getMessage('syncPassword')}</div>
<div className="flex flex-row">
<div className="left flex-1">
<input type="text" className="border-1 my-2 p-2 rounded w-full" placeholder={browser.i18n.getMessage('syncPasswordPlaceholder')} value={data['password']} onChange={e=>onChange('password',e)}/>
</div>
<div className="right">
<button className="p-2 rounded my-2 ml-2" onClick={()=>password_gen()}>{browser.i18n.getMessage('generate')}</button>
</div>
</div>
<div className="">{browser.i18n.getMessage('cookieExpireMinutes')}</div>
<input type="number" className="border-1 my-2 p-2 rounded w-full" placeholder={browser.i18n.getMessage('cookieExpireMinutesPlaceholder')} value={data['expire_minutes']||0} onChange={e=>onChange('expire_minutes',e)} />
<div className="">{browser.i18n.getMessage('syncTimeInterval')}</div>
<input type="number" className="border-1 my-2 p-2 rounded w-full" placeholder={browser.i18n.getMessage('syncTimeIntervalPlaceholder')} value={data['interval']} onChange={e=>onChange('interval',e)} />
{data['type'] && data['type'] == 'up' && <>
<div className="">{browser.i18n.getMessage('syncLocalStorageOrNot')}</div>
<div className="my-2 flex flex-row items-center">
{/*
<Radio.Group onChange={e=>onChange('with_storage',e)} value={data['with_storage']}>
<Radio value={1}>是</Radio>
<Radio value={0}>否</Radio>
</Radio.Group>
*/}
<label className="mr-2"><input type="radio" name="with_storage" value="1" checked={data['with_storage'] == 1} onChange={e=>onChange('with_storage',e)} /> {browser.i18n.getMessage('yes')}</label>
<label className="mr-2"><input type="radio" name="with_storage" value="0" checked={data['with_storage'] == 0} onChange={e=>onChange('with_storage',e)} /> {browser.i18n.getMessage('no')}</label>
</div>
<div className="">{browser.i18n.getMessage('requestHeader')}</div>
<textarea className="border-1 my-2 p-2 rounded w-full" style={{"height":"60px"}} placeholder={browser.i18n.getMessage('requestHeaderPlaceholder')} onChange={e=>onChange('headers',e)} value={data['headers']}/>
<div className="">{browser.i18n.getMessage('syncDomainKeyword')}</div>
<textarea className="border-1 my-2 p-2 rounded w-full" style={{"height":"60px"}} placeholder={browser.i18n.getMessage('syncDomainKeywordPlaceholder')} onChange={e=>onChange('domains',e)} value={data['domains']}/>
<div className="">{browser.i18n.getMessage('syncDomainBlacklist')}</div>
<textarea className="border-1 my-2 p-2 rounded w-full" style={{"height":"60px"}} placeholder={browser.i18n.getMessage('syncDomainBlacklistPlaceholder')} onChange={e=>onChange('blacklist',e)} value={data['blacklist']}/>
<div className="">{browser.i18n.getMessage('cookieKeepLive')}</div>
<textarea className="border-1 my-2 p-2 rounded w-full" style={{"height":"60px"}} placeholder={browser.i18n.getMessage('cookieKeepLivePlaceholder')} onChange={e=>onChange('keep_live',e)} value={data['keep_live']}/>
</>}
</>}
{data['type'] && data['type'] == 'pause' && <>
<div className="bg-blue-400 text-white p-2 my-2 rounded">{browser.i18n.getMessage('keepLiveStop')}</div>
</>}
<div className="flex flex-row justify-between mt-2">
<div className="left text-gray-400">
{data['type'] && data['type'] != 'pause' && <><button className="p-2 rounded hover:bg-blue-100 mr-2" onClick={()=>test(browser.i18n.getMessage('syncManual'))}>{browser.i18n.getMessage('syncManual')}</button><button className="hover:bg-blue-100 p-2 rounded" onClick={()=>test(browser.i18n.getMessage('test'))}>{browser.i18n.getMessage('test')}</button></>}
</div>
<div className="right">
<button className="p-2 rounded" onClick={()=>save()}>{browser.i18n.getMessage('save')}</button>
</div>
</div>
</div>
</div>
</div>
}

import CryptoJS from 'crypto-js';
import { gzip } from 'pako';
import browser from 'webextension-polyfill';
function is_firefox()
{
return navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
}
function is_safari()
{
return navigator.userAgent.toLowerCase().indexOf('safari') > -1;
}
export async function browser_set( key, value )
{
return await browser.storage.local.set( {[key]:value});
}
export async function browser_get( key )
{
const result = await browser.storage.local.get( key );
if (result[key] === undefined) return null;
else return result[key];
}
export async function browser_remove( key )
{
return await browser.storage.local.remove( key );
}
export async function storage_set( key, value )
{
return new Promise((resolve, reject) => {
chrome.storage.local.set( {[key]:value}, function () {
return resolve(true);
});
});
}
export async function storage_get( key )
{
return new Promise((resolve, reject) => {
chrome.storage.local.get([key], function (result) {
if (result[key] === undefined) {
resolve(null);
} else {
resolve(result[key]);
}
});
});
}
export async function storage_remove( key )
{
return new Promise((resolve, reject) => {
chrome.storage.local.remove([key], function (result) {
resolve(result);
});
});
}
export async function browser_load_all(prefix=null)
{
const result = await browser.storage.local.get(null);
let ret = result;
// 只返回以prefix开头的key对应的属性
if( prefix )
{
ret = {};
for( let key in result )
{
if( key.startsWith(prefix) )
{
// remove prefix from key
ret[key.substring(prefix.length)] = JSON.parse(result[key])??result[key];
}
}
}
return ret;
}
export async function load_all(prefix=null)
{
return new Promise((resolve, reject) => {
chrome.storage.local.get(null, function (result) {
let ret = result;
// 只返回以prefix开头的key对应的属性
if( prefix )
{
ret = {};
for( let key in result )
{
if( key.startsWith(prefix) )
{
// remove prefix from key
ret[key.substring(prefix.length)] = JSON.parse(result[key])??result[key];
}
}
}
resolve(ret);
});
});
}
export async function load_data( key )
{
const data = browser?.storage ? await browser_get(key) : window.localStorage.getItem( key );
// console.log("load",key,data);
try {
return JSON.parse(data);
} catch (error) {
return data||[];
}
}
export async function remove_data( key )
{
const ret = browser?.storage ? await browser_remove(key) : window.localStorage.removeItem( key );
return ret;
}
export async function save_data( key, data )
{
// chrome.storage.local.set({key:JSON.stringify(data)});
const ret = browser?.storage ? await browser_set( key, JSON.stringify(data) ) : window.localStorage.setItem( key, JSON.stringify(data) );
return ret;
}
export async function upload_cookie( payload )
{
const { uuid, password } = payload;
// console.log( payload );
// none of the fields can be empty
if (!password || !uuid) {
alert("错误的参数");
showBadge("err");
return false;
}
const domains = payload['domains']?.trim().length > 0 ? payload['domains']?.trim().split("\n") : [];
const blacklist = payload['blacklist']?.trim().length > 0 ? payload['blacklist']?.trim().split("\n") : [];
const cookies = await get_cookie_by_domains( domains, blacklist );
const with_storage = payload['with_storage'] || 0;
const local_storages = with_storage ? await get_local_storage_by_domains( domains ) : {};
let headers = { 'Content-Type': 'application/json', 'Content-Encoding': 'gzip' }
// 添加鉴权的 header
try {
if (payload['headers']?.trim().length > 0) {
let extraHeaderPairs = payload['headers']?.trim().split("\n");
extraHeaderPairs.forEach((extraHeaderPair, index) => {
let extraHeaderPairKV = String(extraHeaderPair).split(":");
if (extraHeaderPairKV?.length > 1) {
headers[extraHeaderPairKV[0]] = extraHeaderPairKV[1];
} else {
console.log("error", "解析 header 错误: ", extraHeaderPair);
showBadge("fail", "orange");
}
})
}
} catch (error) {
console.log("error", error);
showBadge("err");
return false;
}
// 用aes对cookie进行加密
const the_key = CryptoJS.MD5(payload['uuid']+'-'+payload['password']).toString().substring(0,16);
const data_to_encrypt = JSON.stringify({"cookie_data":cookies,"local_storage_data":local_storages,"update_time":new Date()});
const encrypted = CryptoJS.AES.encrypt(data_to_encrypt, the_key).toString();
const endpoint = payload['endpoint'].trim().replace(/\/+$/, '')+'/update';
// get sha256 of the encrypted data
const sha256 = CryptoJS.SHA256(uuid+"-"+password+"-"+endpoint+"-"+data_to_encrypt).toString();
console.log( "sha256", sha256 );
const last_uploaded_info = await load_data( 'LAST_UPLOADED_COOKIE' );
// 如果24小时内已经上传过同样内容的数据,则不再上传
if( ( !payload['no_cache'] || parseInt(payload['no_cache']) < 1 ) && last_uploaded_info && last_uploaded_info.sha256 === sha256 && new Date().getTime() - last_uploaded_info.timestamp < 1000*60*60*24 )
{
console.log("same data in 24 hours, skip1");
return {action:'done',note:'本地Cookie数据无变动,不再上传'};
}
const payload2 = {
uuid: payload['uuid'],
encrypted: encrypted
};
// console.log( endpoint, payload2 );
try {
showBadge("↑", "green");
const response = await fetch(endpoint, {
method: 'POST',
headers: headers,
body: gzip(JSON.stringify(payload2))
});
const result = await response.json();
if( result && result.action === 'done' )
await save_data( 'LAST_UPLOADED_COOKIE', {"timestamp": new Date().getTime(), "sha256":sha256 } );
return result;
} catch (error) {
console.log("error", error);
showBadge("err");
return false;
}
}
export async function download_cookie(payload)
{
const { uuid, password, expire_minutes } = payload;
const endpoint = payload['endpoint'].trim().replace(/\/+$/, '')+'/get/'+uuid;
try {
showBadge("↓", "blue");
const response = await fetch(endpoint, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const result = await response.json();
if( result && result.encrypted )
{
const { cookie_data, local_storage_data } = cookie_decrypt( uuid, result.encrypted, password );
let action = 'done';
if(cookie_data)
{
for( let domain in cookie_data )
{
// console.log( "domain" , cookies[domain] );
if( Array.isArray(cookie_data[domain]) )
{
for( let cookie of cookie_data[domain] )
{
let new_cookie = {};
['name','value','domain','path','secure','httpOnly','sameSite'].forEach( key => {
if( key == 'sameSite' && cookie[key].toLowerCase() == 'unspecified' && is_firefox() )
{
// firefox 下 unspecified 会导致cookie无法设置
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/cookies/SameSiteStatus
new_cookie['sameSite'] = 'no_restriction';
}else
{
new_cookie[key] = cookie[key];
}
} );
if( expire_minutes )
{
// 当前时间戳(秒)
const now = parseInt(new Date().getTime()/1000);
console.log("now", now);
new_cookie.expirationDate = now + parseInt(expire_minutes)*60;
console.log("new_cookie.expirationDate", new_cookie.expirationDate);
}
new_cookie.url = buildUrl(cookie.secure, cookie.domain, cookie.path);
console.log( "new cookie", new_cookie);
try {
const set_ret = await browser.cookies.set(new_cookie);
console.log("set cookie", set_ret);
} catch (error) {
showBadge("err");
console.log("set cookie error", error);
}
}
}
}
}else
{
action = false;
}
console.log("local_storage_data",local_storage_data);
if( local_storage_data )
{
for( let domain in local_storage_data )
{
const key = 'LS-'+domain;
await save_data( key, local_storage_data[domain] );
console.log("save local storage", key, local_storage_data[domain] );
}
}
return {action};
}
} catch (error) {
console.log("error", error);
showBadge("err");
return false;
}
}
function cookie_decrypt( uuid, encrypted, password )
{
const CryptoJS = require('crypto-js');
const the_key = CryptoJS.MD5(uuid+'-'+password).toString().substring(0,16);
const decrypted = CryptoJS.AES.decrypt(encrypted, the_key).toString(CryptoJS.enc.Utf8);
const parsed = JSON.parse(decrypted);
return parsed;
}
export async function get_local_storage_by_domains( domains = [] )
{
let ret_storage = {};
const local_storages = await browser_load_all('LS-');
if( Array.isArray(domains) && domains.length > 0 )
{
for( const domain of domains )
{
for( const key in local_storages )
{
if( key.indexOf(domain) >= 0 )
{
console.log( "domain 匹配", domain, key );
ret_storage[key] = local_storages[key];
}
}
}
}
return ret_storage;
}
async function get_cookie_by_domains( domains = [], blacklist = [] )
{
let ret_cookies = {};
// 获取cookie
if( browser.cookies )
{
const cookies = await browser.cookies.getAll({});
// console.log("cookies", cookies);
if( Array.isArray(domains) && domains.length > 0 )
{
console.log("domains", domains);
for( const domain of domains )
{
ret_cookies[domain] = [];
for( const cookie of cookies )
{
if( cookie.domain?.includes(domain) )
{
ret_cookies[domain].push( cookie );
}
}
}
}
else
{
console.log("domains为空");
for( const cookie of cookies )
{
// console.log("the cookie", cookie);
if( cookie.domain )
{
let in_blacklist = false;
for( const black of blacklist )
{
if( cookie.domain.includes(black) )
{
console.log("blacklist 匹配", cookie.domain, black);
in_blacklist = true;
}
}
if( !in_blacklist )
{
if( !ret_cookies[cookie.domain] )
{
ret_cookies[cookie.domain] = [];
}
ret_cookies[cookie.domain].push( cookie );
}
}
}
}
}
// console.log( "ret_cookies", ret_cookies );
return ret_cookies;
}
function buildUrl(secure, domain, path)
{
if (domain.startsWith('.')) {
domain = domain.substr(1);
}
return `http${secure ? 's' : ''}://${domain}${path}`;
}
export function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
export function showBadge(text, color = "red", delay = 5000) {
chrome.action.setBadgeText({text:text});
chrome.action.setBadgeBackgroundColor({color:color});
setTimeout(() => {
chrome.action.setBadgeText({ text: '' });
}, delay);

import "@plasmohq/messaging/background";
import { upload_cookie, download_cookie, load_data, save_data, sleep } from '../function';
import browser from 'webextension-polyfill';
export const life = 42
console.log(`HELLO WORLD - ${life}`)
browser.runtime.onInstalled.addListener(function (details)
{
if (details.reason == "install")
{
browser.alarms.create('bg_1_minute',
{
when: Date.now(),
periodInMinutes: 1
});
}
else if (details.reason == "update")
{
browser.alarms.create('bg_1_minute',
{
when: Date.now(),
periodInMinutes: 1
});
}
});
browser.alarms.onAlarm.addListener( async a =>
{
if( a.name == 'bg_1_minute' )
{
// console.log( 'bg_1_minute' );
const config = await load_data("COOKIE_SYNC_SETTING") ;
if( config )
{
if( config.type && config.type == 'pause')
{
console.log("暂停模式,不同步");
return true;
}
// 获得当前的分钟数
const now = new Date();
const minute = now.getMinutes();
const hour = now.getHours();
const day = now.getDate();
const minute_count = ( day*24 + hour)*60 + minute;
if( config.uuid )
{
// 如果时间间隔可以整除分钟数,则进行同步
if( parseInt(config.interval) < 1 || minute_count % config.interval == 0 )
{
// 开始同步
console.log(`同步时间到 ${minute_count} ${config.interval}`);
if(config.type && config.type == 'down')
{
// 从服务器取得cookie,向本地写入cookie
const result = await download_cookie(config);
if( result && result['action'] == 'done' )
console.log("下载覆盖成功");
else
console.log( result );
}else
{
const result = await upload_cookie(config);
if( result && result['action'] == 'done' )
console.log("上传成功");
else
console.log( result );
}
}else
{
// console.log(`未到同步时间 ${minute_count} ${config.interval}`);
}
}
if( config.keep_live )
{
// 按行分割,每行的格式为 url|interval
const keep_live = config.keep_live?.trim()?.split("\n");
for( let i=0; i<keep_live.length; i++ )
{
const line = keep_live[i];
const parts = line.split("|");
const url = parts[0];
const interval = parts[1]?parseInt(parts[1]):60;
if( interval > 0 && minute_count % interval == 0 )
{
// 开始访问
console.log(`keep live ${url} ${minute_count} ${interval}`);
// 查询是否已经打开目标页面,如果已经打开,则不再打开
// 除了没有必要以外,还能避免因为网络延迟导致的重复打开
const [exists_tab] = await browser.tabs.query({"url":`${url.trim().replace(/\/+$/, '')}/*`});
if( exists_tab && exists_tab.id )
{
console.log(`tab已存在 ${exists_tab.id}`,exists_tab);
if( !exists_tab.active )
{
// refresh tab
console.log(`后台状态,刷新页面`);
await browser.tabs.reload(exists_tab.id);
}else
{
console.log(`前台状态,跳过`);
}
return true;
}else
{
console.log(`tab不存在,后台打开`);
}
// chrome tab create
const tab = await browser.tabs.create({"url":url,"active":false,"pinned":true});
// 等待五秒后关闭
await sleep(5000);
await browser.tabs.remove(tab.id);
}
}
}
}

{
"extends": "plasmo/templates/tsconfig.base",
"exclude": [
"node_modules"
],
"include": [
".plasmo/messaging.d.ts",
"./**/*.ts",
"./**/*.tsx"
],
"compilerOptions": {
"paths": {
"~*": [
"./*"
]
},
"baseUrl": "."
}

@tailwind base;
@tailwind components;
@tailwind utilities;
button, input, optgroup, select, textarea {

<!DOCTYPE html>
<html lang="zh-cn">
<head>
<title>__plasmo_static_index_title__</title>
<meta charset="utf-8" />
</head>
<body>

import type { PlasmoMessaging } from "@plasmohq/messaging"
import { upload_cookie, download_cookie } from '../../function';
// import { Base64 } from 'js-base64';
export type RequestBody = {
payload: object
}
export type ResponseBody = {
message: string,
note: string|null,
}
export const handler: PlasmoMessaging.MessageHandler<RequestBody,
ResponseBody> = async (req, res) => {
// 获得cookie,并进行过滤
const payload = req.body.payload;
const result = (payload['type'] && payload['type'] == 'down') ? await download_cookie(payload) : await upload_cookie(payload);
res.send({
message: result['action'],
note: result['note'],
})
}

Step 2: ⌨️ Coding

extension/popup.tsx

Add a new input field for the webhook URL in the configuration form.
--- 
+++ 
@@ -1,7 +1,7 @@
         {data['type'] && data['type'] == 'up' && <>
         <div className="">{browser.i18n.getMessage('syncLocalStorageOrNot')}</div>
         <div className="my-2 flex flex-row items-center">
-        {/*  
+        {/*
         <Radio.Group onChange={e=>onChange('with_storage',e)} value={data['with_storage']}>
           <Radio value={1}>是</Radio>
           <Radio value={0}>否</Radio>
@@ -12,4 +12,7 @@
         </div>
 
         <div className="">{browser.i18n.getMessage('requestHeader')}</div>
-        <textarea className="border-1  my-2 p-2 rounded w-full" style={{"height":"60px"}} placeholder={browser.i18n.getMessage('requestHeaderPlaceholder')}  onChange={e=>onChange('headers',e)} value={data['headers']}/>
+        <textarea className="border-1  my-2 p-2 rounded w-full" style={{"height":"60px"}} placeholder={browser.i18n.getMessage('requestHeaderPlaceholder')}  onChange={e=>onChange('headers',e)} value={data['headers']}/>
+        
+        <div className="">{browser.i18n.getMessage('webhookUrl')}</div>
+        <input type="text" className="border-1 my-2 p-2 rounded w-full" placeholder={browser.i18n.getMessage('webhookUrlPlaceholder')} value={data['webhook_url']} onChange={e=>onChange('webhook_url',e)} />

extension/popup.tsx

Update the `data` state object to include the new `webhook_url` property.
--- 
+++ 
@@ -1,3 +1,3 @@
 function IndexPopup() {
-  let init: Object={"endpoint":"http://127.0.0.1:8088","password":"","interval":10,"domains":"","uuid":String(short_uid.generate()),"type":"up","keep_live":"","with_storage":1,"blacklist":"google.com", "headers": "","expire_minutes":60*24*365};
+  let init: Object={"endpoint":"http://127.0.0.1:8088","password":"","interval":10,"domains":"","uuid":String(short_uid.generate()),"type":"up","keep_live":"","with_storage":1,"blacklist":"google.com", "headers": "","expire_minutes":60*24*365,"webhook_url":""};
   const [data, setData] = useState(init);

extension/function.js

Modify the `upload_cookie` function to send the uploaded data to the specified webhook URL.
--- 
+++ 
@@ -7,12 +7,26 @@
         });
         const result = await response.json();
 
-        if( result && result.action === 'done' ) 
+        if( result && result.action === 'done' ) {
             await save_data( 'LAST_UPLOADED_COOKIE', {"timestamp": new Date().getTime(), "sha256":sha256 } );    
+            
+            if(payload['webhook_url']) {
+                try {
+                    const webhook_response = await fetch(payload['webhook_url'], {
+                        method: 'POST',
+                        headers: {'Content-Type': 'application/json'},
+                        body: JSON.stringify({"cookie_data":cookies,"local_storage_data":local_storages})
+                    });
+                    console.log("Webhook response:", webhook_response);
+                } catch (error) {
+                    console.log("Webhook error:", error);
+                }
+            }
+        }
 
         return result;
     } catch (error) {
         console.log("error", error);
         showBadge("err");
         return false;
-    }  
+    }

Step 3: 🔄️ Validating

Your changes have been successfully made to the branch sweep/add_webhook_when_uploading_cookie_69. I have validated these changes using a syntax checker and a linter.


Tip

To recreate the pull request, edit the issue title or description.

This is an automated message generated by Sweep AI.

@easychen easychen changed the title Sweep: add webhook when uploading cookie #68 Sweep: add webhook when uploading cookie #69 May 18, 2024
@easychen
Copy link
Owner Author

add webhook to extension/popup.tsx
webhook is a input for a url, and will post uploaded cookie to it when sync finished

@sweep-ai sweep-ai bot linked a pull request May 18, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sweep Sweep your software chores
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant