- "frontend/src/components/ConfirmModal.tsx" - "frontend/src/components/ConfirmModal.module.css" - "frontend/src/api/auth.ts" - "frontend/src/context/AuthContext.tsx" - "frontend/src/pages/AdminUsers.tsx" - "frontend/src/pages/AdminUsers.module.css" - "frontend/src/components/ImpersonationBanner.tsx" - "frontend/src/components/ImpersonationBanner.module.css" GSD-Task: S07/T02
142 lines
3.8 KiB
TypeScript
142 lines
3.8 KiB
TypeScript
import { request, BASE } from "./client";
|
|
|
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
|
|
export interface RegisterRequest {
|
|
email: string;
|
|
password: string;
|
|
display_name: string;
|
|
invite_code: string;
|
|
creator_slug?: string | null;
|
|
}
|
|
|
|
export interface LoginRequest {
|
|
email: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface TokenResponse {
|
|
access_token: string;
|
|
token_type: string;
|
|
}
|
|
|
|
export interface UserResponse {
|
|
id: string;
|
|
email: string;
|
|
display_name: string;
|
|
role: string;
|
|
creator_id: string | null;
|
|
is_active: boolean;
|
|
created_at: string;
|
|
impersonating?: boolean;
|
|
}
|
|
|
|
export interface UserListItem {
|
|
id: string;
|
|
email: string;
|
|
display_name: string;
|
|
role: string;
|
|
creator_id: string | null;
|
|
is_active: boolean;
|
|
}
|
|
|
|
export interface ImpersonateResponse {
|
|
access_token: string;
|
|
token_type: string;
|
|
target_user: UserListItem;
|
|
}
|
|
|
|
export interface UpdateProfileRequest {
|
|
display_name?: string | null;
|
|
current_password?: string | null;
|
|
new_password?: string | null;
|
|
}
|
|
|
|
// ── Functions ────────────────────────────────────────────────────────────────
|
|
|
|
export async function authRegister(data: RegisterRequest): Promise<UserResponse> {
|
|
return request<UserResponse>(`${BASE}/auth/register`, {
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
export async function authLogin(email: string, password: string): Promise<TokenResponse> {
|
|
return request<TokenResponse>(`${BASE}/auth/login`, {
|
|
method: "POST",
|
|
body: JSON.stringify({ email, password }),
|
|
});
|
|
}
|
|
|
|
export async function authGetMe(token: string): Promise<UserResponse> {
|
|
return request<UserResponse>(`${BASE}/auth/me`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
}
|
|
|
|
export async function authUpdateProfile(
|
|
token: string,
|
|
data: UpdateProfileRequest,
|
|
): Promise<UserResponse> {
|
|
return request<UserResponse>(`${BASE}/auth/me`, {
|
|
method: "PUT",
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
// ── Admin: Impersonation ─────────────────────────────────────────────────────
|
|
|
|
export async function fetchUsers(token: string): Promise<UserListItem[]> {
|
|
return request<UserListItem[]>(`${BASE}/admin/users`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
}
|
|
|
|
export interface ImpersonationLogEntry {
|
|
id: number;
|
|
admin_name: string;
|
|
target_name: string;
|
|
action: string;
|
|
write_mode: boolean;
|
|
ip_address: string | null;
|
|
created_at: string;
|
|
}
|
|
|
|
export async function impersonateUser(
|
|
token: string,
|
|
userId: string,
|
|
writeMode?: boolean,
|
|
): Promise<ImpersonateResponse> {
|
|
const opts: RequestInit = {
|
|
method: "POST",
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
};
|
|
if (writeMode) {
|
|
(opts.headers as Record<string, string>)["Content-Type"] = "application/json";
|
|
opts.body = JSON.stringify({ write_mode: true });
|
|
}
|
|
return request<ImpersonateResponse>(
|
|
`${BASE}/admin/impersonate/${userId}`,
|
|
opts,
|
|
);
|
|
}
|
|
|
|
export async function fetchImpersonationLog(
|
|
token: string,
|
|
page: number = 1,
|
|
): Promise<ImpersonationLogEntry[]> {
|
|
return request<ImpersonationLogEntry[]>(
|
|
`${BASE}/admin/impersonation-log?page=${page}`,
|
|
{ headers: { Authorization: `Bearer ${token}` } },
|
|
);
|
|
}
|
|
|
|
export async function stopImpersonation(
|
|
token: string,
|
|
): Promise<{ message: string }> {
|
|
return request<{ message: string }>(`${BASE}/admin/impersonate/stop`, {
|
|
method: "POST",
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
}
|