mirror of https://github.com/buster-so/buster.git
update request headers
This commit is contained in:
parent
77597cdd93
commit
62a50b0592
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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>
|
||||||
|
]
|
||||||
}),
|
}),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
|
@ -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)
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue