Files
Sub-Store/backend/src/restful/preview.js
2025-01-03 23:37:50 +08:00

327 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { InternalServerError } from './errors';
import { ProxyUtils } from '@/core/proxy-utils';
import { findByName } from '@/utils/database';
import { success, failed } from './response';
import download from '@/utils/download';
import { SUBS_KEY } from '@/constants';
import $ from '@/core/app';
export default function register($app) {
$app.post('/api/preview/sub', compareSub);
$app.post('/api/preview/collection', compareCollection);
$app.post('/api/preview/file', previewFile);
}
async function previewFile(req, res) {
try {
const file = req.body;
let content;
if (
file.source === 'local' &&
!['localFirst', 'remoteFirst'].includes(file.mergeSources)
) {
content = file.content;
} else {
const errors = {};
content = await Promise.all(
file.url
.split(/[\r\n]+/)
.map((i) => i.trim())
.filter((i) => i.length)
.map(async (url) => {
try {
return await download(url, file.ua);
} catch (err) {
errors[url] = err;
$.error(
`文件 ${file.name} 的远程文件 ${url} 发生错误: ${err}`,
);
return '';
}
}),
);
if (
!file.ignoreFailedRemoteFile &&
Object.keys(errors).length > 0
) {
throw new Error(
`文件 ${file.name} 的远程文件 ${Object.keys(errors).join(
', ',
)} 发生错误, 请查看日志`,
);
}
if (file.mergeSources === 'localFirst') {
content.unshift(file.content);
} else if (file.mergeSources === 'remoteFirst') {
content.push(file.content);
}
}
// parse proxies
const files = (Array.isArray(content) ? content : [content]).flat();
let filesContent = files
.filter((i) => i != null && i !== '')
.join('\n');
// apply processors
const processed =
Array.isArray(file.process) && file.process.length > 0
? await ProxyUtils.process(
{ $files: files, $content: filesContent, $file: file },
file.process,
)
: { $content: filesContent, $files: files };
// produce
success(res, {
original: filesContent,
processed: processed?.$content ?? '',
});
} catch (err) {
$.error(err.message ?? err);
failed(
res,
new InternalServerError(
`INTERNAL_SERVER_ERROR`,
`Failed to preview file`,
`Reason: ${err.message ?? err}`,
),
);
}
}
async function compareSub(req, res) {
try {
const sub = req.body;
const target = req.query.target || 'JSON';
let content;
if (
sub.source === 'local' &&
!['localFirst', 'remoteFirst'].includes(sub.mergeSources)
) {
content = sub.content;
} else {
const errors = {};
content = await Promise.all(
sub.url
.split(/[\r\n]+/)
.map((i) => i.trim())
.filter((i) => i.length)
.map(async (url) => {
try {
return await download(
url,
sub.ua,
undefined,
sub.proxy,
undefined,
undefined,
undefined,
true,
);
} catch (err) {
errors[url] = err;
$.error(
`订阅 ${sub.name} 的远程订阅 ${url} 发生错误: ${err}`,
);
return '';
}
}),
);
if (!sub.ignoreFailedRemoteSub && Object.keys(errors).length > 0) {
throw new Error(
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
', ',
)} 发生错误, 请查看日志`,
);
}
if (sub.mergeSources === 'localFirst') {
content.unshift(sub.content);
} else if (sub.mergeSources === 'remoteFirst') {
content.push(sub.content);
}
}
// parse proxies
const original = (Array.isArray(content) ? content : [content])
.map((i) => ProxyUtils.parse(i))
.flat();
// add id
original.forEach((proxy, i) => {
proxy.id = i;
proxy._subName = sub.name;
proxy._subDisplayName = sub.displayName;
});
// apply processors
const processed = await ProxyUtils.process(
original,
sub.process || [],
target,
{ [sub.name]: sub },
);
// produce
success(res, { original, processed });
} catch (err) {
$.error(err.message ?? err);
failed(
res,
new InternalServerError(
`INTERNAL_SERVER_ERROR`,
`Failed to preview subscription`,
`Reason: ${err.message ?? err}`,
),
);
}
}
async function compareCollection(req, res) {
try {
const allSubs = $.read(SUBS_KEY);
const collection = req.body;
const subnames = [...collection.subscriptions];
let subscriptionTags = collection.subscriptionTags;
if (Array.isArray(subscriptionTags) && subscriptionTags.length > 0) {
allSubs.forEach((sub) => {
if (
Array.isArray(sub.tag) &&
sub.tag.length > 0 &&
!subnames.includes(sub.name) &&
sub.tag.some((tag) => subscriptionTags.includes(tag))
) {
subnames.push(sub.name);
}
});
}
const results = {};
const errors = {};
await Promise.all(
subnames.map(async (name) => {
const sub = findByName(allSubs, name);
try {
let raw;
if (
sub.source === 'local' &&
!['localFirst', 'remoteFirst'].includes(
sub.mergeSources,
)
) {
raw = sub.content;
} else {
const errors = {};
raw = await Promise.all(
sub.url
.split(/[\r\n]+/)
.map((i) => i.trim())
.filter((i) => i.length)
.map(async (url) => {
try {
return await download(
url,
sub.ua,
undefined,
sub.proxy,
undefined,
undefined,
undefined,
true,
);
} catch (err) {
errors[url] = err;
$.error(
`订阅 ${sub.name} 的远程订阅 ${url} 发生错误: ${err}`,
);
return '';
}
}),
);
if (
!sub.ignoreFailedRemoteSub &&
Object.keys(errors).length > 0
) {
throw new Error(
`订阅 ${sub.name} 的远程订阅 ${Object.keys(
errors,
).join(', ')} 发生错误, 请查看日志`,
);
}
if (sub.mergeSources === 'localFirst') {
raw.unshift(sub.content);
} else if (sub.mergeSources === 'remoteFirst') {
raw.push(sub.content);
}
}
// parse proxies
let currentProxies = (Array.isArray(raw) ? raw : [raw])
.map((i) => ProxyUtils.parse(i))
.flat();
currentProxies.forEach((proxy) => {
proxy._subName = sub.name;
proxy._subDisplayName = sub.displayName;
proxy._collectionName = collection.name;
proxy._collectionDisplayName = collection.displayName;
});
// apply processors
currentProxies = await ProxyUtils.process(
currentProxies,
sub.process || [],
'JSON',
{ [sub.name]: sub, _collection: collection },
);
results[name] = currentProxies;
} catch (err) {
errors[name] = err;
$.error(
`❌ 处理组合订阅 ${collection.name} 中的子订阅: ${sub.name}时出现错误:${err}`,
);
}
}),
);
if (
!collection.ignoreFailedRemoteSub &&
Object.keys(errors).length > 0
) {
throw new Error(
`组合订阅 ${collection.name} 中的子订阅 ${Object.keys(
errors,
).join(', ')} 发生错误, 请查看日志`,
);
}
// merge proxies with the original order
const original = Array.prototype.concat.apply(
[],
subnames.map((name) => results[name] || []),
);
original.forEach((proxy, i) => {
proxy.id = i;
proxy._collectionName = collection.name;
proxy._collectionDisplayName = collection.displayName;
});
const processed = await ProxyUtils.process(
original,
collection.process || [],
'JSON',
{ _collection: collection },
);
success(res, { original, processed });
} catch (err) {
$.error(err.message ?? err);
failed(
res,
new InternalServerError(
`INTERNAL_SERVER_ERROR`,
`Failed to preview collection`,
`Reason: ${err.message ?? err}`,
),
);
}
}