Files
sharelink/ui/src/api/index.ts
T
shirainbown 54dbba9ea2 feat: 新增受保护链接并将控制台合并为资源访问管理
- 新增 ProtectedLink/LinkVisitRecord,/link/{slug} 门禁页 + 一次性令牌跳转
- 复用邮箱验证体系,验证码邮件区分下载/链接模板
- 控制台下载资源与受保护链接合并为「资源访问管理」单页(页签切换)
- 引用扫描支持 /link/ 链接,角色模板/设置/文档同步更新
- 版本 1.3.0
2026-08-09 14:28:06 +08:00

246 lines
6.6 KiB
TypeScript
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 { axiosInstance } from '@halo-dev/api-client';
import type {
DownloadResource,
DownloadResourceList,
DownloadResourceSpec,
DownloadRecord,
LinkVisitRecord,
ListResult,
ProtectedLink,
ProtectedLinkList,
ProtectedLinkSpec,
ReferencesMap,
} from '@/types';
export const API_BASE = '/apis/console.api.sharelink.halo.run/v1alpha1';
// 后端资源 VO 为扁平结构,这里统一归一化为 Halo 风格结构供组件使用
interface ResourceVo {
name: string;
slug: string;
displayName: string;
description?: string;
attachmentName?: string;
requireEmailVerify?: boolean;
enabled?: boolean;
downloadUrl?: string;
creationTimestamp?: string;
stats?: {
downloadCount?: number;
downloaderCount?: number;
referenceCount?: number;
};
}
function normalizeResource(vo: ResourceVo): DownloadResource {
return {
metadata: { name: vo.name, creationTimestamp: vo.creationTimestamp },
spec: {
slug: vo.slug,
displayName: vo.displayName,
description: vo.description,
attachmentName: vo.attachmentName,
requireEmailVerify: vo.requireEmailVerify,
enabled: vo.enabled,
},
status: { downloadCount: vo.stats?.downloadCount ?? 0 },
stats: {
downloaderCount: vo.stats?.downloaderCount ?? 0,
referenceCount: vo.stats?.referenceCount ?? 0,
},
};
}
export async function listResources(): Promise<DownloadResourceList> {
const { data } = await axiosInstance.get<ResourceVo[]>(
`${API_BASE}/download-resources`
);
const items = (data ?? []).map(normalizeResource);
return { items, total: items.length };
}
// 后端创建/更新均接收扁平的 spec 字段(ResourceRequest
export async function createResource(
spec: DownloadResourceSpec
): Promise<DownloadResource> {
const { data } = await axiosInstance.post<ResourceVo>(
`${API_BASE}/download-resources`,
spec
);
return normalizeResource(data);
}
export async function getResource(name: string): Promise<DownloadResource> {
const { data } = await axiosInstance.get<ResourceVo>(
`${API_BASE}/download-resources/${name}`
);
return normalizeResource(data);
}
export async function updateResource(
name: string,
spec: DownloadResourceSpec
): Promise<DownloadResource> {
const { data } = await axiosInstance.put<ResourceVo>(
`${API_BASE}/download-resources/${name}`,
spec
);
return normalizeResource(data);
}
export async function deleteResource(name: string): Promise<void> {
await axiosInstance.delete(`${API_BASE}/download-resources/${name}`);
}
export async function listRecords(params: {
resourceSlug?: string;
page?: number;
size?: number;
}): Promise<ListResult<DownloadRecord>> {
const { data } = await axiosInstance.get<ListResult<DownloadRecord>>(
`${API_BASE}/download-records`,
{ params }
);
return data;
}
export async function deleteRecord(name: string): Promise<void> {
await axiosInstance.delete(`${API_BASE}/download-records/${name}`);
}
// CSV 导出走浏览器直接导航(console 端点接受 session cookie
export function recordsExportUrl(resourceSlug?: string): string {
const query = resourceSlug
? `?resourceSlug=${encodeURIComponent(resourceSlug)}`
: '';
return `${API_BASE}/download-records/-/export${query}`;
}
export async function getReferences(): Promise<ReferencesMap> {
const { data } = await axiosInstance.get<ReferencesMap>(
`${API_BASE}/references`
);
return data;
}
export async function refreshReferences(): Promise<ReferencesMap> {
const { data } = await axiosInstance.post<ReferencesMap>(
`${API_BASE}/references/-/refresh`
);
return data;
}
// ---------- 受保护链接 ----------
interface LinkVo {
name: string;
slug: string;
displayName: string;
description?: string;
targetUrl?: string;
requireEmailVerify?: boolean;
enabled?: boolean;
visitUrl?: string;
creationTimestamp?: string;
stats?: {
visitCount?: number;
visitorCount?: number;
referenceCount?: number;
};
}
function normalizeLink(vo: LinkVo): ProtectedLink {
return {
metadata: { name: vo.name, creationTimestamp: vo.creationTimestamp },
spec: {
slug: vo.slug,
displayName: vo.displayName,
description: vo.description,
targetUrl: vo.targetUrl,
requireEmailVerify: vo.requireEmailVerify,
enabled: vo.enabled,
},
status: { visitCount: vo.stats?.visitCount ?? 0 },
stats: {
visitorCount: vo.stats?.visitorCount ?? 0,
referenceCount: vo.stats?.referenceCount ?? 0,
},
};
}
export async function listLinks(): Promise<ProtectedLinkList> {
const { data } = await axiosInstance.get<LinkVo[]>(
`${API_BASE}/protected-links`
);
const items = (data ?? []).map(normalizeLink);
return { items, total: items.length };
}
export async function createLink(spec: ProtectedLinkSpec): Promise<ProtectedLink> {
const { data } = await axiosInstance.post<LinkVo>(
`${API_BASE}/protected-links`,
spec
);
return normalizeLink(data);
}
export async function getLink(name: string): Promise<ProtectedLink> {
const { data } = await axiosInstance.get<LinkVo>(
`${API_BASE}/protected-links/${name}`
);
return normalizeLink(data);
}
export async function updateLink(
name: string,
spec: ProtectedLinkSpec
): Promise<ProtectedLink> {
const { data } = await axiosInstance.put<LinkVo>(
`${API_BASE}/protected-links/${name}`,
spec
);
return normalizeLink(data);
}
export async function deleteLink(name: string): Promise<void> {
await axiosInstance.delete(`${API_BASE}/protected-links/${name}`);
}
export async function listLinkRecords(params: {
linkSlug?: string;
page?: number;
size?: number;
}): Promise<ListResult<LinkVisitRecord>> {
const { data } = await axiosInstance.get<ListResult<LinkVisitRecord>>(
`${API_BASE}/link-visit-records`,
{ params }
);
return data;
}
export async function deleteLinkRecord(name: string): Promise<void> {
await axiosInstance.delete(`${API_BASE}/link-visit-records/${name}`);
}
// CSV 导出走浏览器直接导航(console 端点接受 session cookie
export function linkRecordsExportUrl(linkSlug?: string): string {
const query = linkSlug
? `?linkSlug=${encodeURIComponent(linkSlug)}`
: '';
return `${API_BASE}/link-visit-records/-/export${query}`;
}
export async function getLinkReferences(): Promise<ReferencesMap> {
const { data } = await axiosInstance.get<ReferencesMap>(
`${API_BASE}/link-references`
);
return data;
}
export async function refreshLinkReferences(): Promise<ReferencesMap> {
const { data } = await axiosInstance.post<ReferencesMap>(
`${API_BASE}/link-references/-/refresh`
);
return data;
}