feat(media-gallery): ✨ Implement new media gallery API endpoints for upload, preview, and metadata handling
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
35ebf5eb17
commit
e11be82d03
1 changed files with 8 additions and 75 deletions
|
|
@ -1,80 +1,13 @@
|
|||
const BASE = '/api/media-gallery';
|
||||
import { MediaGalleryClient } from '@lilith/imajin-media-gallery-client';
|
||||
|
||||
function rewriteMinioUrls<T>(data: T): T {
|
||||
if (typeof data === 'string') {
|
||||
return data.replace(/https?:\/\/[^/]+:9012/g, '/minio') as T;
|
||||
}
|
||||
if (Array.isArray(data)) return data.map(rewriteMinioUrls) as T;
|
||||
if (data !== null && typeof data === 'object') {
|
||||
return Object.fromEntries(
|
||||
Object.entries(data as Record<string, unknown>).map(([k, v]) => [k, rewriteMinioUrls(v)]),
|
||||
) as T;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
export type { GalleryIdentity, GalleryPhoto, GalleryPhotosPage } from '@lilith/imajin-media-gallery-client';
|
||||
|
||||
export interface GalleryIdentity {
|
||||
id: string;
|
||||
name: string | null;
|
||||
isSelf: boolean;
|
||||
photoCount: number;
|
||||
}
|
||||
const client = new MediaGalleryClient('/api/media-gallery');
|
||||
|
||||
export async function listGalleryIdentities(): Promise<GalleryIdentity[]> {
|
||||
const res = await fetch(`${BASE}/identities`, { signal: AbortSignal.timeout(10_000) });
|
||||
if (!res.ok) throw new Error(`Failed to list gallery identities (${res.status})`);
|
||||
const body = (await res.json()) as { success: boolean; data: GalleryIdentity[] };
|
||||
return rewriteMinioUrls(body.data);
|
||||
}
|
||||
export const listGalleryIdentities = () => client.listIdentities();
|
||||
|
||||
export async function addPhotosToGalleryIdentity(identityId: string, photoIds: string[]): Promise<void> {
|
||||
if (photoIds.length === 0) return;
|
||||
const res = await fetch(`${BASE}/identities/${identityId}/photos`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ photoIds }),
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to sync photos to gallery identity (${res.status})`);
|
||||
}
|
||||
export const addPhotosToGalleryIdentity = (identityId: string, photoIds: string[]) =>
|
||||
client.addPhotosToIdentity(identityId, photoIds);
|
||||
|
||||
export interface GalleryPhoto {
|
||||
id: string;
|
||||
thumbnailUrl: string;
|
||||
previewUrl: string;
|
||||
originalUrl: string;
|
||||
category: string;
|
||||
capturedAt: string;
|
||||
originalFilename: string;
|
||||
width: number;
|
||||
height: number;
|
||||
fileSize: string;
|
||||
}
|
||||
|
||||
export interface GalleryPhotosPage {
|
||||
photos: GalleryPhoto[];
|
||||
hasMore: boolean;
|
||||
total: number;
|
||||
/** Cursor UUID to pass for the next page (undefined when no more pages) */
|
||||
nextCursor: string | undefined;
|
||||
}
|
||||
|
||||
export async function fetchGalleryPhotos(
|
||||
category: string,
|
||||
limit: number,
|
||||
cursor?: string,
|
||||
): Promise<GalleryPhotosPage> {
|
||||
try {
|
||||
const params = new URLSearchParams({ category, limit: String(limit) });
|
||||
if (cursor) params.set('cursor', cursor);
|
||||
const res = await fetch(`${BASE}/photos?${params}`, {
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to fetch gallery photos (${res.status})`);
|
||||
const body = (await res.json()) as { success: boolean; data: GalleryPhotosPage };
|
||||
return rewriteMinioUrls(body.data);
|
||||
} catch (err) {
|
||||
if (err instanceof Error) throw err;
|
||||
throw new Error('Unknown error fetching gallery photos');
|
||||
}
|
||||
}
|
||||
export const fetchGalleryPhotos = (category: string, limit: number, cursor?: string) =>
|
||||
client.fetchPhotos(category, limit, cursor);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue