#!/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', '', ` `); // 提取所有 .ics 文件的 href(可能在 或 中) const hrefs = []; // 匹配所有 href,排除目录 const allHrefs = response.data.match(/([^<]+)<\/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); });