fix: resolve SecurityHeaders page rendering issue
Update frontend API layer to correctly unwrap backend response objects.
Backend returns wrapped responses (e.g., {profiles: [...]}) while frontend
was expecting unwrapped arrays. Fixed 6 API methods in securityHeaders.ts
to properly extract data from response wrappers.
Changes:
- listProfiles(): unwrap .profiles
- getProfile(): unwrap .profile
- createProfile(): unwrap .profile
- updateProfile(): unwrap .profile
- getPresets(): unwrap .presets
- applyPreset(): unwrap .profile
This commit is contained in:
@@ -83,32 +83,32 @@ export const securityHeadersApi = {
|
||||
* List all security header profiles
|
||||
*/
|
||||
async listProfiles(): Promise<SecurityHeaderProfile[]> {
|
||||
const response = await client.get<SecurityHeaderProfile[]>('/security/headers/profiles');
|
||||
return response.data;
|
||||
const response = await client.get<{profiles: SecurityHeaderProfile[]}>('/security/headers/profiles');
|
||||
return response.data.profiles;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a single profile by ID or UUID
|
||||
*/
|
||||
async getProfile(id: number | string): Promise<SecurityHeaderProfile> {
|
||||
const response = await client.get<SecurityHeaderProfile>(`/security/headers/profiles/${id}`);
|
||||
return response.data;
|
||||
const response = await client.get<{profile: SecurityHeaderProfile}>(`/security/headers/profiles/${id}`);
|
||||
return response.data.profile;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new security header profile
|
||||
*/
|
||||
async createProfile(data: CreateProfileRequest): Promise<SecurityHeaderProfile> {
|
||||
const response = await client.post<SecurityHeaderProfile>('/security/headers/profiles', data);
|
||||
return response.data;
|
||||
const response = await client.post<{profile: SecurityHeaderProfile}>('/security/headers/profiles', data);
|
||||
return response.data.profile;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update an existing profile
|
||||
*/
|
||||
async updateProfile(id: number, data: Partial<CreateProfileRequest>): Promise<SecurityHeaderProfile> {
|
||||
const response = await client.put<SecurityHeaderProfile>(`/security/headers/profiles/${id}`, data);
|
||||
return response.data;
|
||||
const response = await client.put<{profile: SecurityHeaderProfile}>(`/security/headers/profiles/${id}`, data);
|
||||
return response.data.profile;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -122,16 +122,16 @@ export const securityHeadersApi = {
|
||||
* Get built-in presets
|
||||
*/
|
||||
async getPresets(): Promise<SecurityHeaderPreset[]> {
|
||||
const response = await client.get<SecurityHeaderPreset[]>('/security/headers/presets');
|
||||
return response.data;
|
||||
const response = await client.get<{presets: SecurityHeaderPreset[]}>('/security/headers/presets');
|
||||
return response.data.presets;
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply a preset to create/update a profile
|
||||
*/
|
||||
async applyPreset(data: ApplyPresetRequest): Promise<SecurityHeaderProfile> {
|
||||
const response = await client.post<SecurityHeaderProfile>('/security/headers/presets/apply', data);
|
||||
return response.data;
|
||||
const response = await client.post<{profile: SecurityHeaderProfile}>('/security/headers/presets/apply', data);
|
||||
return response.data.profile;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,7 @@ export default function SecurityHeaders() {
|
||||
setEditingProfile(null);
|
||||
toast.success(`"${profile.name}" deleted. A backup was created before deletion.`);
|
||||
},
|
||||
onError: (error) => {
|
||||
onError: (error: Error) => {
|
||||
toast.error(`Failed to delete: ${error.message}`);
|
||||
},
|
||||
onSettled: () => {
|
||||
@@ -114,8 +114,8 @@ export default function SecurityHeaders() {
|
||||
createMutation.mutate(clonedData);
|
||||
};
|
||||
|
||||
const customProfiles = profiles?.filter((p) => !p.is_preset) || [];
|
||||
const presetProfiles = profiles?.filter((p) => p.is_preset) || [];
|
||||
const customProfiles = profiles?.filter((p: SecurityHeaderProfile) => !p.is_preset) || [];
|
||||
const presetProfiles = profiles?.filter((p: SecurityHeaderProfile) => p.is_preset) || [];
|
||||
|
||||
return (
|
||||
<PageShell
|
||||
@@ -181,7 +181,7 @@ export default function SecurityHeaders() {
|
||||
<div className="mb-8">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">System Presets</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{presetProfiles.map((profile) => (
|
||||
{presetProfiles.map((profile: SecurityHeaderProfile) => (
|
||||
<Card key={profile.id} className="p-4">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
@@ -241,7 +241,7 @@ export default function SecurityHeaders() {
|
||||
/>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{customProfiles.map((profile) => (
|
||||
{customProfiles.map((profile: SecurityHeaderProfile) => (
|
||||
<Card key={profile.id} className="p-4">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
@@ -291,7 +291,7 @@ export default function SecurityHeaders() {
|
||||
</div>
|
||||
|
||||
{/* Create/Edit Dialog */}
|
||||
<Dialog open={showCreateForm || editingProfile !== null} onOpenChange={(open) => {
|
||||
<Dialog open={showCreateForm || editingProfile !== null} onOpenChange={(open: boolean) => {
|
||||
if (!open) {
|
||||
setShowCreateForm(false);
|
||||
setEditingProfile(null);
|
||||
@@ -318,7 +318,7 @@ export default function SecurityHeaders() {
|
||||
</Dialog>
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<Dialog open={showDeleteConfirm !== null} onOpenChange={(open) => !open && setShowDeleteConfirm(null)}>
|
||||
<Dialog open={showDeleteConfirm !== null} onOpenChange={(open: boolean) => !open && setShowDeleteConfirm(null)}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Confirm Deletion</DialogTitle>
|
||||
|
||||
Reference in New Issue
Block a user