mirror of
https://github.com/easychen/checkchan-dist.git
synced 2026-08-28 17:45:05 +08:00
添加多浏览器Cookie同步
This commit is contained in:
@@ -12,9 +12,9 @@
|
||||
|
||||
## 最新版本
|
||||
|
||||
- 插件·Chrome/Edge:2022.08.17.13.51 [下载](ckc.zip) ([Github下载地址,无需注册](https://github.com/easychen/checkchan-dist/raw/master/ckc.zip))
|
||||
- Docker镜像(云端+远程桌面二合一):2022.08.17.12.05 [Docker Hub](https://hub.docker.com/repository/docker/easychen/checkchan)
|
||||
- 文档:2022.08.17.13.51
|
||||
- 插件·Chrome/Edge:2022.08.17.20.32 [下载](ckc.zip) ([Github下载地址,无需注册](https://github.com/easychen/checkchan-dist/raw/master/ckc.zip))
|
||||
- Docker镜像(云端+远程桌面二合一):2022.08.17.19.39 [Docker Hub](https://hub.docker.com/repository/docker/easychen/checkchan)
|
||||
- 文档:2022.08.17.20.32
|
||||
- 更新日志:[GitHub](https://github.com/easychen/checkchan-dist/commits/main)
|
||||
- 生态:RSSHub浏览器插件整合版([源码](https://github.com/easychen/RSSHub-Radar-with-checkchan) | [下载](https://github.com/easychen/RSSHub-Radar-with-checkchan/releases/download/main/1.7.0.with-checkchan.zip))
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
"2022.08.17.12.05"
|
||||
"2022.08.17.19.39"
|
||||
@@ -50,6 +50,36 @@ app.post( `/rss/upload`, checkApiKey, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.post( `/cookie/sync`, checkApiKey, (req, res) => {
|
||||
if( req.body.direction && req.body.direction == 'down' ) {
|
||||
// 下行cookie
|
||||
const user_name = req.body.password || "own";
|
||||
let cookies_file = `${image_dir}/cookies.${user_name}.base64.txt`;
|
||||
const cookies_64 = fs.readFileSync(cookies_file, 'utf8');
|
||||
res.json({"code":0,"data":cookies_64});
|
||||
}else
|
||||
{
|
||||
// 上行cookie
|
||||
if( req.body.cookies_base64 )
|
||||
{
|
||||
let cookies_base64 = req.body.cookies_base64;
|
||||
const user_name = req.body.password || "own";
|
||||
let cookies_file = `${image_dir}/cookies.${user_name}.base64.txt`;
|
||||
fs.writeFileSync(cookies_file,cookies_base64);
|
||||
res.json({"code":0,"message":"保存成功"});
|
||||
}else
|
||||
{
|
||||
res.json({"code":-1,"message":"cookie参数不能为空"});
|
||||
}
|
||||
|
||||
|
||||
// let cookies = Buffer.from(req.body.cookies_base64,'base64').toString();
|
||||
|
||||
}
|
||||
|
||||
// res.json(req.body);
|
||||
});
|
||||
|
||||
app.post(`/checks/upload`, checkApiKey , (req, res) => {
|
||||
const data = { checks: JSON.parse(req.body.checks)||[], cookies: JSON.parse(req.body.cookies) ||{} };
|
||||
const data_file = get_data_dir()+'/data.json';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"message": "Check酱"
|
||||
},
|
||||
"appDesc": {
|
||||
"message": "监控页面变动,并发送异动到微信。 Build.2022.08.17.13.51",
|
||||
"message": "监控页面变动,并发送异动到微信。 Build.2022.08.17.20.32",
|
||||
"description":"亦支持http status、json和rss监测。配合自架云端,关电脑后也能运行。"
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.f2766b2a.chunk.css",
|
||||
"main.js": "/static/js/main.2411968d.chunk.js",
|
||||
"main.js": "/static/js/main.e0ceed25.chunk.js",
|
||||
"runtime-main.js": "/static/js/runtime-main.17191aad.js",
|
||||
"static/css/2.65280f5c.chunk.css": "/static/css/2.65280f5c.chunk.css",
|
||||
"static/js/2.f4e71d54.chunk.js": "/static/js/2.f4e71d54.chunk.js",
|
||||
"static/js/2.fb5c1c43.chunk.js": "/static/js/2.fb5c1c43.chunk.js",
|
||||
"index.html": "/index.html",
|
||||
"precache-manifest.ef4e4009a9308c36ee12ad207daef55e.js": "/precache-manifest.ef4e4009a9308c36ee12ad207daef55e.js",
|
||||
"precache-manifest.d081260a9ad335232527f33321ad446e.js": "/precache-manifest.d081260a9ad335232527f33321ad446e.js",
|
||||
"service-worker.js": "/service-worker.js",
|
||||
"static/media/index.scss": "/static/media/icons-20.cef8cdbb.woff"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/runtime-main.17191aad.js",
|
||||
"static/css/2.65280f5c.chunk.css",
|
||||
"static/js/2.f4e71d54.chunk.js",
|
||||
"static/js/2.fb5c1c43.chunk.js",
|
||||
"static/css/main.f2766b2a.chunk.css",
|
||||
"static/js/main.2411968d.chunk.js"
|
||||
"static/js/main.e0ceed25.chunk.js"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// import html2canvas from './html2canvas.esm.js';
|
||||
|
||||
import { Base64 } from './base64.mjs';
|
||||
|
||||
let inspector = {};
|
||||
console.log( "load bg.js" );
|
||||
@@ -283,6 +283,12 @@ chrome.runtime.onInstalled.addListener(function (details)
|
||||
periodInMinutes: 1
|
||||
});
|
||||
|
||||
chrome.alarms.create('cookie_sync',
|
||||
{
|
||||
when: Date.now(),
|
||||
periodInMinutes: 1
|
||||
});
|
||||
|
||||
// chrome.alarms.create('auto_sync',
|
||||
// {
|
||||
// when: Date.now(),
|
||||
@@ -292,7 +298,7 @@ chrome.runtime.onInstalled.addListener(function (details)
|
||||
chrome.alarms.create('bg_cookie_sync',
|
||||
{
|
||||
when: Date.now(),
|
||||
periodInMinutes: 61
|
||||
periodInMinutes: 11
|
||||
});
|
||||
|
||||
console.log("create alarms");
|
||||
@@ -405,6 +411,95 @@ function sleep(ms) {
|
||||
|
||||
chrome.alarms.onAlarm.addListener( async a =>
|
||||
{
|
||||
if( a.name == 'cookie_sync' )
|
||||
{
|
||||
const settings = await kv_load('settings');
|
||||
|
||||
// 如果没有配置上行URL,则不同步cookie
|
||||
if( !settings._settings_cookie_sync_url ) return false;
|
||||
|
||||
// 每分钟检查,如果没有到设置的时间,则不同步cookie
|
||||
const interval = settings._settings_cookie_sync_interval || 5;
|
||||
const minute = new Date().getMinutes();
|
||||
if( minute % interval != 0 ) return false;
|
||||
|
||||
if( settings._settings_cookie_sync_direction == 'up' )
|
||||
{
|
||||
// 获得cookie
|
||||
const checks = await load_data('checks');
|
||||
const cookies = await get_cookie_by_checks( checks ) || [];
|
||||
|
||||
// 引入base64 和 aes 库
|
||||
let cookie_string = Base64.encode(JSON.stringify(cookies));
|
||||
|
||||
console.log("cookie_string", cookie_string);
|
||||
const form = new FormData();
|
||||
form.append( 'cookies_base64',cookie_string );
|
||||
form.append( 'direction', settings._settings_cookie_sync_direction );
|
||||
form.append( 'password', settings._settings_cookie_sync_password );
|
||||
try {
|
||||
const response = await fetch( settings._settings_cookie_sync_url, {
|
||||
method: 'POST',
|
||||
body: form
|
||||
} );
|
||||
|
||||
const ret = await response.json();
|
||||
console.log( "ret", ret );
|
||||
return ret;
|
||||
|
||||
} catch (error) {
|
||||
console.log("请求服务器失败。"+error);
|
||||
return false;
|
||||
}
|
||||
}else
|
||||
{
|
||||
// Cookie 下行
|
||||
const form = new FormData();
|
||||
form.append( 'direction', settings._settings_cookie_sync_direction );
|
||||
form.append( 'password', settings._settings_cookie_sync_password );
|
||||
try {
|
||||
const response = await fetch( settings._settings_cookie_sync_url, {
|
||||
method: 'POST',
|
||||
body: form
|
||||
} );
|
||||
|
||||
const ret = await response.json();
|
||||
if( ret.code == 0 && ret.data )
|
||||
{
|
||||
const cookies = JSON.parse(Base64.decode(ret.data));
|
||||
// set cookies to browser
|
||||
for( let domain in cookies )
|
||||
{
|
||||
// console.log( "domain" , cookies[domain] );
|
||||
if( Array.isArray(cookies[domain]) )
|
||||
{
|
||||
for( let cookie of cookies[domain] )
|
||||
{
|
||||
let newcookie = {};
|
||||
['name','value','domain','path','secure','httpOnly','sameSite'].forEach( key => {
|
||||
newcookie[key] = cookie[key];
|
||||
} );
|
||||
newcookie.url = buildUrl(cookie.secure, cookie.domain, cookie.path);
|
||||
// console.log(newcookie);
|
||||
chrome.cookies.set(newcookie, (e)=>{console.log(e)});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.log( "ret", ret );
|
||||
return ret;
|
||||
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.log("请求服务器失败。"+error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if( a.name == 'bg_cookie_sync' )
|
||||
{
|
||||
console.log( 'bg_cookie_sync' );
|
||||
@@ -481,9 +576,15 @@ async function get_cookie_by_checks( checks )
|
||||
for( const item of checks )
|
||||
{
|
||||
// console.log( "item", item );
|
||||
const domain = new URL( item.url ).host;
|
||||
if( !domains.includes( domain ) )
|
||||
domains.push( domain );
|
||||
if( !item.url ) continue;
|
||||
try {
|
||||
const domain = new URL( item.url ).host;
|
||||
if( !domains.includes( domain ) )
|
||||
domains.push( domain );
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// console.log( domains );
|
||||
for( const domain of domains )
|
||||
@@ -544,4 +645,11 @@ async function save_data( data, key = 'checks')
|
||||
// chrome.storage.local.set({key:JSON.stringify(data)});
|
||||
const ret = chrome?.storage ? await storage_set( key, JSON.stringify(data) ) : window.localStorage.setItem( key, JSON.stringify(data) );
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
function buildUrl(secure, domain, path) {
|
||||
if (domain.startsWith('.')) {
|
||||
domain = domain.substr(1);
|
||||
}
|
||||
return `http${secure ? 's' : ''}://${domain}${path}`;
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
//
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED! DO NOT EDIT BY HAND!
|
||||
//
|
||||
;
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined'
|
||||
? module.exports = factory()
|
||||
: typeof define === 'function' && define.amd
|
||||
? define(factory) :
|
||||
// cf. https://github.com/dankogai/js-base64/issues/119
|
||||
(function () {
|
||||
// existing version for noConflict()
|
||||
var _Base64 = global.Base64;
|
||||
var gBase64 = factory();
|
||||
gBase64.noConflict = function () {
|
||||
global.Base64 = _Base64;
|
||||
return gBase64;
|
||||
};
|
||||
if (global.Meteor) { // Meteor.js
|
||||
Base64 = gBase64;
|
||||
}
|
||||
global.Base64 = gBase64;
|
||||
})();
|
||||
}((typeof self !== 'undefined' ? self
|
||||
: typeof window !== 'undefined' ? window
|
||||
: typeof global !== 'undefined' ? global
|
||||
: this), function () {
|
||||
'use strict';
|
||||
/**
|
||||
* base64.ts
|
||||
*
|
||||
* Licensed under the BSD 3-Clause License.
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* References:
|
||||
* http://en.wikipedia.org/wiki/Base64
|
||||
*
|
||||
* @author Dan Kogai (https://github.com/dankogai)
|
||||
*/
|
||||
var version = '3.7.2';
|
||||
/**
|
||||
* @deprecated use lowercase `version`.
|
||||
*/
|
||||
var VERSION = version;
|
||||
var _hasatob = typeof atob === 'function';
|
||||
var _hasbtoa = typeof btoa === 'function';
|
||||
var _hasBuffer = typeof Buffer === 'function';
|
||||
var _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
|
||||
var _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
|
||||
var b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
var b64chs = Array.prototype.slice.call(b64ch);
|
||||
var b64tab = (function (a) {
|
||||
var tab = {};
|
||||
a.forEach(function (c, i) { return tab[c] = i; });
|
||||
return tab;
|
||||
})(b64chs);
|
||||
var b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
|
||||
var _fromCC = String.fromCharCode.bind(String);
|
||||
var _U8Afrom = typeof Uint8Array.from === 'function'
|
||||
? Uint8Array.from.bind(Uint8Array)
|
||||
: function (it, fn) {
|
||||
if (fn === void 0) { fn = function (x) { return x; }; }
|
||||
return new Uint8Array(Array.prototype.slice.call(it, 0).map(fn));
|
||||
};
|
||||
var _mkUriSafe = function (src) { return src
|
||||
.replace(/=/g, '').replace(/[+\/]/g, function (m0) { return m0 == '+' ? '-' : '_'; }); };
|
||||
var _tidyB64 = function (s) { return s.replace(/[^A-Za-z0-9\+\/]/g, ''); };
|
||||
/**
|
||||
* polyfill version of `btoa`
|
||||
*/
|
||||
var btoaPolyfill = function (bin) {
|
||||
// console.log('polyfilled');
|
||||
var u32, c0, c1, c2, asc = '';
|
||||
var pad = bin.length % 3;
|
||||
for (var i = 0; i < bin.length;) {
|
||||
if ((c0 = bin.charCodeAt(i++)) > 255 ||
|
||||
(c1 = bin.charCodeAt(i++)) > 255 ||
|
||||
(c2 = bin.charCodeAt(i++)) > 255)
|
||||
throw new TypeError('invalid character found');
|
||||
u32 = (c0 << 16) | (c1 << 8) | c2;
|
||||
asc += b64chs[u32 >> 18 & 63]
|
||||
+ b64chs[u32 >> 12 & 63]
|
||||
+ b64chs[u32 >> 6 & 63]
|
||||
+ b64chs[u32 & 63];
|
||||
}
|
||||
return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
|
||||
};
|
||||
/**
|
||||
* does what `window.btoa` of web browsers do.
|
||||
* @param {String} bin binary string
|
||||
* @returns {string} Base64-encoded string
|
||||
*/
|
||||
var _btoa = _hasbtoa ? function (bin) { return btoa(bin); }
|
||||
: _hasBuffer ? function (bin) { return Buffer.from(bin, 'binary').toString('base64'); }
|
||||
: btoaPolyfill;
|
||||
var _fromUint8Array = _hasBuffer
|
||||
? function (u8a) { return Buffer.from(u8a).toString('base64'); }
|
||||
: function (u8a) {
|
||||
// cf. https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326
|
||||
var maxargs = 0x1000;
|
||||
var strs = [];
|
||||
for (var i = 0, l = u8a.length; i < l; i += maxargs) {
|
||||
strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
|
||||
}
|
||||
return _btoa(strs.join(''));
|
||||
};
|
||||
/**
|
||||
* converts a Uint8Array to a Base64 string.
|
||||
* @param {boolean} [urlsafe] URL-and-filename-safe a la RFC4648 §5
|
||||
* @returns {string} Base64 string
|
||||
*/
|
||||
var fromUint8Array = function (u8a, urlsafe) {
|
||||
if (urlsafe === void 0) { urlsafe = false; }
|
||||
return urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a);
|
||||
};
|
||||
// This trick is found broken https://github.com/dankogai/js-base64/issues/130
|
||||
// const utob = (src: string) => unescape(encodeURIComponent(src));
|
||||
// reverting good old fationed regexp
|
||||
var cb_utob = function (c) {
|
||||
if (c.length < 2) {
|
||||
var cc = c.charCodeAt(0);
|
||||
return cc < 0x80 ? c
|
||||
: cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6))
|
||||
+ _fromCC(0x80 | (cc & 0x3f)))
|
||||
: (_fromCC(0xe0 | ((cc >>> 12) & 0x0f))
|
||||
+ _fromCC(0x80 | ((cc >>> 6) & 0x3f))
|
||||
+ _fromCC(0x80 | (cc & 0x3f)));
|
||||
}
|
||||
else {
|
||||
var cc = 0x10000
|
||||
+ (c.charCodeAt(0) - 0xD800) * 0x400
|
||||
+ (c.charCodeAt(1) - 0xDC00);
|
||||
return (_fromCC(0xf0 | ((cc >>> 18) & 0x07))
|
||||
+ _fromCC(0x80 | ((cc >>> 12) & 0x3f))
|
||||
+ _fromCC(0x80 | ((cc >>> 6) & 0x3f))
|
||||
+ _fromCC(0x80 | (cc & 0x3f)));
|
||||
}
|
||||
};
|
||||
var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
|
||||
/**
|
||||
* @deprecated should have been internal use only.
|
||||
* @param {string} src UTF-8 string
|
||||
* @returns {string} UTF-16 string
|
||||
*/
|
||||
var utob = function (u) { return u.replace(re_utob, cb_utob); };
|
||||
//
|
||||
var _encode = _hasBuffer
|
||||
? function (s) { return Buffer.from(s, 'utf8').toString('base64'); }
|
||||
: _TE
|
||||
? function (s) { return _fromUint8Array(_TE.encode(s)); }
|
||||
: function (s) { return _btoa(utob(s)); };
|
||||
/**
|
||||
* converts a UTF-8-encoded string to a Base64 string.
|
||||
* @param {boolean} [urlsafe] if `true` make the result URL-safe
|
||||
* @returns {string} Base64 string
|
||||
*/
|
||||
var encode = function (src, urlsafe) {
|
||||
if (urlsafe === void 0) { urlsafe = false; }
|
||||
return urlsafe
|
||||
? _mkUriSafe(_encode(src))
|
||||
: _encode(src);
|
||||
};
|
||||
/**
|
||||
* converts a UTF-8-encoded string to URL-safe Base64 RFC4648 §5.
|
||||
* @returns {string} Base64 string
|
||||
*/
|
||||
var encodeURI = function (src) { return encode(src, true); };
|
||||
// This trick is found broken https://github.com/dankogai/js-base64/issues/130
|
||||
// const btou = (src: string) => decodeURIComponent(escape(src));
|
||||
// reverting good old fationed regexp
|
||||
var re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
|
||||
var cb_btou = function (cccc) {
|
||||
switch (cccc.length) {
|
||||
case 4:
|
||||
var cp = ((0x07 & cccc.charCodeAt(0)) << 18)
|
||||
| ((0x3f & cccc.charCodeAt(1)) << 12)
|
||||
| ((0x3f & cccc.charCodeAt(2)) << 6)
|
||||
| (0x3f & cccc.charCodeAt(3)), offset = cp - 0x10000;
|
||||
return (_fromCC((offset >>> 10) + 0xD800)
|
||||
+ _fromCC((offset & 0x3FF) + 0xDC00));
|
||||
case 3:
|
||||
return _fromCC(((0x0f & cccc.charCodeAt(0)) << 12)
|
||||
| ((0x3f & cccc.charCodeAt(1)) << 6)
|
||||
| (0x3f & cccc.charCodeAt(2)));
|
||||
default:
|
||||
return _fromCC(((0x1f & cccc.charCodeAt(0)) << 6)
|
||||
| (0x3f & cccc.charCodeAt(1)));
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @deprecated should have been internal use only.
|
||||
* @param {string} src UTF-16 string
|
||||
* @returns {string} UTF-8 string
|
||||
*/
|
||||
var btou = function (b) { return b.replace(re_btou, cb_btou); };
|
||||
/**
|
||||
* polyfill version of `atob`
|
||||
*/
|
||||
var atobPolyfill = function (asc) {
|
||||
// console.log('polyfilled');
|
||||
asc = asc.replace(/\s+/g, '');
|
||||
if (!b64re.test(asc))
|
||||
throw new TypeError('malformed base64.');
|
||||
asc += '=='.slice(2 - (asc.length & 3));
|
||||
var u24, bin = '', r1, r2;
|
||||
for (var i = 0; i < asc.length;) {
|
||||
u24 = b64tab[asc.charAt(i++)] << 18
|
||||
| b64tab[asc.charAt(i++)] << 12
|
||||
| (r1 = b64tab[asc.charAt(i++)]) << 6
|
||||
| (r2 = b64tab[asc.charAt(i++)]);
|
||||
bin += r1 === 64 ? _fromCC(u24 >> 16 & 255)
|
||||
: r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255)
|
||||
: _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
|
||||
}
|
||||
return bin;
|
||||
};
|
||||
/**
|
||||
* does what `window.atob` of web browsers do.
|
||||
* @param {String} asc Base64-encoded string
|
||||
* @returns {string} binary string
|
||||
*/
|
||||
var _atob = _hasatob ? function (asc) { return atob(_tidyB64(asc)); }
|
||||
: _hasBuffer ? function (asc) { return Buffer.from(asc, 'base64').toString('binary'); }
|
||||
: atobPolyfill;
|
||||
//
|
||||
var _toUint8Array = _hasBuffer
|
||||
? function (a) { return _U8Afrom(Buffer.from(a, 'base64')); }
|
||||
: function (a) { return _U8Afrom(_atob(a), function (c) { return c.charCodeAt(0); }); };
|
||||
/**
|
||||
* converts a Base64 string to a Uint8Array.
|
||||
*/
|
||||
var toUint8Array = function (a) { return _toUint8Array(_unURI(a)); };
|
||||
//
|
||||
var _decode = _hasBuffer
|
||||
? function (a) { return Buffer.from(a, 'base64').toString('utf8'); }
|
||||
: _TD
|
||||
? function (a) { return _TD.decode(_toUint8Array(a)); }
|
||||
: function (a) { return btou(_atob(a)); };
|
||||
var _unURI = function (a) { return _tidyB64(a.replace(/[-_]/g, function (m0) { return m0 == '-' ? '+' : '/'; })); };
|
||||
/**
|
||||
* converts a Base64 string to a UTF-8 string.
|
||||
* @param {String} src Base64 string. Both normal and URL-safe are supported
|
||||
* @returns {string} UTF-8 string
|
||||
*/
|
||||
var decode = function (src) { return _decode(_unURI(src)); };
|
||||
/**
|
||||
* check if a value is a valid Base64 string
|
||||
* @param {String} src a value to check
|
||||
*/
|
||||
var isValid = function (src) {
|
||||
if (typeof src !== 'string')
|
||||
return false;
|
||||
var s = src.replace(/\s+/g, '').replace(/={0,2}$/, '');
|
||||
return !/[^\s0-9a-zA-Z\+/]/.test(s) || !/[^\s0-9a-zA-Z\-_]/.test(s);
|
||||
};
|
||||
//
|
||||
var _noEnum = function (v) {
|
||||
return {
|
||||
value: v, enumerable: false, writable: true, configurable: true
|
||||
};
|
||||
};
|
||||
/**
|
||||
* extend String.prototype with relevant methods
|
||||
*/
|
||||
var extendString = function () {
|
||||
var _add = function (name, body) { return Object.defineProperty(String.prototype, name, _noEnum(body)); };
|
||||
_add('fromBase64', function () { return decode(this); });
|
||||
_add('toBase64', function (urlsafe) { return encode(this, urlsafe); });
|
||||
_add('toBase64URI', function () { return encode(this, true); });
|
||||
_add('toBase64URL', function () { return encode(this, true); });
|
||||
_add('toUint8Array', function () { return toUint8Array(this); });
|
||||
};
|
||||
/**
|
||||
* extend Uint8Array.prototype with relevant methods
|
||||
*/
|
||||
var extendUint8Array = function () {
|
||||
var _add = function (name, body) { return Object.defineProperty(Uint8Array.prototype, name, _noEnum(body)); };
|
||||
_add('toBase64', function (urlsafe) { return fromUint8Array(this, urlsafe); });
|
||||
_add('toBase64URI', function () { return fromUint8Array(this, true); });
|
||||
_add('toBase64URL', function () { return fromUint8Array(this, true); });
|
||||
};
|
||||
/**
|
||||
* extend Builtin prototypes with relevant methods
|
||||
*/
|
||||
var extendBuiltins = function () {
|
||||
extendString();
|
||||
extendUint8Array();
|
||||
};
|
||||
var gBase64 = {
|
||||
version: version,
|
||||
VERSION: VERSION,
|
||||
atob: _atob,
|
||||
atobPolyfill: atobPolyfill,
|
||||
btoa: _btoa,
|
||||
btoaPolyfill: btoaPolyfill,
|
||||
fromBase64: decode,
|
||||
toBase64: encode,
|
||||
encode: encode,
|
||||
encodeURI: encodeURI,
|
||||
encodeURL: encodeURI,
|
||||
utob: utob,
|
||||
btou: btou,
|
||||
decode: decode,
|
||||
isValid: isValid,
|
||||
fromUint8Array: fromUint8Array,
|
||||
toUint8Array: toUint8Array,
|
||||
extendString: extendString,
|
||||
extendUint8Array: extendUint8Array,
|
||||
extendBuiltins: extendBuiltins
|
||||
};
|
||||
//
|
||||
// export Base64 to the namespace
|
||||
//
|
||||
// ES5 is yet to have Object.assign() that may make transpilers unhappy.
|
||||
// gBase64.Base64 = Object.assign({}, gBase64);
|
||||
gBase64.Base64 = {};
|
||||
Object.keys(gBase64).forEach(function (k) { return gBase64.Base64[k] = gBase64[k]; });
|
||||
return gBase64;
|
||||
}));
|
||||
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
* base64.ts
|
||||
*
|
||||
* Licensed under the BSD 3-Clause License.
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* References:
|
||||
* http://en.wikipedia.org/wiki/Base64
|
||||
*
|
||||
* @author Dan Kogai (https://github.com/dankogai)
|
||||
*/
|
||||
const version = '3.7.2';
|
||||
/**
|
||||
* @deprecated use lowercase `version`.
|
||||
*/
|
||||
const VERSION = version;
|
||||
const _hasatob = typeof atob === 'function';
|
||||
const _hasbtoa = typeof btoa === 'function';
|
||||
const _hasBuffer = typeof Buffer === 'function';
|
||||
const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
|
||||
const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
|
||||
const b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
const b64chs = Array.prototype.slice.call(b64ch);
|
||||
const b64tab = ((a) => {
|
||||
let tab = {};
|
||||
a.forEach((c, i) => tab[c] = i);
|
||||
return tab;
|
||||
})(b64chs);
|
||||
const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
|
||||
const _fromCC = String.fromCharCode.bind(String);
|
||||
const _U8Afrom = typeof Uint8Array.from === 'function'
|
||||
? Uint8Array.from.bind(Uint8Array)
|
||||
: (it, fn = (x) => x) => new Uint8Array(Array.prototype.slice.call(it, 0).map(fn));
|
||||
const _mkUriSafe = (src) => src
|
||||
.replace(/=/g, '').replace(/[+\/]/g, (m0) => m0 == '+' ? '-' : '_');
|
||||
const _tidyB64 = (s) => s.replace(/[^A-Za-z0-9\+\/]/g, '');
|
||||
/**
|
||||
* polyfill version of `btoa`
|
||||
*/
|
||||
const btoaPolyfill = (bin) => {
|
||||
// console.log('polyfilled');
|
||||
let u32, c0, c1, c2, asc = '';
|
||||
const pad = bin.length % 3;
|
||||
for (let i = 0; i < bin.length;) {
|
||||
if ((c0 = bin.charCodeAt(i++)) > 255 ||
|
||||
(c1 = bin.charCodeAt(i++)) > 255 ||
|
||||
(c2 = bin.charCodeAt(i++)) > 255)
|
||||
throw new TypeError('invalid character found');
|
||||
u32 = (c0 << 16) | (c1 << 8) | c2;
|
||||
asc += b64chs[u32 >> 18 & 63]
|
||||
+ b64chs[u32 >> 12 & 63]
|
||||
+ b64chs[u32 >> 6 & 63]
|
||||
+ b64chs[u32 & 63];
|
||||
}
|
||||
return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
|
||||
};
|
||||
/**
|
||||
* does what `window.btoa` of web browsers do.
|
||||
* @param {String} bin binary string
|
||||
* @returns {string} Base64-encoded string
|
||||
*/
|
||||
const _btoa = _hasbtoa ? (bin) => btoa(bin)
|
||||
: _hasBuffer ? (bin) => Buffer.from(bin, 'binary').toString('base64')
|
||||
: btoaPolyfill;
|
||||
const _fromUint8Array = _hasBuffer
|
||||
? (u8a) => Buffer.from(u8a).toString('base64')
|
||||
: (u8a) => {
|
||||
// cf. https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326
|
||||
const maxargs = 0x1000;
|
||||
let strs = [];
|
||||
for (let i = 0, l = u8a.length; i < l; i += maxargs) {
|
||||
strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
|
||||
}
|
||||
return _btoa(strs.join(''));
|
||||
};
|
||||
/**
|
||||
* converts a Uint8Array to a Base64 string.
|
||||
* @param {boolean} [urlsafe] URL-and-filename-safe a la RFC4648 §5
|
||||
* @returns {string} Base64 string
|
||||
*/
|
||||
const fromUint8Array = (u8a, urlsafe = false) => urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a);
|
||||
// This trick is found broken https://github.com/dankogai/js-base64/issues/130
|
||||
// const utob = (src: string) => unescape(encodeURIComponent(src));
|
||||
// reverting good old fationed regexp
|
||||
const cb_utob = (c) => {
|
||||
if (c.length < 2) {
|
||||
var cc = c.charCodeAt(0);
|
||||
return cc < 0x80 ? c
|
||||
: cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6))
|
||||
+ _fromCC(0x80 | (cc & 0x3f)))
|
||||
: (_fromCC(0xe0 | ((cc >>> 12) & 0x0f))
|
||||
+ _fromCC(0x80 | ((cc >>> 6) & 0x3f))
|
||||
+ _fromCC(0x80 | (cc & 0x3f)));
|
||||
}
|
||||
else {
|
||||
var cc = 0x10000
|
||||
+ (c.charCodeAt(0) - 0xD800) * 0x400
|
||||
+ (c.charCodeAt(1) - 0xDC00);
|
||||
return (_fromCC(0xf0 | ((cc >>> 18) & 0x07))
|
||||
+ _fromCC(0x80 | ((cc >>> 12) & 0x3f))
|
||||
+ _fromCC(0x80 | ((cc >>> 6) & 0x3f))
|
||||
+ _fromCC(0x80 | (cc & 0x3f)));
|
||||
}
|
||||
};
|
||||
const re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
|
||||
/**
|
||||
* @deprecated should have been internal use only.
|
||||
* @param {string} src UTF-8 string
|
||||
* @returns {string} UTF-16 string
|
||||
*/
|
||||
const utob = (u) => u.replace(re_utob, cb_utob);
|
||||
//
|
||||
const _encode = _hasBuffer
|
||||
? (s) => Buffer.from(s, 'utf8').toString('base64')
|
||||
: _TE
|
||||
? (s) => _fromUint8Array(_TE.encode(s))
|
||||
: (s) => _btoa(utob(s));
|
||||
/**
|
||||
* converts a UTF-8-encoded string to a Base64 string.
|
||||
* @param {boolean} [urlsafe] if `true` make the result URL-safe
|
||||
* @returns {string} Base64 string
|
||||
*/
|
||||
const encode = (src, urlsafe = false) => urlsafe
|
||||
? _mkUriSafe(_encode(src))
|
||||
: _encode(src);
|
||||
/**
|
||||
* converts a UTF-8-encoded string to URL-safe Base64 RFC4648 §5.
|
||||
* @returns {string} Base64 string
|
||||
*/
|
||||
const encodeURI = (src) => encode(src, true);
|
||||
// This trick is found broken https://github.com/dankogai/js-base64/issues/130
|
||||
// const btou = (src: string) => decodeURIComponent(escape(src));
|
||||
// reverting good old fationed regexp
|
||||
const re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
|
||||
const cb_btou = (cccc) => {
|
||||
switch (cccc.length) {
|
||||
case 4:
|
||||
var cp = ((0x07 & cccc.charCodeAt(0)) << 18)
|
||||
| ((0x3f & cccc.charCodeAt(1)) << 12)
|
||||
| ((0x3f & cccc.charCodeAt(2)) << 6)
|
||||
| (0x3f & cccc.charCodeAt(3)), offset = cp - 0x10000;
|
||||
return (_fromCC((offset >>> 10) + 0xD800)
|
||||
+ _fromCC((offset & 0x3FF) + 0xDC00));
|
||||
case 3:
|
||||
return _fromCC(((0x0f & cccc.charCodeAt(0)) << 12)
|
||||
| ((0x3f & cccc.charCodeAt(1)) << 6)
|
||||
| (0x3f & cccc.charCodeAt(2)));
|
||||
default:
|
||||
return _fromCC(((0x1f & cccc.charCodeAt(0)) << 6)
|
||||
| (0x3f & cccc.charCodeAt(1)));
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @deprecated should have been internal use only.
|
||||
* @param {string} src UTF-16 string
|
||||
* @returns {string} UTF-8 string
|
||||
*/
|
||||
const btou = (b) => b.replace(re_btou, cb_btou);
|
||||
/**
|
||||
* polyfill version of `atob`
|
||||
*/
|
||||
const atobPolyfill = (asc) => {
|
||||
// console.log('polyfilled');
|
||||
asc = asc.replace(/\s+/g, '');
|
||||
if (!b64re.test(asc))
|
||||
throw new TypeError('malformed base64.');
|
||||
asc += '=='.slice(2 - (asc.length & 3));
|
||||
let u24, bin = '', r1, r2;
|
||||
for (let i = 0; i < asc.length;) {
|
||||
u24 = b64tab[asc.charAt(i++)] << 18
|
||||
| b64tab[asc.charAt(i++)] << 12
|
||||
| (r1 = b64tab[asc.charAt(i++)]) << 6
|
||||
| (r2 = b64tab[asc.charAt(i++)]);
|
||||
bin += r1 === 64 ? _fromCC(u24 >> 16 & 255)
|
||||
: r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255)
|
||||
: _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
|
||||
}
|
||||
return bin;
|
||||
};
|
||||
/**
|
||||
* does what `window.atob` of web browsers do.
|
||||
* @param {String} asc Base64-encoded string
|
||||
* @returns {string} binary string
|
||||
*/
|
||||
const _atob = _hasatob ? (asc) => atob(_tidyB64(asc))
|
||||
: _hasBuffer ? (asc) => Buffer.from(asc, 'base64').toString('binary')
|
||||
: atobPolyfill;
|
||||
//
|
||||
const _toUint8Array = _hasBuffer
|
||||
? (a) => _U8Afrom(Buffer.from(a, 'base64'))
|
||||
: (a) => _U8Afrom(_atob(a), c => c.charCodeAt(0));
|
||||
/**
|
||||
* converts a Base64 string to a Uint8Array.
|
||||
*/
|
||||
const toUint8Array = (a) => _toUint8Array(_unURI(a));
|
||||
//
|
||||
const _decode = _hasBuffer
|
||||
? (a) => Buffer.from(a, 'base64').toString('utf8')
|
||||
: _TD
|
||||
? (a) => _TD.decode(_toUint8Array(a))
|
||||
: (a) => btou(_atob(a));
|
||||
const _unURI = (a) => _tidyB64(a.replace(/[-_]/g, (m0) => m0 == '-' ? '+' : '/'));
|
||||
/**
|
||||
* converts a Base64 string to a UTF-8 string.
|
||||
* @param {String} src Base64 string. Both normal and URL-safe are supported
|
||||
* @returns {string} UTF-8 string
|
||||
*/
|
||||
const decode = (src) => _decode(_unURI(src));
|
||||
/**
|
||||
* check if a value is a valid Base64 string
|
||||
* @param {String} src a value to check
|
||||
*/
|
||||
const isValid = (src) => {
|
||||
if (typeof src !== 'string')
|
||||
return false;
|
||||
const s = src.replace(/\s+/g, '').replace(/={0,2}$/, '');
|
||||
return !/[^\s0-9a-zA-Z\+/]/.test(s) || !/[^\s0-9a-zA-Z\-_]/.test(s);
|
||||
};
|
||||
//
|
||||
const _noEnum = (v) => {
|
||||
return {
|
||||
value: v, enumerable: false, writable: true, configurable: true
|
||||
};
|
||||
};
|
||||
/**
|
||||
* extend String.prototype with relevant methods
|
||||
*/
|
||||
const extendString = function () {
|
||||
const _add = (name, body) => Object.defineProperty(String.prototype, name, _noEnum(body));
|
||||
_add('fromBase64', function () { return decode(this); });
|
||||
_add('toBase64', function (urlsafe) { return encode(this, urlsafe); });
|
||||
_add('toBase64URI', function () { return encode(this, true); });
|
||||
_add('toBase64URL', function () { return encode(this, true); });
|
||||
_add('toUint8Array', function () { return toUint8Array(this); });
|
||||
};
|
||||
/**
|
||||
* extend Uint8Array.prototype with relevant methods
|
||||
*/
|
||||
const extendUint8Array = function () {
|
||||
const _add = (name, body) => Object.defineProperty(Uint8Array.prototype, name, _noEnum(body));
|
||||
_add('toBase64', function (urlsafe) { return fromUint8Array(this, urlsafe); });
|
||||
_add('toBase64URI', function () { return fromUint8Array(this, true); });
|
||||
_add('toBase64URL', function () { return fromUint8Array(this, true); });
|
||||
};
|
||||
/**
|
||||
* extend Builtin prototypes with relevant methods
|
||||
*/
|
||||
const extendBuiltins = () => {
|
||||
extendString();
|
||||
extendUint8Array();
|
||||
};
|
||||
const gBase64 = {
|
||||
version: version,
|
||||
VERSION: VERSION,
|
||||
atob: _atob,
|
||||
atobPolyfill: atobPolyfill,
|
||||
btoa: _btoa,
|
||||
btoaPolyfill: btoaPolyfill,
|
||||
fromBase64: decode,
|
||||
toBase64: encode,
|
||||
encode: encode,
|
||||
encodeURI: encodeURI,
|
||||
encodeURL: encodeURI,
|
||||
utob: utob,
|
||||
btou: btou,
|
||||
decode: decode,
|
||||
isValid: isValid,
|
||||
fromUint8Array: fromUint8Array,
|
||||
toUint8Array: toUint8Array,
|
||||
extendString: extendString,
|
||||
extendUint8Array: extendUint8Array,
|
||||
extendBuiltins: extendBuiltins,
|
||||
};
|
||||
// makecjs:CUT //
|
||||
export { version };
|
||||
export { VERSION };
|
||||
export { _atob as atob };
|
||||
export { atobPolyfill };
|
||||
export { _btoa as btoa };
|
||||
export { btoaPolyfill };
|
||||
export { decode as fromBase64 };
|
||||
export { encode as toBase64 };
|
||||
export { utob };
|
||||
export { encode };
|
||||
export { encodeURI };
|
||||
export { encodeURI as encodeURL };
|
||||
export { btou };
|
||||
export { decode };
|
||||
export { isValid };
|
||||
export { fromUint8Array };
|
||||
export { toUint8Array };
|
||||
export { extendString };
|
||||
export { extendUint8Array };
|
||||
export { extendBuiltins };
|
||||
// and finally,
|
||||
export { gBase64 as Base64 };
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"/><link rel="icon" href="/logo.color.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="web content monitor"/><link rel="apple-touch-icon" href="logo.color.png"/><link rel="manifest" href="/manifest.json"/><title>...</title><link rel="stylesheet" href="/tailwind.min.css"><link href="/static/css/2.65280f5c.chunk.css" rel="stylesheet"><link href="/static/css/main.f2766b2a.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="/static/js/runtime-main.17191aad.js"></script><script src="/static/js/2.f4e71d54.chunk.js"></script><script src="/static/js/main.2411968d.chunk.js"></script></body></html>
|
||||
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"/><link rel="icon" href="/logo.color.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="web content monitor"/><link rel="apple-touch-icon" href="logo.color.png"/><link rel="manifest" href="/manifest.json"/><title>...</title><link rel="stylesheet" href="/tailwind.min.css"><link href="/static/css/2.65280f5c.chunk.css" rel="stylesheet"><link href="/static/css/main.f2766b2a.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="/static/js/runtime-main.17191aad.js"></script><script src="/static/js/2.fb5c1c43.chunk.js"></script><script src="/static/js/main.e0ceed25.chunk.js"></script></body></html>
|
||||
@@ -0,0 +1,50 @@
|
||||
self.__precacheManifest = (self.__precacheManifest || []).concat([
|
||||
{
|
||||
"revision": "1148aa9aa0e64aaebb7e39d49ac9cf2d",
|
||||
"url": "/index.html"
|
||||
},
|
||||
{
|
||||
"revision": "094054c58eb14fd97f80",
|
||||
"url": "/static/css/2.65280f5c.chunk.css"
|
||||
},
|
||||
{
|
||||
"revision": "5f29077245e3a359d708",
|
||||
"url": "/static/css/main.f2766b2a.chunk.css"
|
||||
},
|
||||
{
|
||||
"revision": "094054c58eb14fd97f80",
|
||||
"url": "/static/js/2.fb5c1c43.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "5f29077245e3a359d708",
|
||||
"url": "/static/js/main.e0ceed25.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "4d30565b04f5f2725c56",
|
||||
"url": "/static/js/runtime-main.17191aad.js"
|
||||
},
|
||||
{
|
||||
"revision": "05f1cdadfe476395f60e233b15c22155",
|
||||
"url": "/static/media/icons-16.05f1cdad.eot"
|
||||
},
|
||||
{
|
||||
"revision": "3c1c220e7a18286503fb431c7a7fe183",
|
||||
"url": "/static/media/icons-16.3c1c220e.woff"
|
||||
},
|
||||
{
|
||||
"revision": "3cde8748332d1de6b1ae1c2dc5850754",
|
||||
"url": "/static/media/icons-16.3cde8748.ttf"
|
||||
},
|
||||
{
|
||||
"revision": "0a5c76518a68c185baa2c6744456918c",
|
||||
"url": "/static/media/icons-20.0a5c7651.eot"
|
||||
},
|
||||
{
|
||||
"revision": "51ec31f302d0072808e1f83f85fea4cd",
|
||||
"url": "/static/media/icons-20.51ec31f3.ttf"
|
||||
},
|
||||
{
|
||||
"revision": "cef8cdbb9d0ba82e6e19fb0eeba2ac3d",
|
||||
"url": "/static/media/icons-20.cef8cdbb.woff"
|
||||
}
|
||||
]);
|
||||
@@ -14,7 +14,7 @@
|
||||
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
|
||||
|
||||
importScripts(
|
||||
"/precache-manifest.ef4e4009a9308c36ee12ad207daef55e.js"
|
||||
"/precache-manifest.d081260a9ad335232527f33321ad446e.js"
|
||||
);
|
||||
|
||||
self.addEventListener('message', (event) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import './background.js';
|
||||
|
||||
// importScripts('background.js');
|
||||
// importScripts('background.js','check.js');
|
||||
//code
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3
-3
@@ -20,9 +20,9 @@
|
||||
</ol>
|
||||
<h2>最新版本</h2>
|
||||
<ul>
|
||||
<li>插件·Chrome/Edge:2022.08.17.13.51 <a href="ckc.zip">下载</a> (<a href="https://github.com/easychen/checkchan-dist/raw/master/ckc.zip">Github下载地址,无需注册</a>)</li>
|
||||
<li>Docker镜像(云端+远程桌面二合一):2022.08.17.12.05 <a href="https://hub.docker.com/repository/docker/easychen/checkchan" rel="nofollow">Docker Hub</a></li>
|
||||
<li>文档:2022.08.17.13.51</li>
|
||||
<li>插件·Chrome/Edge:2022.08.17.20.32 <a href="ckc.zip">下载</a> (<a href="https://github.com/easychen/checkchan-dist/raw/master/ckc.zip">Github下载地址,无需注册</a>)</li>
|
||||
<li>Docker镜像(云端+远程桌面二合一):2022.08.17.19.39 <a href="https://hub.docker.com/repository/docker/easychen/checkchan" rel="nofollow">Docker Hub</a></li>
|
||||
<li>文档:2022.08.17.20.32</li>
|
||||
<li>更新日志:<a href="https://github.com/easychen/checkchan-dist/commits/main">GitHub</a></li>
|
||||
<li>生态:RSSHub浏览器插件整合版(<a href="https://github.com/easychen/RSSHub-Radar-with-checkchan">源码</a> | <a href="https://github.com/easychen/RSSHub-Radar-with-checkchan/releases/download/main/1.7.0.with-checkchan.zip">下载</a>)</li>
|
||||
</ul>
|
||||
|
||||
+3
-3
@@ -9,9 +9,9 @@
|
||||
</ol>
|
||||
<h2>最新版本</h2>
|
||||
<ul>
|
||||
<li>插件·Chrome/Edge:2022.08.17.13.51 <a href="ckc.zip">下载</a> (<a href="https://github.com/easychen/checkchan-dist/raw/master/ckc.zip">Github下载地址,无需注册</a>)</li>
|
||||
<li>Docker镜像(云端+远程桌面二合一):2022.08.17.12.05 <a href="https://hub.docker.com/repository/docker/easychen/checkchan" rel="nofollow">Docker Hub</a></li>
|
||||
<li>文档:2022.08.17.13.51</li>
|
||||
<li>插件·Chrome/Edge:2022.08.17.20.32 <a href="ckc.zip">下载</a> (<a href="https://github.com/easychen/checkchan-dist/raw/master/ckc.zip">Github下载地址,无需注册</a>)</li>
|
||||
<li>Docker镜像(云端+远程桌面二合一):2022.08.17.19.39 <a href="https://hub.docker.com/repository/docker/easychen/checkchan" rel="nofollow">Docker Hub</a></li>
|
||||
<li>文档:2022.08.17.20.32</li>
|
||||
<li>更新日志:<a href="https://github.com/easychen/checkchan-dist/commits/main">GitHub</a></li>
|
||||
<li>生态:RSSHub浏览器插件整合版(<a href="https://github.com/easychen/RSSHub-Radar-with-checkchan">源码</a> | <a href="https://github.com/easychen/RSSHub-Radar-with-checkchan/releases/download/main/1.7.0.with-checkchan.zip">下载</a>)</li>
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user