Auto backup before evolver run - 2026年 03月 06日 星期五 19:00:05 CST
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 清空 CalDAV 日历事件
|
||||
*/
|
||||
|
||||
import https from 'https';
|
||||
|
||||
const CONFIG = {
|
||||
url: 'https://micecola.top:5001/caldav/zhangqing/home/',
|
||||
user: 'zhangqing',
|
||||
password: 'MiceCola1265'
|
||||
};
|
||||
|
||||
function log(level, message) {
|
||||
const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
||||
console.log(`${timestamp} - ${level} - ${message}`);
|
||||
}
|
||||
|
||||
async function request(method, path = '', body = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = new URL(CONFIG.url + path);
|
||||
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
port: url.port || 443,
|
||||
path: url.pathname,
|
||||
method: method,
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + Buffer.from(`${CONFIG.user}:${CONFIG.password}`).toString('base64'),
|
||||
'Content-Type': 'application/xml; charset=utf-8',
|
||||
'Depth': 'infinity'
|
||||
},
|
||||
rejectUnauthorized: false
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve({ status: res.statusCode, data, headers: res.headers });
|
||||
} else {
|
||||
resolve({ status: res.statusCode, data, headers: res.headers });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
if (body) req.write(body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function getAllEvents() {
|
||||
// 使用 PROPFIND 获取日历内容
|
||||
const response = await request('PROPFIND', '', `<?xml version="1.0" encoding="utf-8"?>
|
||||
<d:propfind xmlns:d="DAV:">
|
||||
<d:prop>
|
||||
<d:resourcetype />
|
||||
<d:getcontenttype />
|
||||
</d:prop>
|
||||
</d:propfind>`);
|
||||
|
||||
// 提取所有 .ics 文件的 href(可能在 <href> 或 <propstat><prop><href> 中)
|
||||
const hrefs = [];
|
||||
|
||||
// 匹配所有 href,排除目录
|
||||
const allHrefs = response.data.match(/<href>([^<]+)<\/href>/gi) || [];
|
||||
for (const match of allHrefs) {
|
||||
const href = match.replace(/<\/?href>/gi, '');
|
||||
// 只保留 .ics 文件,排除目录
|
||||
if (href.endsWith('.ics')) {
|
||||
// 解码 URL 编码
|
||||
const decodedHref = decodeURIComponent(href);
|
||||
if (!hrefs.includes(decodedHref)) {
|
||||
hrefs.push(decodedHref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hrefs;
|
||||
}
|
||||
|
||||
async function deleteEvent(href) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// href 是相对路径,需要用完整 URL
|
||||
const baseUrl = new URL(CONFIG.url);
|
||||
const fullPath = href.startsWith('/') ? href : '/' + href;
|
||||
|
||||
const options = {
|
||||
hostname: baseUrl.hostname,
|
||||
port: baseUrl.port || 443,
|
||||
path: fullPath,
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + Buffer.from(`${CONFIG.user}:${CONFIG.password}`).toString('base64')
|
||||
},
|
||||
rejectUnauthorized: false
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => resolve(res.statusCode));
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
log('INFO', '获取日历事件列表...');
|
||||
|
||||
const hrefs = await getAllEvents();
|
||||
log('INFO', `找到 ${hrefs.length} 个事件`);
|
||||
|
||||
let deleted = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (const href of hrefs) {
|
||||
const status = await deleteEvent(href);
|
||||
if (status >= 200 && status < 300) {
|
||||
deleted++;
|
||||
log('INFO', `已删除: ${href.split('/').pop()}`);
|
||||
} else {
|
||||
failed++;
|
||||
log('ERROR', `删除失败 (${status}): ${href}`);
|
||||
}
|
||||
}
|
||||
|
||||
log('INFO', `完成: 删除 ${deleted} 个,失败 ${failed} 个`);
|
||||
}
|
||||
|
||||
main().catch(e => {
|
||||
log('ERROR', `程序异常: ${e.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user