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;
}