import { request } from "undici";
import {
YuqueConfig,
YuqueDoc,
YuqueDocListItem,
YuqueBook,
YuqueTocItem,
YuqueApiResponse,
YuqueError,
} from "./types.js";
/**
* 从 Cookie 中提取 CSRF Token (yuque_ctoken)
*/
function extractCsrfToken(cookie: string): string | null {
const match = cookie.match(/yuque_ctoken=([^;]+)/);
return match ? match[1] : null;
}
/**
* 构建请求头
*/
function buildHeaders(config: YuqueConfig): Record<string, string> {
const csrfToken = extractCsrfToken(config.cookie);
const headers: Record<string, string> = {
Cookie: config.cookie,
"Content-Type": "application/json",
"Accept": "application/json",
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
"X-Requested-With": "XMLHttpRequest",
"Referer": config.baseUrl,
};
// 添加 CSRF Token(必需)
if (csrfToken) {
headers["x-csrf-token"] = csrfToken;
}
return headers;
}
/**
* 获取语雀文档详情
*/
export async function fetchYuqueDoc(
namespace: string,
slug: string,
config: YuqueConfig
): Promise<YuqueDoc | null> {
try {
const url = `${config.baseUrl}/api/v2/repos/${namespace}/docs/${slug}`;
const response = await request(url, {
method: "GET",
headers: buildHeaders(config),
});
if (response.statusCode === 200) {
const result = (await response.body.json()) as YuqueApiResponse<YuqueDoc>;
return result.data;
} else if (response.statusCode === 404) {
console.error(`Document ${namespace}/${slug} not found`);
return null;
} else {
const errorData = (await response.body.json()) as YuqueError;
console.error("Yuque API error:", errorData);
return null;
}
} catch (error) {
console.error("Error fetching Yuque document:", error);
return null;
}
}
/**
* 获取知识库的文档列表
*/
export async function listYuqueDocs(
namespace: string,
config: YuqueConfig
): Promise<YuqueDocListItem[]> {
try {
const url = `${config.baseUrl}/api/v2/repos/${namespace}/docs`;
const response = await request(url, {
method: "GET",
headers: buildHeaders(config),
});
if (response.statusCode === 200) {
const result = (await response.body.json()) as YuqueApiResponse<YuqueDocListItem[]>;
return result.data || [];
} else {
const errorData = (await response.body.json()) as YuqueError;
console.error("Yuque API error:", errorData);
return [];
}
} catch (error) {
console.error("Error listing Yuque documents:", error);
return [];
}
}
/**
* 获取知识库目录(TOC)
*/
export async function fetchYuqueToc(
namespace: string,
config: YuqueConfig
): Promise<YuqueTocItem[]> {
try {
const url = `${config.baseUrl}/api/v2/repos/${namespace}/toc`;
const response = await request(url, {
method: "GET",
headers: buildHeaders(config),
});
if (response.statusCode === 200) {
const result = (await response.body.json()) as YuqueApiResponse<YuqueTocItem[]>;
return result.data || [];
} else {
const errorData = (await response.body.json()) as YuqueError;
console.error("Yuque API error:", errorData);
return [];
}
} catch (error) {
console.error("Error fetching Yuque TOC:", error);
return [];
}
}
/**
* 获取知识库信息
*/
export async function fetchYuqueBook(
namespace: string,
config: YuqueConfig
): Promise<YuqueBook | null> {
try {
const url = `${config.baseUrl}/api/v2/repos/${namespace}`;
const response = await request(url, {
method: "GET",
headers: buildHeaders(config),
});
if (response.statusCode === 200) {
const result = (await response.body.json()) as YuqueApiResponse<YuqueBook>;
return result.data;
} else {
const errorData = (await response.body.json()) as YuqueError;
console.error("Yuque API error:", errorData);
return null;
}
} catch (error) {
console.error("Error fetching Yuque book:", error);
return null;
}
}
/**
* 搜索知识库中的文档
*/
export async function searchYuqueDocs(
namespace: string,
query: string,
config: YuqueConfig
): Promise<YuqueDocListItem[]> {
try {
// 语雀 API v2 搜索需要使用知识库文档列表 + 客户端过滤
// 因为公开 API 不支持直接搜索
const docs = await listYuqueDocs(namespace, config);
// 简单的客户端搜索:匹配标题或描述
const filteredDocs = docs.filter((doc) => {
const titleMatch = doc.title.toLowerCase().includes(query.toLowerCase());
const descMatch = doc.description?.toLowerCase().includes(query.toLowerCase()) || false;
return titleMatch || descMatch;
});
return filteredDocs;
} catch (error) {
console.error("Error searching Yuque documents:", error);
return [];
}
}
/**
* 验证语雀配置
*/
export async function validateYuqueConfig(config: YuqueConfig): Promise<boolean> {
try {
const url = `${config.baseUrl}/api/v2/user`;
const response = await request(url, {
method: "GET",
headers: buildHeaders(config),
});
return response.statusCode === 200;
} catch (error) {
console.error("Error validating Yuque config:", error);
return false;
}
}