重构为Monorepo:拆分xhs/xhh应用与core包并完成双服务部署改造

This commit is contained in:
2026-03-03 16:06:16 +08:00
parent ed7fbdd5c2
commit 2cbd6b28b2
84 changed files with 6332 additions and 7678 deletions
@@ -0,0 +1,95 @@
interface FeedTargetInput {
feed_id?: string;
xsec_token?: string;
url?: string;
}
interface UserTargetInput {
user_id?: string;
xsec_token?: string;
url?: string;
}
interface FeedTargetResolved {
feedId: string;
xsecToken: string;
}
interface UserTargetResolved {
userId: string;
xsecToken: string;
}
function parseXhsUrl(rawUrl: string): URL {
const trimmed = rawUrl.trim();
if (!trimmed) {
throw new Error('url cannot be empty');
}
if (/^https?:\/\//i.test(trimmed)) {
return new URL(trimmed);
}
if (trimmed.startsWith('/')) {
return new URL(`https://www.xiaohongshu.com${trimmed}`);
}
return new URL(`https://${trimmed}`);
}
function extractFeedIdFromPath(pathname: string): string | undefined {
const patterns = [
/\/explore\/([a-zA-Z0-9_-]+)/,
/\/discovery\/item\/([a-zA-Z0-9_-]+)/,
/\/note\/([a-zA-Z0-9_-]+)/,
];
for (const pattern of patterns) {
const match = pathname.match(pattern);
if (match?.[1]) return match[1];
}
return undefined;
}
function extractUserIdFromPath(pathname: string): string | undefined {
const match = pathname.match(/\/user\/profile\/([a-zA-Z0-9_-]+)/);
return match?.[1];
}
function extractXsecToken(url: URL): string | undefined {
return url.searchParams.get('xsec_token') ?? url.searchParams.get('xsecToken') ?? undefined;
}
export function resolveFeedTarget(input: FeedTargetInput): FeedTargetResolved {
let feedId = input.feed_id?.trim();
let xsecToken = input.xsec_token?.trim();
if (input.url) {
const parsed = parseXhsUrl(input.url);
feedId = feedId || extractFeedIdFromPath(parsed.pathname);
xsecToken = xsecToken || extractXsecToken(parsed);
}
if (!feedId || !xsecToken) {
throw new Error('xhs_get_feed_detail requires either url with feed_id/xsec_token, or both feed_id and xsec_token');
}
return { feedId, xsecToken };
}
export function resolveUserTarget(input: UserTargetInput): UserTargetResolved {
let userId = input.user_id?.trim();
let xsecToken = input.xsec_token?.trim();
if (input.url) {
const parsed = parseXhsUrl(input.url);
userId = userId || extractUserIdFromPath(parsed.pathname);
xsecToken = xsecToken || extractXsecToken(parsed);
}
if (!userId || !xsecToken) {
throw new Error('xhs_get_user_profile requires either url with user_id/xsec_token, or both user_id and xsec_token');
}
return { userId, xsecToken };
}