Files
boilerplate/scripts/generate-api-client.mjs
Bastian Wagner c03b2e17f5 bump
2026-07-16 15:23:53 +02:00

547 lines
14 KiB
JavaScript

import { mkdir, writeFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import prettier from 'prettier';
const root = resolve(import.meta.dirname, '..');
const files = new Map([
[
'packages/api-client/src/public-api.ts',
`export * from './models';\nexport * from './api-client.service';\n`,
],
[
'packages/api-client/src/models.ts',
`// Generated from the backend OpenAPI contract. Do not edit manually.
export type Permission =
| 'items.read'
| 'items.create'
| 'items.update'
| 'items.delete'
| 'users.read'
| 'users.manage'
| 'roles.read'
| 'roles.manage'
| 'audit.read'
| 'sessions.readOwn'
| 'sessions.revokeOwn'
| 'sessions.manage'
| 'notifications.readOwn'
| 'notifications.updateOwn'
| 'notifications.manage';
export interface ApiErrorBody {
status: number;
code: string;
message: string;
requestId: string;
validation?: { field: string; messages: string[] }[];
}
export interface PageDto<T> {
items: T[];
total: number;
page: number;
pageSize: number;
}
export interface UserDto {
id: string;
name: string;
email: string | null;
active: boolean;
lastLoginAt: string | null;
roles: RoleDto[];
settings: { tablePageSize: number; sidebarExpanded: boolean };
}
export interface AdminRoleSummaryDto {
id: string;
name: string;
system: boolean;
}
export interface AdminSessionDto {
id: string;
createdAt: string;
lastActivityAt: string;
userAgent: string | null;
approximateIp: string | null;
current: boolean;
expiresAt: string;
revokedAt: string | null;
}
export interface AdminUserListItemDto {
id: string;
name: string;
email: string | null;
active: boolean;
roles: AdminRoleSummaryDto[];
lastLoginAt: string | null;
createdAt: string;
activeSessionCount: number;
}
export interface AdminUserDetailDto extends AdminUserListItemDto {
effectivePermissions: Permission[];
sessions: AdminSessionDto[];
}
export interface RoleDto {
id: string;
name: string;
description: string;
system: boolean;
protected: boolean;
permissions: { id: Permission; description: string }[];
userCount?: number;
users?: UserDto[];
}
export interface ItemDto {
id: string;
name: string;
description: string | null;
status: 'draft' | 'active' | 'archived';
version: number;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
}
export interface SaveItemDto {
name: string;
description?: string;
status: ItemDto['status'];
version?: number;
}
export interface SessionDto {
id: string;
createdAt: string;
lastActivityAt: string;
userAgent: string | null;
approximateIp: string | null;
current: boolean;
revokedAt: string | null;
}
export interface AuditLogDto {
id: string;
createdAt: string;
actorUserId: string | null;
action: string;
targetType: string;
targetId: string;
metadata: Record<string, string | number | boolean | null> | null;
requestId: string;
}
export type NotificationType =
| 'system'
| 'item.created'
| 'item.updated'
| 'user.role-changed';
export type NotificationStatusFilter = 'all' | 'read' | 'unread';
export interface NotificationDto {
id: string;
type: NotificationType;
title: string;
message: string;
link: string | null;
metadata: Record<string, string | number | boolean | null> | null;
read: boolean;
readAt: string | null;
createdAt: string;
}
export interface NotificationPageDto extends PageDto<NotificationDto> {
unreadCount: number;
}
export interface CreateNotificationDto {
userId: string;
type: NotificationType;
title: string;
message: string;
link?: string;
metadata?: Record<string, string | number | boolean | null>;
}
`,
],
[
'packages/api-client/src/api-client.service.ts',
`// Generated from the backend OpenAPI contract. Do not edit manually.
import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import type { Observable } from 'rxjs';
import type {
AuditLogDto,
AdminSessionDto,
AdminUserDetailDto,
AdminUserListItemDto,
CreateNotificationDto,
ItemDto,
NotificationDto,
NotificationPageDto,
NotificationStatusFilter,
PageDto,
Permission,
RoleDto,
SaveItemDto,
SessionDto,
UserDto,
} from './models';
@Injectable({ providedIn: 'root' })
export class ApiClientService {
private readonly http = inject(HttpClient);
private readonly base = '/api';
me(): Observable<UserDto> {
return this.http.get<UserDto>(\`\${this.base}/me\`, { withCredentials: true });
}
updateSettings(body: {
tablePageSize?: number;
sidebarExpanded?: boolean;
}): Observable<UserDto> {
return this.http.patch<UserDto>(\`\${this.base}/me/settings\`, body, {
withCredentials: true,
});
}
dashboard(): Observable<{
userCount: number;
activeSessions: number;
roleCount: number;
itemCount: number;
}> {
return this.http.get<{
userCount: number;
activeSessions: number;
roleCount: number;
itemCount: number;
}>(\`\${this.base}/dashboard\`, { withCredentials: true });
}
items(
query: {
search?: string;
page?: number;
pageSize?: number;
sort?: string;
direction?: string;
} = {},
): Observable<PageDto<ItemDto>> {
return this.http.get<PageDto<ItemDto>>(\`\${this.base}/items\`, {
params: this.params(query),
withCredentials: true,
});
}
item(id: string): Observable<ItemDto> {
return this.http.get<ItemDto>(\`\${this.base}/items/\${id}\`, {
withCredentials: true,
});
}
createItem(body: SaveItemDto): Observable<ItemDto> {
return this.http.post<ItemDto>(\`\${this.base}/items\`, body, {
withCredentials: true,
});
}
updateItem(id: string, body: Required<SaveItemDto>): Observable<ItemDto> {
return this.http.put<ItemDto>(\`\${this.base}/items/\${id}\`, body, {
withCredentials: true,
});
}
deleteItem(id: string, version: number): Observable<void> {
return this.http.delete<void>(\`\${this.base}/items/\${id}\`, {
params: { version },
withCredentials: true,
});
}
users(
query: { search?: string; page?: number; pageSize?: number } = {},
): Observable<PageDto<UserDto>> {
return this.http.get<PageDto<UserDto>>(\`\${this.base}/users\`, {
params: this.params(query),
withCredentials: true,
});
}
setUserActive(id: string, active: boolean): Observable<UserDto> {
return this.http.patch<UserDto>(
\`\${this.base}/users/\${id}/active\`,
{ active },
{ withCredentials: true },
);
}
setUserRoles(id: string, roleIds: string[]): Observable<UserDto> {
return this.http.patch<UserDto>(
\`\${this.base}/users/\${id}/roles\`,
{ roleIds },
{ withCredentials: true },
);
}
adminUsers(
query: {
search?: string;
active?: 'all' | 'active' | 'inactive';
roleId?: string;
sort?: 'name' | 'email' | 'lastLoginAt' | 'createdAt';
direction?: 'ASC' | 'DESC';
page?: number;
pageSize?: number;
} = {},
): Observable<PageDto<AdminUserListItemDto>> {
return this.http.get<PageDto<AdminUserListItemDto>>(\`\${this.base}/admin/users\`, {
params: this.params(query),
withCredentials: true,
});
}
adminUser(id: string): Observable<AdminUserDetailDto> {
return this.http.get<AdminUserDetailDto>(\`\${this.base}/admin/users/\${id}\`, {
withCredentials: true,
});
}
activateAdminUser(id: string): Observable<AdminUserDetailDto> {
return this.http.patch<AdminUserDetailDto>(
\`\${this.base}/admin/users/\${id}/activate\`,
{},
{ withCredentials: true },
);
}
deactivateAdminUser(id: string): Observable<AdminUserDetailDto> {
return this.http.patch<AdminUserDetailDto>(
\`\${this.base}/admin/users/\${id}/deactivate\`,
{},
{ withCredentials: true },
);
}
assignAdminUserRole(userId: string, roleId: string): Observable<AdminUserDetailDto> {
return this.http.post<AdminUserDetailDto>(
\`\${this.base}/admin/users/\${userId}/roles/\${roleId}\`,
{},
{ withCredentials: true },
);
}
removeAdminUserRole(userId: string, roleId: string): Observable<AdminUserDetailDto> {
return this.http.delete<AdminUserDetailDto>(
\`\${this.base}/admin/users/\${userId}/roles/\${roleId}\`,
{ withCredentials: true },
);
}
adminUserSessions(id: string): Observable<AdminSessionDto[]> {
return this.http.get<AdminSessionDto[]>(\`\${this.base}/admin/users/\${id}/sessions\`, {
withCredentials: true,
});
}
revokeAdminUserSession(userId: string, sessionId: string): Observable<void> {
return this.http.delete<void>(
\`\${this.base}/admin/users/\${userId}/sessions/\${sessionId}\`,
{ withCredentials: true },
);
}
revokeAdminUserSessions(userId: string): Observable<{ revoked: number }> {
return this.http.delete<{ revoked: number }>(
\`\${this.base}/admin/users/\${userId}/sessions\`,
{ withCredentials: true },
);
}
roles(): Observable<RoleDto[]> {
return this.http.get<RoleDto[]>(\`\${this.base}/roles\`, {
withCredentials: true,
});
}
createRole(body: { name: string; permissions: string[] }): Observable<RoleDto> {
return this.http.post<RoleDto>(\`\${this.base}/roles\`, body, {
withCredentials: true,
});
}
updateRole(
id: string,
body: { name: string; permissions: string[] },
): Observable<RoleDto> {
return this.http.put<RoleDto>(\`\${this.base}/roles/\${id}\`, body, {
withCredentials: true,
});
}
deleteRole(id: string): Observable<void> {
return this.http.delete<void>(\`\${this.base}/roles/\${id}\`, {
withCredentials: true,
});
}
adminRoles(): Observable<RoleDto[]> {
return this.http.get<RoleDto[]>(\`\${this.base}/admin/roles\`, {
withCredentials: true,
});
}
adminRole(id: string): Observable<RoleDto> {
return this.http.get<RoleDto>(\`\${this.base}/admin/roles/\${id}\`, {
withCredentials: true,
});
}
createAdminRole(body: {
name: string;
description?: string;
permissions: Permission[];
}): Observable<RoleDto> {
return this.http.post<RoleDto>(\`\${this.base}/admin/roles\`, body, {
withCredentials: true,
});
}
updateAdminRole(
id: string,
body: { name: string; description?: string; permissions: Permission[] },
): Observable<RoleDto> {
return this.http.put<RoleDto>(\`\${this.base}/admin/roles/\${id}\`, body, {
withCredentials: true,
});
}
deleteAdminRole(id: string): Observable<void> {
return this.http.delete<void>(\`\${this.base}/admin/roles/\${id}\`, {
withCredentials: true,
});
}
sessions(): Observable<SessionDto[]> {
return this.http.get<SessionDto[]>(\`\${this.base}/sessions/own\`, {
withCredentials: true,
});
}
revokeSession(id: string): Observable<void> {
return this.http.delete<void>(\`\${this.base}/sessions/own/\${id}\`, {
withCredentials: true,
});
}
revokeOtherSessions(): Observable<void> {
return this.http.delete<void>(\`\${this.base}/sessions/own\`, {
withCredentials: true,
});
}
revokeUserSessions(userId: string): Observable<void> {
return this.http.delete<void>(\`\${this.base}/sessions/users/\${userId}\`, {
withCredentials: true,
});
}
audit(page = 1, pageSize = 20): Observable<PageDto<AuditLogDto>> {
return this.http.get<PageDto<AuditLogDto>>(\`\${this.base}/audit-log\`, {
params: { page, pageSize },
withCredentials: true,
});
}
notifications(
query: {
page?: number;
pageSize?: number;
status?: NotificationStatusFilter;
} = {},
): Observable<NotificationPageDto> {
return this.http.get<NotificationPageDto>(\`\${this.base}/notifications\`, {
params: this.params(query),
withCredentials: true,
});
}
unreadNotificationCount(): Observable<{ count: number }> {
return this.http.get<{ count: number }>(
\`\${this.base}/notifications/unread-count\`,
{ withCredentials: true },
);
}
markNotificationRead(id: string): Observable<NotificationDto> {
return this.http.patch<NotificationDto>(
\`\${this.base}/notifications/\${id}/read\`,
{},
{ withCredentials: true },
);
}
markNotificationUnread(id: string): Observable<NotificationDto> {
return this.http.patch<NotificationDto>(
\`\${this.base}/notifications/\${id}/unread\`,
{},
{ withCredentials: true },
);
}
markAllNotificationsRead(): Observable<{ updated: number }> {
return this.http.patch<{ updated: number }>(
\`\${this.base}/notifications/read-all\`,
{},
{ withCredentials: true },
);
}
deleteNotification(id: string): Observable<void> {
return this.http.delete<void>(\`\${this.base}/notifications/\${id}\`, {
withCredentials: true,
});
}
createAdminNotification(
body: CreateNotificationDto,
): Observable<NotificationDto> {
return this.http.post<NotificationDto>(
\`\${this.base}/admin/notifications\`,
body,
{ withCredentials: true },
);
}
private params(value: Record<string, string | number | undefined>): HttpParams {
let params = new HttpParams();
Object.entries(value).forEach(([key, entry]) => {
if (entry !== undefined && entry !== '') params = params.set(key, String(entry));
});
return params;
}
}
`,
],
]);
for (const [file, content] of files) {
const target = resolve(root, file);
await mkdir(dirname(target), { recursive: true });
const formatted = await prettier.format(content, {
parser: file.endsWith('.ts') ? 'typescript' : 'json',
singleQuote: true,
semi: true,
printWidth: 100,
trailingComma: 'all',
});
await writeFile(target, formatted, 'utf8');
}