update request headers

This commit is contained in:
Nate Kelley 2025-03-18 15:49:50 -06:00
parent 77597cdd93
commit 62a50b0592
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
9 changed files with 118 additions and 15 deletions

View File

@ -1,14 +1,17 @@
import React from 'react'; import React from 'react';
import { ASSET_ICONS } from '../config/assetIcons'; import { ASSET_ICONS } from '../config/assetIcons';
import { Button } from '@/components/ui/buttons'; import { Button } from '@/components/ui/buttons';
import { AppTooltip } from '@/components/ui/tooltip';
export const CollectionButton: React.FC<{ export const CollectionButton: React.FC<{
buttonType?: 'ghost' | 'default'; buttonType?: 'ghost' | 'default';
useText?: boolean; useText?: boolean;
}> = ({ buttonType = 'default', useText = false }) => { }> = ({ buttonType = 'default', useText = false }) => {
return ( return (
<Button prefix={<ASSET_ICONS.collections />} variant={buttonType}> <AppTooltip title={!useText ? 'Add to collection' : ''}>
{useText ? 'Collections' : ''} <Button prefix={<ASSET_ICONS.collections />} variant={buttonType}>
</Button> {useText ? 'Collections' : ''}
</Button>
</AppTooltip>
); );
}; };

View File

@ -1,9 +1,9 @@
import { Button } from '@/components/ui/buttons'; import { Button } from '@/components/ui/buttons';
import { ShareRight3 } from '@/components/ui/icons'; import { ShareRight, ShareRight3 } from '@/components/ui/icons';
import React from 'react'; import React from 'react';
export const ShareButton = React.memo(() => { export const ShareButton = React.memo(() => {
return <Button variant="ghost" prefix={<ShareRight3 />} />; return <Button variant="ghost" prefix={<ShareRight />} />;
}); });
ShareButton.displayName = 'ShareButton'; ShareButton.displayName = 'ShareButton';

View File

@ -164,3 +164,61 @@ export const CustomOverlay: Story = {
) )
} }
}; };
export const EmptyColumnSizes: Story = {
args: {
rows: [
{
id: uuidv4(),
items: [
{
id: uuidv4(),
children: <ExampleContent text="Row 1 Item 1" />
},
{
id: uuidv4(),
children: <ExampleContent text="Row 1 Item 2" />
}
],
columnSizes: [],
rowHeight: MIN_ROW_HEIGHT
},
{
id: uuidv4(),
items: [
{
id: uuidv4(),
children: <ExampleContent text="Row 2 Item 1" />
},
{
id: uuidv4(),
children: <ExampleContent text="Row 2 Item 2" />
},
{
id: uuidv4(),
children: <ExampleContent text="Row 2 Item 3" />
}
],
columnSizes: [],
rowHeight: MIN_ROW_HEIGHT
},
{
id: uuidv4(),
items: [
{
id: uuidv4(),
children: <ExampleContent text="Row 3 Item 1" />
},
{
id: uuidv4(),
children: <ExampleContent text="Row 3 Item 2" />
}
],
columnSizes: [],
rowHeight: MIN_ROW_HEIGHT
}
],
onRowLayoutChange: fn(),
readOnly: false
}
};

View File

