mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
fix(settings): extract shared response mappers to prevent server/client shape drift
Addresses PR review feedback — prefetch.ts duplicated response mapping logic from client hooks. Extracted mapGeneralSettingsResponse and mapUserProfileResponse as shared functions used by both client fetch and server prefetch.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import type { QueryClient } from '@tanstack/react-query'
|
||||
import { headers } from 'next/headers'
|
||||
import { getInternalApiBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { generalSettingsKeys } from '@/hooks/queries/general-settings'
|
||||
import { userProfileKeys } from '@/hooks/queries/user-profile'
|
||||
import { generalSettingsKeys, mapGeneralSettingsResponse } from '@/hooks/queries/general-settings'
|
||||
import { mapUserProfileResponse, userProfileKeys } from '@/hooks/queries/user-profile'
|
||||
|
||||
/**
|
||||
* Forwards incoming request cookies so server-side API fetches authenticate correctly.
|
||||
@@ -29,17 +29,7 @@ export function prefetchGeneralSettings(queryClient: QueryClient) {
|
||||
})
|
||||
if (!response.ok) throw new Error(`Settings prefetch failed: ${response.status}`)
|
||||
const { data } = await response.json()
|
||||
return {
|
||||
autoConnect: data.autoConnect ?? true,
|
||||
showTrainingControls: data.showTrainingControls ?? false,
|
||||
superUserModeEnabled: data.superUserModeEnabled ?? true,
|
||||
theme: data.theme || 'system',
|
||||
telemetryEnabled: data.telemetryEnabled ?? true,
|
||||
billingUsageNotificationsEnabled: data.billingUsageNotificationsEnabled ?? true,
|
||||
errorNotificationsEnabled: data.errorNotificationsEnabled ?? true,
|
||||
snapToGridSize: data.snapToGridSize ?? 0,
|
||||
showActionBar: data.showActionBar ?? true,
|
||||
}
|
||||
return mapGeneralSettingsResponse(data)
|
||||
},
|
||||
staleTime: 60 * 60 * 1000,
|
||||
})
|
||||
@@ -61,14 +51,7 @@ export function prefetchUserProfile(queryClient: QueryClient) {
|
||||
})
|
||||
if (!response.ok) throw new Error(`Profile prefetch failed: ${response.status}`)
|
||||
const { user } = await response.json()
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name || '',
|
||||
email: user.email || '',
|
||||
image: user.image || null,
|
||||
createdAt: user.createdAt,
|
||||
updatedAt: user.updatedAt,
|
||||
}
|
||||
return mapUserProfileResponse(user)
|
||||
},
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
|
||||
@@ -28,6 +28,24 @@ export interface GeneralSettings {
|
||||
showActionBar: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Map raw API response data to GeneralSettings with defaults.
|
||||
* Shared by both client fetch and server prefetch to prevent shape drift.
|
||||
*/
|
||||
export function mapGeneralSettingsResponse(data: Record<string, unknown>): GeneralSettings {
|
||||
return {
|
||||
autoConnect: (data.autoConnect as boolean) ?? true,
|
||||
showTrainingControls: (data.showTrainingControls as boolean) ?? false,
|
||||
superUserModeEnabled: (data.superUserModeEnabled as boolean) ?? true,
|
||||
theme: (data.theme as GeneralSettings['theme']) || 'system',
|
||||
telemetryEnabled: (data.telemetryEnabled as boolean) ?? true,
|
||||
billingUsageNotificationsEnabled: (data.billingUsageNotificationsEnabled as boolean) ?? true,
|
||||
errorNotificationsEnabled: (data.errorNotificationsEnabled as boolean) ?? true,
|
||||
snapToGridSize: (data.snapToGridSize as number) ?? 0,
|
||||
showActionBar: (data.showActionBar as boolean) ?? true,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch general settings from API
|
||||
*/
|
||||
@@ -39,18 +57,7 @@ async function fetchGeneralSettings(signal?: AbortSignal): Promise<GeneralSettin
|
||||
}
|
||||
|
||||
const { data } = await response.json()
|
||||
|
||||
return {
|
||||
autoConnect: data.autoConnect ?? true,
|
||||
showTrainingControls: data.showTrainingControls ?? false,
|
||||
superUserModeEnabled: data.superUserModeEnabled ?? true,
|
||||
theme: data.theme || 'system',
|
||||
telemetryEnabled: data.telemetryEnabled ?? true,
|
||||
billingUsageNotificationsEnabled: data.billingUsageNotificationsEnabled ?? true,
|
||||
errorNotificationsEnabled: data.errorNotificationsEnabled ?? true,
|
||||
snapToGridSize: data.snapToGridSize ?? 0,
|
||||
showActionBar: data.showActionBar ?? true,
|
||||
}
|
||||
return mapGeneralSettingsResponse(data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,21 @@ export interface UserProfile {
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Map raw API response user object to UserProfile.
|
||||
* Shared by both client fetch and server prefetch to prevent shape drift.
|
||||
*/
|
||||
export function mapUserProfileResponse(user: Record<string, unknown>): UserProfile {
|
||||
return {
|
||||
id: user.id as string,
|
||||
name: (user.name as string) || '',
|
||||
email: (user.email as string) || '',
|
||||
image: (user.image as string) || null,
|
||||
createdAt: user.createdAt as string,
|
||||
updatedAt: user.updatedAt as string,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch user profile from API
|
||||
*/
|
||||
@@ -35,15 +50,7 @@ async function fetchUserProfile(signal?: AbortSignal): Promise<UserProfile> {
|
||||
}
|
||||
|
||||
const { user } = await response.json()
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name || '',
|
||||
email: user.email || '',
|
||||
image: user.image || null,
|
||||
createdAt: user.createdAt,
|
||||
updatedAt: user.updatedAt,
|
||||
}
|
||||
return mapUserProfileResponse(user)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user