sync.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import { Updater } from "../typing";
  2. import { ApiPath, STORAGE_KEY, StoreKey } from "../constant";
  3. import { createPersistStore } from "../utils/store";
  4. import {
  5. AppState,
  6. getLocalAppState,
  7. GetStoreState,
  8. mergeAppState,
  9. setLocalAppState,
  10. } from "../utils/sync";
  11. import { downloadAs, readFromFile } from "../utils";
  12. import { showToast } from "../components/ui-lib";
  13. import Locale from "../locales";
  14. import { createSyncClient, ProviderType } from "../utils/cloud";
  15. import { corsPath } from "../utils/cors";
  16. export interface WebDavConfig {
  17. server: string;
  18. username: string;
  19. password: string;
  20. }
  21. export type SyncStore = GetStoreState<typeof useSyncStore>;
  22. const DEFAULT_SYNC_STATE = {
  23. provider: ProviderType.WebDAV,
  24. useProxy: true,
  25. proxyUrl: corsPath(ApiPath.Cors),
  26. webdav: {
  27. endpoint: "",
  28. username: "",
  29. password: "",
  30. },
  31. upstash: {
  32. endpoint: "",
  33. username: STORAGE_KEY,
  34. apiKey: "",
  35. },
  36. lastSyncTime: 0,
  37. lastProvider: "",
  38. };
  39. export const useSyncStore = createPersistStore(
  40. DEFAULT_SYNC_STATE,
  41. (set, get) => ({
  42. coundSync() {
  43. const config = get()[get().provider];
  44. return Object.values(config).every((c) => c.toString().length > 0);
  45. },
  46. markSyncTime() {
  47. set({ lastSyncTime: Date.now(), lastProvider: get().provider });
  48. },
  49. export() {
  50. const state = getLocalAppState();
  51. const fileName = `Backup-${new Date().toLocaleString()}.json`;
  52. downloadAs(JSON.stringify(state), fileName);
  53. },
  54. async import() {
  55. const rawContent = await readFromFile();
  56. try {
  57. const remoteState = JSON.parse(rawContent) as AppState;
  58. const localState = getLocalAppState();
  59. mergeAppState(localState, remoteState);
  60. setLocalAppState(localState);
  61. location.reload();
  62. } catch (e) {
  63. console.error("[Import]", e);
  64. showToast(Locale.Settings.Sync.ImportFailed);
  65. }
  66. },
  67. getClient() {
  68. const provider = get().provider;
  69. const client = createSyncClient(provider, get());
  70. return client;
  71. },
  72. async sync() {
  73. const localState = getLocalAppState();
  74. const provider = get().provider;
  75. const config = get()[provider];
  76. const client = this.getClient();
  77. try {
  78. const remoteState = JSON.parse(
  79. await client.get(config.username),
  80. ) as AppState;
  81. mergeAppState(localState, remoteState);
  82. setLocalAppState(localState);
  83. } catch (e) {
  84. console.log("[Sync] failed to get remoate state", e);
  85. }
  86. await client.set(config.username, JSON.stringify(localState));
  87. this.markSyncTime();
  88. },
  89. async check() {
  90. const client = this.getClient();
  91. return await client.check();
  92. },
  93. }),
  94. {
  95. name: StoreKey.Sync,
  96. version: 1.1,
  97. migrate(persistedState, version) {
  98. const newState = persistedState as typeof DEFAULT_SYNC_STATE;
  99. if (version < 1.1) {
  100. newState.upstash.username = STORAGE_KEY;
  101. }
  102. return newState as any;
  103. },
  104. },
  105. );