mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-clawer.git
synced 2026-08-12 08:50:33 +00:00
Compare commits
2 Commits
2eea007652
...
817a5572af
| Author | SHA1 | Date | |
|---|---|---|---|
| 817a5572af | |||
| 9015b5dadf |
5
.codegraph/.gitignore
vendored
Normal file
5
.codegraph/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# CodeGraph data files — local to each machine, not for committing.
|
||||
# Ignore everything in .codegraph/ except this file itself, so transient
|
||||
# files (the database, daemon.pid, sockets, logs) never show up in git.
|
||||
*
|
||||
!.gitignore
|
||||
@ -2,6 +2,7 @@ const express = require('express');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const axios = require('axios');
|
||||
const { randomUUID } = require('crypto');
|
||||
require('dotenv').config();
|
||||
|
||||
const CAPTURE_FILE = path.join(__dirname, '..', '..', 'captures.jsonl');
|
||||
@ -9,6 +10,22 @@ const CAPTURE_FILE = path.join(__dirname, '..', '..', 'captures.jsonl');
|
||||
let capturedData = [];
|
||||
let clients = [];
|
||||
|
||||
function ensureCaptureId(data) {
|
||||
if (!data || typeof data !== 'object') return data;
|
||||
if (typeof data.id !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(data.id)) {
|
||||
data.id = randomUUID();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function rewriteCaptureFile() {
|
||||
fs.writeFileSync(
|
||||
CAPTURE_FILE,
|
||||
capturedData.map(data => JSON.stringify(data)).join('\n') + (capturedData.length ? '\n' : ''),
|
||||
'utf-8'
|
||||
);
|
||||
}
|
||||
|
||||
// ======================== 数据持久化 ========================
|
||||
|
||||
/**
|
||||
@ -25,7 +42,10 @@ function loadFromFile() {
|
||||
try { return JSON.parse(line); }
|
||||
catch { return null; }
|
||||
})
|
||||
.filter(Boolean);
|
||||
.filter(Boolean)
|
||||
.map(ensureCaptureId);
|
||||
// 旧记录没有稳定 ID;补齐后写回,确保选择与删除跨刷新保持一致。
|
||||
rewriteCaptureFile();
|
||||
console.log(`Loaded ${capturedData.length} historical records from ${CAPTURE_FILE}`);
|
||||
}
|
||||
}
|
||||
@ -177,7 +197,7 @@ function createApp() {
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
app.post('/api/capture', (req, res) => {
|
||||
const data = req.body;
|
||||
const data = ensureCaptureId(req.body);
|
||||
capturedData.push(data);
|
||||
console.log('Captured data:', data.path);
|
||||
// 持久化
|
||||
@ -207,6 +227,27 @@ function createApp() {
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
app.post('/api/delete', (req, res) => {
|
||||
const ids = Array.isArray(req.body.ids) ? [...new Set(req.body.ids.filter(id => typeof id === 'string'))] : [];
|
||||
if (ids.length === 0) return res.status(400).json({ error: 'ids must be a non-empty string array' });
|
||||
|
||||
const idSet = new Set(ids);
|
||||
const previousData = capturedData;
|
||||
const nextData = capturedData.filter(item => !idSet.has(item.id));
|
||||
const deleted = previousData.length - nextData.length;
|
||||
|
||||
try {
|
||||
capturedData = nextData;
|
||||
rewriteCaptureFile();
|
||||
broadcastData();
|
||||
res.json({ success: true, deleted });
|
||||
} catch (e) {
|
||||
capturedData = previousData;
|
||||
console.error('Failed to persist deleted captures:', e.message);
|
||||
res.status(500).json({ error: 'Failed to persist deleted captures' });
|
||||
}
|
||||
});
|
||||
|
||||
// 请求重放端点
|
||||
app.post('/api/replay', async (req, res) => {
|
||||
const { path: apiPath, method, params, crypto, rawPath, requestHeaders } = req.body;
|
||||
@ -216,7 +257,10 @@ function createApp() {
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await replayRequest({ path: apiPath, method, params, crypto, rawPath, requestHeaders });
|
||||
if (!['eapi', 'linuxapi', 'api'].includes(crypto)) {
|
||||
return res.status(400).json({ error: `Unsupported replay crypto: ${crypto || 'unknown'}` });
|
||||
}
|
||||
const result = ensureCaptureId(await replayRequest({ path: apiPath, method, params, crypto, rawPath, requestHeaders }));
|
||||
// 将重放结果也加入抓包列表
|
||||
capturedData.push(result);
|
||||
appendToFile(result);
|
||||
@ -308,4 +352,4 @@ if (require.main === module) {
|
||||
startClient();
|
||||
}
|
||||
|
||||
module.exports = { startClient, createApp };
|
||||
module.exports = { startClient, createApp };
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "网易云音乐抓包工具",
|
||||
"short_name": "抓包工具",
|
||||
"description": "网易云音乐客户端抓包与调试工具",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#f0f2f5",
|
||||
"theme_color": "#C20C0C",
|
||||
"categories": ["developer tools", "utilities"],
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
// 无缓存透传 Service Worker
|
||||
// 仅用于 PWA 安装能力,不缓存任何资源
|
||||
|
||||
self.addEventListener('install', function() {
|
||||
// 立即激活,不等待页面关闭
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener('activate', function() {
|
||||
// 接管所有客户端
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', function(event) {
|
||||
// 纯透传:不缓存任何内容,每次都请求网络
|
||||
// 如果网络不可用,请求失败由浏览器自行处理
|
||||
event.respondWith(fetch(event.request));
|
||||
});
|
||||
@ -96,6 +96,7 @@ const domainList = [
|
||||
'y.163.com',
|
||||
'interface.music.163.com',
|
||||
'interface3.music.163.com',
|
||||
'interfacepc.music.163.com',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -449,6 +450,7 @@ hook.request.after = (ctx) => {
|
||||
duration,
|
||||
requestHeaders: ctx.requestHeaders,
|
||||
responseHeaders,
|
||||
isNetease: true,
|
||||
};
|
||||
axios.post(`http://localhost:${process.env.PORT || 3000}/api/capture`, dataToSend)
|
||||
.catch(err => logger.error('Failed to send data to frontend:', err));
|
||||
@ -468,6 +470,7 @@ hook.request.after = (ctx) => {
|
||||
duration,
|
||||
requestHeaders: ctx.requestHeaders,
|
||||
responseHeaders,
|
||||
isNetease: true,
|
||||
};
|
||||
axios.post(`http://localhost:${process.env.PORT || 3000}/api/capture`, dataToSend)
|
||||
.catch(err => logger.error('Failed to send data to frontend:', err));
|
||||
@ -593,4 +596,4 @@ hook.negotiate.before = (ctx) => {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = hook;
|
||||
module.exports = hook;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user