fix(core): fix ios blob upload (#10263)

This commit is contained in:
EYHN
2025-02-19 10:07:46 +08:00
committed by GitHub
parent b20d316d60
commit 5a7ab880c1
21 changed files with 481 additions and 226 deletions
+40
View File
@@ -8,8 +8,11 @@ import { router } from '@affine/core/mobile/router';
import { configureCommonModules } from '@affine/core/modules';
import { AIButtonProvider } from '@affine/core/modules/ai-button';
import {
AuthProvider,
AuthService,
DefaultServerService,
ServerScope,
ServerService,
ServersService,
ValidatorProvider,
} from '@affine/core/modules/cloud';
@@ -51,9 +54,11 @@ import { RouterProvider } from 'react-router-dom';
import { BlocksuiteMenuConfigProvider } from './bs-menu-config';
import { ModalConfigProvider } from './modal-config';
import { Auth } from './plugins/auth';
import { Hashcash } from './plugins/hashcash';
import { Intelligents } from './plugins/intelligents';
import { NbStoreNativeDBApis } from './plugins/nbstore';
import { writeEndpointToken } from './proxy';
import { enableNavigationGesture$ } from './web-navigation-control';
const storeManagerClient = createStoreManagerClient();
@@ -134,6 +139,41 @@ framework.impl(AIButtonProvider, {
return Intelligents.dismissIntelligentsButton();
},
});
framework.scope(ServerScope).override(AuthProvider, resolver => {
const serverService = resolver.get(ServerService);
const endpoint = serverService.server.baseUrl;
return {
async signInMagicLink(email, linkToken) {
const { token } = await Auth.signInMagicLink({
endpoint,
email,
token: linkToken,
});
await writeEndpointToken(endpoint, token);
},
async signInOauth(code, state, _provider) {
const { token } = await Auth.signInOauth({
endpoint,
code,
state,
});
await writeEndpointToken(endpoint, token);
return {};
},
async signInPassword(credential) {
const { token } = await Auth.signInPassword({
endpoint,
...credential,
});
await writeEndpointToken(endpoint, token);
},
async signOut() {
await Auth.signOut({
endpoint,
});
},
};
});
const frameworkProvider = framework.provider();
@@ -0,0 +1,20 @@
export interface AuthPlugin {
signInMagicLink(options: {
endpoint: string;
email: string;
token: string;
}): Promise<{ token: string }>;
signInOauth(options: {
endpoint: string;
code: string;
state: string;
}): Promise<{ token: string }>;
signInPassword(options: {
endpoint: string;
email: string;
password: string;
verifyToken?: string;
challenge?: string;
}): Promise<{ token: string }>;
signOut(options: { endpoint: string }): Promise<void>;
}
@@ -0,0 +1,8 @@
import { registerPlugin } from '@capacitor/core';
import type { AuthPlugin } from './definitions';
const Auth = registerPlugin<AuthPlugin>('Auth');
export * from './definitions';
export { Auth };
+65
View File
@@ -0,0 +1,65 @@
import { openDB } from 'idb';
/**
* the below code includes the custom fetch and xmlhttprequest implementation for ios webview.
* should be included in the entry file of the app or webworker.
*/
const rawFetch = globalThis.fetch;
globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
const request = new Request(input, init);
const origin = new URL(request.url, globalThis.location.origin).origin;
const token = await readEndpointToken(origin);
if (token) {
request.headers.set('Authorization', `Bearer ${token}`);
}
return rawFetch(request);
};
const rawXMLHttpRequest = globalThis.XMLHttpRequest;
globalThis.XMLHttpRequest = class extends rawXMLHttpRequest {
override send(body?: Document | XMLHttpRequestBodyInit | null): void {
const origin = new URL(this.responseURL, globalThis.location.origin).origin;
readEndpointToken(origin).then(
token => {
if (token) {
this.setRequestHeader('Authorization', `Bearer ${token}`);
}
return super.send(body);
},
() => {
throw new Error('Failed to read token');
}
);
}
};
export async function readEndpointToken(
endpoint: string
): Promise<string | null> {
const idb = await openDB('affine-token', 1, {
upgrade(db) {
if (!db.objectStoreNames.contains('tokens')) {
db.createObjectStore('tokens', { keyPath: 'endpoint' });
}
},
});
const token = await idb.get('tokens', endpoint);
return token ? token.token : null;
}
export async function writeEndpointToken(endpoint: string, token: string) {
const db = await openDB('affine-token', 1, {
upgrade(db) {
if (!db.objectStoreNames.contains('tokens')) {
db.createObjectStore('tokens', { keyPath: 'endpoint' });
}
},
});
await db.put('tokens', { endpoint, token });
}
+1 -61
View File
@@ -1,62 +1,2 @@
import '@affine/core/bootstrap/browser';
/**
* the below code includes the custom fetch and xmlhttprequest implementation for ios webview.
* should be included in the entry file of the app or webworker.
*/
/*
* we override the browser's fetch function with our custom fetch function to
* overcome the restrictions of cross-domain and third-party cookies in ios webview.
*
* the custom fetch function will convert the request to `affine-http://` or `affine-https://`
* and send the request to the server.
*/
const rawFetch = globalThis.fetch;
globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
let url = new URL(
typeof input === 'string'
? input
: input instanceof URL
? input.toString()
: input.url,
globalThis.location.origin
).href;
if (url.startsWith('capacitor:')) {
return rawFetch(input, init);
}
if (url.startsWith('http:')) {
url = 'affine-http:' + url.slice(5);
}
if (url.startsWith('https:')) {
url = 'affine-https:' + url.slice(6);
}
return rawFetch(url, input instanceof Request ? input : init);
};
const rawXMLHttpRequest = globalThis.XMLHttpRequest;
globalThis.XMLHttpRequest = class extends rawXMLHttpRequest {
override open(
method: string,
url: string,
async?: boolean,
user?: string,
password?: string
) {
let normalizedUrl = new URL(url, globalThis.location.origin).href;
if (normalizedUrl.startsWith('http:')) {
url = 'affine-http:' + url.slice(5);
}
if (normalizedUrl.startsWith('https:')) {
url = 'affine-https:' + url.slice(6);
}
(super.open as any)(method, url, async, user, password);
}
};
import './proxy';
+16 -1
View File
@@ -1,7 +1,10 @@
import './setup';
import { broadcastChannelStorages } from '@affine/nbstore/broadcast-channel';
import { cloudStorages } from '@affine/nbstore/cloud';
import {
cloudStorages,
configureSocketAuthMethod,
} from '@affine/nbstore/cloud';
import {
bindNativeDBApis,
type NativeDBApis,
@@ -14,6 +17,18 @@ import {
import { type MessageCommunicapable, OpConsumer } from '@toeverything/infra/op';
import { AsyncCall } from 'async-call-rpc';
import { readEndpointToken } from './proxy';
configureSocketAuthMethod((endpoint, cb) => {
readEndpointToken(endpoint)
.then(token => {
cb({ token });
})
.catch(e => {
console.error(e);
});
});
globalThis.addEventListener('message', e => {
if (e.data.type === 'native-db-api-channel') {
const port = e.ports[0] as MessagePort;