@reverbia/sdk 1.0.0-next.20251218225706 → 1.0.0-next.20251219154503
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/index.cjs +1588 -114
- package/dist/react/index.d.mts +1037 -32
- package/dist/react/index.d.ts +1037 -32
- package/dist/react/index.mjs +1589 -111
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -38,18 +38,28 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
38
38
|
// src/react/index.ts
|
|
39
39
|
var index_exports = {};
|
|
40
40
|
__export(index_exports, {
|
|
41
|
+
BACKUP_DRIVE_CONVERSATIONS_FOLDER: () => DEFAULT_CONVERSATIONS_FOLDER,
|
|
42
|
+
BACKUP_DRIVE_ROOT_FOLDER: () => DEFAULT_ROOT_FOLDER,
|
|
43
|
+
BACKUP_ICLOUD_FOLDER: () => DEFAULT_BACKUP_FOLDER2,
|
|
44
|
+
BackupAuthProvider: () => BackupAuthProvider,
|
|
41
45
|
ChatConversation: () => Conversation,
|
|
42
46
|
ChatMessage: () => Message,
|
|
43
47
|
DEFAULT_BACKUP_FOLDER: () => DEFAULT_BACKUP_FOLDER,
|
|
44
48
|
DEFAULT_DRIVE_CONVERSATIONS_FOLDER: () => DEFAULT_CONVERSATIONS_FOLDER,
|
|
45
49
|
DEFAULT_DRIVE_ROOT_FOLDER: () => DEFAULT_ROOT_FOLDER,
|
|
50
|
+
DEFAULT_DROPBOX_FOLDER: () => DEFAULT_BACKUP_FOLDER,
|
|
51
|
+
DEFAULT_ICLOUD_BACKUP_FOLDER: () => DEFAULT_BACKUP_FOLDER2,
|
|
46
52
|
DEFAULT_TOOL_SELECTOR_MODEL: () => DEFAULT_TOOL_SELECTOR_MODEL,
|
|
47
53
|
DropboxAuthProvider: () => DropboxAuthProvider,
|
|
54
|
+
GoogleDriveAuthProvider: () => GoogleDriveAuthProvider,
|
|
55
|
+
ICloudAuthProvider: () => ICloudAuthProvider,
|
|
48
56
|
StoredMemoryModel: () => Memory,
|
|
49
57
|
StoredModelPreferenceModel: () => ModelPreference,
|
|
50
58
|
chatStorageMigrations: () => chatStorageMigrations,
|
|
51
59
|
chatStorageSchema: () => chatStorageSchema,
|
|
52
60
|
clearDropboxToken: () => clearToken,
|
|
61
|
+
clearGoogleDriveToken: () => clearGoogleDriveToken,
|
|
62
|
+
clearICloudAuth: () => clearICloudAuth,
|
|
53
63
|
createMemoryContextSystemMessage: () => createMemoryContextSystemMessage,
|
|
54
64
|
decryptData: () => decryptData,
|
|
55
65
|
decryptDataBytes: () => decryptDataBytes,
|
|
@@ -60,8 +70,11 @@ __export(index_exports, {
|
|
|
60
70
|
generateCompositeKey: () => generateCompositeKey,
|
|
61
71
|
generateConversationId: () => generateConversationId,
|
|
62
72
|
generateUniqueKey: () => generateUniqueKey,
|
|
63
|
-
|
|
73
|
+
getGoogleDriveStoredToken: () => getGoogleDriveStoredToken,
|
|
74
|
+
hasDropboxCredentials: () => hasDropboxCredentials,
|
|
64
75
|
hasEncryptionKey: () => hasEncryptionKey,
|
|
76
|
+
hasGoogleDriveCredentials: () => hasGoogleDriveCredentials,
|
|
77
|
+
hasICloudCredentials: () => hasICloudCredentials,
|
|
65
78
|
memoryStorageSchema: () => memoryStorageSchema,
|
|
66
79
|
requestEncryptionKey: () => requestEncryptionKey,
|
|
67
80
|
sdkMigrations: () => sdkMigrations,
|
|
@@ -69,13 +82,17 @@ __export(index_exports, {
|
|
|
69
82
|
sdkSchema: () => sdkSchema,
|
|
70
83
|
selectTool: () => selectTool,
|
|
71
84
|
settingsStorageSchema: () => settingsStorageSchema,
|
|
72
|
-
|
|
85
|
+
useBackup: () => useBackup,
|
|
86
|
+
useBackupAuth: () => useBackupAuth,
|
|
73
87
|
useChat: () => useChat,
|
|
74
88
|
useChatStorage: () => useChatStorage,
|
|
75
89
|
useDropboxAuth: () => useDropboxAuth,
|
|
76
90
|
useDropboxBackup: () => useDropboxBackup,
|
|
77
91
|
useEncryption: () => useEncryption,
|
|
92
|
+
useGoogleDriveAuth: () => useGoogleDriveAuth,
|
|
78
93
|
useGoogleDriveBackup: () => useGoogleDriveBackup,
|
|
94
|
+
useICloudAuth: () => useICloudAuth,
|
|
95
|
+
useICloudBackup: () => useICloudBackup,
|
|
79
96
|
useImageGeneration: () => useImageGeneration,
|
|
80
97
|
useMemoryStorage: () => useMemoryStorage,
|
|
81
98
|
useModels: () => useModels,
|
|
@@ -3065,6 +3082,36 @@ var postApiV1Search = (options) => {
|
|
|
3065
3082
|
}
|
|
3066
3083
|
});
|
|
3067
3084
|
};
|
|
3085
|
+
var postAuthOauthByProviderExchange = (options) => {
|
|
3086
|
+
return (options.client ?? client).post({
|
|
3087
|
+
url: "/auth/oauth/{provider}/exchange",
|
|
3088
|
+
...options,
|
|
3089
|
+
headers: {
|
|
3090
|
+
"Content-Type": "application/json",
|
|
3091
|
+
...options.headers
|
|
3092
|
+
}
|
|
3093
|
+
});
|
|
3094
|
+
};
|
|
3095
|
+
var postAuthOauthByProviderRefresh = (options) => {
|
|
3096
|
+
return (options.client ?? client).post({
|
|
3097
|
+
url: "/auth/oauth/{provider}/refresh",
|
|
3098
|
+
...options,
|
|
3099
|
+
headers: {
|
|
3100
|
+
"Content-Type": "application/json",
|
|
3101
|
+
...options.headers
|
|
3102
|
+
}
|
|
3103
|
+
});
|
|
3104
|
+
};
|
|
3105
|
+
var postAuthOauthByProviderRevoke = (options) => {
|
|
3106
|
+
return (options.client ?? client).post({
|
|
3107
|
+
url: "/auth/oauth/{provider}/revoke",
|
|
3108
|
+
...options,
|
|
3109
|
+
headers: {
|
|
3110
|
+
"Content-Type": "application/json",
|
|
3111
|
+
...options.headers
|
|
3112
|
+
}
|
|
3113
|
+
});
|
|
3114
|
+
};
|
|
3068
3115
|
|
|
3069
3116
|
// src/lib/memory/constants.ts
|
|
3070
3117
|
var DEFAULT_LOCAL_EMBEDDING_MODEL = "Snowflake/snowflake-arctic-embed-xs";
|
|
@@ -4608,116 +4655,196 @@ async function performDropboxImport(userAddress, token, deps, onProgress, backup
|
|
|
4608
4655
|
// src/react/useDropboxAuth.ts
|
|
4609
4656
|
var import_react10 = require("react");
|
|
4610
4657
|
|
|
4611
|
-
// src/lib/backup/
|
|
4612
|
-
var
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
var VERIFIER_STORAGE_KEY = "dropbox_code_verifier";
|
|
4616
|
-
function generateCodeVerifier() {
|
|
4617
|
-
const array = new Uint8Array(32);
|
|
4618
|
-
crypto.getRandomValues(array);
|
|
4619
|
-
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
4620
|
-
}
|
|
4621
|
-
async function generateCodeChallenge(verifier) {
|
|
4622
|
-
const encoder = new TextEncoder();
|
|
4623
|
-
const data = encoder.encode(verifier);
|
|
4624
|
-
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
4625
|
-
const base64 = btoa(String.fromCharCode(...new Uint8Array(hash)));
|
|
4626
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
4658
|
+
// src/lib/backup/oauth/storage.ts
|
|
4659
|
+
var STORAGE_KEY_PREFIX = "oauth_token_";
|
|
4660
|
+
function getStorageKey2(provider) {
|
|
4661
|
+
return `${STORAGE_KEY_PREFIX}${provider}`;
|
|
4627
4662
|
}
|
|
4628
|
-
function
|
|
4663
|
+
function getStoredTokenData(provider) {
|
|
4629
4664
|
if (typeof window === "undefined") return null;
|
|
4630
|
-
|
|
4665
|
+
try {
|
|
4666
|
+
const stored = localStorage.getItem(getStorageKey2(provider));
|
|
4667
|
+
if (!stored) return null;
|
|
4668
|
+
const data = JSON.parse(stored);
|
|
4669
|
+
if (!data.accessToken) return null;
|
|
4670
|
+
return data;
|
|
4671
|
+
} catch {
|
|
4672
|
+
return null;
|
|
4673
|
+
}
|
|
4631
4674
|
}
|
|
4632
|
-
function
|
|
4675
|
+
function storeTokenData(provider, data) {
|
|
4633
4676
|
if (typeof window === "undefined") return;
|
|
4634
|
-
|
|
4677
|
+
localStorage.setItem(getStorageKey2(provider), JSON.stringify(data));
|
|
4635
4678
|
}
|
|
4636
|
-
function
|
|
4679
|
+
function clearTokenData(provider) {
|
|
4637
4680
|
if (typeof window === "undefined") return;
|
|
4638
|
-
|
|
4681
|
+
localStorage.removeItem(getStorageKey2(provider));
|
|
4639
4682
|
}
|
|
4640
|
-
function
|
|
4641
|
-
if (
|
|
4642
|
-
|
|
4683
|
+
function isTokenExpired(data, bufferSeconds = 60) {
|
|
4684
|
+
if (!data) return true;
|
|
4685
|
+
if (!data.expiresAt) return false;
|
|
4686
|
+
const now = Date.now();
|
|
4687
|
+
const bufferMs = bufferSeconds * 1e3;
|
|
4688
|
+
return data.expiresAt - bufferMs <= now;
|
|
4643
4689
|
}
|
|
4644
|
-
function
|
|
4645
|
-
|
|
4646
|
-
|
|
4690
|
+
function getValidAccessToken(provider) {
|
|
4691
|
+
const data = getStoredTokenData(provider);
|
|
4692
|
+
if (!data) return null;
|
|
4693
|
+
if (data.expiresAt && isTokenExpired(data)) {
|
|
4694
|
+
return null;
|
|
4695
|
+
}
|
|
4696
|
+
return data.accessToken;
|
|
4647
4697
|
}
|
|
4648
|
-
function
|
|
4649
|
-
|
|
4650
|
-
|
|
4698
|
+
function getRefreshToken(provider) {
|
|
4699
|
+
const data = getStoredTokenData(provider);
|
|
4700
|
+
return data?.refreshToken ?? null;
|
|
4701
|
+
}
|
|
4702
|
+
function tokenResponseToStoredData(accessToken, expiresIn, refreshToken, scope) {
|
|
4703
|
+
const data = {
|
|
4704
|
+
accessToken,
|
|
4705
|
+
refreshToken,
|
|
4706
|
+
scope
|
|
4707
|
+
};
|
|
4708
|
+
if (expiresIn) {
|
|
4709
|
+
data.expiresAt = Date.now() + expiresIn * 1e3;
|
|
4710
|
+
}
|
|
4711
|
+
return data;
|
|
4651
4712
|
}
|
|
4713
|
+
|
|
4714
|
+
// src/lib/backup/dropbox/auth.ts
|
|
4715
|
+
var PROVIDER = "dropbox";
|
|
4716
|
+
var STATE_STORAGE_KEY = "dropbox_oauth_state";
|
|
4717
|
+
var DROPBOX_AUTH_URL = "https://www.dropbox.com/oauth2/authorize";
|
|
4652
4718
|
function getRedirectUri(callbackPath) {
|
|
4653
4719
|
if (typeof window === "undefined") return "";
|
|
4654
4720
|
return `${window.location.origin}${callbackPath}`;
|
|
4655
4721
|
}
|
|
4656
|
-
|
|
4722
|
+
function generateState() {
|
|
4723
|
+
const array = new Uint8Array(16);
|
|
4724
|
+
crypto.getRandomValues(array);
|
|
4725
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
4726
|
+
""
|
|
4727
|
+
);
|
|
4728
|
+
}
|
|
4729
|
+
function storeOAuthState(state) {
|
|
4730
|
+
if (typeof window === "undefined") return;
|
|
4731
|
+
sessionStorage.setItem(STATE_STORAGE_KEY, state);
|
|
4732
|
+
}
|
|
4733
|
+
function getAndClearOAuthState() {
|
|
4734
|
+
if (typeof window === "undefined") return null;
|
|
4735
|
+
const state = sessionStorage.getItem(STATE_STORAGE_KEY);
|
|
4736
|
+
sessionStorage.removeItem(STATE_STORAGE_KEY);
|
|
4737
|
+
return state;
|
|
4738
|
+
}
|
|
4739
|
+
function isDropboxCallback() {
|
|
4740
|
+
if (typeof window === "undefined") return false;
|
|
4741
|
+
const url = new URL(window.location.href);
|
|
4742
|
+
const code = url.searchParams.get("code");
|
|
4743
|
+
const state = url.searchParams.get("state");
|
|
4744
|
+
const storedState = sessionStorage.getItem(STATE_STORAGE_KEY);
|
|
4745
|
+
return !!code && !!state && state === storedState;
|
|
4746
|
+
}
|
|
4747
|
+
async function handleDropboxCallback(callbackPath, apiClient) {
|
|
4657
4748
|
if (typeof window === "undefined") return null;
|
|
4658
4749
|
const url = new URL(window.location.href);
|
|
4659
4750
|
const code = url.searchParams.get("code");
|
|
4660
4751
|
const state = url.searchParams.get("state");
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4752
|
+
const storedState = getAndClearOAuthState();
|
|
4753
|
+
if (!code || !state || state !== storedState) {
|
|
4754
|
+
return null;
|
|
4755
|
+
}
|
|
4664
4756
|
try {
|
|
4665
|
-
const response = await
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
},
|
|
4670
|
-
body: new URLSearchParams({
|
|
4757
|
+
const response = await postAuthOauthByProviderExchange({
|
|
4758
|
+
client: apiClient,
|
|
4759
|
+
path: { provider: PROVIDER },
|
|
4760
|
+
body: {
|
|
4671
4761
|
code,
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
redirect_uri: getRedirectUri(callbackPath),
|
|
4675
|
-
code_verifier: verifier
|
|
4676
|
-
})
|
|
4762
|
+
redirect_uri: getRedirectUri(callbackPath)
|
|
4763
|
+
}
|
|
4677
4764
|
});
|
|
4678
|
-
if (!response.
|
|
4679
|
-
throw new Error("
|
|
4765
|
+
if (!response.data?.access_token) {
|
|
4766
|
+
throw new Error("No access token in response");
|
|
4680
4767
|
}
|
|
4681
|
-
const
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4768
|
+
const tokenData = tokenResponseToStoredData(
|
|
4769
|
+
response.data.access_token,
|
|
4770
|
+
response.data.expires_in,
|
|
4771
|
+
response.data.refresh_token,
|
|
4772
|
+
response.data.scope
|
|
4773
|
+
);
|
|
4774
|
+
storeTokenData(PROVIDER, tokenData);
|
|
4688
4775
|
window.history.replaceState({}, "", window.location.pathname);
|
|
4689
|
-
return
|
|
4776
|
+
return response.data.access_token;
|
|
4777
|
+
} catch {
|
|
4778
|
+
return null;
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
async function refreshDropboxToken(apiClient) {
|
|
4782
|
+
const refreshToken = getRefreshToken(PROVIDER);
|
|
4783
|
+
if (!refreshToken) return null;
|
|
4784
|
+
try {
|
|
4785
|
+
const response = await postAuthOauthByProviderRefresh({
|
|
4786
|
+
client: apiClient,
|
|
4787
|
+
path: { provider: PROVIDER },
|
|
4788
|
+
body: { refresh_token: refreshToken }
|
|
4789
|
+
});
|
|
4790
|
+
if (!response.data?.access_token) {
|
|
4791
|
+
throw new Error("No access token in refresh response");
|
|
4792
|
+
}
|
|
4793
|
+
const currentData = getStoredTokenData(PROVIDER);
|
|
4794
|
+
const tokenData = tokenResponseToStoredData(
|
|
4795
|
+
response.data.access_token,
|
|
4796
|
+
response.data.expires_in,
|
|
4797
|
+
response.data.refresh_token ?? currentData?.refreshToken,
|
|
4798
|
+
response.data.scope ?? currentData?.scope
|
|
4799
|
+
);
|
|
4800
|
+
storeTokenData(PROVIDER, tokenData);
|
|
4801
|
+
return response.data.access_token;
|
|
4690
4802
|
} catch {
|
|
4691
|
-
|
|
4803
|
+
clearTokenData(PROVIDER);
|
|
4692
4804
|
return null;
|
|
4693
4805
|
}
|
|
4694
4806
|
}
|
|
4807
|
+
async function revokeDropboxToken(apiClient) {
|
|
4808
|
+
const tokenData = getStoredTokenData(PROVIDER);
|
|
4809
|
+
if (!tokenData) return;
|
|
4810
|
+
try {
|
|
4811
|
+
const tokenToRevoke = tokenData.refreshToken ?? tokenData.accessToken;
|
|
4812
|
+
await postAuthOauthByProviderRevoke({
|
|
4813
|
+
client: apiClient,
|
|
4814
|
+
path: { provider: PROVIDER },
|
|
4815
|
+
body: { token: tokenToRevoke }
|
|
4816
|
+
});
|
|
4817
|
+
} catch {
|
|
4818
|
+
} finally {
|
|
4819
|
+
clearTokenData(PROVIDER);
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
async function getDropboxAccessToken(apiClient) {
|
|
4823
|
+
const validToken = getValidAccessToken(PROVIDER);
|
|
4824
|
+
if (validToken) return validToken;
|
|
4825
|
+
return refreshDropboxToken(apiClient);
|
|
4826
|
+
}
|
|
4695
4827
|
async function startDropboxAuth(appKey, callbackPath) {
|
|
4696
|
-
const
|
|
4697
|
-
|
|
4698
|
-
storeVerifier(verifier);
|
|
4828
|
+
const state = generateState();
|
|
4829
|
+
storeOAuthState(state);
|
|
4699
4830
|
const params = new URLSearchParams({
|
|
4700
4831
|
client_id: appKey,
|
|
4701
4832
|
redirect_uri: getRedirectUri(callbackPath),
|
|
4702
4833
|
response_type: "code",
|
|
4703
|
-
|
|
4704
|
-
code_challenge_method: "S256",
|
|
4705
|
-
state: "dropbox_auth",
|
|
4834
|
+
state,
|
|
4706
4835
|
token_access_type: "offline"
|
|
4836
|
+
// Request refresh token
|
|
4707
4837
|
});
|
|
4708
4838
|
window.location.href = `${DROPBOX_AUTH_URL}?${params.toString()}`;
|
|
4709
4839
|
return new Promise(() => {
|
|
4710
4840
|
});
|
|
4711
4841
|
}
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
const
|
|
4717
|
-
|
|
4718
|
-
return storedToken;
|
|
4719
|
-
}
|
|
4720
|
-
return startDropboxAuth(appKey, callbackPath);
|
|
4842
|
+
function clearToken() {
|
|
4843
|
+
clearTokenData(PROVIDER);
|
|
4844
|
+
}
|
|
4845
|
+
function hasDropboxCredentials() {
|
|
4846
|
+
const data = getStoredTokenData(PROVIDER);
|
|
4847
|
+
return !!(data?.accessToken || data?.refreshToken);
|
|
4721
4848
|
}
|
|
4722
4849
|
|
|
4723
4850
|
// src/react/useDropboxAuth.ts
|
|
@@ -4725,26 +4852,41 @@ var DropboxAuthContext = (0, import_react10.createContext)(null);
|
|
|
4725
4852
|
function DropboxAuthProvider({
|
|
4726
4853
|
appKey,
|
|
4727
4854
|
callbackPath = "/auth/dropbox/callback",
|
|
4855
|
+
apiClient,
|
|
4728
4856
|
children
|
|
4729
4857
|
}) {
|
|
4730
4858
|
const [accessToken, setAccessToken] = (0, import_react10.useState)(null);
|
|
4731
4859
|
const isConfigured = !!appKey;
|
|
4732
4860
|
(0, import_react10.useEffect)(() => {
|
|
4733
|
-
const
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4861
|
+
const checkStoredToken = async () => {
|
|
4862
|
+
if (hasDropboxCredentials()) {
|
|
4863
|
+
const token = await getDropboxAccessToken(apiClient);
|
|
4864
|
+
if (token) {
|
|
4865
|
+
setAccessToken(token);
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
};
|
|
4869
|
+
checkStoredToken();
|
|
4870
|
+
}, [apiClient]);
|
|
4738
4871
|
(0, import_react10.useEffect)(() => {
|
|
4739
|
-
if (!isConfigured
|
|
4872
|
+
if (!isConfigured) return;
|
|
4740
4873
|
const handleCallback = async () => {
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4874
|
+
if (isDropboxCallback()) {
|
|
4875
|
+
const token = await handleDropboxCallback(callbackPath, apiClient);
|
|
4876
|
+
if (token) {
|
|
4877
|
+
setAccessToken(token);
|
|
4878
|
+
}
|
|
4744
4879
|
}
|
|
4745
4880
|
};
|
|
4746
4881
|
handleCallback();
|
|
4747
|
-
}, [
|
|
4882
|
+
}, [callbackPath, isConfigured, apiClient]);
|
|
4883
|
+
const refreshTokenFn = (0, import_react10.useCallback)(async () => {
|
|
4884
|
+
const token = await getDropboxAccessToken(apiClient);
|
|
4885
|
+
if (token) {
|
|
4886
|
+
setAccessToken(token);
|
|
4887
|
+
}
|
|
4888
|
+
return token;
|
|
4889
|
+
}, [apiClient]);
|
|
4748
4890
|
const requestAccess = (0, import_react10.useCallback)(async () => {
|
|
4749
4891
|
if (!isConfigured || !appKey) {
|
|
4750
4892
|
throw new Error("Dropbox is not configured");
|
|
@@ -4752,17 +4894,17 @@ function DropboxAuthProvider({
|
|
|
4752
4894
|
if (accessToken) {
|
|
4753
4895
|
return accessToken;
|
|
4754
4896
|
}
|
|
4755
|
-
const storedToken =
|
|
4897
|
+
const storedToken = await getDropboxAccessToken(apiClient);
|
|
4756
4898
|
if (storedToken) {
|
|
4757
4899
|
setAccessToken(storedToken);
|
|
4758
4900
|
return storedToken;
|
|
4759
4901
|
}
|
|
4760
|
-
return
|
|
4761
|
-
}, [accessToken, appKey, callbackPath, isConfigured]);
|
|
4762
|
-
const logout = (0, import_react10.useCallback)(() => {
|
|
4763
|
-
|
|
4902
|
+
return startDropboxAuth(appKey, callbackPath);
|
|
4903
|
+
}, [accessToken, appKey, callbackPath, isConfigured, apiClient]);
|
|
4904
|
+
const logout = (0, import_react10.useCallback)(async () => {
|
|
4905
|
+
await revokeDropboxToken(apiClient);
|
|
4764
4906
|
setAccessToken(null);
|
|
4765
|
-
}, []);
|
|
4907
|
+
}, [apiClient]);
|
|
4766
4908
|
return (0, import_react10.createElement)(
|
|
4767
4909
|
DropboxAuthContext.Provider,
|
|
4768
4910
|
{
|
|
@@ -4771,7 +4913,8 @@ function DropboxAuthProvider({
|
|
|
4771
4913
|
isAuthenticated: !!accessToken,
|
|
4772
4914
|
isConfigured,
|
|
4773
4915
|
requestAccess,
|
|
4774
|
-
logout
|
|
4916
|
+
logout,
|
|
4917
|
+
refreshToken: refreshTokenFn
|
|
4775
4918
|
}
|
|
4776
4919
|
},
|
|
4777
4920
|
children
|
|
@@ -4798,25 +4941,25 @@ function useDropboxBackup(options) {
|
|
|
4798
4941
|
const {
|
|
4799
4942
|
accessToken: dropboxToken,
|
|
4800
4943
|
isConfigured: isDropboxConfigured,
|
|
4801
|
-
requestAccess:
|
|
4944
|
+
requestAccess: requestDropboxAccess
|
|
4802
4945
|
} = useDropboxAuth();
|
|
4803
4946
|
const deps = (0, import_react11.useMemo)(
|
|
4804
4947
|
() => ({
|
|
4805
|
-
requestDropboxAccess
|
|
4948
|
+
requestDropboxAccess,
|
|
4806
4949
|
requestEncryptionKey: requestEncryptionKey2,
|
|
4807
4950
|
exportConversation,
|
|
4808
4951
|
importConversation
|
|
4809
4952
|
}),
|
|
4810
|
-
[
|
|
4953
|
+
[requestDropboxAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
4811
4954
|
);
|
|
4812
4955
|
const ensureToken = (0, import_react11.useCallback)(async () => {
|
|
4813
4956
|
if (dropboxToken) return dropboxToken;
|
|
4814
4957
|
try {
|
|
4815
|
-
return await
|
|
4958
|
+
return await requestDropboxAccess();
|
|
4816
4959
|
} catch {
|
|
4817
4960
|
return null;
|
|
4818
4961
|
}
|
|
4819
|
-
}, [dropboxToken,
|
|
4962
|
+
}, [dropboxToken, requestDropboxAccess]);
|
|
4820
4963
|
const backup = (0, import_react11.useCallback)(
|
|
4821
4964
|
async (backupOptions) => {
|
|
4822
4965
|
if (!userAddress) {
|
|
@@ -4876,9 +5019,241 @@ function useDropboxBackup(options) {
|
|
|
4876
5019
|
};
|
|
4877
5020
|
}
|
|
4878
5021
|
|
|
4879
|
-
// src/react/
|
|
5022
|
+
// src/react/useGoogleDriveAuth.ts
|
|
4880
5023
|
var import_react12 = require("react");
|
|
4881
5024
|
|
|
5025
|
+
// src/lib/backup/google/auth.ts
|
|
5026
|
+
var PROVIDER2 = "google-drive";
|
|
5027
|
+
var CODE_STORAGE_KEY = "google_oauth_state";
|
|
5028
|
+
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
5029
|
+
var DRIVE_SCOPES = [
|
|
5030
|
+
"https://www.googleapis.com/auth/drive.file"
|
|
5031
|
+
// Access to files created by the app
|
|
5032
|
+
].join(" ");
|
|
5033
|
+
function getRedirectUri2(callbackPath) {
|
|
5034
|
+
if (typeof window === "undefined") return "";
|
|
5035
|
+
return `${window.location.origin}${callbackPath}`;
|
|
5036
|
+
}
|
|
5037
|
+
function generateState2() {
|
|
5038
|
+
const array = new Uint8Array(16);
|
|
5039
|
+
crypto.getRandomValues(array);
|
|
5040
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
5041
|
+
""
|
|
5042
|
+
);
|
|
5043
|
+
}
|
|
5044
|
+
function storeOAuthState2(state) {
|
|
5045
|
+
if (typeof window === "undefined") return;
|
|
5046
|
+
sessionStorage.setItem(CODE_STORAGE_KEY, state);
|
|
5047
|
+
}
|
|
5048
|
+
function getAndClearOAuthState2() {
|
|
5049
|
+
if (typeof window === "undefined") return null;
|
|
5050
|
+
const state = sessionStorage.getItem(CODE_STORAGE_KEY);
|
|
5051
|
+
sessionStorage.removeItem(CODE_STORAGE_KEY);
|
|
5052
|
+
return state;
|
|
5053
|
+
}
|
|
5054
|
+
function isGoogleDriveCallback() {
|
|
5055
|
+
if (typeof window === "undefined") return false;
|
|
5056
|
+
const url = new URL(window.location.href);
|
|
5057
|
+
const code = url.searchParams.get("code");
|
|
5058
|
+
const state = url.searchParams.get("state");
|
|
5059
|
+
const storedState = sessionStorage.getItem(CODE_STORAGE_KEY);
|
|
5060
|
+
return !!code && !!state && state === storedState;
|
|
5061
|
+
}
|
|
5062
|
+
async function handleGoogleDriveCallback(callbackPath, apiClient) {
|
|
5063
|
+
if (typeof window === "undefined") return null;
|
|
5064
|
+
const url = new URL(window.location.href);
|
|
5065
|
+
const code = url.searchParams.get("code");
|
|
5066
|
+
const state = url.searchParams.get("state");
|
|
5067
|
+
const storedState = getAndClearOAuthState2();
|
|
5068
|
+
if (!code || !state || state !== storedState) {
|
|
5069
|
+
return null;
|
|
5070
|
+
}
|
|
5071
|
+
try {
|
|
5072
|
+
const response = await postAuthOauthByProviderExchange({
|
|
5073
|
+
client: apiClient,
|
|
5074
|
+
path: { provider: PROVIDER2 },
|
|
5075
|
+
body: {
|
|
5076
|
+
code,
|
|
5077
|
+
redirect_uri: getRedirectUri2(callbackPath)
|
|
5078
|
+
}
|
|
5079
|
+
});
|
|
5080
|
+
if (!response.data?.access_token) {
|
|
5081
|
+
throw new Error("No access token in response");
|
|
5082
|
+
}
|
|
5083
|
+
const tokenData = tokenResponseToStoredData(
|
|
5084
|
+
response.data.access_token,
|
|
5085
|
+
response.data.expires_in,
|
|
5086
|
+
response.data.refresh_token,
|
|
5087
|
+
response.data.scope
|
|
5088
|
+
);
|
|
5089
|
+
storeTokenData(PROVIDER2, tokenData);
|
|
5090
|
+
window.history.replaceState({}, "", window.location.pathname);
|
|
5091
|
+
return response.data.access_token;
|
|
5092
|
+
} catch {
|
|
5093
|
+
return null;
|
|
5094
|
+
}
|
|
5095
|
+
}
|
|
5096
|
+
async function refreshGoogleDriveToken(apiClient) {
|
|
5097
|
+
const refreshToken = getRefreshToken(PROVIDER2);
|
|
5098
|
+
if (!refreshToken) return null;
|
|
5099
|
+
try {
|
|
5100
|
+
const response = await postAuthOauthByProviderRefresh({
|
|
5101
|
+
client: apiClient,
|
|
5102
|
+
path: { provider: PROVIDER2 },
|
|
5103
|
+
body: { refresh_token: refreshToken }
|
|
5104
|
+
});
|
|
5105
|
+
if (!response.data?.access_token) {
|
|
5106
|
+
throw new Error("No access token in refresh response");
|
|
5107
|
+
}
|
|
5108
|
+
const currentData = getStoredTokenData(PROVIDER2);
|
|
5109
|
+
const tokenData = tokenResponseToStoredData(
|
|
5110
|
+
response.data.access_token,
|
|
5111
|
+
response.data.expires_in,
|
|
5112
|
+
response.data.refresh_token ?? currentData?.refreshToken,
|
|
5113
|
+
response.data.scope ?? currentData?.scope
|
|
5114
|
+
);
|
|
5115
|
+
storeTokenData(PROVIDER2, tokenData);
|
|
5116
|
+
return response.data.access_token;
|
|
5117
|
+
} catch {
|
|
5118
|
+
clearTokenData(PROVIDER2);
|
|
5119
|
+
return null;
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5122
|
+
async function revokeGoogleDriveToken(apiClient) {
|
|
5123
|
+
const tokenData = getStoredTokenData(PROVIDER2);
|
|
5124
|
+
if (!tokenData) return;
|
|
5125
|
+
try {
|
|
5126
|
+
const tokenToRevoke = tokenData.refreshToken ?? tokenData.accessToken;
|
|
5127
|
+
await postAuthOauthByProviderRevoke({
|
|
5128
|
+
client: apiClient,
|
|
5129
|
+
path: { provider: PROVIDER2 },
|
|
5130
|
+
body: { token: tokenToRevoke }
|
|
5131
|
+
});
|
|
5132
|
+
} catch {
|
|
5133
|
+
} finally {
|
|
5134
|
+
clearTokenData(PROVIDER2);
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
async function getGoogleDriveAccessToken(apiClient) {
|
|
5138
|
+
const validToken = getValidAccessToken(PROVIDER2);
|
|
5139
|
+
if (validToken) return validToken;
|
|
5140
|
+
return refreshGoogleDriveToken(apiClient);
|
|
5141
|
+
}
|
|
5142
|
+
async function startGoogleDriveAuth(clientId, callbackPath) {
|
|
5143
|
+
const state = generateState2();
|
|
5144
|
+
storeOAuthState2(state);
|
|
5145
|
+
const params = new URLSearchParams({
|
|
5146
|
+
client_id: clientId,
|
|
5147
|
+
redirect_uri: getRedirectUri2(callbackPath),
|
|
5148
|
+
response_type: "code",
|
|
5149
|
+
scope: DRIVE_SCOPES,
|
|
5150
|
+
state,
|
|
5151
|
+
access_type: "offline",
|
|
5152
|
+
// Request refresh token
|
|
5153
|
+
prompt: "consent"
|
|
5154
|
+
// Force consent to always get refresh token
|
|
5155
|
+
});
|
|
5156
|
+
window.location.href = `${GOOGLE_AUTH_URL}?${params.toString()}`;
|
|
5157
|
+
return new Promise(() => {
|
|
5158
|
+
});
|
|
5159
|
+
}
|
|
5160
|
+
function getGoogleDriveStoredToken() {
|
|
5161
|
+
return getValidAccessToken(PROVIDER2);
|
|
5162
|
+
}
|
|
5163
|
+
function clearGoogleDriveToken() {
|
|
5164
|
+
clearTokenData(PROVIDER2);
|
|
5165
|
+
}
|
|
5166
|
+
function hasGoogleDriveCredentials() {
|
|
5167
|
+
const data = getStoredTokenData(PROVIDER2);
|
|
5168
|
+
return !!(data?.accessToken || data?.refreshToken);
|
|
5169
|
+
}
|
|
5170
|
+
|
|
5171
|
+
// src/react/useGoogleDriveAuth.ts
|
|
5172
|
+
var GoogleDriveAuthContext = (0, import_react12.createContext)(null);
|
|
5173
|
+
function GoogleDriveAuthProvider({
|
|
5174
|
+
clientId,
|
|
5175
|
+
callbackPath = "/auth/google/callback",
|
|
5176
|
+
apiClient,
|
|
5177
|
+
children
|
|
5178
|
+
}) {
|
|
5179
|
+
const [accessToken, setAccessToken] = (0, import_react12.useState)(null);
|
|
5180
|
+
const isConfigured = !!clientId;
|
|
5181
|
+
(0, import_react12.useEffect)(() => {
|
|
5182
|
+
const checkStoredToken = async () => {
|
|
5183
|
+
if (hasGoogleDriveCredentials()) {
|
|
5184
|
+
const token = await getGoogleDriveAccessToken(apiClient);
|
|
5185
|
+
if (token) {
|
|
5186
|
+
setAccessToken(token);
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
};
|
|
5190
|
+
checkStoredToken();
|
|
5191
|
+
}, [apiClient]);
|
|
5192
|
+
(0, import_react12.useEffect)(() => {
|
|
5193
|
+
if (!isConfigured) return;
|
|
5194
|
+
const handleCallback = async () => {
|
|
5195
|
+
if (isGoogleDriveCallback()) {
|
|
5196
|
+
const token = await handleGoogleDriveCallback(callbackPath, apiClient);
|
|
5197
|
+
if (token) {
|
|
5198
|
+
setAccessToken(token);
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
};
|
|
5202
|
+
handleCallback();
|
|
5203
|
+
}, [callbackPath, isConfigured, apiClient]);
|
|
5204
|
+
const refreshTokenFn = (0, import_react12.useCallback)(async () => {
|
|
5205
|
+
const token = await getGoogleDriveAccessToken(apiClient);
|
|
5206
|
+
if (token) {
|
|
5207
|
+
setAccessToken(token);
|
|
5208
|
+
}
|
|
5209
|
+
return token;
|
|
5210
|
+
}, [apiClient]);
|
|
5211
|
+
const requestAccess = (0, import_react12.useCallback)(async () => {
|
|
5212
|
+
if (!isConfigured || !clientId) {
|
|
5213
|
+
throw new Error("Google Drive is not configured");
|
|
5214
|
+
}
|
|
5215
|
+
if (accessToken) {
|
|
5216
|
+
return accessToken;
|
|
5217
|
+
}
|
|
5218
|
+
const storedToken = await getGoogleDriveAccessToken(apiClient);
|
|
5219
|
+
if (storedToken) {
|
|
5220
|
+
setAccessToken(storedToken);
|
|
5221
|
+
return storedToken;
|
|
5222
|
+
}
|
|
5223
|
+
return startGoogleDriveAuth(clientId, callbackPath);
|
|
5224
|
+
}, [accessToken, clientId, callbackPath, isConfigured, apiClient]);
|
|
5225
|
+
const logout = (0, import_react12.useCallback)(async () => {
|
|
5226
|
+
await revokeGoogleDriveToken(apiClient);
|
|
5227
|
+
setAccessToken(null);
|
|
5228
|
+
}, [apiClient]);
|
|
5229
|
+
return (0, import_react12.createElement)(
|
|
5230
|
+
GoogleDriveAuthContext.Provider,
|
|
5231
|
+
{
|
|
5232
|
+
value: {
|
|
5233
|
+
accessToken,
|
|
5234
|
+
isAuthenticated: !!accessToken,
|
|
5235
|
+
isConfigured,
|
|
5236
|
+
requestAccess,
|
|
5237
|
+
logout,
|
|
5238
|
+
refreshToken: refreshTokenFn
|
|
5239
|
+
}
|
|
5240
|
+
},
|
|
5241
|
+
children
|
|
5242
|
+
);
|
|
5243
|
+
}
|
|
5244
|
+
function useGoogleDriveAuth() {
|
|
5245
|
+
const context = (0, import_react12.useContext)(GoogleDriveAuthContext);
|
|
5246
|
+
if (!context) {
|
|
5247
|
+
throw new Error(
|
|
5248
|
+
"useGoogleDriveAuth must be used within GoogleDriveAuthProvider"
|
|
5249
|
+
);
|
|
5250
|
+
}
|
|
5251
|
+
return context;
|
|
5252
|
+
}
|
|
5253
|
+
|
|
5254
|
+
// src/react/useGoogleDriveBackup.ts
|
|
5255
|
+
var import_react13 = require("react");
|
|
5256
|
+
|
|
4882
5257
|
// src/lib/backup/google/api.ts
|
|
4883
5258
|
var DRIVE_API_URL = "https://www.googleapis.com/drive/v3";
|
|
4884
5259
|
var DRIVE_UPLOAD_URL = "https://www.googleapis.com/upload/drive/v3";
|
|
@@ -5184,37 +5559,35 @@ function useGoogleDriveBackup(options) {
|
|
|
5184
5559
|
const {
|
|
5185
5560
|
database,
|
|
5186
5561
|
userAddress,
|
|
5187
|
-
accessToken,
|
|
5188
|
-
requestDriveAccess,
|
|
5189
5562
|
requestEncryptionKey: requestEncryptionKey2,
|
|
5190
5563
|
exportConversation,
|
|
5191
5564
|
importConversation,
|
|
5192
5565
|
rootFolder = DEFAULT_ROOT_FOLDER,
|
|
5193
5566
|
conversationsFolder = DEFAULT_CONVERSATIONS_FOLDER
|
|
5194
5567
|
} = options;
|
|
5195
|
-
const
|
|
5568
|
+
const {
|
|
5569
|
+
accessToken: driveToken,
|
|
5570
|
+
isConfigured: isDriveConfigured,
|
|
5571
|
+
requestAccess: requestDriveAccess
|
|
5572
|
+
} = useGoogleDriveAuth();
|
|
5573
|
+
const deps = (0, import_react13.useMemo)(
|
|
5196
5574
|
() => ({
|
|
5197
5575
|
requestDriveAccess,
|
|
5198
5576
|
requestEncryptionKey: requestEncryptionKey2,
|
|
5199
5577
|
exportConversation,
|
|
5200
5578
|
importConversation
|
|
5201
5579
|
}),
|
|
5202
|
-
[
|
|
5203
|
-
requestDriveAccess,
|
|
5204
|
-
requestEncryptionKey2,
|
|
5205
|
-
exportConversation,
|
|
5206
|
-
importConversation
|
|
5207
|
-
]
|
|
5580
|
+
[requestDriveAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
5208
5581
|
);
|
|
5209
|
-
const ensureToken = (0,
|
|
5210
|
-
if (
|
|
5582
|
+
const ensureToken = (0, import_react13.useCallback)(async () => {
|
|
5583
|
+
if (driveToken) return driveToken;
|
|
5211
5584
|
try {
|
|
5212
5585
|
return await requestDriveAccess();
|
|
5213
5586
|
} catch {
|
|
5214
5587
|
return null;
|
|
5215
5588
|
}
|
|
5216
|
-
}, [
|
|
5217
|
-
const backup = (0,
|
|
5589
|
+
}, [driveToken, requestDriveAccess]);
|
|
5590
|
+
const backup = (0, import_react13.useCallback)(
|
|
5218
5591
|
async (backupOptions) => {
|
|
5219
5592
|
if (!userAddress) {
|
|
5220
5593
|
return { error: "Please sign in to backup to Google Drive" };
|
|
@@ -5241,7 +5614,7 @@ function useGoogleDriveBackup(options) {
|
|
|
5241
5614
|
},
|
|
5242
5615
|
[database, userAddress, ensureToken, deps, rootFolder, conversationsFolder]
|
|
5243
5616
|
);
|
|
5244
|
-
const restore = (0,
|
|
5617
|
+
const restore = (0, import_react13.useCallback)(
|
|
5245
5618
|
async (restoreOptions) => {
|
|
5246
5619
|
if (!userAddress) {
|
|
5247
5620
|
return { error: "Please sign in to restore from Google Drive" };
|
|
@@ -5270,23 +5643,1117 @@ function useGoogleDriveBackup(options) {
|
|
|
5270
5643
|
return {
|
|
5271
5644
|
backup,
|
|
5272
5645
|
restore,
|
|
5273
|
-
|
|
5646
|
+
isConfigured: isDriveConfigured,
|
|
5647
|
+
isAuthenticated: !!driveToken
|
|
5648
|
+
};
|
|
5649
|
+
}
|
|
5650
|
+
|
|
5651
|
+
// src/react/useICloudAuth.ts
|
|
5652
|
+
var import_react14 = require("react");
|
|
5653
|
+
|
|
5654
|
+
// src/lib/backup/icloud/api.ts
|
|
5655
|
+
var CLOUDKIT_JS_URL = "https://cdn.apple-cloudkit.com/ck/2/cloudkit.js";
|
|
5656
|
+
var DEFAULT_BACKUP_FOLDER2 = "conversations";
|
|
5657
|
+
var DEFAULT_CONTAINER_ID = "iCloud.Memoryless";
|
|
5658
|
+
var RECORD_TYPE = "ConversationBackup";
|
|
5659
|
+
var cloudKitLoadPromise = null;
|
|
5660
|
+
function isCloudKitAvailable() {
|
|
5661
|
+
return typeof window !== "undefined" && !!window.CloudKit;
|
|
5662
|
+
}
|
|
5663
|
+
async function loadCloudKit() {
|
|
5664
|
+
if (typeof window === "undefined") {
|
|
5665
|
+
throw new Error("CloudKit JS can only be loaded in browser environment");
|
|
5666
|
+
}
|
|
5667
|
+
if (window.CloudKit) {
|
|
5668
|
+
return;
|
|
5669
|
+
}
|
|
5670
|
+
if (cloudKitLoadPromise) {
|
|
5671
|
+
return cloudKitLoadPromise;
|
|
5672
|
+
}
|
|
5673
|
+
cloudKitLoadPromise = new Promise((resolve, reject) => {
|
|
5674
|
+
const script = document.createElement("script");
|
|
5675
|
+
script.src = CLOUDKIT_JS_URL;
|
|
5676
|
+
script.async = true;
|
|
5677
|
+
script.onload = () => {
|
|
5678
|
+
if (window.CloudKit) {
|
|
5679
|
+
resolve();
|
|
5680
|
+
} else {
|
|
5681
|
+
reject(new Error("CloudKit JS loaded but CloudKit object not found"));
|
|
5682
|
+
}
|
|
5683
|
+
};
|
|
5684
|
+
script.onerror = () => {
|
|
5685
|
+
cloudKitLoadPromise = null;
|
|
5686
|
+
reject(new Error("Failed to load CloudKit JS"));
|
|
5687
|
+
};
|
|
5688
|
+
document.head.appendChild(script);
|
|
5689
|
+
});
|
|
5690
|
+
return cloudKitLoadPromise;
|
|
5691
|
+
}
|
|
5692
|
+
async function ensureCloudKitLoaded() {
|
|
5693
|
+
if (!isCloudKitAvailable()) {
|
|
5694
|
+
await loadCloudKit();
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
async function configureCloudKit(config) {
|
|
5698
|
+
await ensureCloudKitLoaded();
|
|
5699
|
+
ensureAuthElements();
|
|
5700
|
+
window.CloudKit.configure({
|
|
5701
|
+
containers: [
|
|
5702
|
+
{
|
|
5703
|
+
containerIdentifier: config.containerIdentifier,
|
|
5704
|
+
apiTokenAuth: {
|
|
5705
|
+
apiToken: config.apiToken,
|
|
5706
|
+
persist: true,
|
|
5707
|
+
signInButton: {
|
|
5708
|
+
id: "apple-sign-in-button",
|
|
5709
|
+
theme: "black"
|
|
5710
|
+
},
|
|
5711
|
+
signOutButton: {
|
|
5712
|
+
id: "apple-sign-out-button",
|
|
5713
|
+
theme: "black"
|
|
5714
|
+
}
|
|
5715
|
+
},
|
|
5716
|
+
environment: config.environment
|
|
5717
|
+
}
|
|
5718
|
+
]
|
|
5719
|
+
});
|
|
5720
|
+
}
|
|
5721
|
+
async function getContainer() {
|
|
5722
|
+
await ensureCloudKitLoaded();
|
|
5723
|
+
return window.CloudKit.getDefaultContainer();
|
|
5724
|
+
}
|
|
5725
|
+
function ensureAuthElements() {
|
|
5726
|
+
let signInButton = document.getElementById("apple-sign-in-button");
|
|
5727
|
+
let signOutButton = document.getElementById("apple-sign-out-button");
|
|
5728
|
+
if (!signInButton) {
|
|
5729
|
+
signInButton = document.createElement("div");
|
|
5730
|
+
signInButton.id = "apple-sign-in-button";
|
|
5731
|
+
signInButton.style.position = "fixed";
|
|
5732
|
+
signInButton.style.top = "-9999px";
|
|
5733
|
+
signInButton.style.left = "-9999px";
|
|
5734
|
+
document.body.appendChild(signInButton);
|
|
5735
|
+
}
|
|
5736
|
+
if (!signOutButton) {
|
|
5737
|
+
signOutButton = document.createElement("div");
|
|
5738
|
+
signOutButton.id = "apple-sign-out-button";
|
|
5739
|
+
signOutButton.style.position = "fixed";
|
|
5740
|
+
signOutButton.style.top = "-9999px";
|
|
5741
|
+
signOutButton.style.left = "-9999px";
|
|
5742
|
+
document.body.appendChild(signOutButton);
|
|
5743
|
+
}
|
|
5744
|
+
return { signIn: signInButton, signOut: signOutButton };
|
|
5745
|
+
}
|
|
5746
|
+
async function authenticateICloud() {
|
|
5747
|
+
const container = await getContainer();
|
|
5748
|
+
ensureAuthElements();
|
|
5749
|
+
return container.setUpAuth();
|
|
5750
|
+
}
|
|
5751
|
+
async function requestICloudSignIn() {
|
|
5752
|
+
const container = await getContainer();
|
|
5753
|
+
const { signIn } = ensureAuthElements();
|
|
5754
|
+
const existingUser = await container.setUpAuth();
|
|
5755
|
+
if (existingUser) {
|
|
5756
|
+
return existingUser;
|
|
5757
|
+
}
|
|
5758
|
+
console.log("[CloudKit] Sign-in container innerHTML:", signIn.innerHTML);
|
|
5759
|
+
console.log("[CloudKit] Sign-in container children:", signIn.children.length);
|
|
5760
|
+
const appleButton = signIn.querySelector("a, button, [role='button'], div[id*='apple']");
|
|
5761
|
+
console.log("[CloudKit] Found button element:", appleButton);
|
|
5762
|
+
if (appleButton) {
|
|
5763
|
+
console.log("[CloudKit] Clicking button...");
|
|
5764
|
+
appleButton.click();
|
|
5765
|
+
} else {
|
|
5766
|
+
const anyClickable = signIn.firstElementChild;
|
|
5767
|
+
if (anyClickable) {
|
|
5768
|
+
console.log("[CloudKit] Clicking first child element:", anyClickable);
|
|
5769
|
+
anyClickable.click();
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
return container.whenUserSignsIn();
|
|
5773
|
+
}
|
|
5774
|
+
async function uploadFileToICloud(filename, content) {
|
|
5775
|
+
const container = await getContainer();
|
|
5776
|
+
const database = container.privateCloudDatabase;
|
|
5777
|
+
const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
5778
|
+
const arrayBuffer = await content.arrayBuffer();
|
|
5779
|
+
const base64Data = btoa(
|
|
5780
|
+
String.fromCharCode(...new Uint8Array(arrayBuffer))
|
|
5781
|
+
);
|
|
5782
|
+
const record = {
|
|
5783
|
+
recordType: RECORD_TYPE,
|
|
5784
|
+
recordName,
|
|
5785
|
+
fields: {
|
|
5786
|
+
filename: { value: filename },
|
|
5787
|
+
data: { value: base64Data },
|
|
5788
|
+
size: { value: content.size },
|
|
5789
|
+
contentType: { value: content.type || "application/json" }
|
|
5790
|
+
}
|
|
5791
|
+
};
|
|
5792
|
+
const response = await database.saveRecords(record);
|
|
5793
|
+
if (!response.records || response.records.length === 0) {
|
|
5794
|
+
throw new Error("Failed to upload file to iCloud");
|
|
5795
|
+
}
|
|
5796
|
+
const savedRecord = response.records[0];
|
|
5797
|
+
return {
|
|
5798
|
+
recordName: savedRecord.recordName,
|
|
5799
|
+
filename,
|
|
5800
|
+
modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
|
|
5801
|
+
size: content.size
|
|
5802
|
+
};
|
|
5803
|
+
}
|
|
5804
|
+
async function listICloudFiles() {
|
|
5805
|
+
const container = await getContainer();
|
|
5806
|
+
const database = container.privateCloudDatabase;
|
|
5807
|
+
const query = {
|
|
5808
|
+
recordType: RECORD_TYPE
|
|
5809
|
+
// Note: Sorting requires SORTABLE index on the field in CloudKit Dashboard
|
|
5810
|
+
// For now, we skip sorting and sort client-side after fetching
|
|
5811
|
+
};
|
|
5812
|
+
const allRecords = [];
|
|
5813
|
+
let response = await database.performQuery(query);
|
|
5814
|
+
if (response.records) {
|
|
5815
|
+
allRecords.push(...response.records);
|
|
5816
|
+
}
|
|
5817
|
+
while (response.continuationMarker) {
|
|
5818
|
+
break;
|
|
5819
|
+
}
|
|
5820
|
+
const files = allRecords.map((record) => ({
|
|
5821
|
+
recordName: record.recordName,
|
|
5822
|
+
filename: record.fields.filename?.value ?? "",
|
|
5823
|
+
modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
|
|
5824
|
+
size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
|
|
5825
|
+
}));
|
|
5826
|
+
return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
|
|
5827
|
+
}
|
|
5828
|
+
async function downloadICloudFile(recordName) {
|
|
5829
|
+
const container = await getContainer();
|
|
5830
|
+
const database = container.privateCloudDatabase;
|
|
5831
|
+
const response = await database.fetchRecords([{ recordName }], {
|
|
5832
|
+
desiredKeys: ["filename", "data", "contentType"]
|
|
5833
|
+
});
|
|
5834
|
+
if (!response.records || response.records.length === 0) {
|
|
5835
|
+
throw new Error(`File not found: ${recordName}`);
|
|
5836
|
+
}
|
|
5837
|
+
const record = response.records[0];
|
|
5838
|
+
const dataField = record.fields.data?.value;
|
|
5839
|
+
if (!dataField) {
|
|
5840
|
+
throw new Error("No data in record");
|
|
5841
|
+
}
|
|
5842
|
+
if (typeof dataField === "string") {
|
|
5843
|
+
const binaryString = atob(dataField);
|
|
5844
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
5845
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
5846
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
5847
|
+
}
|
|
5848
|
+
return new Blob([bytes], { type: "application/json" });
|
|
5849
|
+
}
|
|
5850
|
+
if (typeof dataField === "object" && "downloadURL" in dataField) {
|
|
5851
|
+
const fetchResponse = await fetch(
|
|
5852
|
+
dataField.downloadURL
|
|
5853
|
+
);
|
|
5854
|
+
if (!fetchResponse.ok) {
|
|
5855
|
+
throw new Error(`Failed to download from iCloud: ${fetchResponse.status}`);
|
|
5856
|
+
}
|
|
5857
|
+
return fetchResponse.blob();
|
|
5858
|
+
}
|
|
5859
|
+
throw new Error("Unknown data format in iCloud record");
|
|
5860
|
+
}
|
|
5861
|
+
async function findICloudFile(filename) {
|
|
5862
|
+
const container = await getContainer();
|
|
5863
|
+
const database = container.privateCloudDatabase;
|
|
5864
|
+
const query = {
|
|
5865
|
+
recordType: RECORD_TYPE,
|
|
5866
|
+
filterBy: [
|
|
5867
|
+
{
|
|
5868
|
+
fieldName: "filename",
|
|
5869
|
+
comparator: "EQUALS",
|
|
5870
|
+
fieldValue: { value: filename }
|
|
5871
|
+
}
|
|
5872
|
+
]
|
|
5873
|
+
};
|
|
5874
|
+
const response = await database.performQuery(query);
|
|
5875
|
+
if (!response.records || response.records.length === 0) {
|
|
5876
|
+
return null;
|
|
5877
|
+
}
|
|
5878
|
+
const record = response.records[0];
|
|
5879
|
+
return {
|
|
5880
|
+
recordName: record.recordName,
|
|
5881
|
+
filename: record.fields.filename?.value ?? "",
|
|
5882
|
+
modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
|
|
5883
|
+
size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
|
|
5884
|
+
};
|
|
5885
|
+
}
|
|
5886
|
+
|
|
5887
|
+
// src/react/useICloudAuth.ts
|
|
5888
|
+
var ICloudAuthContext = (0, import_react14.createContext)(null);
|
|
5889
|
+
function ICloudAuthProvider({
|
|
5890
|
+
apiToken,
|
|
5891
|
+
containerIdentifier = DEFAULT_CONTAINER_ID,
|
|
5892
|
+
environment = "production",
|
|
5893
|
+
children
|
|
5894
|
+
}) {
|
|
5895
|
+
const [isAuthenticated, setIsAuthenticated] = (0, import_react14.useState)(false);
|
|
5896
|
+
const [userRecordName, setUserRecordName] = (0, import_react14.useState)(null);
|
|
5897
|
+
const [isAvailable, setIsAvailable] = (0, import_react14.useState)(false);
|
|
5898
|
+
const [isConfigured, setIsConfigured] = (0, import_react14.useState)(false);
|
|
5899
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(false);
|
|
5900
|
+
(0, import_react14.useEffect)(() => {
|
|
5901
|
+
if (!apiToken || typeof window === "undefined") {
|
|
5902
|
+
return;
|
|
5903
|
+
}
|
|
5904
|
+
const initCloudKit = async () => {
|
|
5905
|
+
setIsLoading(true);
|
|
5906
|
+
try {
|
|
5907
|
+
await loadCloudKit();
|
|
5908
|
+
setIsAvailable(true);
|
|
5909
|
+
const config = {
|
|
5910
|
+
containerIdentifier,
|
|
5911
|
+
apiToken,
|
|
5912
|
+
environment
|
|
5913
|
+
};
|
|
5914
|
+
await configureCloudKit(config);
|
|
5915
|
+
setIsConfigured(true);
|
|
5916
|
+
try {
|
|
5917
|
+
const userIdentity = await authenticateICloud();
|
|
5918
|
+
if (userIdentity) {
|
|
5919
|
+
setIsAuthenticated(true);
|
|
5920
|
+
setUserRecordName(userIdentity.userRecordName);
|
|
5921
|
+
}
|
|
5922
|
+
} catch {
|
|
5923
|
+
}
|
|
5924
|
+
} catch {
|
|
5925
|
+
setIsAvailable(false);
|
|
5926
|
+
setIsConfigured(false);
|
|
5927
|
+
} finally {
|
|
5928
|
+
setIsLoading(false);
|
|
5929
|
+
}
|
|
5930
|
+
};
|
|
5931
|
+
initCloudKit();
|
|
5932
|
+
}, [apiToken, containerIdentifier, environment]);
|
|
5933
|
+
const requestAccess = (0, import_react14.useCallback)(async () => {
|
|
5934
|
+
if (!isConfigured) {
|
|
5935
|
+
throw new Error("iCloud is not configured");
|
|
5936
|
+
}
|
|
5937
|
+
if (isAuthenticated) {
|
|
5938
|
+
return;
|
|
5939
|
+
}
|
|
5940
|
+
try {
|
|
5941
|
+
const userIdentity = await requestICloudSignIn();
|
|
5942
|
+
setIsAuthenticated(true);
|
|
5943
|
+
setUserRecordName(userIdentity.userRecordName);
|
|
5944
|
+
} catch (err) {
|
|
5945
|
+
throw new Error(
|
|
5946
|
+
err instanceof Error ? err.message : "Failed to sign in to iCloud"
|
|
5947
|
+
);
|
|
5948
|
+
}
|
|
5949
|
+
}, [isAuthenticated, isConfigured]);
|
|
5950
|
+
const logout = (0, import_react14.useCallback)(() => {
|
|
5951
|
+
setIsAuthenticated(false);
|
|
5952
|
+
setUserRecordName(null);
|
|
5953
|
+
}, []);
|
|
5954
|
+
return (0, import_react14.createElement)(
|
|
5955
|
+
ICloudAuthContext.Provider,
|
|
5956
|
+
{
|
|
5957
|
+
value: {
|
|
5958
|
+
isAuthenticated,
|
|
5959
|
+
isConfigured,
|
|
5960
|
+
isAvailable,
|
|
5961
|
+
userRecordName,
|
|
5962
|
+
requestAccess,
|
|
5963
|
+
logout
|
|
5964
|
+
}
|
|
5965
|
+
},
|
|
5966
|
+
children
|
|
5967
|
+
);
|
|
5968
|
+
}
|
|
5969
|
+
function useICloudAuth() {
|
|
5970
|
+
const context = (0, import_react14.useContext)(ICloudAuthContext);
|
|
5971
|
+
if (!context) {
|
|
5972
|
+
throw new Error("useICloudAuth must be used within ICloudAuthProvider");
|
|
5973
|
+
}
|
|
5974
|
+
return context;
|
|
5975
|
+
}
|
|
5976
|
+
function hasICloudCredentials() {
|
|
5977
|
+
return isCloudKitAvailable();
|
|
5978
|
+
}
|
|
5979
|
+
function clearICloudAuth() {
|
|
5980
|
+
}
|
|
5981
|
+
|
|
5982
|
+
// src/react/useICloudBackup.ts
|
|
5983
|
+
var import_react15 = require("react");
|
|
5984
|
+
|
|
5985
|
+
// src/lib/backup/icloud/backup.ts
|
|
5986
|
+
var isAuthError3 = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
|
|
5987
|
+
async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
|
|
5988
|
+
try {
|
|
5989
|
+
await deps.requestEncryptionKey(userAddress);
|
|
5990
|
+
const filename = `${conversationId}.json`;
|
|
5991
|
+
const existingFile = await findICloudFile(filename);
|
|
5992
|
+
if (existingFile) {
|
|
5993
|
+
const { Q: Q4 } = await import("@nozbe/watermelondb");
|
|
5994
|
+
const conversationsCollection = database.get("conversations");
|
|
5995
|
+
const records = await conversationsCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
5996
|
+
if (records.length > 0) {
|
|
5997
|
+
const conversation = conversationToStored(records[0]);
|
|
5998
|
+
const localUpdated = conversation.updatedAt.getTime();
|
|
5999
|
+
const remoteModified = existingFile.modifiedAt.getTime();
|
|
6000
|
+
if (localUpdated <= remoteModified) {
|
|
6001
|
+
return "skipped";
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6004
|
+
}
|
|
6005
|
+
const exportResult = await deps.exportConversation(
|
|
6006
|
+
conversationId,
|
|
6007
|
+
userAddress
|
|
6008
|
+
);
|
|
6009
|
+
if (!exportResult.success || !exportResult.blob) {
|
|
6010
|
+
return "failed";
|
|
6011
|
+
}
|
|
6012
|
+
await uploadFileToICloud(filename, exportResult.blob);
|
|
6013
|
+
return "uploaded";
|
|
6014
|
+
} catch (err) {
|
|
6015
|
+
if (isAuthError3(err) && !_retried) {
|
|
6016
|
+
try {
|
|
6017
|
+
await deps.requestICloudAccess();
|
|
6018
|
+
return pushConversationToICloud(
|
|
6019
|
+
database,
|
|
6020
|
+
conversationId,
|
|
6021
|
+
userAddress,
|
|
6022
|
+
deps,
|
|
6023
|
+
true
|
|
6024
|
+
);
|
|
6025
|
+
} catch {
|
|
6026
|
+
return "failed";
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
return "failed";
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
6032
|
+
async function performICloudExport(database, userAddress, deps, onProgress) {
|
|
6033
|
+
await deps.requestEncryptionKey(userAddress);
|
|
6034
|
+
const { Q: Q4 } = await import("@nozbe/watermelondb");
|
|
6035
|
+
const conversationsCollection = database.get("conversations");
|
|
6036
|
+
const records = await conversationsCollection.query(Q4.where("is_deleted", false)).fetch();
|
|
6037
|
+
const conversations = records.map(conversationToStored);
|
|
6038
|
+
const total = conversations.length;
|
|
6039
|
+
if (total === 0) {
|
|
6040
|
+
return { success: true, uploaded: 0, skipped: 0, total: 0 };
|
|
6041
|
+
}
|
|
6042
|
+
let uploaded = 0;
|
|
6043
|
+
let skipped = 0;
|
|
6044
|
+
for (let i = 0; i < conversations.length; i++) {
|
|
6045
|
+
const conv = conversations[i];
|
|
6046
|
+
onProgress?.(i + 1, total);
|
|
6047
|
+
const result = await pushConversationToICloud(
|
|
6048
|
+
database,
|
|
6049
|
+
conv.conversationId,
|
|
6050
|
+
userAddress,
|
|
6051
|
+
deps
|
|
6052
|
+
);
|
|
6053
|
+
if (result === "uploaded") uploaded++;
|
|
6054
|
+
if (result === "skipped") skipped++;
|
|
6055
|
+
}
|
|
6056
|
+
return { success: true, uploaded, skipped, total };
|
|
6057
|
+
}
|
|
6058
|
+
async function performICloudImport(userAddress, deps, onProgress) {
|
|
6059
|
+
await deps.requestEncryptionKey(userAddress);
|
|
6060
|
+
const remoteFiles = await listICloudFiles();
|
|
6061
|
+
if (remoteFiles.length === 0) {
|
|
6062
|
+
return {
|
|
6063
|
+
success: false,
|
|
6064
|
+
restored: 0,
|
|
6065
|
+
failed: 0,
|
|
6066
|
+
total: 0,
|
|
6067
|
+
noBackupsFound: true
|
|
6068
|
+
};
|
|
6069
|
+
}
|
|
6070
|
+
const jsonFiles = remoteFiles.filter(
|
|
6071
|
+
(file) => file.filename.endsWith(".json")
|
|
6072
|
+
);
|
|
6073
|
+
const total = jsonFiles.length;
|
|
6074
|
+
let restored = 0;
|
|
6075
|
+
let failed = 0;
|
|
6076
|
+
for (let i = 0; i < jsonFiles.length; i++) {
|
|
6077
|
+
const file = jsonFiles[i];
|
|
6078
|
+
onProgress?.(i + 1, total);
|
|
6079
|
+
try {
|
|
6080
|
+
const blob = await downloadICloudFile(file.recordName);
|
|
6081
|
+
const result = await deps.importConversation(blob, userAddress);
|
|
6082
|
+
if (result.success) {
|
|
6083
|
+
restored++;
|
|
6084
|
+
} else {
|
|
6085
|
+
failed++;
|
|
6086
|
+
}
|
|
6087
|
+
} catch (err) {
|
|
6088
|
+
if (isAuthError3(err)) {
|
|
6089
|
+
try {
|
|
6090
|
+
await deps.requestICloudAccess();
|
|
6091
|
+
const blob = await downloadICloudFile(file.recordName);
|
|
6092
|
+
const result = await deps.importConversation(blob, userAddress);
|
|
6093
|
+
if (result.success) {
|
|
6094
|
+
restored++;
|
|
6095
|
+
} else {
|
|
6096
|
+
failed++;
|
|
6097
|
+
}
|
|
6098
|
+
} catch {
|
|
6099
|
+
failed++;
|
|
6100
|
+
}
|
|
6101
|
+
} else {
|
|
6102
|
+
failed++;
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
return { success: true, restored, failed, total };
|
|
6107
|
+
}
|
|
6108
|
+
|
|
6109
|
+
// src/react/useICloudBackup.ts
|
|
6110
|
+
function useICloudBackup(options) {
|
|
6111
|
+
const {
|
|
6112
|
+
database,
|
|
6113
|
+
userAddress,
|
|
6114
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6115
|
+
exportConversation,
|
|
6116
|
+
importConversation
|
|
6117
|
+
} = options;
|
|
6118
|
+
const {
|
|
6119
|
+
isAuthenticated,
|
|
6120
|
+
isConfigured,
|
|
6121
|
+
isAvailable,
|
|
6122
|
+
requestAccess
|
|
6123
|
+
} = useICloudAuth();
|
|
6124
|
+
const deps = (0, import_react15.useMemo)(
|
|
6125
|
+
() => ({
|
|
6126
|
+
requestICloudAccess: requestAccess,
|
|
6127
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6128
|
+
exportConversation,
|
|
6129
|
+
importConversation
|
|
6130
|
+
}),
|
|
6131
|
+
[requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6132
|
+
);
|
|
6133
|
+
const ensureAuthenticated = (0, import_react15.useCallback)(async () => {
|
|
6134
|
+
if (isAuthenticated) return true;
|
|
6135
|
+
try {
|
|
6136
|
+
await requestAccess();
|
|
6137
|
+
return true;
|
|
6138
|
+
} catch {
|
|
6139
|
+
return false;
|
|
6140
|
+
}
|
|
6141
|
+
}, [isAuthenticated, requestAccess]);
|
|
6142
|
+
const backup = (0, import_react15.useCallback)(
|
|
6143
|
+
async (backupOptions) => {
|
|
6144
|
+
if (!userAddress) {
|
|
6145
|
+
return { error: "Please sign in to backup to iCloud" };
|
|
6146
|
+
}
|
|
6147
|
+
if (!isAvailable) {
|
|
6148
|
+
return { error: "CloudKit JS is not loaded" };
|
|
6149
|
+
}
|
|
6150
|
+
if (!isConfigured) {
|
|
6151
|
+
return { error: "iCloud is not configured" };
|
|
6152
|
+
}
|
|
6153
|
+
const authenticated = await ensureAuthenticated();
|
|
6154
|
+
if (!authenticated) {
|
|
6155
|
+
return { error: "iCloud access denied" };
|
|
6156
|
+
}
|
|
6157
|
+
try {
|
|
6158
|
+
return await performICloudExport(
|
|
6159
|
+
database,
|
|
6160
|
+
userAddress,
|
|
6161
|
+
deps,
|
|
6162
|
+
backupOptions?.onProgress
|
|
6163
|
+
);
|
|
6164
|
+
} catch (err) {
|
|
6165
|
+
return {
|
|
6166
|
+
error: err instanceof Error ? err.message : "Failed to backup to iCloud"
|
|
6167
|
+
};
|
|
6168
|
+
}
|
|
6169
|
+
},
|
|
6170
|
+
[database, userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
|
|
6171
|
+
);
|
|
6172
|
+
const restore = (0, import_react15.useCallback)(
|
|
6173
|
+
async (restoreOptions) => {
|
|
6174
|
+
if (!userAddress) {
|
|
6175
|
+
return { error: "Please sign in to restore from iCloud" };
|
|
6176
|
+
}
|
|
6177
|
+
if (!isAvailable) {
|
|
6178
|
+
return { error: "CloudKit JS is not loaded" };
|
|
6179
|
+
}
|
|
6180
|
+
if (!isConfigured) {
|
|
6181
|
+
return { error: "iCloud is not configured" };
|
|
6182
|
+
}
|
|
6183
|
+
const authenticated = await ensureAuthenticated();
|
|
6184
|
+
if (!authenticated) {
|
|
6185
|
+
return { error: "iCloud access denied" };
|
|
6186
|
+
}
|
|
6187
|
+
try {
|
|
6188
|
+
return await performICloudImport(
|
|
6189
|
+
userAddress,
|
|
6190
|
+
deps,
|
|
6191
|
+
restoreOptions?.onProgress
|
|
6192
|
+
);
|
|
6193
|
+
} catch (err) {
|
|
6194
|
+
return {
|
|
6195
|
+
error: err instanceof Error ? err.message : "Failed to restore from iCloud"
|
|
6196
|
+
};
|
|
6197
|
+
}
|
|
6198
|
+
},
|
|
6199
|
+
[userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
|
|
6200
|
+
);
|
|
6201
|
+
return {
|
|
6202
|
+
backup,
|
|
6203
|
+
restore,
|
|
6204
|
+
isConfigured,
|
|
6205
|
+
isAuthenticated,
|
|
6206
|
+
isAvailable
|
|
6207
|
+
};
|
|
6208
|
+
}
|
|
6209
|
+
|
|
6210
|
+
// src/react/useBackupAuth.ts
|
|
6211
|
+
var import_react16 = require("react");
|
|
6212
|
+
var BackupAuthContext = (0, import_react16.createContext)(null);
|
|
6213
|
+
function BackupAuthProvider({
|
|
6214
|
+
dropboxAppKey,
|
|
6215
|
+
dropboxCallbackPath = "/auth/dropbox/callback",
|
|
6216
|
+
googleClientId,
|
|
6217
|
+
googleCallbackPath = "/auth/google/callback",
|
|
6218
|
+
icloudApiToken,
|
|
6219
|
+
icloudContainerIdentifier = DEFAULT_CONTAINER_ID,
|
|
6220
|
+
icloudEnvironment = "production",
|
|
6221
|
+
apiClient,
|
|
6222
|
+
children
|
|
6223
|
+
}) {
|
|
6224
|
+
const [dropboxToken, setDropboxToken] = (0, import_react16.useState)(null);
|
|
6225
|
+
const isDropboxConfigured = !!dropboxAppKey;
|
|
6226
|
+
const [googleToken, setGoogleToken] = (0, import_react16.useState)(null);
|
|
6227
|
+
const isGoogleConfigured = !!googleClientId;
|
|
6228
|
+
const [icloudAuthenticated, setIcloudAuthenticated] = (0, import_react16.useState)(false);
|
|
6229
|
+
const [icloudUserRecordName, setIcloudUserRecordName] = (0, import_react16.useState)(null);
|
|
6230
|
+
const [isIcloudAvailable, setIsIcloudAvailable] = (0, import_react16.useState)(false);
|
|
6231
|
+
const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
|
|
6232
|
+
(0, import_react16.useEffect)(() => {
|
|
6233
|
+
const checkStoredTokens = async () => {
|
|
6234
|
+
if (hasDropboxCredentials()) {
|
|
6235
|
+
const token = await getDropboxAccessToken(apiClient);
|
|
6236
|
+
if (token) {
|
|
6237
|
+
setDropboxToken(token);
|
|
6238
|
+
}
|
|
6239
|
+
}
|
|
6240
|
+
if (hasGoogleDriveCredentials()) {
|
|
6241
|
+
const token = await getGoogleDriveAccessToken(apiClient);
|
|
6242
|
+
if (token) {
|
|
6243
|
+
setGoogleToken(token);
|
|
6244
|
+
}
|
|
6245
|
+
}
|
|
6246
|
+
};
|
|
6247
|
+
checkStoredTokens();
|
|
6248
|
+
}, [apiClient]);
|
|
6249
|
+
(0, import_react16.useEffect)(() => {
|
|
6250
|
+
if (!icloudApiToken || typeof window === "undefined") {
|
|
6251
|
+
return;
|
|
6252
|
+
}
|
|
6253
|
+
const initCloudKit = async () => {
|
|
6254
|
+
try {
|
|
6255
|
+
await loadCloudKit();
|
|
6256
|
+
setIsIcloudAvailable(true);
|
|
6257
|
+
const config = {
|
|
6258
|
+
containerIdentifier: icloudContainerIdentifier,
|
|
6259
|
+
apiToken: icloudApiToken,
|
|
6260
|
+
environment: icloudEnvironment
|
|
6261
|
+
};
|
|
6262
|
+
await configureCloudKit(config);
|
|
6263
|
+
try {
|
|
6264
|
+
const userIdentity = await authenticateICloud();
|
|
6265
|
+
if (userIdentity) {
|
|
6266
|
+
setIcloudAuthenticated(true);
|
|
6267
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6268
|
+
}
|
|
6269
|
+
} catch {
|
|
6270
|
+
}
|
|
6271
|
+
} catch {
|
|
6272
|
+
setIsIcloudAvailable(false);
|
|
6273
|
+
}
|
|
6274
|
+
};
|
|
6275
|
+
initCloudKit();
|
|
6276
|
+
}, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
|
|
6277
|
+
(0, import_react16.useEffect)(() => {
|
|
6278
|
+
if (!isDropboxConfigured) return;
|
|
6279
|
+
const handleCallback = async () => {
|
|
6280
|
+
if (isDropboxCallback()) {
|
|
6281
|
+
const token = await handleDropboxCallback(
|
|
6282
|
+
dropboxCallbackPath,
|
|
6283
|
+
apiClient
|
|
6284
|
+
);
|
|
6285
|
+
if (token) {
|
|
6286
|
+
setDropboxToken(token);
|
|
6287
|
+
}
|
|
6288
|
+
}
|
|
6289
|
+
};
|
|
6290
|
+
handleCallback();
|
|
6291
|
+
}, [dropboxCallbackPath, isDropboxConfigured, apiClient]);
|
|
6292
|
+
(0, import_react16.useEffect)(() => {
|
|
6293
|
+
if (!isGoogleConfigured) return;
|
|
6294
|
+
const handleCallback = async () => {
|
|
6295
|
+
if (isGoogleDriveCallback()) {
|
|
6296
|
+
const token = await handleGoogleDriveCallback(
|
|
6297
|
+
googleCallbackPath,
|
|
6298
|
+
apiClient
|
|
6299
|
+
);
|
|
6300
|
+
if (token) {
|
|
6301
|
+
setGoogleToken(token);
|
|
6302
|
+
}
|
|
6303
|
+
}
|
|
6304
|
+
};
|
|
6305
|
+
handleCallback();
|
|
6306
|
+
}, [googleCallbackPath, isGoogleConfigured, apiClient]);
|
|
6307
|
+
const refreshDropboxTokenFn = (0, import_react16.useCallback)(async () => {
|
|
6308
|
+
const token = await getDropboxAccessToken(apiClient);
|
|
6309
|
+
if (token) {
|
|
6310
|
+
setDropboxToken(token);
|
|
6311
|
+
}
|
|
6312
|
+
return token;
|
|
6313
|
+
}, [apiClient]);
|
|
6314
|
+
const requestDropboxAccess = (0, import_react16.useCallback)(async () => {
|
|
6315
|
+
if (!isDropboxConfigured || !dropboxAppKey) {
|
|
6316
|
+
throw new Error("Dropbox is not configured");
|
|
6317
|
+
}
|
|
6318
|
+
if (dropboxToken) {
|
|
6319
|
+
return dropboxToken;
|
|
6320
|
+
}
|
|
6321
|
+
const storedToken = await getDropboxAccessToken(apiClient);
|
|
6322
|
+
if (storedToken) {
|
|
6323
|
+
setDropboxToken(storedToken);
|
|
6324
|
+
return storedToken;
|
|
6325
|
+
}
|
|
6326
|
+
return startDropboxAuth(dropboxAppKey, dropboxCallbackPath);
|
|
6327
|
+
}, [
|
|
6328
|
+
dropboxToken,
|
|
6329
|
+
dropboxAppKey,
|
|
6330
|
+
dropboxCallbackPath,
|
|
6331
|
+
isDropboxConfigured,
|
|
6332
|
+
apiClient
|
|
6333
|
+
]);
|
|
6334
|
+
const logoutDropbox = (0, import_react16.useCallback)(async () => {
|
|
6335
|
+
await revokeDropboxToken(apiClient);
|
|
6336
|
+
setDropboxToken(null);
|
|
6337
|
+
}, [apiClient]);
|
|
6338
|
+
const refreshGoogleTokenFn = (0, import_react16.useCallback)(async () => {
|
|
6339
|
+
const token = await getGoogleDriveAccessToken(apiClient);
|
|
6340
|
+
if (token) {
|
|
6341
|
+
setGoogleToken(token);
|
|
6342
|
+
}
|
|
6343
|
+
return token;
|
|
6344
|
+
}, [apiClient]);
|
|
6345
|
+
const requestGoogleAccess = (0, import_react16.useCallback)(async () => {
|
|
6346
|
+
if (!isGoogleConfigured || !googleClientId) {
|
|
6347
|
+
throw new Error("Google Drive is not configured");
|
|
6348
|
+
}
|
|
6349
|
+
if (googleToken) {
|
|
6350
|
+
return googleToken;
|
|
6351
|
+
}
|
|
6352
|
+
const storedToken = await getGoogleDriveAccessToken(apiClient);
|
|
6353
|
+
if (storedToken) {
|
|
6354
|
+
setGoogleToken(storedToken);
|
|
6355
|
+
return storedToken;
|
|
6356
|
+
}
|
|
6357
|
+
return startGoogleDriveAuth(googleClientId, googleCallbackPath);
|
|
6358
|
+
}, [
|
|
6359
|
+
googleToken,
|
|
6360
|
+
googleClientId,
|
|
6361
|
+
googleCallbackPath,
|
|
6362
|
+
isGoogleConfigured,
|
|
6363
|
+
apiClient
|
|
6364
|
+
]);
|
|
6365
|
+
const logoutGoogle = (0, import_react16.useCallback)(async () => {
|
|
6366
|
+
await revokeGoogleDriveToken(apiClient);
|
|
6367
|
+
setGoogleToken(null);
|
|
6368
|
+
}, [apiClient]);
|
|
6369
|
+
const refreshIcloudTokenFn = (0, import_react16.useCallback)(async () => {
|
|
6370
|
+
try {
|
|
6371
|
+
const userIdentity = await authenticateICloud();
|
|
6372
|
+
if (userIdentity) {
|
|
6373
|
+
setIcloudAuthenticated(true);
|
|
6374
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6375
|
+
return userIdentity.userRecordName;
|
|
6376
|
+
}
|
|
6377
|
+
} catch {
|
|
6378
|
+
}
|
|
6379
|
+
return null;
|
|
6380
|
+
}, []);
|
|
6381
|
+
const requestIcloudAccess = (0, import_react16.useCallback)(async () => {
|
|
6382
|
+
if (!isIcloudConfigured) {
|
|
6383
|
+
throw new Error("iCloud is not configured");
|
|
6384
|
+
}
|
|
6385
|
+
if (icloudAuthenticated && icloudUserRecordName) {
|
|
6386
|
+
return icloudUserRecordName;
|
|
6387
|
+
}
|
|
6388
|
+
try {
|
|
6389
|
+
const userIdentity = await requestICloudSignIn();
|
|
6390
|
+
setIcloudAuthenticated(true);
|
|
6391
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6392
|
+
return userIdentity.userRecordName;
|
|
6393
|
+
} catch (err) {
|
|
6394
|
+
throw new Error(
|
|
6395
|
+
err instanceof Error ? err.message : "Failed to sign in to iCloud"
|
|
6396
|
+
);
|
|
6397
|
+
}
|
|
6398
|
+
}, [icloudAuthenticated, icloudUserRecordName, isIcloudConfigured]);
|
|
6399
|
+
const logoutIcloud = (0, import_react16.useCallback)(async () => {
|
|
6400
|
+
setIcloudAuthenticated(false);
|
|
6401
|
+
setIcloudUserRecordName(null);
|
|
6402
|
+
}, []);
|
|
6403
|
+
const logoutAll = (0, import_react16.useCallback)(async () => {
|
|
6404
|
+
await Promise.all([
|
|
6405
|
+
isDropboxConfigured ? logoutDropbox() : Promise.resolve(),
|
|
6406
|
+
isGoogleConfigured ? logoutGoogle() : Promise.resolve(),
|
|
6407
|
+
isIcloudConfigured ? logoutIcloud() : Promise.resolve()
|
|
6408
|
+
]);
|
|
6409
|
+
}, [isDropboxConfigured, isGoogleConfigured, isIcloudConfigured, logoutDropbox, logoutGoogle, logoutIcloud]);
|
|
6410
|
+
const dropboxState = {
|
|
6411
|
+
accessToken: dropboxToken,
|
|
6412
|
+
isAuthenticated: !!dropboxToken,
|
|
6413
|
+
isConfigured: isDropboxConfigured,
|
|
6414
|
+
requestAccess: requestDropboxAccess,
|
|
6415
|
+
logout: logoutDropbox,
|
|
6416
|
+
refreshToken: refreshDropboxTokenFn
|
|
6417
|
+
};
|
|
6418
|
+
const googleDriveState = {
|
|
6419
|
+
accessToken: googleToken,
|
|
6420
|
+
isAuthenticated: !!googleToken,
|
|
6421
|
+
isConfigured: isGoogleConfigured,
|
|
6422
|
+
requestAccess: requestGoogleAccess,
|
|
6423
|
+
logout: logoutGoogle,
|
|
6424
|
+
refreshToken: refreshGoogleTokenFn
|
|
6425
|
+
};
|
|
6426
|
+
const icloudState = {
|
|
6427
|
+
accessToken: icloudUserRecordName,
|
|
6428
|
+
// Use userRecordName as the "token" for iCloud
|
|
6429
|
+
isAuthenticated: icloudAuthenticated,
|
|
6430
|
+
isConfigured: isIcloudConfigured,
|
|
6431
|
+
requestAccess: requestIcloudAccess,
|
|
6432
|
+
logout: logoutIcloud,
|
|
6433
|
+
refreshToken: refreshIcloudTokenFn
|
|
6434
|
+
};
|
|
6435
|
+
return (0, import_react16.createElement)(
|
|
6436
|
+
BackupAuthContext.Provider,
|
|
6437
|
+
{
|
|
6438
|
+
value: {
|
|
6439
|
+
dropbox: dropboxState,
|
|
6440
|
+
googleDrive: googleDriveState,
|
|
6441
|
+
icloud: icloudState,
|
|
6442
|
+
hasAnyProvider: isDropboxConfigured || isGoogleConfigured || isIcloudConfigured,
|
|
6443
|
+
hasAnyAuthentication: !!dropboxToken || !!googleToken || icloudAuthenticated,
|
|
6444
|
+
logoutAll
|
|
6445
|
+
}
|
|
6446
|
+
},
|
|
6447
|
+
children
|
|
6448
|
+
);
|
|
6449
|
+
}
|
|
6450
|
+
function useBackupAuth() {
|
|
6451
|
+
const context = (0, import_react16.useContext)(BackupAuthContext);
|
|
6452
|
+
if (!context) {
|
|
6453
|
+
throw new Error("useBackupAuth must be used within BackupAuthProvider");
|
|
6454
|
+
}
|
|
6455
|
+
return context;
|
|
6456
|
+
}
|
|
6457
|
+
|
|
6458
|
+
// src/react/useBackup.ts
|
|
6459
|
+
var import_react17 = require("react");
|
|
6460
|
+
function useBackup(options) {
|
|
6461
|
+
const {
|
|
6462
|
+
database,
|
|
6463
|
+
userAddress,
|
|
6464
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6465
|
+
exportConversation,
|
|
6466
|
+
importConversation,
|
|
6467
|
+
dropboxFolder = DEFAULT_BACKUP_FOLDER,
|
|
6468
|
+
googleRootFolder = DEFAULT_ROOT_FOLDER,
|
|
6469
|
+
googleConversationsFolder = DEFAULT_CONVERSATIONS_FOLDER
|
|
6470
|
+
} = options;
|
|
6471
|
+
const {
|
|
6472
|
+
dropbox: dropboxAuth,
|
|
6473
|
+
googleDrive: googleDriveAuth,
|
|
6474
|
+
icloud: icloudAuth,
|
|
6475
|
+
hasAnyProvider,
|
|
6476
|
+
hasAnyAuthentication,
|
|
6477
|
+
logoutAll
|
|
6478
|
+
} = useBackupAuth();
|
|
6479
|
+
const dropboxDeps = (0, import_react17.useMemo)(
|
|
6480
|
+
() => ({
|
|
6481
|
+
requestDropboxAccess: dropboxAuth.requestAccess,
|
|
6482
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6483
|
+
exportConversation,
|
|
6484
|
+
importConversation
|
|
6485
|
+
}),
|
|
6486
|
+
[dropboxAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6487
|
+
);
|
|
6488
|
+
const googleDriveDeps = (0, import_react17.useMemo)(
|
|
6489
|
+
() => ({
|
|
6490
|
+
requestDriveAccess: googleDriveAuth.requestAccess,
|
|
6491
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6492
|
+
exportConversation,
|
|
6493
|
+
importConversation
|
|
6494
|
+
}),
|
|
6495
|
+
[googleDriveAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6496
|
+
);
|
|
6497
|
+
const icloudDeps = (0, import_react17.useMemo)(
|
|
6498
|
+
() => ({
|
|
6499
|
+
requestICloudAccess: async () => {
|
|
6500
|
+
await icloudAuth.requestAccess();
|
|
6501
|
+
},
|
|
6502
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6503
|
+
exportConversation,
|
|
6504
|
+
importConversation
|
|
6505
|
+
}),
|
|
6506
|
+
[icloudAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6507
|
+
);
|
|
6508
|
+
const dropboxBackup = (0, import_react17.useCallback)(
|
|
6509
|
+
async (backupOptions) => {
|
|
6510
|
+
if (!userAddress) {
|
|
6511
|
+
return { error: "Please sign in to backup to Dropbox" };
|
|
6512
|
+
}
|
|
6513
|
+
let token = dropboxAuth.accessToken;
|
|
6514
|
+
if (!token) {
|
|
6515
|
+
try {
|
|
6516
|
+
token = await dropboxAuth.requestAccess();
|
|
6517
|
+
} catch {
|
|
6518
|
+
return { error: "Dropbox access denied" };
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
try {
|
|
6522
|
+
return await performDropboxExport(
|
|
6523
|
+
database,
|
|
6524
|
+
userAddress,
|
|
6525
|
+
token,
|
|
6526
|
+
dropboxDeps,
|
|
6527
|
+
backupOptions?.onProgress,
|
|
6528
|
+
dropboxFolder
|
|
6529
|
+
);
|
|
6530
|
+
} catch (err) {
|
|
6531
|
+
return {
|
|
6532
|
+
error: err instanceof Error ? err.message : "Failed to backup to Dropbox"
|
|
6533
|
+
};
|
|
6534
|
+
}
|
|
6535
|
+
},
|
|
6536
|
+
[database, userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
|
|
6537
|
+
);
|
|
6538
|
+
const dropboxRestore = (0, import_react17.useCallback)(
|
|
6539
|
+
async (restoreOptions) => {
|
|
6540
|
+
if (!userAddress) {
|
|
6541
|
+
return { error: "Please sign in to restore from Dropbox" };
|
|
6542
|
+
}
|
|
6543
|
+
let token = dropboxAuth.accessToken;
|
|
6544
|
+
if (!token) {
|
|
6545
|
+
try {
|
|
6546
|
+
token = await dropboxAuth.requestAccess();
|
|
6547
|
+
} catch {
|
|
6548
|
+
return { error: "Dropbox access denied" };
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6551
|
+
try {
|
|
6552
|
+
return await performDropboxImport(
|
|
6553
|
+
userAddress,
|
|
6554
|
+
token,
|
|
6555
|
+
dropboxDeps,
|
|
6556
|
+
restoreOptions?.onProgress,
|
|
6557
|
+
dropboxFolder
|
|
6558
|
+
);
|
|
6559
|
+
} catch (err) {
|
|
6560
|
+
return {
|
|
6561
|
+
error: err instanceof Error ? err.message : "Failed to restore from Dropbox"
|
|
6562
|
+
};
|
|
6563
|
+
}
|
|
6564
|
+
},
|
|
6565
|
+
[userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
|
|
6566
|
+
);
|
|
6567
|
+
const googleDriveBackup = (0, import_react17.useCallback)(
|
|
6568
|
+
async (backupOptions) => {
|
|
6569
|
+
if (!userAddress) {
|
|
6570
|
+
return { error: "Please sign in to backup to Google Drive" };
|
|
6571
|
+
}
|
|
6572
|
+
let token = googleDriveAuth.accessToken;
|
|
6573
|
+
if (!token) {
|
|
6574
|
+
try {
|
|
6575
|
+
token = await googleDriveAuth.requestAccess();
|
|
6576
|
+
} catch {
|
|
6577
|
+
return { error: "Google Drive access denied" };
|
|
6578
|
+
}
|
|
6579
|
+
}
|
|
6580
|
+
try {
|
|
6581
|
+
return await performGoogleDriveExport(
|
|
6582
|
+
database,
|
|
6583
|
+
userAddress,
|
|
6584
|
+
token,
|
|
6585
|
+
googleDriveDeps,
|
|
6586
|
+
backupOptions?.onProgress,
|
|
6587
|
+
googleRootFolder,
|
|
6588
|
+
googleConversationsFolder
|
|
6589
|
+
);
|
|
6590
|
+
} catch (err) {
|
|
6591
|
+
return {
|
|
6592
|
+
error: err instanceof Error ? err.message : "Failed to backup to Google Drive"
|
|
6593
|
+
};
|
|
6594
|
+
}
|
|
6595
|
+
},
|
|
6596
|
+
[
|
|
6597
|
+
database,
|
|
6598
|
+
userAddress,
|
|
6599
|
+
googleDriveAuth,
|
|
6600
|
+
googleDriveDeps,
|
|
6601
|
+
googleRootFolder,
|
|
6602
|
+
googleConversationsFolder
|
|
6603
|
+
]
|
|
6604
|
+
);
|
|
6605
|
+
const googleDriveRestore = (0, import_react17.useCallback)(
|
|
6606
|
+
async (restoreOptions) => {
|
|
6607
|
+
if (!userAddress) {
|
|
6608
|
+
return { error: "Please sign in to restore from Google Drive" };
|
|
6609
|
+
}
|
|
6610
|
+
let token = googleDriveAuth.accessToken;
|
|
6611
|
+
if (!token) {
|
|
6612
|
+
try {
|
|
6613
|
+
token = await googleDriveAuth.requestAccess();
|
|
6614
|
+
} catch {
|
|
6615
|
+
return { error: "Google Drive access denied" };
|
|
6616
|
+
}
|
|
6617
|
+
}
|
|
6618
|
+
try {
|
|
6619
|
+
return await performGoogleDriveImport(
|
|
6620
|
+
userAddress,
|
|
6621
|
+
token,
|
|
6622
|
+
googleDriveDeps,
|
|
6623
|
+
restoreOptions?.onProgress,
|
|
6624
|
+
googleRootFolder,
|
|
6625
|
+
googleConversationsFolder
|
|
6626
|
+
);
|
|
6627
|
+
} catch (err) {
|
|
6628
|
+
return {
|
|
6629
|
+
error: err instanceof Error ? err.message : "Failed to restore from Google Drive"
|
|
6630
|
+
};
|
|
6631
|
+
}
|
|
6632
|
+
},
|
|
6633
|
+
[
|
|
6634
|
+
userAddress,
|
|
6635
|
+
googleDriveAuth,
|
|
6636
|
+
googleDriveDeps,
|
|
6637
|
+
googleRootFolder,
|
|
6638
|
+
googleConversationsFolder
|
|
6639
|
+
]
|
|
6640
|
+
);
|
|
6641
|
+
const dropboxState = {
|
|
6642
|
+
isConfigured: dropboxAuth.isConfigured,
|
|
6643
|
+
isAuthenticated: dropboxAuth.isAuthenticated,
|
|
6644
|
+
backup: dropboxBackup,
|
|
6645
|
+
restore: dropboxRestore,
|
|
6646
|
+
connect: dropboxAuth.requestAccess,
|
|
6647
|
+
disconnect: dropboxAuth.logout
|
|
6648
|
+
};
|
|
6649
|
+
const googleDriveState = {
|
|
6650
|
+
isConfigured: googleDriveAuth.isConfigured,
|
|
6651
|
+
isAuthenticated: googleDriveAuth.isAuthenticated,
|
|
6652
|
+
backup: googleDriveBackup,
|
|
6653
|
+
restore: googleDriveRestore,
|
|
6654
|
+
connect: googleDriveAuth.requestAccess,
|
|
6655
|
+
disconnect: googleDriveAuth.logout
|
|
6656
|
+
};
|
|
6657
|
+
const icloudBackup = (0, import_react17.useCallback)(
|
|
6658
|
+
async (backupOptions) => {
|
|
6659
|
+
if (!userAddress) {
|
|
6660
|
+
return { error: "Please sign in to backup to iCloud" };
|
|
6661
|
+
}
|
|
6662
|
+
if (!icloudAuth.isConfigured) {
|
|
6663
|
+
return { error: "iCloud is not configured" };
|
|
6664
|
+
}
|
|
6665
|
+
if (!icloudAuth.isAuthenticated) {
|
|
6666
|
+
try {
|
|
6667
|
+
await icloudAuth.requestAccess();
|
|
6668
|
+
} catch {
|
|
6669
|
+
return { error: "iCloud access denied" };
|
|
6670
|
+
}
|
|
6671
|
+
}
|
|
6672
|
+
try {
|
|
6673
|
+
return await performICloudExport(
|
|
6674
|
+
database,
|
|
6675
|
+
userAddress,
|
|
6676
|
+
icloudDeps,
|
|
6677
|
+
backupOptions?.onProgress
|
|
6678
|
+
);
|
|
6679
|
+
} catch (err) {
|
|
6680
|
+
return {
|
|
6681
|
+
error: err instanceof Error ? err.message : "Failed to backup to iCloud"
|
|
6682
|
+
};
|
|
6683
|
+
}
|
|
6684
|
+
},
|
|
6685
|
+
[database, userAddress, icloudAuth, icloudDeps]
|
|
6686
|
+
);
|
|
6687
|
+
const icloudRestore = (0, import_react17.useCallback)(
|
|
6688
|
+
async (restoreOptions) => {
|
|
6689
|
+
if (!userAddress) {
|
|
6690
|
+
return { error: "Please sign in to restore from iCloud" };
|
|
6691
|
+
}
|
|
6692
|
+
if (!icloudAuth.isConfigured) {
|
|
6693
|
+
return { error: "iCloud is not configured" };
|
|
6694
|
+
}
|
|
6695
|
+
if (!icloudAuth.isAuthenticated) {
|
|
6696
|
+
try {
|
|
6697
|
+
await icloudAuth.requestAccess();
|
|
6698
|
+
} catch {
|
|
6699
|
+
return { error: "iCloud access denied" };
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6702
|
+
try {
|
|
6703
|
+
return await performICloudImport(
|
|
6704
|
+
userAddress,
|
|
6705
|
+
icloudDeps,
|
|
6706
|
+
restoreOptions?.onProgress
|
|
6707
|
+
);
|
|
6708
|
+
} catch (err) {
|
|
6709
|
+
return {
|
|
6710
|
+
error: err instanceof Error ? err.message : "Failed to restore from iCloud"
|
|
6711
|
+
};
|
|
6712
|
+
}
|
|
6713
|
+
},
|
|
6714
|
+
[userAddress, icloudAuth, icloudDeps]
|
|
6715
|
+
);
|
|
6716
|
+
const icloudState = {
|
|
6717
|
+
isConfigured: icloudAuth.isConfigured,
|
|
6718
|
+
isAuthenticated: icloudAuth.isAuthenticated,
|
|
6719
|
+
backup: icloudBackup,
|
|
6720
|
+
restore: icloudRestore,
|
|
6721
|
+
connect: icloudAuth.requestAccess,
|
|
6722
|
+
disconnect: icloudAuth.logout
|
|
6723
|
+
};
|
|
6724
|
+
return {
|
|
6725
|
+
dropbox: dropboxState,
|
|
6726
|
+
googleDrive: googleDriveState,
|
|
6727
|
+
icloud: icloudState,
|
|
6728
|
+
hasAnyProvider,
|
|
6729
|
+
hasAnyAuthentication,
|
|
6730
|
+
disconnectAll: logoutAll
|
|
5274
6731
|
};
|
|
5275
6732
|
}
|
|
5276
6733
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5277
6734
|
0 && (module.exports = {
|
|
6735
|
+
BACKUP_DRIVE_CONVERSATIONS_FOLDER,
|
|
6736
|
+
BACKUP_DRIVE_ROOT_FOLDER,
|
|
6737
|
+
BACKUP_ICLOUD_FOLDER,
|
|
6738
|
+
BackupAuthProvider,
|
|
5278
6739
|
ChatConversation,
|
|
5279
6740
|
ChatMessage,
|
|
5280
6741
|
DEFAULT_BACKUP_FOLDER,
|
|
5281
6742
|
DEFAULT_DRIVE_CONVERSATIONS_FOLDER,
|
|
5282
6743
|
DEFAULT_DRIVE_ROOT_FOLDER,
|
|
6744
|
+
DEFAULT_DROPBOX_FOLDER,
|
|
6745
|
+
DEFAULT_ICLOUD_BACKUP_FOLDER,
|
|
5283
6746
|
DEFAULT_TOOL_SELECTOR_MODEL,
|
|
5284
6747
|
DropboxAuthProvider,
|
|
6748
|
+
GoogleDriveAuthProvider,
|
|
6749
|
+
ICloudAuthProvider,
|
|
5285
6750
|
StoredMemoryModel,
|
|
5286
6751
|
StoredModelPreferenceModel,
|
|
5287
6752
|
chatStorageMigrations,
|
|
5288
6753
|
chatStorageSchema,
|
|
5289
6754
|
clearDropboxToken,
|
|
6755
|
+
clearGoogleDriveToken,
|
|
6756
|
+
clearICloudAuth,
|
|
5290
6757
|
createMemoryContextSystemMessage,
|
|
5291
6758
|
decryptData,
|
|
5292
6759
|
decryptDataBytes,
|
|
@@ -5297,8 +6764,11 @@ function useGoogleDriveBackup(options) {
|
|
|
5297
6764
|
generateCompositeKey,
|
|
5298
6765
|
generateConversationId,
|
|
5299
6766
|
generateUniqueKey,
|
|
5300
|
-
|
|
6767
|
+
getGoogleDriveStoredToken,
|
|
6768
|
+
hasDropboxCredentials,
|
|
5301
6769
|
hasEncryptionKey,
|
|
6770
|
+
hasGoogleDriveCredentials,
|
|
6771
|
+
hasICloudCredentials,
|
|
5302
6772
|
memoryStorageSchema,
|
|
5303
6773
|
requestEncryptionKey,
|
|
5304
6774
|
sdkMigrations,
|
|
@@ -5306,13 +6776,17 @@ function useGoogleDriveBackup(options) {
|
|
|
5306
6776
|
sdkSchema,
|
|
5307
6777
|
selectTool,
|
|
5308
6778
|
settingsStorageSchema,
|
|
5309
|
-
|
|
6779
|
+
useBackup,
|
|
6780
|
+
useBackupAuth,
|
|
5310
6781
|
useChat,
|
|
5311
6782
|
useChatStorage,
|
|
5312
6783
|
useDropboxAuth,
|
|
5313
6784
|
useDropboxBackup,
|
|
5314
6785
|
useEncryption,
|
|
6786
|
+
useGoogleDriveAuth,
|
|
5315
6787
|
useGoogleDriveBackup,
|
|
6788
|
+
useICloudAuth,
|
|
6789
|
+
useICloudBackup,
|
|
5316
6790
|
useImageGeneration,
|
|
5317
6791
|
useMemoryStorage,
|
|
5318
6792
|
useModels,
|