123 lines
3.0 KiB
TypeScript
123 lines
3.0 KiB
TypeScript
import type { MarketRecord } from "./types";
|
|
|
|
interface FetchResponseLike {
|
|
json(): Promise<unknown>;
|
|
ok: boolean;
|
|
}
|
|
|
|
type FetchLike = (
|
|
input: string,
|
|
init?: RequestInit
|
|
) => Promise<FetchResponseLike>;
|
|
|
|
interface AuthorBaseClientOptions {
|
|
baseUrl?: string;
|
|
fetchImpl?: FetchLike;
|
|
timeoutMs?: number;
|
|
}
|
|
|
|
export function createAuthorBaseClient(options: AuthorBaseClientOptions = {}) {
|
|
const baseUrl = options.baseUrl ?? resolveBaseUrl();
|
|
const fetchImpl = options.fetchImpl ?? defaultFetch;
|
|
const timeoutMs = options.timeoutMs ?? 8000;
|
|
|
|
return {
|
|
async loadAuthorBaseInfo(authorId: string): Promise<MarketRecord> {
|
|
const controller = new AbortController();
|
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
|
|
try {
|
|
const response = await fetchImpl(
|
|
buildAuthorBaseInfoUrl(authorId, baseUrl),
|
|
{
|
|
credentials: "include",
|
|
method: "GET",
|
|
signal: controller.signal
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
return buildFailedRecord(authorId, "request-failed");
|
|
}
|
|
|
|
return mapAuthorBaseInfoResponse(authorId, await response.json());
|
|
} catch (error) {
|
|
return buildFailedRecord(
|
|
authorId,
|
|
error instanceof Error && error.name === "AbortError"
|
|
? "timeout"
|
|
: "request-failed"
|
|
);
|
|
} finally {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
export function buildAuthorBaseInfoUrl(
|
|
authorId: string,
|
|
baseUrl: string
|
|
): string {
|
|
const url = new URL("/gw/api/author/get_author_base_info", baseUrl);
|
|
url.searchParams.set("o_author_id", authorId);
|
|
url.searchParams.set("platform_source", "1");
|
|
url.searchParams.set("platform_channel", "1");
|
|
url.searchParams.set("recommend", "true");
|
|
url.searchParams.set("need_sec_uid", "true");
|
|
url.searchParams.set("need_linkage_info", "true");
|
|
return url.toString();
|
|
}
|
|
|
|
export function mapAuthorBaseInfoResponse(
|
|
authorId: string,
|
|
payload: unknown
|
|
): MarketRecord {
|
|
if (!isRecord(payload)) {
|
|
return buildFailedRecord(authorId, "bad-response");
|
|
}
|
|
|
|
const authorName = readString(payload.nick_name);
|
|
if (!authorName) {
|
|
return buildFailedRecord(authorId, "missing-rate");
|
|
}
|
|
|
|
return {
|
|
authorId,
|
|
authorName,
|
|
status: "success"
|
|
};
|
|
}
|
|
|
|
function buildFailedRecord(
|
|
authorId: string,
|
|
failureReason: MarketRecord["failureReason"]
|
|
): MarketRecord {
|
|
return {
|
|
authorId,
|
|
authorName: "",
|
|
failureReason,
|
|
status: "failed"
|
|
};
|
|
}
|
|
|
|
function readString(value: unknown): string | null {
|
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
}
|
|
|
|
function resolveBaseUrl(): string {
|
|
if (typeof location !== "undefined" && location.origin) {
|
|
return location.origin;
|
|
}
|
|
|
|
return "https://xingtu.cn";
|
|
}
|
|
|
|
async function defaultFetch(input: string, init?: RequestInit) {
|
|
return fetch(input, init);
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === "object" && value !== null;
|
|
}
|