Files
reactbin/ui/src/app/services/image.service.ts
agatha 61d923d5be Feat: Replace UUID image identifiers with 8-character base62 short IDs
Short IDs become the canonical identifier in URLs (/i/:short_id),
MinIO/R2 storage keys, and all API responses. Hash-based deduplication
is preserved. Includes two-phase Alembic migration (003 adds nullable
column, 004 enforces NOT NULL) with a backfill script to copy storage
objects and populate short_id for existing images.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:13:55 +00:00

65 lines
1.7 KiB
TypeScript

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface ImageRecord {
id: string;
short_id: string;
hash: string;
filename: string;
mime_type: string;
size_bytes: number;
width: number;
height: number;
storage_key: string;
thumbnail_key: string | null;
file_url: string;
thumbnail_url: string | null;
created_at: string;
tags: string[];
duplicate?: boolean;
}
export interface ImageListResponse {
items: ImageRecord[];
total: number;
limit: number;
offset: number;
}
@Injectable({ providedIn: 'root' })
export class ImageService {
private readonly base = '/api/v1';
constructor(private http: HttpClient) {}
upload(file: File, tags: string[]): Observable<ImageRecord> {
const form = new FormData();
form.append('file', file);
if (tags.length) {
form.append('tags', tags.join(','));
}
return this.http.post<ImageRecord>(`${this.base}/images`, form);
}
list(tagFilter: string[] = [], limit = 50, offset = 0): Observable<ImageListResponse> {
let params = new HttpParams().set('limit', limit).set('offset', offset);
if (tagFilter.length) {
params = params.set('tags', tagFilter.join(','));
}
return this.http.get<ImageListResponse>(`${this.base}/images`, { params });
}
get(id: string): Observable<ImageRecord> {
return this.http.get<ImageRecord>(`${this.base}/i/${id}`);
}
updateTags(id: string, tags: string[]): Observable<ImageRecord> {
return this.http.patch<ImageRecord>(`${this.base}/i/${id}/tags`, { tags });
}
delete(id: string): Observable<void> {
return this.http.delete<void>(`${this.base}/i/${id}`);
}
}