mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
* feat(sync): integrate onedrive with pkce and operation log sync * fix(sync): add oauth state validation and token refresh concurrency control * fix(sync): refactor onedrive oauth cleanup and reduce download API calls - Replace setInterval-based OAuth state pruning with on-demand _pruneExpiredOAuthStates() to avoid background timer overhead - Optimize downloadFile to read ETag from content response headers first, falling back to a metadata request only when needed (reduces API calls from 2 to 1 in the common case) - Narrow OneDrive class interface from SyncProviderServiceInterface to FileSyncProvider for stronger type guarantees - Accept optional devPath constructor parameter for non-production folder namespacing - Extract isOneDriveClientIdRequired to a helper function in sync-form.const.ts for readability - Change default OneDrive sync folder name to 'Super Productivity' Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 review feedback for OneDrive sync Critical fixes: - Detect invalid_grant in token refresh responses and clear credentials - Reset _tokenRefreshInFlightPromise in clearAuthCredentials to prevent stale refresh results from overwriting cleared state - Re-read config before persisting refreshed tokens to avoid race with concurrent clearAuthCredentials calls Important fixes: - Extract OAuth state management to shared oauth-state.util.ts to fix circular dependency between OneDrive provider and callback handler - Add 401 retry pattern (_is401Retry) matching Dropbox's approach: on 401, proactively refresh token and retry, only clear credentials if retry fails - Remove unused auth status translation keys (AUTH_SUCCESS, BTN_AUTHENTICATE, BTN_REAUTHENTICATE, REAUTH_SUCCESS, STATUS_CONFIGURED, STATUS_NOT_CONFIGURED) and form props - Remove unused requireAuth parameter from _cfgOrError Minor fixes: - Replace path-exposing log messages with structured logging - Remove unused _formatHttpErrorDetails helper - Return empty ETag fallback in getFileRev instead of throwing - Add folder cache invalidation comment for path changes - Add afterEach to restore global fetch in tests - Add PKCE defence-in-depth comment in auth code dialog Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): use eslint-disable-next-line for @microsoft.graph.conflictBehavior Replace spread+computed-property workaround with a plain property plus an explicit eslint-disable-next-line comment. Clearer intent: the naming violation is a Graph API requirement, not accidental. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): pre-save OneDrive form config in reauth() to prevent MissingCredentialsSPError Re-auth was failing silently on Linux because getAuthHelper() reads clientId from IndexedDB (privateCfg), but the form values were never written before calling configuredAuthForSyncProviderIfNecessary(). - Extract shared BTN_REAUTHENTICATE and REAUTH_SUCCESS translation keys - Add OneDrive to OAUTH_SYNC_PROVIDERS - Pre-save form config in reauth() matching the existing save() flow Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): adapt TooManyRequestsAPIError to new constructor signature after rebase * refactor(sync): migrate OneDrive provider to packages/sync-providers Move OneDrive core logic into the shared sync-providers package, matching the pattern used by Dropbox, WebDAV, and other providers. The app-side code is now a thin adapter with a createOneDriveProvider() factory function. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): re-throw MissingRefreshTokenAPIError from _requestOAuthToken catch block The catch block in _requestOAuthToken was swallowing the MissingRefreshTokenAPIError thrown after clearing credentials on invalid_grant, causing it to fall through to a generic HttpNotOkAPIError instead. Now re-throws MissingRefreshTokenAPIError explicitly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 second review critical and important items - Replace _is401Retry instance field with per-call parameter to prevent concurrent 401 races (Critical #1) - Fix _requestOAuthToken catch block to re-throw MissingRefreshTokenAPIError instead of swallowing it (Critical #2) - Fix _parseOAuthCallback defaulting unknown provider to 'unknown' instead of 'dropbox' to prevent misrouting (Critical #3 + Important #6) - Change conflictBehavior from 'replace' to 'fail' to prevent data loss if a file exists with the same name as the folder (Critical #4) - Await in-flight token refresh promise in clearAuthCredentials before clearing, to close the refresh-during-clear race (Important #5) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 review important and minor items - Extract duplicate OneDrive pre-auth save block into _persistOneDriveFormCfgBeforeAuth() method (Important #7) - Add GET probe in _ensureSyncFolderExists to skip per-segment creation when folder already exists (Important #9) - Redact sensitive params (code, code_verifier, refresh_token, access_token) from token endpoint and Graph error bodies (Important #11) - Remove targetPath from _mapAndThrow error messages to prevent logging user content (Important #12) - Add OAuth state validation in manual paste flow for OneDrive (Important #13) - Fix unknown provider routing in _parseOAuthCallback (Important #14) - Guard Electron IPC listener against post-destroy emissions (Important #15) - Remove dead authStatus Formly field from sync-form.const.ts (Minor) - Simplify Headers builder in _ensureSyncFolderExists (Minor) - Fix spec afterEach to restore original fetch instead of undefined (Minor) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): fix clearAuthCredentials race and add unit tests for OneDrive Fix a race condition in clearAuthCredentials where _tokenRefreshInFlightPromise was nulled AFTER awaiting, causing the invalid_grant handler's clearAuthCredentials to wait on itself. Now captures the promise and nulls the field before awaiting. Add 5 new unit tests covering critical code paths: - 401 token refresh and retry - 401 retry failure with credential clearing - 400 invalid_grant credential clearing - 412 precondition failed mapping - Concurrent token refresh deduplication Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 third review items - Simplify clearAuthCredentials: null _tokenRefreshInFlightPromise without awaiting to avoid deadlock when called from inside the refresh IIFE - Add superproductivity:// scheme validation in Electron OAuth listener - Fix invalid_grant test to assert MissingRefreshTokenAPIError - Fix folder cache test to match GET-probe optimization - Set setComplete default in beforeEach Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(sync): fix OAuthCallbackHandler spec to expect 'unknown' provider The _parseOAuthCallback now returns 'unknown' instead of 'dropbox' for URLs without an explicit provider path segment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 fourth review items - Handle generic 401 from token endpoint (clear creds + MissingRefreshTokenAPIError) - Remove user-supplied paths from error constructors (rule 9 compliance) - Soften clearAuthCredentials comment to reflect actual TOCTOU guarantee - Tighten Electron scheme gate to match native path prefix - Add scheme info to Electron rejection log - Fix folder-cache test to assert GET probe count - Remove redundant per-test setComplete stub Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 fifth review items - Scope invalid_grant/401 credential clearing to refresh_token grant only (bad auth code exchange no longer wipes existing credentials) - Redact OAuth code/state from Electron protocol handler logs - Add @sp/sync-providers/onedrive TS path alias to tsconfig files - Default undefined sync config fields instead of overwriting with undefined - Handle OneDrive in provider-switch branch (preserve encryptKey) - Update sync-config test expectations to match default values Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 sixth review items - Use If-None-Match: * for null-rev uploads to prevent concurrent overwrite - Guard in-flight refresh against stale credentials (match refreshToken/clientId/tenantId) - Gate OneDrive provider option behind Electron/Native (web CORS unsupported) - Don't clear credentials on transient token endpoint errors (429/5xx) - Preserve null syncProvider instead of defaulting to WebDAV - Hydrate full OneDrive privateCfg on provider switch - Remove duplicate syncInterval/isManualSyncOnly Formly controls - Revert non-English locale changes (zh.json, zh-tw.json) - Redact URL fragments (#) in Electron protocol handler logs - listFiles rethrows non-404 errors instead of swallowing all - Update 401 test expectations for new rethrow behavior Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): guard credential clearing against stale concurrent refresh Prevent a stale in-flight refresh from wiping newer credentials after a concurrent re-auth. The refresh IIFE now throws a plain Error (not MissingRefreshTokenAPIError) when it detects config drift, and all clearAuthCredentials() call sites now verify the stored config still matches before clearing. Also fix listFiles() to handle 404 from _requestJson (which throws HttpNotOkAPIError, not RemoteFileNotFoundAPIError). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 eighth review items - Replace If-None-Match:* with @microsoft.graph.conflictBehavior=fail query param for upload create-only semantics - Add identity change detection in _persistOneDriveFormCfgBeforeAuth to clear tokens when useCustomApp/clientId/tenantId change - Extract _clearIfConfigMatches helper to guard credential clearing against stale concurrent refresh - Restrict folder probe fallthrough to 404 only, propagate other errors - Restore locale files from upstream master - Centralize IS_ONEDRIVE_SUPPORTED in onedrive-auth-mode.const.ts, use in factory and form config Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 ninth review items - Paginate listFiles via @odata.nextLink to avoid silent data loss - Fix logger misuse (log -> normal with proper string message) - Replace as any with OneDrivePrivateCfg in dialog-sync-cfg - Throw NoRevAPIError when uploadFile response lacks eTag - Omit undefined optional booleans from global config to prevent overwrite - Move OneDrive dynamic import inside IS_ONEDRIVE_SUPPORTED gate - Require state param for OneDrive full-URL paste (CSRF) - Add id_token to sensitive keys for body redaction Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): add OneDrive type alias to electron tsconfig paths The @sp/sync-providers/onedrive path alias was missing from electron/tsconfig.electron.json, causing the Electron build to fail with "Cannot find module '@sp/sync-providers/onedrive'". The alias was already present in tsconfig.base.json but electron uses its own paths. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
53 lines
2.4 KiB
JSON
53 lines
2.4 KiB
JSON
{
|
|
"compilerOptions": {
|
|
"allowSyntheticDefaultImports": true,
|
|
"sourceMap": true,
|
|
"resolveJsonModule": true,
|
|
"declaration": false,
|
|
"moduleResolution": "node",
|
|
"emitDecoratorMetadata": true,
|
|
"experimentalDecorators": true,
|
|
"skipLibCheck": true,
|
|
"paths": {
|
|
"@sp/sync-providers/dropbox": ["node_modules/@sp/sync-providers/dist/dropbox.d.ts"],
|
|
"@sp/sync-providers/local-file": [
|
|
"node_modules/@sp/sync-providers/dist/local-file.d.ts"
|
|
],
|
|
"@sp/sync-providers/webdav": ["node_modules/@sp/sync-providers/dist/webdav.d.ts"],
|
|
"@sp/sync-providers/super-sync": [
|
|
"node_modules/@sp/sync-providers/dist/super-sync.d.ts"
|
|
]
|
|
},
|
|
"typeRoots": ["node_modules/@types"],
|
|
"downlevelIteration": true,
|
|
"lib": ["dom", "es2022"],
|
|
"target": "es2022",
|
|
"module": "commonjs",
|
|
"esModuleInterop": true,
|
|
"baseUrl": "..",
|
|
"paths": {
|
|
"@super-productivity/plugin-api": ["packages/plugin-api/src/index.ts"],
|
|
"@sp/shared-schema": ["packages/shared-schema/dist/index.d.ts"],
|
|
"@sp/sync-core": ["packages/sync-core/dist/index.d.ts"],
|
|
"@sp/sync-providers/dropbox": ["packages/sync-providers/dist/dropbox.d.ts"],
|
|
"@sp/sync-providers/webdav": ["packages/sync-providers/dist/webdav.d.ts"],
|
|
"@sp/sync-providers/local-file": ["packages/sync-providers/dist/local-file.d.ts"],
|
|
"@sp/sync-providers/super-sync": ["packages/sync-providers/dist/super-sync.d.ts"],
|
|
"@sp/sync-providers/http": ["packages/sync-providers/dist/http.d.ts"],
|
|
"@sp/sync-providers/errors": ["packages/sync-providers/dist/errors.d.ts"],
|
|
"@sp/sync-providers/credential-store": [
|
|
"packages/sync-providers/dist/credential-store.d.ts"
|
|
],
|
|
"@sp/sync-providers/file-based": ["packages/sync-providers/dist/file-based.d.ts"],
|
|
"@sp/sync-providers/pkce": ["packages/sync-providers/dist/pkce.d.ts"],
|
|
"@sp/sync-providers/platform": ["packages/sync-providers/dist/platform.d.ts"],
|
|
"@sp/sync-providers/provider-types": [
|
|
"packages/sync-providers/dist/provider-types.d.ts"
|
|
],
|
|
"@sp/sync-providers/log": ["packages/sync-providers/dist/log.d.ts"],
|
|
"@sp/sync-providers/onedrive": ["packages/sync-providers/dist/onedrive.d.ts"]
|
|
}
|
|
},
|
|
"include": ["main.ts", "**/*.ts", "../src/app/core/window-ea.d.ts"],
|
|
"exclude": ["../node_modules", "**/*.spec.ts"]
|
|
}
|