@ -57,8 +57,6 @@ export const BusterResizeableGrid: React.FC<{
const [rows, setRows] = useState<BusterResizeableGridRow[]>(serverRows); const [rows, setRows] = useState<BusterResizeableGridRow[]>(serverRows);
const styleRef = useRef<HTMLStyleElement>(undefined); const styleRef = useRef<HTMLStyleElement>(undefined);
console.log(serverRows);
const onRowLayoutChangePreflight = useMemoizedFn((newLayout: BusterResizeableGridRow[]) => { const onRowLayoutChangePreflight = useMemoizedFn((newLayout: BusterResizeableGridRow[]) => {
const filteredRows = newRowPreflight(newLayout); const filteredRows = newRowPreflight(newLayout);

View File

@ -10,6 +10,7 @@ import { ShareDashboardButton } from '@/components/features/buttons/ShareDashboa
import { Button } from '@/components/ui/buttons'; import { Button } from '@/components/ui/buttons';
import { Plus } from '@/components/ui/icons'; import { Plus } from '@/components/ui/icons';
import { DashboardThreeDotMenu } from './DashboardThreeDotMenu'; import { DashboardThreeDotMenu } from './DashboardThreeDotMenu';
import { AppTooltip } from '@/components/ui/tooltip';
export const DashboardContainerHeaderButtons: React.FC<FileContainerButtonsProps> = React.memo( export const DashboardContainerHeaderButtons: React.FC<FileContainerButtonsProps> = React.memo(
() => { () => {
@ -19,7 +20,8 @@ export const DashboardContainerHeaderButtons: React.FC<FileContainerButtonsProps
return ( return (
<FileButtonContainer> <FileButtonContainer>
<SaveToCollectionButton /> <SaveToCollectionButton />
<ShareDashboardButton dashboardId={selectedFileId} /> <AddContentToDashboardButton /> <ShareDashboardButton dashboardId={selectedFileId} />
<AddContentToDashboardButton />
<DashboardThreeDotMenu dashboardId={selectedFileId} /> <DashboardThreeDotMenu dashboardId={selectedFileId} />
<HideButtonContainer show={renderViewLayoutKey === 'file'}> <HideButtonContainer show={renderViewLayoutKey === 'file'}>
<CreateChatButton /> <CreateChatButton />
@ -39,9 +41,9 @@ SaveToCollectionButton.displayName = 'SaveToCollectionButton';
const AddContentToDashboardButton = React.memo(() => { const AddContentToDashboardButton = React.memo(() => {
return ( return (
<div> <AppTooltip title="Add to dashboard">
<Button variant="ghost" prefix={<Plus />} /> <Button variant="ghost" prefix={<Plus />} />
</div> </AppTooltip>
); );
}); });
AddContentToDashboardButton.displayName = 'AddContentToDashboardButton'; AddContentToDashboardButton.displayName = 'AddContentToDashboardButton';

View File

@ -251,7 +251,11 @@ const useFilterDashboardSelectMenu = () => {
label: 'Filter dashboard', label: 'Filter dashboard',
value: 'filter-dashboard', value: 'filter-dashboard',
icon: <Filter />, icon: <Filter />,
items: [<div key="coming-soon">Coming soon...</div>] items: [
<div className="p-2" key="coming-soon">
Coming soon...
</div>
]
}), }),
[] []
); );

View File

@ -13,7 +13,6 @@ export async function middleware(request: NextRequest) {
nextPathnameMiddleware(request, supabaseResponse); nextPathnameMiddleware(request, supabaseResponse);
if (performUserCheck && !user) { if (performUserCheck && !user) {
console.log(performUserCheck, user, request.nextUrl.pathname);
return NextResponse.redirect( return NextResponse.redirect(
new URL(createBusterRoute({ route: BusterRoutes.AUTH_LOGIN }), process.env.NEXT_PUBLIC_URL) new URL(createBusterRoute({ route: BusterRoutes.AUTH_LOGIN }), process.env.NEXT_PUBLIC_URL)
); );

View File

@ -43,7 +43,7 @@ const embedCspHeader = {
// Fonts // Fonts
"font-src 'self' https://fonts.gstatic.com", "font-src 'self' https://fonts.gstatic.com",
// Frame ancestors - allow embedding from any domain for /embed routes // Frame ancestors - allow embedding from any domain for /embed routes
'frame-ancestors *', `frame-ancestors 'self' *`,
// Connect sources for API calls // Connect sources for API calls
"connect-src 'self' https://*.vercel.app https://*.supabase.co wss://*.supabase.co", "connect-src 'self' https://*.vercel.app https://*.supabase.co wss://*.supabase.co",
// Media // Media
@ -65,15 +65,23 @@ export const cspPolicyMiddleware = (request: NextRequest) => {
// Add CSP headers based on route // Add CSP headers based on route
request.headers.set( request.headers.set(
'Content-Security-Policy', 'Content-Security-Policy',
isEmbedRoute (isEmbedRoute
? embedCspHeader['Content-Security-Policy'] ? embedCspHeader['Content-Security-Policy']
: defaultCspHeader['Content-Security-Policy'] : defaultCspHeader['Content-Security-Policy']
).trim()
); );
// Add additional security headers // Add additional security headers
if (!isEmbedRoute) request.headers.set('X-Frame-Options', 'DENY'); if (isEmbedRoute) {
request.headers.set('X-Frame-Options', 'ALLOW-FROM *');
} else {
request.headers.set('X-Frame-Options', 'DENY');
}
request.headers.set('X-Content-Type-Options', 'nosniff'); request.headers.set('X-Content-Type-Options', 'nosniff');
request.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin'); request.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
request.headers.set('frame-ancestors', '*');
console.log('request.headers', isEmbedRoute ? 'embed' : 'default', request.headers);
return request; return request;
}; };

View File

@ -0,0 +1,31 @@
import { isEmbedPage } from './publicPageMiddleware';
// Unit tests for isEmbedPage
describe('isEmbedPage', () => {
it('should return true for embed metric routes', () => {
const request = {
nextUrl: {
pathname: '/embed/metrics/123'
}
} as any;
expect(isEmbedPage(request)).toBe(true);
});
it('should return true for embed dashboard routes', () => {
const request = {
nextUrl: {
pathname: '/embed/dashboards/456'
}
} as any;
expect(isEmbedPage(request)).toBe(true);
});
it('should return false for non-embed routes', () => {
const request = {
nextUrl: {
pathname: '/metrics/123'
}
} as any;
expect(isEmbedPage(request)).toBe(false);
});
});