Implemented a complete Angular 20 migration with modern standalone components architecture and production-ready Docker deployment: **Frontend Migration:** - Created Angular 20 application with standalone components (no NgModules) - Implemented three main components: artifacts-list, upload-form, query-form - Added TypeScript models and services for type-safe API communication - Migrated dark theme UI with all existing features - Configured routing and navigation between views - Set up development proxy for seamless API integration - Reactive forms with validation for upload and query functionality - Auto-refresh artifacts every 5 seconds with RxJS observables - Client-side sorting, filtering, and search capabilities - Tags displayed as inline badges, SIM source grouping support **Docker Integration:** - Multi-stage Dockerfile for Angular (Node 24 build, nginx Alpine serve) - nginx configuration for SPA routing and API proxy - Updated docker-compose.yml with frontend service on port 80 - Health checks for all services - Production-optimized build with gzip compression and asset caching **Technical Stack:** - Angular 20 with standalone components - TypeScript for type safety - RxJS for reactive programming - nginx as reverse proxy - Multi-stage Docker builds for optimal image size All features fully functional and tested in Docker environment. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
import { Injectable } from '@angular/core';
|
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
|
import { Observable } from 'rxjs';
|
|
import { Artifact, ArtifactQuery, ApiInfo } from '../models/artifact.model';
|
|
|
|
@Injectable({
|
|
providedIn: 'root'
|
|
})
|
|
export class ArtifactService {
|
|
private apiUrl = '/api/v1/artifacts';
|
|
private baseUrl = '/api';
|
|
|
|
constructor(private http: HttpClient) {}
|
|
|
|
getApiInfo(): Observable<ApiInfo> {
|
|
return this.http.get<ApiInfo>(this.baseUrl);
|
|
}
|
|
|
|
listArtifacts(limit: number = 100, offset: number = 0): Observable<Artifact[]> {
|
|
const params = new HttpParams()
|
|
.set('limit', limit.toString())
|
|
.set('offset', offset.toString());
|
|
return this.http.get<Artifact[]>(this.apiUrl + '/', { params });
|
|
}
|
|
|
|
getArtifact(id: number): Observable<Artifact> {
|
|
return this.http.get<Artifact>(`${this.apiUrl}/${id}`);
|
|
}
|
|
|
|
queryArtifacts(query: ArtifactQuery): Observable<Artifact[]> {
|
|
return this.http.post<Artifact[]>(`${this.apiUrl}/query`, query);
|
|
}
|
|
|
|
uploadArtifact(formData: FormData): Observable<Artifact> {
|
|
return this.http.post<Artifact>(`${this.apiUrl}/upload`, formData);
|
|
}
|
|
|
|
downloadArtifact(id: number): Observable<Blob> {
|
|
return this.http.get(`${this.apiUrl}/${id}/download`, {
|
|
responseType: 'blob'
|
|
});
|
|
}
|
|
|
|
deleteArtifact(id: number): Observable<any> {
|
|
return this.http.delete(`${this.apiUrl}/${id}`);
|
|
}
|
|
|
|
generateSeedData(count: number): Observable<any> {
|
|
return this.http.post(`/api/v1/seed/generate/${count}`, {});
|
|
}
|
|
}
|