feat: initial scaffold — MVP catalogue de données d'API

- Backend NestJS : CRUD api_entries + categories, upload YAML multi-fichiers,
  génération docs AsyncAPI (@asyncapi/cli@6.0.0) et OpenAPI (redocly)
- Fix: route wildcard GET /api/apis/:id/*path pour servir les assets statiques
  (CSS/JS) générés par AsyncAPI HTML template (contournement bug path-to-regexp v8)
- Frontend Angular 19 : pages catalog, browse, api-detail, doc-viewer, upload (DSFR)
- Seeder de données de démo (idempotent)
- Docker Compose dev + Dockerfiles + manifests K8s
- Documentation : README, architecture, référence API REST

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
z3n
2026-05-22 08:42:54 +00:00
commit 921a6e652b
87 changed files with 16719 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApiEntry, ApiListResponse, ApiListQuery } from '@datacat/shared';
@Injectable({ providedIn: 'root' })
export class ApiService {
private http = inject(HttpClient);
private baseUrl = '/api/apis';
list(query?: ApiListQuery): Observable<ApiListResponse> {
const params: Record<string, string> = {};
if (query?.search) params['search'] = query.search;
if (query?.type) params['type'] = query.type;
if (query?.categoryId) params['categoryId'] = query.categoryId;
if (query?.page) params['page'] = String(query.page);
if (query?.limit) params['limit'] = String(query.limit);
return this.http.get<ApiListResponse>(this.baseUrl, { params });
}
get(id: string): Observable<ApiEntry> {
return this.http.get<ApiEntry>(`${this.baseUrl}/${id}`);
}
update(id: string, data: { title?: string; version?: string; description?: string }): Observable<ApiEntry> {
return this.http.patch<ApiEntry>(`${this.baseUrl}/${id}`, data);
}
delete(id: string): Observable<void> {
return this.http.delete<void>(`${this.baseUrl}/${id}`);
}
regenerate(id: string): Observable<ApiEntry> {
return this.http.post<ApiEntry>(`${this.baseUrl}/${id}/regenerate`, {});
}
upload(formData: FormData): Observable<ApiEntry> {
return this.http.post<ApiEntry>('/api/uploads', formData);
}
}