Backend changes:
- Add format and platform columns to Package model with validation
- Add database migrations for new columns with indexes
- Create PackageDetailResponse schema with aggregated fields:
- tag_count, artifact_count, total_size
- latest_tag, latest_upload_at
- recent_tags (last 5 tags)
- Update list_packages endpoint:
- Add pagination (page, limit)
- Add search by name/description
- Add sort (name, created_at, updated_at) and order (asc, desc)
- Add format and platform filters
- Return aggregated metadata for each package
- Add GET /api/v1/project/{project}/packages/{package_name} endpoint
- Returns single package with full metadata
- Optional include_tags parameter for all tags
- Update create_package to accept format and platform
Frontend changes:
- Update Package type with format, platform, and optional metadata fields
- Update listPackages to handle paginated response
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import { Project, Package, Tag, Artifact, UploadResponse } from './types';
|
|
|
|
const API_BASE = '/api/v1';
|
|
|
|
async function handleResponse<T>(response: Response): Promise<T> {
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
|
|
throw new Error(error.detail || `HTTP ${response.status}`);
|
|
}
|
|
return response.json();
|
|
}
|
|
|
|
// Paginated response type
|
|
interface PaginatedResponse<T> {
|
|
items: T[];
|
|
pagination: {
|
|
page: number;
|
|
limit: number;
|
|
total: number;
|
|
total_pages: number;
|
|
};
|
|
}
|
|
|
|
// Project API
|
|
export async function listProjects(): Promise<Project[]> {
|
|
const response = await fetch(`${API_BASE}/projects`);
|
|
const data = await handleResponse<PaginatedResponse<Project>>(response);
|
|
return data.items;
|
|
}
|
|
|
|
export async function createProject(data: { name: string; description?: string; is_public?: boolean }): Promise<Project> {
|
|
const response = await fetch(`${API_BASE}/projects`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
});
|
|
return handleResponse<Project>(response);
|
|
}
|
|
|
|
export async function getProject(name: string): Promise<Project> {
|
|
const response = await fetch(`${API_BASE}/projects/${name}`);
|
|
return handleResponse<Project>(response);
|
|
}
|
|
|
|
// Package API
|
|
export async function listPackages(projectName: string): Promise<Package[]> {
|
|
const response = await fetch(`${API_BASE}/project/${projectName}/packages`);
|
|
const data = await handleResponse<PaginatedResponse<Package>>(response);
|
|
return data.items;
|
|
}
|
|
|
|
export async function createPackage(projectName: string, data: { name: string; description?: string }): Promise<Package> {
|
|
const response = await fetch(`${API_BASE}/project/${projectName}/packages`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
});
|
|
return handleResponse<Package>(response);
|
|
}
|
|
|
|
// Tag API
|
|
export async function listTags(projectName: string, packageName: string): Promise<Tag[]> {
|
|
const response = await fetch(`${API_BASE}/project/${projectName}/${packageName}/tags`);
|
|
return handleResponse<Tag[]>(response);
|
|
}
|
|
|
|
export async function createTag(projectName: string, packageName: string, data: { name: string; artifact_id: string }): Promise<Tag> {
|
|
const response = await fetch(`${API_BASE}/project/${projectName}/${packageName}/tags`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
});
|
|
return handleResponse<Tag>(response);
|
|
}
|
|
|
|
// Artifact API
|
|
export async function getArtifact(artifactId: string): Promise<Artifact> {
|
|
const response = await fetch(`${API_BASE}/artifact/${artifactId}`);
|
|
return handleResponse<Artifact>(response);
|
|
}
|
|
|
|
// Upload
|
|
export async function uploadArtifact(projectName: string, packageName: string, file: File, tag?: string): Promise<UploadResponse> {
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
if (tag) {
|
|
formData.append('tag', tag);
|
|
}
|
|
|
|
const response = await fetch(`${API_BASE}/project/${projectName}/${packageName}/upload`, {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
return handleResponse<UploadResponse>(response);
|
|
}
|
|
|
|
// Download URL
|
|
export function getDownloadUrl(projectName: string, packageName: string, ref: string): string {
|
|
return `${API_BASE}/project/${projectName}/${packageName}/+/${ref}`;
|
|
}
|