AI Implementation feature(867): Admin Area Players Management (#56)
This commit was merged in pull request #56.
This commit is contained in:
@@ -10,6 +10,7 @@ const FRIENDLY_MESSAGES: Record<string, string> = {
|
||||
FORBIDDEN: 'You are not allowed to perform this action.',
|
||||
NOT_FOUND: 'Not found.',
|
||||
VALIDATION_FAILED: 'Invalid input.',
|
||||
LAST_ADMIN: 'At least one admin must remain in the system. The action was rejected.',
|
||||
};
|
||||
|
||||
function friendlyMessage(status: number, body: any): string {
|
||||
@@ -48,5 +49,7 @@ function shouldSuppress(url: string | undefined, status: number): boolean {
|
||||
if (status === 401 && /\/api\/v1\/auth\/(login|csrf|register)/.test(url)) return true;
|
||||
// SSE / event-status snapshot errors are non-fatal (SSE will deliver the next frame).
|
||||
if (status === 401 && url.endsWith('/api/v1/challenges/status')) return true;
|
||||
// Admin Players modal owns its own inline error presentation.
|
||||
if (/\/api\/v1\/admin\/players\/.+\/password/.test(url)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,19 @@ export interface AdminUser {
|
||||
id: string;
|
||||
username: string;
|
||||
role: 'admin' | 'player';
|
||||
status?: 'enabled' | 'disabled';
|
||||
createdAt?: string;
|
||||
}
|
||||
|
||||
export interface AdminResetPasswordBody {
|
||||
newPassword: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
export class AdminLastAdminError extends Error {
|
||||
constructor() {
|
||||
super('At least one admin must remain in the system.');
|
||||
}
|
||||
}
|
||||
|
||||
export interface GeneralSettings {
|
||||
@@ -172,8 +185,57 @@ export class AdminService {
|
||||
private readonly http = inject(HttpClient);
|
||||
|
||||
async listUsers(): Promise<AdminUser[]> {
|
||||
return this.listPlayers();
|
||||
}
|
||||
|
||||
async listPlayers(): Promise<AdminUser[]> {
|
||||
const rows = await firstValueFrom(
|
||||
this.http.get<AdminUser[]>('/api/v1/admin/players', { withCredentials: true }),
|
||||
);
|
||||
return rows.map((u) => ({
|
||||
id: u.id,
|
||||
username: u.username,
|
||||
role: u.role,
|
||||
status: u.status ?? 'enabled',
|
||||
createdAt: u.createdAt ?? new Date().toISOString(),
|
||||
}));
|
||||
}
|
||||
|
||||
async updatePlayerRole(id: string, role: 'admin' | 'player'): Promise<AdminUser> {
|
||||
return firstValueFrom(
|
||||
this.http.get<AdminUser[]>('/api/v1/admin/users', { withCredentials: true }),
|
||||
this.http.patch<AdminUser>(
|
||||
`/api/v1/admin/players/${encodeURIComponent(id)}/role`,
|
||||
{ role },
|
||||
{ withCredentials: true },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async updatePlayerStatus(id: string, enabled: boolean): Promise<AdminUser> {
|
||||
return firstValueFrom(
|
||||
this.http.patch<AdminUser>(
|
||||
`/api/v1/admin/players/${encodeURIComponent(id)}/status`,
|
||||
{ enabled },
|
||||
{ withCredentials: true },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async resetPlayerPassword(id: string, body: AdminResetPasswordBody): Promise<void> {
|
||||
await firstValueFrom(
|
||||
this.http.patch<void>(
|
||||
`/api/v1/admin/players/${encodeURIComponent(id)}/password`,
|
||||
body,
|
||||
{ withCredentials: true },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async deletePlayer(id: string): Promise<void> {
|
||||
await firstValueFrom(
|
||||
this.http.delete<void>(`/api/v1/admin/players/${encodeURIComponent(id)}`, {
|
||||
withCredentials: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user