diff --git a/apps/nextjs-app/src/features/app/utils/clipboard.spec.ts b/apps/nextjs-app/src/features/app/utils/clipboard.spec.ts
index 9d1d02da5..b63cafd13 100644
--- a/apps/nextjs-app/src/features/app/utils/clipboard.spec.ts
+++ b/apps/nextjs-app/src/features/app/utils/clipboard.spec.ts
@@ -25,9 +25,18 @@ vi.mock('@teable/core', () => {
};
});
+vi.mock('zod', () => {
+ return {
+ z: {
+ array: () => ({
+ safeParse: (data: string) => ({ success: true, data }),
+ }),
+ },
+ };
+});
+
describe('clipboard', () => {
- const html =
- '
Name | Count | Status |
---|
John | 20 | light |
Tom | 30 | medium |
Bob | 40 | heavy |
';
+ const html = `John | 20 | light |
Tom | 30 | medium |
Bob | 40 | heavy |
`;
const expectedHeader = [
{
diff --git a/apps/nextjs-app/src/features/app/utils/clipboard.ts b/apps/nextjs-app/src/features/app/utils/clipboard.ts
index a9480a0c2..2db925393 100644
--- a/apps/nextjs-app/src/features/app/utils/clipboard.ts
+++ b/apps/nextjs-app/src/features/app/utils/clipboard.ts
@@ -1,7 +1,9 @@
import { FieldType, fieldVoSchema, parseClipboardText, type IFieldVo } from '@teable/core';
+import { z } from 'zod';
import { fromZodError } from 'zod-validation-error';
const teableHtmlMarker = 'data-teable-html-marker';
+const teableHeader = 'data-teable-html-header';
export const serializerHtml = (data: string, headers: IFieldVo[]) => {
const tableData = parseClipboardText(data);
@@ -18,13 +20,8 @@ export const serializerHtml = (data: string, headers: IFieldVo[]) => {
.join('')}`;
})
.join('');
- const headerContent = headers
- .map((header) => {
- return ``;
- })
- .join('');
- return `${headerContent}
${bodyContent}
`;
+ return ``;
};
export const extractTableHeader = (html?: string) => {
@@ -34,23 +31,16 @@ export const extractTableHeader = (html?: string) => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const table = doc.querySelector('table');
- const headerRow = table?.querySelector('thead tr');
- const headerCells = headerRow?.querySelectorAll('th') || [];
-
- const headers = Array.from(headerCells);
- let error = '';
- const result = headers.map((cell) => {
- const fieldVoStr = cell.getAttribute('data-field');
- const fieldVo = fieldVoStr ? JSON.parse(decodeURIComponent(fieldVoStr)) : undefined;
-
- const validate = fieldVoSchema.safeParse(fieldVo);
- if (validate.success) {
- return fieldVo;
- }
- error = fromZodError(validate.error).message;
- return undefined;
- }) as IFieldVo[];
- return error ? { result: undefined, error } : { result };
+ const headerStr = table?.getAttribute(teableHeader);
+ const headers = headerStr ? JSON.parse(decodeURIComponent(headerStr)) : undefined;
+ if (!headers) {
+ return { result: undefined };
+ }
+ const validate = z.array(fieldVoSchema).safeParse(headers);
+ if (!validate.success) {
+ return { result: undefined, error: fromZodError(validate.error).message };
+ }
+ return { result: validate.data };
};
export const isTeableHTML = (html: string) => {
diff --git a/plugins/next.config.mjs b/plugins/next.config.mjs
index d6c041c4a..ac117984d 100644
--- a/plugins/next.config.mjs
+++ b/plugins/next.config.mjs
@@ -29,8 +29,8 @@ const nextConfig = {
}
}),
{
- key: 'X-Frame-Options',
- value: 'SAMEORIGIN'
+ key: 'Content-Security-Policy',
+ value: 'frame-ancestors *'
},
{ key: 'Cross-Origin-Opener-Policy', value: isProd ? 'same-origin' : 'unsafe-none' },
{ key: 'Cross-Origin-Embedder-Policy', value: isProd ? 'same-origin' : 'unsafe-none' }