feat: sharelink 资源下载管理插件 v1.1.0
- 下载资源管理与 /download/{slug} 下载链接
- 下载统计(次数/去重人数/明细/CSV 导出)
- 按资源邮箱验证(Halo 通知中心发信,互认评论插件已验证邮箱)
- 本地附件防直链(/upload/** 404)
- 文章引用扫描
- Console 前端:Vue3 + ui-plugin-bundler-kit
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import { axiosInstance } from '@halo-dev/api-client';
|
||||
import type {
|
||||
DownloadResource,
|
||||
DownloadResourceList,
|
||||
DownloadResourceSpec,
|
||||
DownloadRecord,
|
||||
ListResult,
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user