@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.mjs
CHANGED
|
@@ -2995,6 +2995,36 @@ var postApiV1Search = (options) => {
|
|
|
2995
2995
|
}
|
|
2996
2996
|
});
|
|
2997
2997
|
};
|
|
2998
|
+
var postAuthOauthByProviderExchange = (options) => {
|
|
2999
|
+
return (options.client ?? client).post({
|
|
3000
|
+
url: "/auth/oauth/{provider}/exchange",
|
|
3001
|
+
...options,
|
|
3002
|
+
headers: {
|
|
3003
|
+
"Content-Type": "application/json",
|
|
3004
|
+
...options.headers
|
|
3005
|
+
}
|
|
3006
|
+
});
|
|
3007
|
+
};
|
|
3008
|
+
var postAuthOauthByProviderRefresh = (options) => {
|
|
3009
|
+
return (options.client ?? client).post({
|
|
3010
|
+
url: "/auth/oauth/{provider}/refresh",
|
|
3011
|
+
...options,
|
|
3012
|
+
headers: {
|
|
3013
|
+
"Content-Type": "application/json",
|
|
3014
|
+
...options.headers
|
|
3015
|
+
}
|
|
3016
|
+
});
|
|
3017
|
+
};
|
|
3018
|
+
var postAuthOauthByProviderRevoke = (options) => {
|
|
3019
|
+
return (options.client ?? client).post({
|
|
3020
|
+
url: "/auth/oauth/{provider}/revoke",
|
|
3021
|
+
...options,
|
|
3022
|
+
headers: {
|
|
3023
|
+
"Content-Type": "application/json",
|
|
3024
|
+
...options.headers
|
|
3025
|
+
}
|
|
3026
|
+
});
|
|
3027
|
+
};
|
|
2998
3028
|
|
|
2999
3029
|
// src/lib/memory/constants.ts
|
|
3000
3030
|
var DEFAULT_LOCAL_EMBEDDING_MODEL = "Snowflake/snowflake-arctic-embed-xs";
|
|
@@ -4545,116 +4575,196 @@ import {
|
|
|
4545
4575
|
useState as useState10
|
|
4546
4576
|
} from "react";
|
|
4547
4577
|
|
|
4548
|
-
// src/lib/backup/
|
|
4549
|
-
var
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
var VERIFIER_STORAGE_KEY = "dropbox_code_verifier";
|
|
4553
|
-
function generateCodeVerifier() {
|
|
4554
|
-
const array = new Uint8Array(32);
|
|
4555
|
-
crypto.getRandomValues(array);
|
|
4556
|
-
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
4557
|
-
}
|
|
4558
|
-
async function generateCodeChallenge(verifier) {
|
|
4559
|
-
const encoder = new TextEncoder();
|
|
4560
|
-
const data = encoder.encode(verifier);
|
|
4561
|
-
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
4562
|
-
const base64 = btoa(String.fromCharCode(...new Uint8Array(hash)));
|
|
4563
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
4578
|
+
// src/lib/backup/oauth/storage.ts
|
|
4579
|
+
var STORAGE_KEY_PREFIX = "oauth_token_";
|
|
4580
|
+
function getStorageKey2(provider) {
|
|
4581
|
+
return `${STORAGE_KEY_PREFIX}${provider}`;
|
|
4564
4582
|
}
|
|
4565
|
-
function
|
|
4583
|
+
function getStoredTokenData(provider) {
|
|
4566
4584
|
if (typeof window === "undefined") return null;
|
|
4567
|
-
|
|
4585
|
+
try {
|
|
4586
|
+
const stored = localStorage.getItem(getStorageKey2(provider));
|
|
4587
|
+
if (!stored) return null;
|
|
4588
|
+
const data = JSON.parse(stored);
|
|
4589
|
+
if (!data.accessToken) return null;
|
|
4590
|
+
return data;
|
|
4591
|
+
} catch {
|
|
4592
|
+
return null;
|
|
4593
|
+
}
|
|
4568
4594
|
}
|
|
4569
|
-
function
|
|
4595
|
+
function storeTokenData(provider, data) {
|
|
4570
4596
|
if (typeof window === "undefined") return;
|
|
4571
|
-
|
|
4597
|
+
localStorage.setItem(getStorageKey2(provider), JSON.stringify(data));
|
|
4572
4598
|
}
|
|
4573
|
-
function
|
|
4599
|
+
function clearTokenData(provider) {
|
|
4574
4600
|
if (typeof window === "undefined") return;
|
|
4575
|
-
|
|
4601
|
+
localStorage.removeItem(getStorageKey2(provider));
|
|
4576
4602
|
}
|
|
4577
|
-
function
|
|
4578
|
-
if (
|
|
4579
|
-
|
|
4603
|
+
function isTokenExpired(data, bufferSeconds = 60) {
|
|
4604
|
+
if (!data) return true;
|
|
4605
|
+
if (!data.expiresAt) return false;
|
|
4606
|
+
const now = Date.now();
|
|
4607
|
+
const bufferMs = bufferSeconds * 1e3;
|
|
4608
|
+
return data.expiresAt - bufferMs <= now;
|
|
4580
4609
|
}
|
|
4581
|
-
function
|
|
4582
|
-
|
|
4583
|
-
|
|
4610
|
+
function getValidAccessToken(provider) {
|
|
4611
|
+
const data = getStoredTokenData(provider);
|
|
4612
|
+
if (!data) return null;
|
|
4613
|
+
if (data.expiresAt && isTokenExpired(data)) {
|
|
4614
|
+
return null;
|
|
4615
|
+
}
|
|
4616
|
+
return data.accessToken;
|
|
4584
4617
|
}
|
|
4585
|
-
function
|
|
4586
|
-
|
|
4587
|
-
|
|
4618
|
+
function getRefreshToken(provider) {
|
|
4619
|
+
const data = getStoredTokenData(provider);
|
|
4620
|
+
return data?.refreshToken ?? null;
|
|
4621
|
+
}
|
|
4622
|
+
function tokenResponseToStoredData(accessToken, expiresIn, refreshToken, scope) {
|
|
4623
|
+
const data = {
|
|
4624
|
+
accessToken,
|
|
4625
|
+
refreshToken,
|
|
4626
|
+
scope
|
|
4627
|
+
};
|
|
4628
|
+
if (expiresIn) {
|
|
4629
|
+
data.expiresAt = Date.now() + expiresIn * 1e3;
|
|
4630
|
+
}
|
|
4631
|
+
return data;
|
|
4588
4632
|
}
|
|
4633
|
+
|
|
4634
|
+
// src/lib/backup/dropbox/auth.ts
|
|
4635
|
+
var PROVIDER = "dropbox";
|
|
4636
|
+
var STATE_STORAGE_KEY = "dropbox_oauth_state";
|
|
4637
|
+
var DROPBOX_AUTH_URL = "https://www.dropbox.com/oauth2/authorize";
|
|
4589
4638
|
function getRedirectUri(callbackPath) {
|
|
4590
4639
|
if (typeof window === "undefined") return "";
|
|
4591
4640
|
return `${window.location.origin}${callbackPath}`;
|
|
4592
4641
|
}
|
|
4593
|
-
|
|
4642
|
+
function generateState() {
|
|
4643
|
+
const array = new Uint8Array(16);
|
|
4644
|
+
crypto.getRandomValues(array);
|
|
4645
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
4646
|
+
""
|
|
4647
|
+
);
|
|
4648
|
+
}
|
|
4649
|
+
function storeOAuthState(state) {
|
|
4650
|
+
if (typeof window === "undefined") return;
|
|
4651
|
+
sessionStorage.setItem(STATE_STORAGE_KEY, state);
|
|
4652
|
+
}
|
|
4653
|
+
function getAndClearOAuthState() {
|
|
4654
|
+
if (typeof window === "undefined") return null;
|
|
4655
|
+
const state = sessionStorage.getItem(STATE_STORAGE_KEY);
|
|
4656
|
+
sessionStorage.removeItem(STATE_STORAGE_KEY);
|
|
4657
|
+
return state;
|
|
4658
|
+
}
|
|
4659
|
+
function isDropboxCallback() {
|
|
4660
|
+
if (typeof window === "undefined") return false;
|
|
4661
|
+
const url = new URL(window.location.href);
|
|
4662
|
+
const code = url.searchParams.get("code");
|
|
4663
|
+
const state = url.searchParams.get("state");
|
|
4664
|
+
const storedState = sessionStorage.getItem(STATE_STORAGE_KEY);
|
|
4665
|
+
return !!code && !!state && state === storedState;
|
|
4666
|
+
}
|
|
4667
|
+
async function handleDropboxCallback(callbackPath, apiClient) {
|
|
4594
4668
|
if (typeof window === "undefined") return null;
|
|
4595
4669
|
const url = new URL(window.location.href);
|
|
4596
4670
|
const code = url.searchParams.get("code");
|
|
4597
4671
|
const state = url.searchParams.get("state");
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4672
|
+
const storedState = getAndClearOAuthState();
|
|
4673
|
+
if (!code || !state || state !== storedState) {
|
|
4674
|
+
return null;
|
|
4675
|
+
}
|
|
4601
4676
|
try {
|
|
4602
|
-
const response = await
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
},
|
|
4607
|
-
body: new URLSearchParams({
|
|
4677
|
+
const response = await postAuthOauthByProviderExchange({
|
|
4678
|
+
client: apiClient,
|
|
4679
|
+
path: { provider: PROVIDER },
|
|
4680
|
+
body: {
|
|
4608
4681
|
code,
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
redirect_uri: getRedirectUri(callbackPath),
|
|
4612
|
-
code_verifier: verifier
|
|
4613
|
-
})
|
|
4682
|
+
redirect_uri: getRedirectUri(callbackPath)
|
|
4683
|
+
}
|
|
4614
4684
|
});
|
|
4615
|
-
if (!response.
|
|
4616
|
-
throw new Error("
|
|
4685
|
+
if (!response.data?.access_token) {
|
|
4686
|
+
throw new Error("No access token in response");
|
|
4617
4687
|
}
|
|
4618
|
-
const
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4688
|
+
const tokenData = tokenResponseToStoredData(
|
|
4689
|
+
response.data.access_token,
|
|
4690
|
+
response.data.expires_in,
|
|
4691
|
+
response.data.refresh_token,
|
|
4692
|
+
response.data.scope
|
|
4693
|
+
);
|
|
4694
|
+
storeTokenData(PROVIDER, tokenData);
|
|
4625
4695
|
window.history.replaceState({}, "", window.location.pathname);
|
|
4626
|
-
return
|
|
4696
|
+
return response.data.access_token;
|
|
4697
|
+
} catch {
|
|
4698
|
+
return null;
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
async function refreshDropboxToken(apiClient) {
|
|
4702
|
+
const refreshToken = getRefreshToken(PROVIDER);
|
|
4703
|
+
if (!refreshToken) return null;
|
|
4704
|
+
try {
|
|
4705
|
+
const response = await postAuthOauthByProviderRefresh({
|
|
4706
|
+
client: apiClient,
|
|
4707
|
+
path: { provider: PROVIDER },
|
|
4708
|
+
body: { refresh_token: refreshToken }
|
|
4709
|
+
});
|
|
4710
|
+
if (!response.data?.access_token) {
|
|
4711
|
+
throw new Error("No access token in refresh response");
|
|
4712
|
+
}
|
|
4713
|
+
const currentData = getStoredTokenData(PROVIDER);
|
|
4714
|
+
const tokenData = tokenResponseToStoredData(
|
|
4715
|
+
response.data.access_token,
|
|
4716
|
+
response.data.expires_in,
|
|
4717
|
+
response.data.refresh_token ?? currentData?.refreshToken,
|
|
4718
|
+
response.data.scope ?? currentData?.scope
|
|
4719
|
+
);
|
|
4720
|
+
storeTokenData(PROVIDER, tokenData);
|
|
4721
|
+
return response.data.access_token;
|
|
4627
4722
|
} catch {
|
|
4628
|
-
|
|
4723
|
+
clearTokenData(PROVIDER);
|
|
4629
4724
|
return null;
|
|
4630
4725
|
}
|
|
4631
4726
|
}
|
|
4727
|
+
async function revokeDropboxToken(apiClient) {
|
|
4728
|
+
const tokenData = getStoredTokenData(PROVIDER);
|
|
4729
|
+
if (!tokenData) return;
|
|
4730
|
+
try {
|
|
4731
|
+
const tokenToRevoke = tokenData.refreshToken ?? tokenData.accessToken;
|
|
4732
|
+
await postAuthOauthByProviderRevoke({
|
|
4733
|
+
client: apiClient,
|
|
4734
|
+
path: { provider: PROVIDER },
|
|
4735
|
+
body: { token: tokenToRevoke }
|
|
4736
|
+
});
|
|
4737
|
+
} catch {
|
|
4738
|
+
} finally {
|
|
4739
|
+
clearTokenData(PROVIDER);
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
async function getDropboxAccessToken(apiClient) {
|
|
4743
|
+
const validToken = getValidAccessToken(PROVIDER);
|
|
4744
|
+
if (validToken) return validToken;
|
|
4745
|
+
return refreshDropboxToken(apiClient);
|
|
4746
|
+
}
|
|
4632
4747
|
async function startDropboxAuth(appKey, callbackPath) {
|
|
4633
|
-
const
|
|
4634
|
-
|
|
4635
|
-
storeVerifier(verifier);
|
|
4748
|
+
const state = generateState();
|
|
4749
|
+
storeOAuthState(state);
|
|
4636
4750
|
const params = new URLSearchParams({
|
|
4637
4751
|
client_id: appKey,
|
|
4638
4752
|
redirect_uri: getRedirectUri(callbackPath),
|
|
4639
4753
|
response_type: "code",
|
|
4640
|
-
|
|
4641
|
-
code_challenge_method: "S256",
|
|
4642
|
-
state: "dropbox_auth",
|
|
4754
|
+
state,
|
|
4643
4755
|
token_access_type: "offline"
|
|
4756
|
+
// Request refresh token
|
|
4644
4757
|
});
|
|
4645
4758
|
window.location.href = `${DROPBOX_AUTH_URL}?${params.toString()}`;
|
|
4646
4759
|
return new Promise(() => {
|
|
4647
4760
|
});
|
|
4648
4761
|
}
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
const
|
|
4654
|
-
|
|
4655
|
-
return storedToken;
|
|
4656
|
-
}
|
|
4657
|
-
return startDropboxAuth(appKey, callbackPath);
|
|
4762
|
+
function clearToken() {
|
|
4763
|
+
clearTokenData(PROVIDER);
|
|
4764
|
+
}
|
|
4765
|
+
function hasDropboxCredentials() {
|
|
4766
|
+
const data = getStoredTokenData(PROVIDER);
|
|
4767
|
+
return !!(data?.accessToken || data?.refreshToken);
|
|
4658
4768
|
}
|
|
4659
4769
|
|
|
4660
4770
|
// src/react/useDropboxAuth.ts
|
|
@@ -4662,26 +4772,41 @@ var DropboxAuthContext = createContext(null);
|
|
|
4662
4772
|
function DropboxAuthProvider({
|
|
4663
4773
|
appKey,
|
|
4664
4774
|
callbackPath = "/auth/dropbox/callback",
|
|
4775
|
+
apiClient,
|
|
4665
4776
|
children
|
|
4666
4777
|
}) {
|
|
4667
4778
|
const [accessToken, setAccessToken] = useState10(null);
|
|
4668
4779
|
const isConfigured = !!appKey;
|
|
4669
4780
|
useEffect6(() => {
|
|
4670
|
-
const
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4781
|
+
const checkStoredToken = async () => {
|
|
4782
|
+
if (hasDropboxCredentials()) {
|
|
4783
|
+
const token = await getDropboxAccessToken(apiClient);
|
|
4784
|
+
if (token) {
|
|
4785
|
+
setAccessToken(token);
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
};
|
|
4789
|
+
checkStoredToken();
|
|
4790
|
+
}, [apiClient]);
|
|
4675
4791
|
useEffect6(() => {
|
|
4676
|
-
if (!isConfigured
|
|
4792
|
+
if (!isConfigured) return;
|
|
4677
4793
|
const handleCallback = async () => {
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4794
|
+
if (isDropboxCallback()) {
|
|
4795
|
+
const token = await handleDropboxCallback(callbackPath, apiClient);
|
|
4796
|
+
if (token) {
|
|
4797
|
+
setAccessToken(token);
|
|
4798
|
+
}
|
|
4681
4799
|
}
|
|
4682
4800
|
};
|
|
4683
4801
|
handleCallback();
|
|
4684
|
-
}, [
|
|
4802
|
+
}, [callbackPath, isConfigured, apiClient]);
|
|
4803
|
+
const refreshTokenFn = useCallback10(async () => {
|
|
4804
|
+
const token = await getDropboxAccessToken(apiClient);
|
|
4805
|
+
if (token) {
|
|
4806
|
+
setAccessToken(token);
|
|
4807
|
+
}
|
|
4808
|
+
return token;
|
|
4809
|
+
}, [apiClient]);
|
|
4685
4810
|
const requestAccess = useCallback10(async () => {
|
|
4686
4811
|
if (!isConfigured || !appKey) {
|
|
4687
4812
|
throw new Error("Dropbox is not configured");
|
|
@@ -4689,17 +4814,17 @@ function DropboxAuthProvider({
|
|
|
4689
4814
|
if (accessToken) {
|
|
4690
4815
|
return accessToken;
|
|
4691
4816
|
}
|
|
4692
|
-
const storedToken =
|
|
4817
|
+
const storedToken = await getDropboxAccessToken(apiClient);
|
|
4693
4818
|
if (storedToken) {
|
|
4694
4819
|
setAccessToken(storedToken);
|
|
4695
4820
|
return storedToken;
|
|
4696
4821
|
}
|
|
4697
|
-
return
|
|
4698
|
-
}, [accessToken, appKey, callbackPath, isConfigured]);
|
|
4699
|
-
const logout = useCallback10(() => {
|
|
4700
|
-
|
|
4822
|
+
return startDropboxAuth(appKey, callbackPath);
|
|
4823
|
+
}, [accessToken, appKey, callbackPath, isConfigured, apiClient]);
|
|
4824
|
+
const logout = useCallback10(async () => {
|
|
4825
|
+
await revokeDropboxToken(apiClient);
|
|
4701
4826
|
setAccessToken(null);
|
|
4702
|
-
}, []);
|
|
4827
|
+
}, [apiClient]);
|
|
4703
4828
|
return createElement(
|
|
4704
4829
|
DropboxAuthContext.Provider,
|
|
4705
4830
|
{
|
|
@@ -4708,7 +4833,8 @@ function DropboxAuthProvider({
|
|
|
4708
4833
|
isAuthenticated: !!accessToken,
|
|
4709
4834
|
isConfigured,
|
|
4710
4835
|
requestAccess,
|
|
4711
|
-
logout
|
|
4836
|
+
logout,
|
|
4837
|
+
refreshToken: refreshTokenFn
|
|
4712
4838
|
}
|
|
4713
4839
|
},
|
|
4714
4840
|
children
|
|
@@ -4735,25 +4861,25 @@ function useDropboxBackup(options) {
|
|
|
4735
4861
|
const {
|
|
4736
4862
|
accessToken: dropboxToken,
|
|
4737
4863
|
isConfigured: isDropboxConfigured,
|
|
4738
|
-
requestAccess:
|
|
4864
|
+
requestAccess: requestDropboxAccess
|
|
4739
4865
|
} = useDropboxAuth();
|
|
4740
4866
|
const deps = useMemo4(
|
|
4741
4867
|
() => ({
|
|
4742
|
-
requestDropboxAccess
|
|
4868
|
+
requestDropboxAccess,
|
|
4743
4869
|
requestEncryptionKey: requestEncryptionKey2,
|
|
4744
4870
|
exportConversation,
|
|
4745
4871
|
importConversation
|
|
4746
4872
|
}),
|
|
4747
|
-
[
|
|
4873
|
+
[requestDropboxAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
4748
4874
|
);
|
|
4749
4875
|
const ensureToken = useCallback11(async () => {
|
|
4750
4876
|
if (dropboxToken) return dropboxToken;
|
|
4751
4877
|
try {
|
|
4752
|
-
return await
|
|
4878
|
+
return await requestDropboxAccess();
|
|
4753
4879
|
} catch {
|
|
4754
4880
|
return null;
|
|
4755
4881
|
}
|
|
4756
|
-
}, [dropboxToken,
|
|
4882
|
+
}, [dropboxToken, requestDropboxAccess]);
|
|
4757
4883
|
const backup = useCallback11(
|
|
4758
4884
|
async (backupOptions) => {
|
|
4759
4885
|
if (!userAddress) {
|
|
@@ -4813,8 +4939,247 @@ function useDropboxBackup(options) {
|
|
|
4813
4939
|
};
|
|
4814
4940
|
}
|
|
4815
4941
|
|
|
4942
|
+
// src/react/useGoogleDriveAuth.ts
|
|
4943
|
+
import {
|
|
4944
|
+
createContext as createContext2,
|
|
4945
|
+
createElement as createElement2,
|
|
4946
|
+
useCallback as useCallback12,
|
|
4947
|
+
useContext as useContext2,
|
|
4948
|
+
useEffect as useEffect7,
|
|
4949
|
+
useState as useState11
|
|
4950
|
+
} from "react";
|
|
4951
|
+
|
|
4952
|
+
// src/lib/backup/google/auth.ts
|
|
4953
|
+
var PROVIDER2 = "google-drive";
|
|
4954
|
+
var CODE_STORAGE_KEY = "google_oauth_state";
|
|
4955
|
+
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
4956
|
+
var DRIVE_SCOPES = [
|
|
4957
|
+
"https://www.googleapis.com/auth/drive.file"
|
|
4958
|
+
// Access to files created by the app
|
|
4959
|
+
].join(" ");
|
|
4960
|
+
function getRedirectUri2(callbackPath) {
|
|
4961
|
+
if (typeof window === "undefined") return "";
|
|
4962
|
+
return `${window.location.origin}${callbackPath}`;
|
|
4963
|
+
}
|
|
4964
|
+
function generateState2() {
|
|
4965
|
+
const array = new Uint8Array(16);
|
|
4966
|
+
crypto.getRandomValues(array);
|
|
4967
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
4968
|
+
""
|
|
4969
|
+
);
|
|
4970
|
+
}
|
|
4971
|
+
function storeOAuthState2(state) {
|
|
4972
|
+
if (typeof window === "undefined") return;
|
|
4973
|
+
sessionStorage.setItem(CODE_STORAGE_KEY, state);
|
|
4974
|
+
}
|
|
4975
|
+
function getAndClearOAuthState2() {
|
|
4976
|
+
if (typeof window === "undefined") return null;
|
|
4977
|
+
const state = sessionStorage.getItem(CODE_STORAGE_KEY);
|
|
4978
|
+
sessionStorage.removeItem(CODE_STORAGE_KEY);
|
|
4979
|
+
return state;
|
|
4980
|
+
}
|
|
4981
|
+
function isGoogleDriveCallback() {
|
|
4982
|
+
if (typeof window === "undefined") return false;
|
|
4983
|
+
const url = new URL(window.location.href);
|
|
4984
|
+
const code = url.searchParams.get("code");
|
|
4985
|
+
const state = url.searchParams.get("state");
|
|
4986
|
+
const storedState = sessionStorage.getItem(CODE_STORAGE_KEY);
|
|
4987
|
+
return !!code && !!state && state === storedState;
|
|
4988
|
+
}
|
|
4989
|
+
async function handleGoogleDriveCallback(callbackPath, apiClient) {
|
|
4990
|
+
if (typeof window === "undefined") return null;
|
|
4991
|
+
const url = new URL(window.location.href);
|
|
4992
|
+
const code = url.searchParams.get("code");
|
|
4993
|
+
const state = url.searchParams.get("state");
|
|
4994
|
+
const storedState = getAndClearOAuthState2();
|
|
4995
|
+
if (!code || !state || state !== storedState) {
|
|
4996
|
+
return null;
|
|
4997
|
+
}
|
|
4998
|
+
try {
|
|
4999
|
+
const response = await postAuthOauthByProviderExchange({
|
|
5000
|
+
client: apiClient,
|
|
5001
|
+
path: { provider: PROVIDER2 },
|
|
5002
|
+
body: {
|
|
5003
|
+
code,
|
|
5004
|
+
redirect_uri: getRedirectUri2(callbackPath)
|
|
5005
|
+
}
|
|
5006
|
+
});
|
|
5007
|
+
if (!response.data?.access_token) {
|
|
5008
|
+
throw new Error("No access token in response");
|
|
5009
|
+
}
|
|
5010
|
+
const tokenData = tokenResponseToStoredData(
|
|
5011
|
+
response.data.access_token,
|
|
5012
|
+
response.data.expires_in,
|
|
5013
|
+
response.data.refresh_token,
|
|
5014
|
+
response.data.scope
|
|
5015
|
+
);
|
|
5016
|
+
storeTokenData(PROVIDER2, tokenData);
|
|
5017
|
+
window.history.replaceState({}, "", window.location.pathname);
|
|
5018
|
+
return response.data.access_token;
|
|
5019
|
+
} catch {
|
|
5020
|
+
return null;
|
|
5021
|
+
}
|
|
5022
|
+
}
|
|
5023
|
+
async function refreshGoogleDriveToken(apiClient) {
|
|
5024
|
+
const refreshToken = getRefreshToken(PROVIDER2);
|
|
5025
|
+
if (!refreshToken) return null;
|
|
5026
|
+
try {
|
|
5027
|
+
const response = await postAuthOauthByProviderRefresh({
|
|
5028
|
+
client: apiClient,
|
|
5029
|
+
path: { provider: PROVIDER2 },
|
|
5030
|
+
body: { refresh_token: refreshToken }
|
|
5031
|
+
});
|
|
5032
|
+
if (!response.data?.access_token) {
|
|
5033
|
+
throw new Error("No access token in refresh response");
|
|
5034
|
+
}
|
|
5035
|
+
const currentData = getStoredTokenData(PROVIDER2);
|
|
5036
|
+
const tokenData = tokenResponseToStoredData(
|
|
5037
|
+
response.data.access_token,
|
|
5038
|
+
response.data.expires_in,
|
|
5039
|
+
response.data.refresh_token ?? currentData?.refreshToken,
|
|
5040
|
+
response.data.scope ?? currentData?.scope
|
|
5041
|
+
);
|
|
5042
|
+
storeTokenData(PROVIDER2, tokenData);
|
|
5043
|
+
return response.data.access_token;
|
|
5044
|
+
} catch {
|
|
5045
|
+
clearTokenData(PROVIDER2);
|
|
5046
|
+
return null;
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
async function revokeGoogleDriveToken(apiClient) {
|
|
5050
|
+
const tokenData = getStoredTokenData(PROVIDER2);
|
|
5051
|
+
if (!tokenData) return;
|
|
5052
|
+
try {
|
|
5053
|
+
const tokenToRevoke = tokenData.refreshToken ?? tokenData.accessToken;
|
|
5054
|
+
await postAuthOauthByProviderRevoke({
|
|
5055
|
+
client: apiClient,
|
|
5056
|
+
path: { provider: PROVIDER2 },
|
|
5057
|
+
body: { token: tokenToRevoke }
|
|
5058
|
+
});
|
|
5059
|
+
} catch {
|
|
5060
|
+
} finally {
|
|
5061
|
+
clearTokenData(PROVIDER2);
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
async function getGoogleDriveAccessToken(apiClient) {
|
|
5065
|
+
const validToken = getValidAccessToken(PROVIDER2);
|
|
5066
|
+
if (validToken) return validToken;
|
|
5067
|
+
return refreshGoogleDriveToken(apiClient);
|
|
5068
|
+
}
|
|
5069
|
+
async function startGoogleDriveAuth(clientId, callbackPath) {
|
|
5070
|
+
const state = generateState2();
|
|
5071
|
+
storeOAuthState2(state);
|
|
5072
|
+
const params = new URLSearchParams({
|
|
5073
|
+
client_id: clientId,
|
|
5074
|
+
redirect_uri: getRedirectUri2(callbackPath),
|
|
5075
|
+
response_type: "code",
|
|
5076
|
+
scope: DRIVE_SCOPES,
|
|
5077
|
+
state,
|
|
5078
|
+
access_type: "offline",
|
|
5079
|
+
// Request refresh token
|
|
5080
|
+
prompt: "consent"
|
|
5081
|
+
// Force consent to always get refresh token
|
|
5082
|
+
});
|
|
5083
|
+
window.location.href = `${GOOGLE_AUTH_URL}?${params.toString()}`;
|
|
5084
|
+
return new Promise(() => {
|
|
5085
|
+
});
|
|
5086
|
+
}
|
|
5087
|
+
function getGoogleDriveStoredToken() {
|
|
5088
|
+
return getValidAccessToken(PROVIDER2);
|
|
5089
|
+
}
|
|
5090
|
+
function clearGoogleDriveToken() {
|
|
5091
|
+
clearTokenData(PROVIDER2);
|
|
5092
|
+
}
|
|
5093
|
+
function hasGoogleDriveCredentials() {
|
|
5094
|
+
const data = getStoredTokenData(PROVIDER2);
|
|
5095
|
+
return !!(data?.accessToken || data?.refreshToken);
|
|
5096
|
+
}
|
|
5097
|
+
|
|
5098
|
+
// src/react/useGoogleDriveAuth.ts
|
|
5099
|
+
var GoogleDriveAuthContext = createContext2(null);
|
|
5100
|
+
function GoogleDriveAuthProvider({
|
|
5101
|
+
clientId,
|
|
5102
|
+
callbackPath = "/auth/google/callback",
|
|
5103
|
+
apiClient,
|
|
5104
|
+
children
|
|
5105
|
+
}) {
|
|
5106
|
+
const [accessToken, setAccessToken] = useState11(null);
|
|
5107
|
+
const isConfigured = !!clientId;
|
|
5108
|
+
useEffect7(() => {
|
|
5109
|
+
const checkStoredToken = async () => {
|
|
5110
|
+
if (hasGoogleDriveCredentials()) {
|
|
5111
|
+
const token = await getGoogleDriveAccessToken(apiClient);
|
|
5112
|
+
if (token) {
|
|
5113
|
+
setAccessToken(token);
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
};
|
|
5117
|
+
checkStoredToken();
|
|
5118
|
+
}, [apiClient]);
|
|
5119
|
+
useEffect7(() => {
|
|
5120
|
+
if (!isConfigured) return;
|
|
5121
|
+
const handleCallback = async () => {
|
|
5122
|
+
if (isGoogleDriveCallback()) {
|
|
5123
|
+
const token = await handleGoogleDriveCallback(callbackPath, apiClient);
|
|
5124
|
+
if (token) {
|
|
5125
|
+
setAccessToken(token);
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5128
|
+
};
|
|
5129
|
+
handleCallback();
|
|
5130
|
+
}, [callbackPath, isConfigured, apiClient]);
|
|
5131
|
+
const refreshTokenFn = useCallback12(async () => {
|
|
5132
|
+
const token = await getGoogleDriveAccessToken(apiClient);
|
|
5133
|
+
if (token) {
|
|
5134
|
+
setAccessToken(token);
|
|
5135
|
+
}
|
|
5136
|
+
return token;
|
|
5137
|
+
}, [apiClient]);
|
|
5138
|
+
const requestAccess = useCallback12(async () => {
|
|
5139
|
+
if (!isConfigured || !clientId) {
|
|
5140
|
+
throw new Error("Google Drive is not configured");
|
|
5141
|
+
}
|
|
5142
|
+
if (accessToken) {
|
|
5143
|
+
return accessToken;
|
|
5144
|
+
}
|
|
5145
|
+
const storedToken = await getGoogleDriveAccessToken(apiClient);
|
|
5146
|
+
if (storedToken) {
|
|
5147
|
+
setAccessToken(storedToken);
|
|
5148
|
+
return storedToken;
|
|
5149
|
+
}
|
|
5150
|
+
return startGoogleDriveAuth(clientId, callbackPath);
|
|
5151
|
+
}, [accessToken, clientId, callbackPath, isConfigured, apiClient]);
|
|
5152
|
+
const logout = useCallback12(async () => {
|
|
5153
|
+
await revokeGoogleDriveToken(apiClient);
|
|
5154
|
+
setAccessToken(null);
|
|
5155
|
+
}, [apiClient]);
|
|
5156
|
+
return createElement2(
|
|
5157
|
+
GoogleDriveAuthContext.Provider,
|
|
5158
|
+
{
|
|
5159
|
+
value: {
|
|
5160
|
+
accessToken,
|
|
5161
|
+
isAuthenticated: !!accessToken,
|
|
5162
|
+
isConfigured,
|
|
5163
|
+
requestAccess,
|
|
5164
|
+
logout,
|
|
5165
|
+
refreshToken: refreshTokenFn
|
|
5166
|
+
}
|
|
5167
|
+
},
|
|
5168
|
+
children
|
|
5169
|
+
);
|
|
5170
|
+
}
|
|
5171
|
+
function useGoogleDriveAuth() {
|
|
5172
|
+
const context = useContext2(GoogleDriveAuthContext);
|
|
5173
|
+
if (!context) {
|
|
5174
|
+
throw new Error(
|
|
5175
|
+
"useGoogleDriveAuth must be used within GoogleDriveAuthProvider"
|
|
5176
|
+
);
|
|
5177
|
+
}
|
|
5178
|
+
return context;
|
|
5179
|
+
}
|
|
5180
|
+
|
|
4816
5181
|
// src/react/useGoogleDriveBackup.ts
|
|
4817
|
-
import { useCallback as
|
|
5182
|
+
import { useCallback as useCallback13, useMemo as useMemo5 } from "react";
|
|
4818
5183
|
|
|
4819
5184
|
// src/lib/backup/google/api.ts
|
|
4820
5185
|
var DRIVE_API_URL = "https://www.googleapis.com/drive/v3";
|
|
@@ -5121,14 +5486,17 @@ function useGoogleDriveBackup(options) {
|
|
|
5121
5486
|
const {
|
|
5122
5487
|
database,
|
|
5123
5488
|
userAddress,
|
|
5124
|
-
accessToken,
|
|
5125
|
-
requestDriveAccess,
|
|
5126
5489
|
requestEncryptionKey: requestEncryptionKey2,
|
|
5127
5490
|
exportConversation,
|
|
5128
5491
|
importConversation,
|
|
5129
5492
|
rootFolder = DEFAULT_ROOT_FOLDER,
|
|
5130
5493
|
conversationsFolder = DEFAULT_CONVERSATIONS_FOLDER
|
|
5131
5494
|
} = options;
|
|
5495
|
+
const {
|
|
5496
|
+
accessToken: driveToken,
|
|
5497
|
+
isConfigured: isDriveConfigured,
|
|
5498
|
+
requestAccess: requestDriveAccess
|
|
5499
|
+
} = useGoogleDriveAuth();
|
|
5132
5500
|
const deps = useMemo5(
|
|
5133
5501
|
() => ({
|
|
5134
5502
|
requestDriveAccess,
|
|
@@ -5136,22 +5504,17 @@ function useGoogleDriveBackup(options) {
|
|
|
5136
5504
|
exportConversation,
|
|
5137
5505
|
importConversation
|
|
5138
5506
|
}),
|
|
5139
|
-
[
|
|
5140
|
-
requestDriveAccess,
|
|
5141
|
-
requestEncryptionKey2,
|
|
5142
|
-
exportConversation,
|
|
5143
|
-
importConversation
|
|
5144
|
-
]
|
|
5507
|
+
[requestDriveAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
5145
5508
|
);
|
|
5146
|
-
const ensureToken =
|
|
5147
|
-
if (
|
|
5509
|
+
const ensureToken = useCallback13(async () => {
|
|
5510
|
+
if (driveToken) return driveToken;
|
|
5148
5511
|
try {
|
|
5149
5512
|
return await requestDriveAccess();
|
|
5150
5513
|
} catch {
|
|
5151
5514
|
return null;
|
|
5152
5515
|
}
|
|
5153
|
-
}, [
|
|
5154
|
-
const backup =
|
|
5516
|
+
}, [driveToken, requestDriveAccess]);
|
|
5517
|
+
const backup = useCallback13(
|
|
5155
5518
|
async (backupOptions) => {
|
|
5156
5519
|
if (!userAddress) {
|
|
5157
5520
|
return { error: "Please sign in to backup to Google Drive" };
|
|
@@ -5178,7 +5541,7 @@ function useGoogleDriveBackup(options) {
|
|
|
5178
5541
|
},
|
|
5179
5542
|
[database, userAddress, ensureToken, deps, rootFolder, conversationsFolder]
|
|
5180
5543
|
);
|
|
5181
|
-
const restore =
|
|
5544
|
+
const restore = useCallback13(
|
|
5182
5545
|
async (restoreOptions) => {
|
|
5183
5546
|
if (!userAddress) {
|
|
5184
5547
|
return { error: "Please sign in to restore from Google Drive" };
|
|
@@ -5207,22 +5570,1130 @@ function useGoogleDriveBackup(options) {
|
|
|
5207
5570
|
return {
|
|
5208
5571
|
backup,
|
|
5209
5572
|
restore,
|
|
5210
|
-
|
|
5573
|
+
isConfigured: isDriveConfigured,
|
|
5574
|
+
isAuthenticated: !!driveToken
|
|
5575
|
+
};
|
|
5576
|
+
}
|
|
5577
|
+
|
|
5578
|
+
// src/react/useICloudAuth.ts
|
|
5579
|
+
import {
|
|
5580
|
+
createContext as createContext3,
|
|
5581
|
+
createElement as createElement3,
|
|
5582
|
+
useCallback as useCallback14,
|
|
5583
|
+
useContext as useContext3,
|
|
5584
|
+
useEffect as useEffect8,
|
|
5585
|
+
useState as useState12
|
|
5586
|
+
} from "react";
|
|
5587
|
+
|
|
5588
|
+
// src/lib/backup/icloud/api.ts
|
|
5589
|
+
var CLOUDKIT_JS_URL = "https://cdn.apple-cloudkit.com/ck/2/cloudkit.js";
|
|
5590
|
+
var DEFAULT_BACKUP_FOLDER2 = "conversations";
|
|
5591
|
+
var DEFAULT_CONTAINER_ID = "iCloud.Memoryless";
|
|
5592
|
+
var RECORD_TYPE = "ConversationBackup";
|
|
5593
|
+
var cloudKitLoadPromise = null;
|
|
5594
|
+
function isCloudKitAvailable() {
|
|
5595
|
+
return typeof window !== "undefined" && !!window.CloudKit;
|
|
5596
|
+
}
|
|
5597
|
+
async function loadCloudKit() {
|
|
5598
|
+
if (typeof window === "undefined") {
|
|
5599
|
+
throw new Error("CloudKit JS can only be loaded in browser environment");
|
|
5600
|
+
}
|
|
5601
|
+
if (window.CloudKit) {
|
|
5602
|
+
return;
|
|
5603
|
+
}
|
|
5604
|
+
if (cloudKitLoadPromise) {
|
|
5605
|
+
return cloudKitLoadPromise;
|
|
5606
|
+
}
|
|
5607
|
+
cloudKitLoadPromise = new Promise((resolve, reject) => {
|
|
5608
|
+
const script = document.createElement("script");
|
|
5609
|
+
script.src = CLOUDKIT_JS_URL;
|
|
5610
|
+
script.async = true;
|
|
5611
|
+
script.onload = () => {
|
|
5612
|
+
if (window.CloudKit) {
|
|
5613
|
+
resolve();
|
|
5614
|
+
} else {
|
|
5615
|
+
reject(new Error("CloudKit JS loaded but CloudKit object not found"));
|
|
5616
|
+
}
|
|
5617
|
+
};
|
|
5618
|
+
script.onerror = () => {
|
|
5619
|
+
cloudKitLoadPromise = null;
|
|
5620
|
+
reject(new Error("Failed to load CloudKit JS"));
|
|
5621
|
+
};
|
|
5622
|
+
document.head.appendChild(script);
|
|
5623
|
+
});
|
|
5624
|
+
return cloudKitLoadPromise;
|
|
5625
|
+
}
|
|
5626
|
+
async function ensureCloudKitLoaded() {
|
|
5627
|
+
if (!isCloudKitAvailable()) {
|
|
5628
|
+
await loadCloudKit();
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
async function configureCloudKit(config) {
|
|
5632
|
+
await ensureCloudKitLoaded();
|
|
5633
|
+
ensureAuthElements();
|
|
5634
|
+
window.CloudKit.configure({
|
|
5635
|
+
containers: [
|
|
5636
|
+
{
|
|
5637
|
+
containerIdentifier: config.containerIdentifier,
|
|
5638
|
+
apiTokenAuth: {
|
|
5639
|
+
apiToken: config.apiToken,
|
|
5640
|
+
persist: true,
|
|
5641
|
+
signInButton: {
|
|
5642
|
+
id: "apple-sign-in-button",
|
|
5643
|
+
theme: "black"
|
|
5644
|
+
},
|
|
5645
|
+
signOutButton: {
|
|
5646
|
+
id: "apple-sign-out-button",
|
|
5647
|
+
theme: "black"
|
|
5648
|
+
}
|
|
5649
|
+
},
|
|
5650
|
+
environment: config.environment
|
|
5651
|
+
}
|
|
5652
|
+
]
|
|
5653
|
+
});
|
|
5654
|
+
}
|
|
5655
|
+
async function getContainer() {
|
|
5656
|
+
await ensureCloudKitLoaded();
|
|
5657
|
+
return window.CloudKit.getDefaultContainer();
|
|
5658
|
+
}
|
|
5659
|
+
function ensureAuthElements() {
|
|
5660
|
+
let signInButton = document.getElementById("apple-sign-in-button");
|
|
5661
|
+
let signOutButton = document.getElementById("apple-sign-out-button");
|
|
5662
|
+
if (!signInButton) {
|
|
5663
|
+
signInButton = document.createElement("div");
|
|
5664
|
+
signInButton.id = "apple-sign-in-button";
|
|
5665
|
+
signInButton.style.position = "fixed";
|
|
5666
|
+
signInButton.style.top = "-9999px";
|
|
5667
|
+
signInButton.style.left = "-9999px";
|
|
5668
|
+
document.body.appendChild(signInButton);
|
|
5669
|
+
}
|
|
5670
|
+
if (!signOutButton) {
|
|
5671
|
+
signOutButton = document.createElement("div");
|
|
5672
|
+
signOutButton.id = "apple-sign-out-button";
|
|
5673
|
+
signOutButton.style.position = "fixed";
|
|
5674
|
+
signOutButton.style.top = "-9999px";
|
|
5675
|
+
signOutButton.style.left = "-9999px";
|
|
5676
|
+
document.body.appendChild(signOutButton);
|
|
5677
|
+
}
|
|
5678
|
+
return { signIn: signInButton, signOut: signOutButton };
|
|
5679
|
+
}
|
|
5680
|
+
async function authenticateICloud() {
|
|
5681
|
+
const container = await getContainer();
|
|
5682
|
+
ensureAuthElements();
|
|
5683
|
+
return container.setUpAuth();
|
|
5684
|
+
}
|
|
5685
|
+
async function requestICloudSignIn() {
|
|
5686
|
+
const container = await getContainer();
|
|
5687
|
+
const { signIn } = ensureAuthElements();
|
|
5688
|
+
const existingUser = await container.setUpAuth();
|
|
5689
|
+
if (existingUser) {
|
|
5690
|
+
return existingUser;
|
|
5691
|
+
}
|
|
5692
|
+
console.log("[CloudKit] Sign-in container innerHTML:", signIn.innerHTML);
|
|
5693
|
+
console.log("[CloudKit] Sign-in container children:", signIn.children.length);
|
|
5694
|
+
const appleButton = signIn.querySelector("a, button, [role='button'], div[id*='apple']");
|
|
5695
|
+
console.log("[CloudKit] Found button element:", appleButton);
|
|
5696
|
+
if (appleButton) {
|
|
5697
|
+
console.log("[CloudKit] Clicking button...");
|
|
5698
|
+
appleButton.click();
|
|
5699
|
+
} else {
|
|
5700
|
+
const anyClickable = signIn.firstElementChild;
|
|
5701
|
+
if (anyClickable) {
|
|
5702
|
+
console.log("[CloudKit] Clicking first child element:", anyClickable);
|
|
5703
|
+
anyClickable.click();
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
return container.whenUserSignsIn();
|
|
5707
|
+
}
|
|
5708
|
+
async function uploadFileToICloud(filename, content) {
|
|
5709
|
+
const container = await getContainer();
|
|
5710
|
+
const database = container.privateCloudDatabase;
|
|
5711
|
+
const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
5712
|
+
const arrayBuffer = await content.arrayBuffer();
|
|
5713
|
+
const base64Data = btoa(
|
|
5714
|
+
String.fromCharCode(...new Uint8Array(arrayBuffer))
|
|
5715
|
+
);
|
|
5716
|
+
const record = {
|
|
5717
|
+
recordType: RECORD_TYPE,
|
|
5718
|
+
recordName,
|
|
5719
|
+
fields: {
|
|
5720
|
+
filename: { value: filename },
|
|
5721
|
+
data: { value: base64Data },
|
|
5722
|
+
size: { value: content.size },
|
|
5723
|
+
contentType: { value: content.type || "application/json" }
|
|
5724
|
+
}
|
|
5725
|
+
};
|
|
5726
|
+
const response = await database.saveRecords(record);
|
|
5727
|
+
if (!response.records || response.records.length === 0) {
|
|
5728
|
+
throw new Error("Failed to upload file to iCloud");
|
|
5729
|
+
}
|
|
5730
|
+
const savedRecord = response.records[0];
|
|
5731
|
+
return {
|
|
5732
|
+
recordName: savedRecord.recordName,
|
|
5733
|
+
filename,
|
|
5734
|
+
modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
|
|
5735
|
+
size: content.size
|
|
5736
|
+
};
|
|
5737
|
+
}
|
|
5738
|
+
async function listICloudFiles() {
|
|
5739
|
+
const container = await getContainer();
|
|
5740
|
+
const database = container.privateCloudDatabase;
|
|
5741
|
+
const query = {
|
|
5742
|
+
recordType: RECORD_TYPE
|
|
5743
|
+
// Note: Sorting requires SORTABLE index on the field in CloudKit Dashboard
|
|
5744
|
+
// For now, we skip sorting and sort client-side after fetching
|
|
5745
|
+
};
|
|
5746
|
+
const allRecords = [];
|
|
5747
|
+
let response = await database.performQuery(query);
|
|
5748
|
+
if (response.records) {
|
|
5749
|
+
allRecords.push(...response.records);
|
|
5750
|
+
}
|
|
5751
|
+
while (response.continuationMarker) {
|
|
5752
|
+
break;
|
|
5753
|
+
}
|
|
5754
|
+
const files = allRecords.map((record) => ({
|
|
5755
|
+
recordName: record.recordName,
|
|
5756
|
+
filename: record.fields.filename?.value ?? "",
|
|
5757
|
+
modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
|
|
5758
|
+
size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
|
|
5759
|
+
}));
|
|
5760
|
+
return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
|
|
5761
|
+
}
|
|
5762
|
+
async function downloadICloudFile(recordName) {
|
|
5763
|
+
const container = await getContainer();
|
|
5764
|
+
const database = container.privateCloudDatabase;
|
|
5765
|
+
const response = await database.fetchRecords([{ recordName }], {
|
|
5766
|
+
desiredKeys: ["filename", "data", "contentType"]
|
|
5767
|
+
});
|
|
5768
|
+
if (!response.records || response.records.length === 0) {
|
|
5769
|
+
throw new Error(`File not found: ${recordName}`);
|
|
5770
|
+
}
|
|
5771
|
+
const record = response.records[0];
|
|
5772
|
+
const dataField = record.fields.data?.value;
|
|
5773
|
+
if (!dataField) {
|
|
5774
|
+
throw new Error("No data in record");
|
|
5775
|
+
}
|
|
5776
|
+
if (typeof dataField === "string") {
|
|
5777
|
+
const binaryString = atob(dataField);
|
|
5778
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
5779
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
5780
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
5781
|
+
}
|
|
5782
|
+
return new Blob([bytes], { type: "application/json" });
|
|
5783
|
+
}
|
|
5784
|
+
if (typeof dataField === "object" && "downloadURL" in dataField) {
|
|
5785
|
+
const fetchResponse = await fetch(
|
|
5786
|
+
dataField.downloadURL
|
|
5787
|
+
);
|
|
5788
|
+
if (!fetchResponse.ok) {
|
|
5789
|
+
throw new Error(`Failed to download from iCloud: ${fetchResponse.status}`);
|
|
5790
|
+
}
|
|
5791
|
+
return fetchResponse.blob();
|
|
5792
|
+
}
|
|
5793
|
+
throw new Error("Unknown data format in iCloud record");
|
|
5794
|
+
}
|
|
5795
|
+
async function findICloudFile(filename) {
|
|
5796
|
+
const container = await getContainer();
|
|
5797
|
+
const database = container.privateCloudDatabase;
|
|
5798
|
+
const query = {
|
|
5799
|
+
recordType: RECORD_TYPE,
|
|
5800
|
+
filterBy: [
|
|
5801
|
+
{
|
|
5802
|
+
fieldName: "filename",
|
|
5803
|
+
comparator: "EQUALS",
|
|
5804
|
+
fieldValue: { value: filename }
|
|
5805
|
+
}
|
|
5806
|
+
]
|
|
5807
|
+
};
|
|
5808
|
+
const response = await database.performQuery(query);
|
|
5809
|
+
if (!response.records || response.records.length === 0) {
|
|
5810
|
+
return null;
|
|
5811
|
+
}
|
|
5812
|
+
const record = response.records[0];
|
|
5813
|
+
return {
|
|
5814
|
+
recordName: record.recordName,
|
|
5815
|
+
filename: record.fields.filename?.value ?? "",
|
|
5816
|
+
modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
|
|
5817
|
+
size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
|
|
5818
|
+
};
|
|
5819
|
+
}
|
|
5820
|
+
|
|
5821
|
+
// src/react/useICloudAuth.ts
|
|
5822
|
+
var ICloudAuthContext = createContext3(null);
|
|
5823
|
+
function ICloudAuthProvider({
|
|
5824
|
+
apiToken,
|
|
5825
|
+
containerIdentifier = DEFAULT_CONTAINER_ID,
|
|
5826
|
+
environment = "production",
|
|
5827
|
+
children
|
|
5828
|
+
}) {
|
|
5829
|
+
const [isAuthenticated, setIsAuthenticated] = useState12(false);
|
|
5830
|
+
const [userRecordName, setUserRecordName] = useState12(null);
|
|
5831
|
+
const [isAvailable, setIsAvailable] = useState12(false);
|
|
5832
|
+
const [isConfigured, setIsConfigured] = useState12(false);
|
|
5833
|
+
const [isLoading, setIsLoading] = useState12(false);
|
|
5834
|
+
useEffect8(() => {
|
|
5835
|
+
if (!apiToken || typeof window === "undefined") {
|
|
5836
|
+
return;
|
|
5837
|
+
}
|
|
5838
|
+
const initCloudKit = async () => {
|
|
5839
|
+
setIsLoading(true);
|
|
5840
|
+
try {
|
|
5841
|
+
await loadCloudKit();
|
|
5842
|
+
setIsAvailable(true);
|
|
5843
|
+
const config = {
|
|
5844
|
+
containerIdentifier,
|
|
5845
|
+
apiToken,
|
|
5846
|
+
environment
|
|
5847
|
+
};
|
|
5848
|
+
await configureCloudKit(config);
|
|
5849
|
+
setIsConfigured(true);
|
|
5850
|
+
try {
|
|
5851
|
+
const userIdentity = await authenticateICloud();
|
|
5852
|
+
if (userIdentity) {
|
|
5853
|
+
setIsAuthenticated(true);
|
|
5854
|
+
setUserRecordName(userIdentity.userRecordName);
|
|
5855
|
+
}
|
|
5856
|
+
} catch {
|
|
5857
|
+
}
|
|
5858
|
+
} catch {
|
|
5859
|
+
setIsAvailable(false);
|
|
5860
|
+
setIsConfigured(false);
|
|
5861
|
+
} finally {
|
|
5862
|
+
setIsLoading(false);
|
|
5863
|
+
}
|
|
5864
|
+
};
|
|
5865
|
+
initCloudKit();
|
|
5866
|
+
}, [apiToken, containerIdentifier, environment]);
|
|
5867
|
+
const requestAccess = useCallback14(async () => {
|
|
5868
|
+
if (!isConfigured) {
|
|
5869
|
+
throw new Error("iCloud is not configured");
|
|
5870
|
+
}
|
|
5871
|
+
if (isAuthenticated) {
|
|
5872
|
+
return;
|
|
5873
|
+
}
|
|
5874
|
+
try {
|
|
5875
|
+
const userIdentity = await requestICloudSignIn();
|
|
5876
|
+
setIsAuthenticated(true);
|
|
5877
|
+
setUserRecordName(userIdentity.userRecordName);
|
|
5878
|
+
} catch (err) {
|
|
5879
|
+
throw new Error(
|
|
5880
|
+
err instanceof Error ? err.message : "Failed to sign in to iCloud"
|
|
5881
|
+
);
|
|
5882
|
+
}
|
|
5883
|
+
}, [isAuthenticated, isConfigured]);
|
|
5884
|
+
const logout = useCallback14(() => {
|
|
5885
|
+
setIsAuthenticated(false);
|
|
5886
|
+
setUserRecordName(null);
|
|
5887
|
+
}, []);
|
|
5888
|
+
return createElement3(
|
|
5889
|
+
ICloudAuthContext.Provider,
|
|
5890
|
+
{
|
|
5891
|
+
value: {
|
|
5892
|
+
isAuthenticated,
|
|
5893
|
+
isConfigured,
|
|
5894
|
+
isAvailable,
|
|
5895
|
+
userRecordName,
|
|
5896
|
+
requestAccess,
|
|
5897
|
+
logout
|
|
5898
|
+
}
|
|
5899
|
+
},
|
|
5900
|
+
children
|
|
5901
|
+
);
|
|
5902
|
+
}
|
|
5903
|
+
function useICloudAuth() {
|
|
5904
|
+
const context = useContext3(ICloudAuthContext);
|
|
5905
|
+
if (!context) {
|
|
5906
|
+
throw new Error("useICloudAuth must be used within ICloudAuthProvider");
|
|
5907
|
+
}
|
|
5908
|
+
return context;
|
|
5909
|
+
}
|
|
5910
|
+
function hasICloudCredentials() {
|
|
5911
|
+
return isCloudKitAvailable();
|
|
5912
|
+
}
|
|
5913
|
+
function clearICloudAuth() {
|
|
5914
|
+
}
|
|
5915
|
+
|
|
5916
|
+
// src/react/useICloudBackup.ts
|
|
5917
|
+
import { useCallback as useCallback15, useMemo as useMemo6 } from "react";
|
|
5918
|
+
|
|
5919
|
+
// src/lib/backup/icloud/backup.ts
|
|
5920
|
+
var isAuthError3 = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
|
|
5921
|
+
async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
|
|
5922
|
+
try {
|
|
5923
|
+
await deps.requestEncryptionKey(userAddress);
|
|
5924
|
+
const filename = `${conversationId}.json`;
|
|
5925
|
+
const existingFile = await findICloudFile(filename);
|
|
5926
|
+
if (existingFile) {
|
|
5927
|
+
const { Q: Q4 } = await import("@nozbe/watermelondb");
|
|
5928
|
+
const conversationsCollection = database.get("conversations");
|
|
5929
|
+
const records = await conversationsCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
5930
|
+
if (records.length > 0) {
|
|
5931
|
+
const conversation = conversationToStored(records[0]);
|
|
5932
|
+
const localUpdated = conversation.updatedAt.getTime();
|
|
5933
|
+
const remoteModified = existingFile.modifiedAt.getTime();
|
|
5934
|
+
if (localUpdated <= remoteModified) {
|
|
5935
|
+
return "skipped";
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
const exportResult = await deps.exportConversation(
|
|
5940
|
+
conversationId,
|
|
5941
|
+
userAddress
|
|
5942
|
+
);
|
|
5943
|
+
if (!exportResult.success || !exportResult.blob) {
|
|
5944
|
+
return "failed";
|
|
5945
|
+
}
|
|
5946
|
+
await uploadFileToICloud(filename, exportResult.blob);
|
|
5947
|
+
return "uploaded";
|
|
5948
|
+
} catch (err) {
|
|
5949
|
+
if (isAuthError3(err) && !_retried) {
|
|
5950
|
+
try {
|
|
5951
|
+
await deps.requestICloudAccess();
|
|
5952
|
+
return pushConversationToICloud(
|
|
5953
|
+
database,
|
|
5954
|
+
conversationId,
|
|
5955
|
+
userAddress,
|
|
5956
|
+
deps,
|
|
5957
|
+
true
|
|
5958
|
+
);
|
|
5959
|
+
} catch {
|
|
5960
|
+
return "failed";
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5963
|
+
return "failed";
|
|
5964
|
+
}
|
|
5965
|
+
}
|
|
5966
|
+
async function performICloudExport(database, userAddress, deps, onProgress) {
|
|
5967
|
+
await deps.requestEncryptionKey(userAddress);
|
|
5968
|
+
const { Q: Q4 } = await import("@nozbe/watermelondb");
|
|
5969
|
+
const conversationsCollection = database.get("conversations");
|
|
5970
|
+
const records = await conversationsCollection.query(Q4.where("is_deleted", false)).fetch();
|
|
5971
|
+
const conversations = records.map(conversationToStored);
|
|
5972
|
+
const total = conversations.length;
|
|
5973
|
+
if (total === 0) {
|
|
5974
|
+
return { success: true, uploaded: 0, skipped: 0, total: 0 };
|
|
5975
|
+
}
|
|
5976
|
+
let uploaded = 0;
|
|
5977
|
+
let skipped = 0;
|
|
5978
|
+
for (let i = 0; i < conversations.length; i++) {
|
|
5979
|
+
const conv = conversations[i];
|
|
5980
|
+
onProgress?.(i + 1, total);
|
|
5981
|
+
const result = await pushConversationToICloud(
|
|
5982
|
+
database,
|
|
5983
|
+
conv.conversationId,
|
|
5984
|
+
userAddress,
|
|
5985
|
+
deps
|
|
5986
|
+
);
|
|
5987
|
+
if (result === "uploaded") uploaded++;
|
|
5988
|
+
if (result === "skipped") skipped++;
|
|
5989
|
+
}
|
|
5990
|
+
return { success: true, uploaded, skipped, total };
|
|
5991
|
+
}
|
|
5992
|
+
async function performICloudImport(userAddress, deps, onProgress) {
|
|
5993
|
+
await deps.requestEncryptionKey(userAddress);
|
|
5994
|
+
const remoteFiles = await listICloudFiles();
|
|
5995
|
+
if (remoteFiles.length === 0) {
|
|
5996
|
+
return {
|
|
5997
|
+
success: false,
|
|
5998
|
+
restored: 0,
|
|
5999
|
+
failed: 0,
|
|
6000
|
+
total: 0,
|
|
6001
|
+
noBackupsFound: true
|
|
6002
|
+
};
|
|
6003
|
+
}
|
|
6004
|
+
const jsonFiles = remoteFiles.filter(
|
|
6005
|
+
(file) => file.filename.endsWith(".json")
|
|
6006
|
+
);
|
|
6007
|
+
const total = jsonFiles.length;
|
|
6008
|
+
let restored = 0;
|
|
6009
|
+
let failed = 0;
|
|
6010
|
+
for (let i = 0; i < jsonFiles.length; i++) {
|
|
6011
|
+
const file = jsonFiles[i];
|
|
6012
|
+
onProgress?.(i + 1, total);
|
|
6013
|
+
try {
|
|
6014
|
+
const blob = await downloadICloudFile(file.recordName);
|
|
6015
|
+
const result = await deps.importConversation(blob, userAddress);
|
|
6016
|
+
if (result.success) {
|
|
6017
|
+
restored++;
|
|
6018
|
+
} else {
|
|
6019
|
+
failed++;
|
|
6020
|
+
}
|
|
6021
|
+
} catch (err) {
|
|
6022
|
+
if (isAuthError3(err)) {
|
|
6023
|
+
try {
|
|
6024
|
+
await deps.requestICloudAccess();
|
|
6025
|
+
const blob = await downloadICloudFile(file.recordName);
|
|
6026
|
+
const result = await deps.importConversation(blob, userAddress);
|
|
6027
|
+
if (result.success) {
|
|
6028
|
+
restored++;
|
|
6029
|
+
} else {
|
|
6030
|
+
failed++;
|
|
6031
|
+
}
|
|
6032
|
+
} catch {
|
|
6033
|
+
failed++;
|
|
6034
|
+
}
|
|
6035
|
+
} else {
|
|
6036
|
+
failed++;
|
|
6037
|
+
}
|
|
6038
|
+
}
|
|
6039
|
+
}
|
|
6040
|
+
return { success: true, restored, failed, total };
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6043
|
+
// src/react/useICloudBackup.ts
|
|
6044
|
+
function useICloudBackup(options) {
|
|
6045
|
+
const {
|
|
6046
|
+
database,
|
|
6047
|
+
userAddress,
|
|
6048
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6049
|
+
exportConversation,
|
|
6050
|
+
importConversation
|
|
6051
|
+
} = options;
|
|
6052
|
+
const {
|
|
6053
|
+
isAuthenticated,
|
|
6054
|
+
isConfigured,
|
|
6055
|
+
isAvailable,
|
|
6056
|
+
requestAccess
|
|
6057
|
+
} = useICloudAuth();
|
|
6058
|
+
const deps = useMemo6(
|
|
6059
|
+
() => ({
|
|
6060
|
+
requestICloudAccess: requestAccess,
|
|
6061
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6062
|
+
exportConversation,
|
|
6063
|
+
importConversation
|
|
6064
|
+
}),
|
|
6065
|
+
[requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6066
|
+
);
|
|
6067
|
+
const ensureAuthenticated = useCallback15(async () => {
|
|
6068
|
+
if (isAuthenticated) return true;
|
|
6069
|
+
try {
|
|
6070
|
+
await requestAccess();
|
|
6071
|
+
return true;
|
|
6072
|
+
} catch {
|
|
6073
|
+
return false;
|
|
6074
|
+
}
|
|
6075
|
+
}, [isAuthenticated, requestAccess]);
|
|
6076
|
+
const backup = useCallback15(
|
|
6077
|
+
async (backupOptions) => {
|
|
6078
|
+
if (!userAddress) {
|
|
6079
|
+
return { error: "Please sign in to backup to iCloud" };
|
|
6080
|
+
}
|
|
6081
|
+
if (!isAvailable) {
|
|
6082
|
+
return { error: "CloudKit JS is not loaded" };
|
|
6083
|
+
}
|
|
6084
|
+
if (!isConfigured) {
|
|
6085
|
+
return { error: "iCloud is not configured" };
|
|
6086
|
+
}
|
|
6087
|
+
const authenticated = await ensureAuthenticated();
|
|
6088
|
+
if (!authenticated) {
|
|
6089
|
+
return { error: "iCloud access denied" };
|
|
6090
|
+
}
|
|
6091
|
+
try {
|
|
6092
|
+
return await performICloudExport(
|
|
6093
|
+
database,
|
|
6094
|
+
userAddress,
|
|
6095
|
+
deps,
|
|
6096
|
+
backupOptions?.onProgress
|
|
6097
|
+
);
|
|
6098
|
+
} catch (err) {
|
|
6099
|
+
return {
|
|
6100
|
+
error: err instanceof Error ? err.message : "Failed to backup to iCloud"
|
|
6101
|
+
};
|
|
6102
|
+
}
|
|
6103
|
+
},
|
|
6104
|
+
[database, userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
|
|
6105
|
+
);
|
|
6106
|
+
const restore = useCallback15(
|
|
6107
|
+
async (restoreOptions) => {
|
|
6108
|
+
if (!userAddress) {
|
|
6109
|
+
return { error: "Please sign in to restore from iCloud" };
|
|
6110
|
+
}
|
|
6111
|
+
if (!isAvailable) {
|
|
6112
|
+
return { error: "CloudKit JS is not loaded" };
|
|
6113
|
+
}
|
|
6114
|
+
if (!isConfigured) {
|
|
6115
|
+
return { error: "iCloud is not configured" };
|
|
6116
|
+
}
|
|
6117
|
+
const authenticated = await ensureAuthenticated();
|
|
6118
|
+
if (!authenticated) {
|
|
6119
|
+
return { error: "iCloud access denied" };
|
|
6120
|
+
}
|
|
6121
|
+
try {
|
|
6122
|
+
return await performICloudImport(
|
|
6123
|
+
userAddress,
|
|
6124
|
+
deps,
|
|
6125
|
+
restoreOptions?.onProgress
|
|
6126
|
+
);
|
|
6127
|
+
} catch (err) {
|
|
6128
|
+
return {
|
|
6129
|
+
error: err instanceof Error ? err.message : "Failed to restore from iCloud"
|
|
6130
|
+
};
|
|
6131
|
+
}
|
|
6132
|
+
},
|
|
6133
|
+
[userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
|
|
6134
|
+
);
|
|
6135
|
+
return {
|
|
6136
|
+
backup,
|
|
6137
|
+
restore,
|
|
6138
|
+
isConfigured,
|
|
6139
|
+
isAuthenticated,
|
|
6140
|
+
isAvailable
|
|
6141
|
+
};
|
|
6142
|
+
}
|
|
6143
|
+
|
|
6144
|
+
// src/react/useBackupAuth.ts
|
|
6145
|
+
import {
|
|
6146
|
+
createContext as createContext4,
|
|
6147
|
+
createElement as createElement4,
|
|
6148
|
+
useCallback as useCallback16,
|
|
6149
|
+
useContext as useContext4,
|
|
6150
|
+
useEffect as useEffect9,
|
|
6151
|
+
useState as useState13
|
|
6152
|
+
} from "react";
|
|
6153
|
+
var BackupAuthContext = createContext4(null);
|
|
6154
|
+
function BackupAuthProvider({
|
|
6155
|
+
dropboxAppKey,
|
|
6156
|
+
dropboxCallbackPath = "/auth/dropbox/callback",
|
|
6157
|
+
googleClientId,
|
|
6158
|
+
googleCallbackPath = "/auth/google/callback",
|
|
6159
|
+
icloudApiToken,
|
|
6160
|
+
icloudContainerIdentifier = DEFAULT_CONTAINER_ID,
|
|
6161
|
+
icloudEnvironment = "production",
|
|
6162
|
+
apiClient,
|
|
6163
|
+
children
|
|
6164
|
+
}) {
|
|
6165
|
+
const [dropboxToken, setDropboxToken] = useState13(null);
|
|
6166
|
+
const isDropboxConfigured = !!dropboxAppKey;
|
|
6167
|
+
const [googleToken, setGoogleToken] = useState13(null);
|
|
6168
|
+
const isGoogleConfigured = !!googleClientId;
|
|
6169
|
+
const [icloudAuthenticated, setIcloudAuthenticated] = useState13(false);
|
|
6170
|
+
const [icloudUserRecordName, setIcloudUserRecordName] = useState13(null);
|
|
6171
|
+
const [isIcloudAvailable, setIsIcloudAvailable] = useState13(false);
|
|
6172
|
+
const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
|
|
6173
|
+
useEffect9(() => {
|
|
6174
|
+
const checkStoredTokens = async () => {
|
|
6175
|
+
if (hasDropboxCredentials()) {
|
|
6176
|
+
const token = await getDropboxAccessToken(apiClient);
|
|
6177
|
+
if (token) {
|
|
6178
|
+
setDropboxToken(token);
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
if (hasGoogleDriveCredentials()) {
|
|
6182
|
+
const token = await getGoogleDriveAccessToken(apiClient);
|
|
6183
|
+
if (token) {
|
|
6184
|
+
setGoogleToken(token);
|
|
6185
|
+
}
|
|
6186
|
+
}
|
|
6187
|
+
};
|
|
6188
|
+
checkStoredTokens();
|
|
6189
|
+
}, [apiClient]);
|
|
6190
|
+
useEffect9(() => {
|
|
6191
|
+
if (!icloudApiToken || typeof window === "undefined") {
|
|
6192
|
+
return;
|
|
6193
|
+
}
|
|
6194
|
+
const initCloudKit = async () => {
|
|
6195
|
+
try {
|
|
6196
|
+
await loadCloudKit();
|
|
6197
|
+
setIsIcloudAvailable(true);
|
|
6198
|
+
const config = {
|
|
6199
|
+
containerIdentifier: icloudContainerIdentifier,
|
|
6200
|
+
apiToken: icloudApiToken,
|
|
6201
|
+
environment: icloudEnvironment
|
|
6202
|
+
};
|
|
6203
|
+
await configureCloudKit(config);
|
|
6204
|
+
try {
|
|
6205
|
+
const userIdentity = await authenticateICloud();
|
|
6206
|
+
if (userIdentity) {
|
|
6207
|
+
setIcloudAuthenticated(true);
|
|
6208
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6209
|
+
}
|
|
6210
|
+
} catch {
|
|
6211
|
+
}
|
|
6212
|
+
} catch {
|
|
6213
|
+
setIsIcloudAvailable(false);
|
|
6214
|
+
}
|
|
6215
|
+
};
|
|
6216
|
+
initCloudKit();
|
|
6217
|
+
}, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
|
|
6218
|
+
useEffect9(() => {
|
|
6219
|
+
if (!isDropboxConfigured) return;
|
|
6220
|
+
const handleCallback = async () => {
|
|
6221
|
+
if (isDropboxCallback()) {
|
|
6222
|
+
const token = await handleDropboxCallback(
|
|
6223
|
+
dropboxCallbackPath,
|
|
6224
|
+
apiClient
|
|
6225
|
+
);
|
|
6226
|
+
if (token) {
|
|
6227
|
+
setDropboxToken(token);
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
};
|
|
6231
|
+
handleCallback();
|
|
6232
|
+
}, [dropboxCallbackPath, isDropboxConfigured, apiClient]);
|
|
6233
|
+
useEffect9(() => {
|
|
6234
|
+
if (!isGoogleConfigured) return;
|
|
6235
|
+
const handleCallback = async () => {
|
|
6236
|
+
if (isGoogleDriveCallback()) {
|
|
6237
|
+
const token = await handleGoogleDriveCallback(
|
|
6238
|
+
googleCallbackPath,
|
|
6239
|
+
apiClient
|
|
6240
|
+
);
|
|
6241
|
+
if (token) {
|
|
6242
|
+
setGoogleToken(token);
|
|
6243
|
+
}
|
|
6244
|
+
}
|
|
6245
|
+
};
|
|
6246
|
+
handleCallback();
|
|
6247
|
+
}, [googleCallbackPath, isGoogleConfigured, apiClient]);
|
|
6248
|
+
const refreshDropboxTokenFn = useCallback16(async () => {
|
|
6249
|
+
const token = await getDropboxAccessToken(apiClient);
|
|
6250
|
+
if (token) {
|
|
6251
|
+
setDropboxToken(token);
|
|
6252
|
+
}
|
|
6253
|
+
return token;
|
|
6254
|
+
}, [apiClient]);
|
|
6255
|
+
const requestDropboxAccess = useCallback16(async () => {
|
|
6256
|
+
if (!isDropboxConfigured || !dropboxAppKey) {
|
|
6257
|
+
throw new Error("Dropbox is not configured");
|
|
6258
|
+
}
|
|
6259
|
+
if (dropboxToken) {
|
|
6260
|
+
return dropboxToken;
|
|
6261
|
+
}
|
|
6262
|
+
const storedToken = await getDropboxAccessToken(apiClient);
|
|
6263
|
+
if (storedToken) {
|
|
6264
|
+
setDropboxToken(storedToken);
|
|
6265
|
+
return storedToken;
|
|
6266
|
+
}
|
|
6267
|
+
return startDropboxAuth(dropboxAppKey, dropboxCallbackPath);
|
|
6268
|
+
}, [
|
|
6269
|
+
dropboxToken,
|
|
6270
|
+
dropboxAppKey,
|
|
6271
|
+
dropboxCallbackPath,
|
|
6272
|
+
isDropboxConfigured,
|
|
6273
|
+
apiClient
|
|
6274
|
+
]);
|
|
6275
|
+
const logoutDropbox = useCallback16(async () => {
|
|
6276
|
+
await revokeDropboxToken(apiClient);
|
|
6277
|
+
setDropboxToken(null);
|
|
6278
|
+
}, [apiClient]);
|
|
6279
|
+
const refreshGoogleTokenFn = useCallback16(async () => {
|
|
6280
|
+
const token = await getGoogleDriveAccessToken(apiClient);
|
|
6281
|
+
if (token) {
|
|
6282
|
+
setGoogleToken(token);
|
|
6283
|
+
}
|
|
6284
|
+
return token;
|
|
6285
|
+
}, [apiClient]);
|
|
6286
|
+
const requestGoogleAccess = useCallback16(async () => {
|
|
6287
|
+
if (!isGoogleConfigured || !googleClientId) {
|
|
6288
|
+
throw new Error("Google Drive is not configured");
|
|
6289
|
+
}
|
|
6290
|
+
if (googleToken) {
|
|
6291
|
+
return googleToken;
|
|
6292
|
+
}
|
|
6293
|
+
const storedToken = await getGoogleDriveAccessToken(apiClient);
|
|
6294
|
+
if (storedToken) {
|
|
6295
|
+
setGoogleToken(storedToken);
|
|
6296
|
+
return storedToken;
|
|
6297
|
+
}
|
|
6298
|
+
return startGoogleDriveAuth(googleClientId, googleCallbackPath);
|
|
6299
|
+
}, [
|
|
6300
|
+
googleToken,
|
|
6301
|
+
googleClientId,
|
|
6302
|
+
googleCallbackPath,
|
|
6303
|
+
isGoogleConfigured,
|
|
6304
|
+
apiClient
|
|
6305
|
+
]);
|
|
6306
|
+
const logoutGoogle = useCallback16(async () => {
|
|
6307
|
+
await revokeGoogleDriveToken(apiClient);
|
|
6308
|
+
setGoogleToken(null);
|
|
6309
|
+
}, [apiClient]);
|
|
6310
|
+
const refreshIcloudTokenFn = useCallback16(async () => {
|
|
6311
|
+
try {
|
|
6312
|
+
const userIdentity = await authenticateICloud();
|
|
6313
|
+
if (userIdentity) {
|
|
6314
|
+
setIcloudAuthenticated(true);
|
|
6315
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6316
|
+
return userIdentity.userRecordName;
|
|
6317
|
+
}
|
|
6318
|
+
} catch {
|
|
6319
|
+
}
|
|
6320
|
+
return null;
|
|
6321
|
+
}, []);
|
|
6322
|
+
const requestIcloudAccess = useCallback16(async () => {
|
|
6323
|
+
if (!isIcloudConfigured) {
|
|
6324
|
+
throw new Error("iCloud is not configured");
|
|
6325
|
+
}
|
|
6326
|
+
if (icloudAuthenticated && icloudUserRecordName) {
|
|
6327
|
+
return icloudUserRecordName;
|
|
6328
|
+
}
|
|
6329
|
+
try {
|
|
6330
|
+
const userIdentity = await requestICloudSignIn();
|
|
6331
|
+
setIcloudAuthenticated(true);
|
|
6332
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6333
|
+
return userIdentity.userRecordName;
|
|
6334
|
+
} catch (err) {
|
|
6335
|
+
throw new Error(
|
|
6336
|
+
err instanceof Error ? err.message : "Failed to sign in to iCloud"
|
|
6337
|
+
);
|
|
6338
|
+
}
|
|
6339
|
+
}, [icloudAuthenticated, icloudUserRecordName, isIcloudConfigured]);
|
|
6340
|
+
const logoutIcloud = useCallback16(async () => {
|
|
6341
|
+
setIcloudAuthenticated(false);
|
|
6342
|
+
setIcloudUserRecordName(null);
|
|
6343
|
+
}, []);
|
|
6344
|
+
const logoutAll = useCallback16(async () => {
|
|
6345
|
+
await Promise.all([
|
|
6346
|
+
isDropboxConfigured ? logoutDropbox() : Promise.resolve(),
|
|
6347
|
+
isGoogleConfigured ? logoutGoogle() : Promise.resolve(),
|
|
6348
|
+
isIcloudConfigured ? logoutIcloud() : Promise.resolve()
|
|
6349
|
+
]);
|
|
6350
|
+
}, [isDropboxConfigured, isGoogleConfigured, isIcloudConfigured, logoutDropbox, logoutGoogle, logoutIcloud]);
|
|
6351
|
+
const dropboxState = {
|
|
6352
|
+
accessToken: dropboxToken,
|
|
6353
|
+
isAuthenticated: !!dropboxToken,
|
|
6354
|
+
isConfigured: isDropboxConfigured,
|
|
6355
|
+
requestAccess: requestDropboxAccess,
|
|
6356
|
+
logout: logoutDropbox,
|
|
6357
|
+
refreshToken: refreshDropboxTokenFn
|
|
6358
|
+
};
|
|
6359
|
+
const googleDriveState = {
|
|
6360
|
+
accessToken: googleToken,
|
|
6361
|
+
isAuthenticated: !!googleToken,
|
|
6362
|
+
isConfigured: isGoogleConfigured,
|
|
6363
|
+
requestAccess: requestGoogleAccess,
|
|
6364
|
+
logout: logoutGoogle,
|
|
6365
|
+
refreshToken: refreshGoogleTokenFn
|
|
6366
|
+
};
|
|
6367
|
+
const icloudState = {
|
|
6368
|
+
accessToken: icloudUserRecordName,
|
|
6369
|
+
// Use userRecordName as the "token" for iCloud
|
|
6370
|
+
isAuthenticated: icloudAuthenticated,
|
|
6371
|
+
isConfigured: isIcloudConfigured,
|
|
6372
|
+
requestAccess: requestIcloudAccess,
|
|
6373
|
+
logout: logoutIcloud,
|
|
6374
|
+
refreshToken: refreshIcloudTokenFn
|
|
6375
|
+
};
|
|
6376
|
+
return createElement4(
|
|
6377
|
+
BackupAuthContext.Provider,
|
|
6378
|
+
{
|
|
6379
|
+
value: {
|
|
6380
|
+
dropbox: dropboxState,
|
|
6381
|
+
googleDrive: googleDriveState,
|
|
6382
|
+
icloud: icloudState,
|
|
6383
|
+
hasAnyProvider: isDropboxConfigured || isGoogleConfigured || isIcloudConfigured,
|
|
6384
|
+
hasAnyAuthentication: !!dropboxToken || !!googleToken || icloudAuthenticated,
|
|
6385
|
+
logoutAll
|
|
6386
|
+
}
|
|
6387
|
+
},
|
|
6388
|
+
children
|
|
6389
|
+
);
|
|
6390
|
+
}
|
|
6391
|
+
function useBackupAuth() {
|
|
6392
|
+
const context = useContext4(BackupAuthContext);
|
|
6393
|
+
if (!context) {
|
|
6394
|
+
throw new Error("useBackupAuth must be used within BackupAuthProvider");
|
|
6395
|
+
}
|
|
6396
|
+
return context;
|
|
6397
|
+
}
|
|
6398
|
+
|
|
6399
|
+
// src/react/useBackup.ts
|
|
6400
|
+
import { useCallback as useCallback17, useMemo as useMemo7 } from "react";
|
|
6401
|
+
function useBackup(options) {
|
|
6402
|
+
const {
|
|
6403
|
+
database,
|
|
6404
|
+
userAddress,
|
|
6405
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6406
|
+
exportConversation,
|
|
6407
|
+
importConversation,
|
|
6408
|
+
dropboxFolder = DEFAULT_BACKUP_FOLDER,
|
|
6409
|
+
googleRootFolder = DEFAULT_ROOT_FOLDER,
|
|
6410
|
+
googleConversationsFolder = DEFAULT_CONVERSATIONS_FOLDER
|
|
6411
|
+
} = options;
|
|
6412
|
+
const {
|
|
6413
|
+
dropbox: dropboxAuth,
|
|
6414
|
+
googleDrive: googleDriveAuth,
|
|
6415
|
+
icloud: icloudAuth,
|
|
6416
|
+
hasAnyProvider,
|
|
6417
|
+
hasAnyAuthentication,
|
|
6418
|
+
logoutAll
|
|
6419
|
+
} = useBackupAuth();
|
|
6420
|
+
const dropboxDeps = useMemo7(
|
|
6421
|
+
() => ({
|
|
6422
|
+
requestDropboxAccess: dropboxAuth.requestAccess,
|
|
6423
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6424
|
+
exportConversation,
|
|
6425
|
+
importConversation
|
|
6426
|
+
}),
|
|
6427
|
+
[dropboxAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6428
|
+
);
|
|
6429
|
+
const googleDriveDeps = useMemo7(
|
|
6430
|
+
() => ({
|
|
6431
|
+
requestDriveAccess: googleDriveAuth.requestAccess,
|
|
6432
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6433
|
+
exportConversation,
|
|
6434
|
+
importConversation
|
|
6435
|
+
}),
|
|
6436
|
+
[googleDriveAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6437
|
+
);
|
|
6438
|
+
const icloudDeps = useMemo7(
|
|
6439
|
+
() => ({
|
|
6440
|
+
requestICloudAccess: async () => {
|
|
6441
|
+
await icloudAuth.requestAccess();
|
|
6442
|
+
},
|
|
6443
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6444
|
+
exportConversation,
|
|
6445
|
+
importConversation
|
|
6446
|
+
}),
|
|
6447
|
+
[icloudAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6448
|
+
);
|
|
6449
|
+
const dropboxBackup = useCallback17(
|
|
6450
|
+
async (backupOptions) => {
|
|
6451
|
+
if (!userAddress) {
|
|
6452
|
+
return { error: "Please sign in to backup to Dropbox" };
|
|
6453
|
+
}
|
|
6454
|
+
let token = dropboxAuth.accessToken;
|
|
6455
|
+
if (!token) {
|
|
6456
|
+
try {
|
|
6457
|
+
token = await dropboxAuth.requestAccess();
|
|
6458
|
+
} catch {
|
|
6459
|
+
return { error: "Dropbox access denied" };
|
|
6460
|
+
}
|
|
6461
|
+
}
|
|
6462
|
+
try {
|
|
6463
|
+
return await performDropboxExport(
|
|
6464
|
+
database,
|
|
6465
|
+
userAddress,
|
|
6466
|
+
token,
|
|
6467
|
+
dropboxDeps,
|
|
6468
|
+
backupOptions?.onProgress,
|
|
6469
|
+
dropboxFolder
|
|
6470
|
+
);
|
|
6471
|
+
} catch (err) {
|
|
6472
|
+
return {
|
|
6473
|
+
error: err instanceof Error ? err.message : "Failed to backup to Dropbox"
|
|
6474
|
+
};
|
|
6475
|
+
}
|
|
6476
|
+
},
|
|
6477
|
+
[database, userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
|
|
6478
|
+
);
|
|
6479
|
+
const dropboxRestore = useCallback17(
|
|
6480
|
+
async (restoreOptions) => {
|
|
6481
|
+
if (!userAddress) {
|
|
6482
|
+
return { error: "Please sign in to restore from Dropbox" };
|
|
6483
|
+
}
|
|
6484
|
+
let token = dropboxAuth.accessToken;
|
|
6485
|
+
if (!token) {
|
|
6486
|
+
try {
|
|
6487
|
+
token = await dropboxAuth.requestAccess();
|
|
6488
|
+
} catch {
|
|
6489
|
+
return { error: "Dropbox access denied" };
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
try {
|
|
6493
|
+
return await performDropboxImport(
|
|
6494
|
+
userAddress,
|
|
6495
|
+
token,
|
|
6496
|
+
dropboxDeps,
|
|
6497
|
+
restoreOptions?.onProgress,
|
|
6498
|
+
dropboxFolder
|
|
6499
|
+
);
|
|
6500
|
+
} catch (err) {
|
|
6501
|
+
return {
|
|
6502
|
+
error: err instanceof Error ? err.message : "Failed to restore from Dropbox"
|
|
6503
|
+
};
|
|
6504
|
+
}
|
|
6505
|
+
},
|
|
6506
|
+
[userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
|
|
6507
|
+
);
|
|
6508
|
+
const googleDriveBackup = useCallback17(
|
|
6509
|
+
async (backupOptions) => {
|
|
6510
|
+
if (!userAddress) {
|
|
6511
|
+
return { error: "Please sign in to backup to Google Drive" };
|
|
6512
|
+
}
|
|
6513
|
+
let token = googleDriveAuth.accessToken;
|
|
6514
|
+
if (!token) {
|
|
6515
|
+
try {
|
|
6516
|
+
token = await googleDriveAuth.requestAccess();
|
|
6517
|
+
} catch {
|
|
6518
|
+
return { error: "Google Drive access denied" };
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
try {
|
|
6522
|
+
return await performGoogleDriveExport(
|
|
6523
|
+
database,
|
|
6524
|
+
userAddress,
|
|
6525
|
+
token,
|
|
6526
|
+
googleDriveDeps,
|
|
6527
|
+
backupOptions?.onProgress,
|
|
6528
|
+
googleRootFolder,
|
|
6529
|
+
googleConversationsFolder
|
|
6530
|
+
);
|
|
6531
|
+
} catch (err) {
|
|
6532
|
+
return {
|
|
6533
|
+
error: err instanceof Error ? err.message : "Failed to backup to Google Drive"
|
|
6534
|
+
};
|
|
6535
|
+
}
|
|
6536
|
+
},
|
|
6537
|
+
[
|
|
6538
|
+
database,
|
|
6539
|
+
userAddress,
|
|
6540
|
+
googleDriveAuth,
|
|
6541
|
+
googleDriveDeps,
|
|
6542
|
+
googleRootFolder,
|
|
6543
|
+
googleConversationsFolder
|
|
6544
|
+
]
|
|
6545
|
+
);
|
|
6546
|
+
const googleDriveRestore = useCallback17(
|
|
6547
|
+
async (restoreOptions) => {
|
|
6548
|
+
if (!userAddress) {
|
|
6549
|
+
return { error: "Please sign in to restore from Google Drive" };
|
|
6550
|
+
}
|
|
6551
|
+
let token = googleDriveAuth.accessToken;
|
|
6552
|
+
if (!token) {
|
|
6553
|
+
try {
|
|
6554
|
+
token = await googleDriveAuth.requestAccess();
|
|
6555
|
+
} catch {
|
|
6556
|
+
return { error: "Google Drive access denied" };
|
|
6557
|
+
}
|
|
6558
|
+
}
|
|
6559
|
+
try {
|
|
6560
|
+
return await performGoogleDriveImport(
|
|
6561
|
+
userAddress,
|
|
6562
|
+
token,
|
|
6563
|
+
googleDriveDeps,
|
|
6564
|
+
restoreOptions?.onProgress,
|
|
6565
|
+
googleRootFolder,
|
|
6566
|
+
googleConversationsFolder
|
|
6567
|
+
);
|
|
6568
|
+
} catch (err) {
|
|
6569
|
+
return {
|
|
6570
|
+
error: err instanceof Error ? err.message : "Failed to restore from Google Drive"
|
|
6571
|
+
};
|
|
6572
|
+
}
|
|
6573
|
+
},
|
|
6574
|
+
[
|
|
6575
|
+
userAddress,
|
|
6576
|
+
googleDriveAuth,
|
|
6577
|
+
googleDriveDeps,
|
|
6578
|
+
googleRootFolder,
|
|
6579
|
+
googleConversationsFolder
|
|
6580
|
+
]
|
|
6581
|
+
);
|
|
6582
|
+
const dropboxState = {
|
|
6583
|
+
isConfigured: dropboxAuth.isConfigured,
|
|
6584
|
+
isAuthenticated: dropboxAuth.isAuthenticated,
|
|
6585
|
+
backup: dropboxBackup,
|
|
6586
|
+
restore: dropboxRestore,
|
|
6587
|
+
connect: dropboxAuth.requestAccess,
|
|
6588
|
+
disconnect: dropboxAuth.logout
|
|
6589
|
+
};
|
|
6590
|
+
const googleDriveState = {
|
|
6591
|
+
isConfigured: googleDriveAuth.isConfigured,
|
|
6592
|
+
isAuthenticated: googleDriveAuth.isAuthenticated,
|
|
6593
|
+
backup: googleDriveBackup,
|
|
6594
|
+
restore: googleDriveRestore,
|
|
6595
|
+
connect: googleDriveAuth.requestAccess,
|
|
6596
|
+
disconnect: googleDriveAuth.logout
|
|
6597
|
+
};
|
|
6598
|
+
const icloudBackup = useCallback17(
|
|
6599
|
+
async (backupOptions) => {
|
|
6600
|
+
if (!userAddress) {
|
|
6601
|
+
return { error: "Please sign in to backup to iCloud" };
|
|
6602
|
+
}
|
|
6603
|
+
if (!icloudAuth.isConfigured) {
|
|
6604
|
+
return { error: "iCloud is not configured" };
|
|
6605
|
+
}
|
|
6606
|
+
if (!icloudAuth.isAuthenticated) {
|
|
6607
|
+
try {
|
|
6608
|
+
await icloudAuth.requestAccess();
|
|
6609
|
+
} catch {
|
|
6610
|
+
return { error: "iCloud access denied" };
|
|
6611
|
+
}
|
|
6612
|
+
}
|
|
6613
|
+
try {
|
|
6614
|
+
return await performICloudExport(
|
|
6615
|
+
database,
|
|
6616
|
+
userAddress,
|
|
6617
|
+
icloudDeps,
|
|
6618
|
+
backupOptions?.onProgress
|
|
6619
|
+
);
|
|
6620
|
+
} catch (err) {
|
|
6621
|
+
return {
|
|
6622
|
+
error: err instanceof Error ? err.message : "Failed to backup to iCloud"
|
|
6623
|
+
};
|
|
6624
|
+
}
|
|
6625
|
+
},
|
|
6626
|
+
[database, userAddress, icloudAuth, icloudDeps]
|
|
6627
|
+
);
|
|
6628
|
+
const icloudRestore = useCallback17(
|
|
6629
|
+
async (restoreOptions) => {
|
|
6630
|
+
if (!userAddress) {
|
|
6631
|
+
return { error: "Please sign in to restore from iCloud" };
|
|
6632
|
+
}
|
|
6633
|
+
if (!icloudAuth.isConfigured) {
|
|
6634
|
+
return { error: "iCloud is not configured" };
|
|
6635
|
+
}
|
|
6636
|
+
if (!icloudAuth.isAuthenticated) {
|
|
6637
|
+
try {
|
|
6638
|
+
await icloudAuth.requestAccess();
|
|
6639
|
+
} catch {
|
|
6640
|
+
return { error: "iCloud access denied" };
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
try {
|
|
6644
|
+
return await performICloudImport(
|
|
6645
|
+
userAddress,
|
|
6646
|
+
icloudDeps,
|
|
6647
|
+
restoreOptions?.onProgress
|
|
6648
|
+
);
|
|
6649
|
+
} catch (err) {
|
|
6650
|
+
return {
|
|
6651
|
+
error: err instanceof Error ? err.message : "Failed to restore from iCloud"
|
|
6652
|
+
};
|
|
6653
|
+
}
|
|
6654
|
+
},
|
|
6655
|
+
[userAddress, icloudAuth, icloudDeps]
|
|
6656
|
+
);
|
|
6657
|
+
const icloudState = {
|
|
6658
|
+
isConfigured: icloudAuth.isConfigured,
|
|
6659
|
+
isAuthenticated: icloudAuth.isAuthenticated,
|
|
6660
|
+
backup: icloudBackup,
|
|
6661
|
+
restore: icloudRestore,
|
|
6662
|
+
connect: icloudAuth.requestAccess,
|
|
6663
|
+
disconnect: icloudAuth.logout
|
|
6664
|
+
};
|
|
6665
|
+
return {
|
|
6666
|
+
dropbox: dropboxState,
|
|
6667
|
+
googleDrive: googleDriveState,
|
|
6668
|
+
icloud: icloudState,
|
|
6669
|
+
hasAnyProvider,
|
|
6670
|
+
hasAnyAuthentication,
|
|
6671
|
+
disconnectAll: logoutAll
|
|
5211
6672
|
};
|
|
5212
6673
|
}
|
|
5213
6674
|
export {
|
|
6675
|
+
DEFAULT_CONVERSATIONS_FOLDER as BACKUP_DRIVE_CONVERSATIONS_FOLDER,
|
|
6676
|
+
DEFAULT_ROOT_FOLDER as BACKUP_DRIVE_ROOT_FOLDER,
|
|
6677
|
+
DEFAULT_BACKUP_FOLDER2 as BACKUP_ICLOUD_FOLDER,
|
|
6678
|
+
BackupAuthProvider,
|
|
5214
6679
|
Conversation as ChatConversation,
|
|
5215
6680
|
Message as ChatMessage,
|
|
5216
6681
|
DEFAULT_BACKUP_FOLDER,
|
|
5217
6682
|
DEFAULT_CONVERSATIONS_FOLDER as DEFAULT_DRIVE_CONVERSATIONS_FOLDER,
|
|
5218
6683
|
DEFAULT_ROOT_FOLDER as DEFAULT_DRIVE_ROOT_FOLDER,
|
|
6684
|
+
DEFAULT_BACKUP_FOLDER as DEFAULT_DROPBOX_FOLDER,
|
|
6685
|
+
DEFAULT_BACKUP_FOLDER2 as DEFAULT_ICLOUD_BACKUP_FOLDER,
|
|
5219
6686
|
DEFAULT_TOOL_SELECTOR_MODEL,
|
|
5220
6687
|
DropboxAuthProvider,
|
|
6688
|
+
GoogleDriveAuthProvider,
|
|
6689
|
+
ICloudAuthProvider,
|
|
5221
6690
|
Memory as StoredMemoryModel,
|
|
5222
6691
|
ModelPreference as StoredModelPreferenceModel,
|
|
5223
6692
|
chatStorageMigrations,
|
|
5224
6693
|
chatStorageSchema,
|
|
5225
6694
|
clearToken as clearDropboxToken,
|
|
6695
|
+
clearGoogleDriveToken,
|
|
6696
|
+
clearICloudAuth,
|
|
5226
6697
|
createMemoryContextSystemMessage,
|
|
5227
6698
|
decryptData,
|
|
5228
6699
|
decryptDataBytes,
|
|
@@ -5233,8 +6704,11 @@ export {
|
|
|
5233
6704
|
generateCompositeKey,
|
|
5234
6705
|
generateConversationId,
|
|
5235
6706
|
generateUniqueKey,
|
|
5236
|
-
|
|
6707
|
+
getGoogleDriveStoredToken,
|
|
6708
|
+
hasDropboxCredentials,
|
|
5237
6709
|
hasEncryptionKey,
|
|
6710
|
+
hasGoogleDriveCredentials,
|
|
6711
|
+
hasICloudCredentials,
|
|
5238
6712
|
memoryStorageSchema,
|
|
5239
6713
|
requestEncryptionKey,
|
|
5240
6714
|
sdkMigrations,
|
|
@@ -5242,13 +6716,17 @@ export {
|
|
|
5242
6716
|
sdkSchema,
|
|
5243
6717
|
selectTool,
|
|
5244
6718
|
settingsStorageSchema,
|
|
5245
|
-
|
|
6719
|
+
useBackup,
|
|
6720
|
+
useBackupAuth,
|
|
5246
6721
|
useChat,
|
|
5247
6722
|
useChatStorage,
|
|
5248
6723
|
useDropboxAuth,
|
|
5249
6724
|
useDropboxBackup,
|
|
5250
6725
|
useEncryption,
|
|
6726
|
+
useGoogleDriveAuth,
|
|
5251
6727
|
useGoogleDriveBackup,
|
|
6728
|
+
useICloudAuth,
|
|
6729
|
+
useICloudBackup,
|
|
5252
6730
|
useImageGeneration,
|
|
5253
6731
|
useMemoryStorage,
|
|
5254
6732
|
useModels,
|