fix: the image lost after exporting (#3150)

Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
xiaodong zuo 2023-07-12 10:21:23 +08:00 committed by GitHub
parent 3968deb6d4
commit 4f88774999
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 117 additions and 20 deletions

21
.github/workflows/workers.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Deploy Cloudflare Worker
on:
push:
branches:
- master
paths:
- packages/workers/**
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
environment: production
steps:
- uses: actions/checkout@v2
- name: Publish
uses: cloudflare/wrangler-action@2.0.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
workingDirectory: 'packages/workers'

View File

@ -24,6 +24,7 @@ const buildPreset = {
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://affine.pro/blog/whats-new-affine-0630',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
enablePreloading: true,
enableNewSettingModal: true,
enableNewSettingUnstableApi: false,
@ -41,6 +42,7 @@ const buildPreset = {
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://affine.pro/blog/whats-new-affine-0630',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
enablePreloading: true,
enableNewSettingModal: true,
enableNewSettingUnstableApi: false,

View File

@ -1,7 +1,6 @@
import { pushNotificationAtom } from '@affine/component/notification-center';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { PageBlockModel } from '@blocksuite/blocks';
import { ContentParser } from '@blocksuite/blocks/content-parser';
import {
ArrowRightSmallIcon,
ExportIcon,
@ -11,16 +10,16 @@ import {
ExportToPngIcon,
} from '@blocksuite/icons';
import { useSetAtom } from 'jotai';
import { useCallback, useRef } from 'react';
import { useCallback } from 'react';
import { Menu, MenuItem } from '../../..';
import { getContentParser } from './get-content-parser';
import type { CommonMenuItemProps } from './types';
export const ExportToPdfMenuItem = ({
onSelect,
}: CommonMenuItemProps<{ type: 'pdf' }>) => {
const t = useAFFiNEI18N();
const contentParserRef = useRef<ContentParser>();
const { currentEditor } = globalThis;
const setPushNotification = useSetAtom(pushNotificationAtom);
@ -53,9 +52,7 @@ export const ExportToPdfMenuItem = ({
});
});
} else {
const contentParser =
contentParserRef.current ??
(contentParserRef.current = new ContentParser(currentEditor.page));
const contentParser = getContentParser(currentEditor.page);
contentParser
.exportPdf()
@ -95,17 +92,14 @@ export const ExportToHtmlMenuItem = ({
onSelect,
}: CommonMenuItemProps<{ type: 'html' }>) => {
const t = useAFFiNEI18N();
const contentParserRef = useRef<ContentParser>();
const { currentEditor } = globalThis;
const setPushNotification = useSetAtom(pushNotificationAtom);
const onClickExportHtml = useCallback(() => {
if (!currentEditor) {
return;
}
if (!contentParserRef.current) {
contentParserRef.current = new ContentParser(currentEditor.page);
}
contentParserRef.current
const contentParser = getContentParser(currentEditor.page);
contentParser
.exportHtml()
.then(() => {
onSelect?.({ type: 'html' });
@ -138,7 +132,6 @@ export const ExportToPngMenuItem = ({
onSelect,
}: CommonMenuItemProps<{ type: 'png' }>) => {
const t = useAFFiNEI18N();
const contentParserRef = useRef<ContentParser>();
const { currentEditor } = globalThis;
const setPushNotification = useSetAtom(pushNotificationAtom);
@ -146,9 +139,7 @@ export const ExportToPngMenuItem = ({
if (!currentEditor) {
return;
}
const contentParser =
contentParserRef.current ??
(contentParserRef.current = new ContentParser(currentEditor.page));
const contentParser = getContentParser(currentEditor.page);
contentParser
.exportPng()
@ -189,17 +180,14 @@ export const ExportToMarkdownMenuItem = ({
onSelect,
}: CommonMenuItemProps<{ type: 'markdown' }>) => {
const t = useAFFiNEI18N();
const contentParserRef = useRef<ContentParser>();
const { currentEditor } = globalThis;
const setPushNotification = useSetAtom(pushNotificationAtom);
const onClickExportMarkdown = useCallback(() => {
if (!currentEditor) {
return;
}
if (!contentParserRef.current) {
contentParserRef.current = new ContentParser(currentEditor.page);
}
contentParserRef.current
const contentParser = getContentParser(currentEditor.page);
contentParser
.exportMarkdown()
.then(() => {
onSelect?.({ type: 'markdown' });

View File

@ -0,0 +1,18 @@
import { ContentParser } from '@blocksuite/blocks/content-parser';
import type { Page } from '@blocksuite/store';
const contentParserWeakMap = new WeakMap<Page, ContentParser>();
export function getContentParser(page: Page) {
if (!contentParserWeakMap.has(page)) {
contentParserWeakMap.set(
page,
new ContentParser(page, {
imageProxyEndpoint: !environment.isDesktop
? runtimeConfig.imageProxyUrl
: undefined,
})
);
}
return contentParserWeakMap.get(page) as ContentParser;
}

View File

@ -63,6 +63,8 @@ export const buildFlagsSchema = z.object({
enableBroadcastChannelProvider: z.boolean(),
enableDebugPage: z.boolean(),
changelogUrl: z.string(),
// see: packages/workers
imageProxyUrl: z.string(),
enablePreloading: z.boolean(),
enableNewSettingModal: z.boolean(),
enableNewSettingUnstableApi: z.boolean(),

View File

@ -0,0 +1,63 @@
const ALLOW_ORIGIN = ['https://affine.pro', 'https://affine.fail'];
function isString(s: any): boolean {
return typeof s === 'string' || s instanceof String;
}
function isOriginAllowed(
origin: string,
allowedOrigin: string | RegExp | Array<string | RegExp>
): boolean {
if (Array.isArray(allowedOrigin)) {
for (let i = 0; i < allowedOrigin.length; ++i) {
if (isOriginAllowed(origin, allowedOrigin[i])) {
return true;
}
}
return false;
} else if (isString(allowedOrigin)) {
return origin === allowedOrigin;
} else if (allowedOrigin instanceof RegExp) {
return allowedOrigin.test(origin);
} else {
return !!allowedOrigin;
}
}
async function proxyImage(request: Request): Promise<Response> {
const url = new URL(request.url);
const imageURL = url.searchParams.get('url');
if (!imageURL) {
return new Response('Missing "url" parameter', { status: 400 });
}
const imageRequest = new Request(imageURL, {
method: 'GET',
headers: request.headers,
});
const response = await fetch(imageRequest);
const modifiedResponse = new Response(response.body);
modifiedResponse.headers.set('Access-Control-Allow-Origin', '*');
modifiedResponse.headers.set('Access-Control-Allow-Methods', 'GET');
return modifiedResponse;
}
const handler = {
async fetch(request: Request) {
if (!isOriginAllowed(request.headers.get('Origin') || '', ALLOW_ORIGIN)) {
return new Response('unauthorized', { status: 401 });
}
const url = new URL(request.url);
if (url.pathname.startsWith('/proxy/image')) {
return await proxyImage(request);
}
return new Response('not found', { status: 404 });
},
};
export default handler;

View File

@ -0,0 +1,3 @@
name = "workers"
main = "./src/index.ts"
compatibility_date = "2023-07-11"