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>
113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
// TODO sort by direction
|
|
export enum IPC {
|
|
SHUTDOWN_NOW = 'SHUTDOWN_NOW',
|
|
EXIT = 'EXIT',
|
|
RELAUNCH = 'RELAUNCH',
|
|
|
|
JIRA_CB_EVENT = 'JIRA_RESPONSE',
|
|
JIRA_MAKE_REQUEST_EVENT = 'JIRA_MAKE_REQUEST_EVENT',
|
|
JIRA_SETUP_IMG_HEADERS = 'JIRA_SETUP_IMG_HEADERS',
|
|
REGISTER_GLOBAL_SHORTCUTS_EVENT = 'REGISTER_GLOBAL_SHORTCUTS',
|
|
IDLE_TIME = 'IDLE_TIME',
|
|
RESUME = 'RESUME',
|
|
SUSPEND = 'SUSPEND',
|
|
|
|
SHOW_OR_FOCUS = 'SHOW_OR_FOCUS',
|
|
LOCK_SCREEN = 'LOCK_SCREEN',
|
|
|
|
APP_READY = 'APP_READY',
|
|
ERROR = 'ELECTRON_ERROR',
|
|
CURRENT_TASK_UPDATED = 'CURRENT_TASK_UPDATED',
|
|
REQUEST_CURRENT_TASK_FOR_TASK_WIDGET = 'REQUEST_CURRENT_TASK_FOR_TASK_WIDGET',
|
|
TASK_TOGGLE_START = 'TASK_TOGGLE_START',
|
|
SHOW_ADD_TASK_BAR = 'SHOW_ADD_TASK_BAR',
|
|
ADD_TASK_FROM_APP_URI = 'ADD_TASK_FROM_APP_URI',
|
|
ADD_NOTE = 'ADD_NOTE',
|
|
TRANSFER_SETTINGS_REQUESTED = 'TRANSFER_SETTINGS_REQUESTED',
|
|
TRANSFER_SETTINGS_TO_ELECTRON = 'TRANSFER_SETTINGS_TO_ELECTRON',
|
|
|
|
EXEC = 'EXEC',
|
|
|
|
BACKUP = 'BACKUP',
|
|
BACKUP_IS_AVAILABLE = 'BACKUP_IS_AVAILABLE',
|
|
BACKUP_LOAD_DATA = 'BACKUP_LOAD_DATA',
|
|
|
|
SET_PROGRESS_BAR = 'SET_PROGRESS_BAR',
|
|
FLASH_FRAME = 'FLASH_FRAME',
|
|
|
|
NOTIFY_ON_CLOSE = 'NOTIFY_ON_CLOSE',
|
|
REGISTER_BEFORE_CLOSE = 'REGISTER_BEFORE_CLOSE',
|
|
UNREGISTER_BEFORE_CLOSE = 'UNREGISTER_BEFORE_CLOSE',
|
|
BEFORE_CLOSE_DONE = 'BEFORE_CLOSE_DONE',
|
|
|
|
FILE_SYNC_LOAD = 'FILE_SYNC_LOAD',
|
|
FILE_SYNC_SAVE = 'FILE_SYNC_SAVE',
|
|
FILE_SYNC_REMOVE = 'FILE_SYNC_REMOVE',
|
|
FILE_SYNC_LIST_FILES = 'FILE_SYNC_LIST_FILES',
|
|
FILE_SYNC_GET_REV_AND_CLIENT_UPDATE = 'FILE_SYNC_GET_REV_AND_CLIENT_UPDATE',
|
|
|
|
CHECK_DIR_EXISTS = 'CHECK_DIR_EXISTS',
|
|
PICK_DIRECTORY = 'PICK_DIRECTORY',
|
|
SHOW_OPEN_DIALOG = 'SHOW_OPEN_DIALOG',
|
|
READ_LOCAL_IMAGE_AS_DATA_URL = 'READ_LOCAL_IMAGE_AS_DATA_URL',
|
|
TO_FILE_URL = 'TO_FILE_URL',
|
|
|
|
ANY_FILE_DOWNLOADED = 'ANY_FILE_DOWNLOADED',
|
|
|
|
FULL_SCREEN_BLOCKER = 'FULL_SCREEN_BLOCKER',
|
|
ENTER_FULL_SCREEN = 'ENTER_FULL_SCREEN',
|
|
LEAVE_FULL_SCREEN = 'LEAVE_FULL_SCREEN',
|
|
|
|
GET_PATH = 'GET_PATH',
|
|
GET_BACKUP_PATH = 'GET_BACKUP_PATH',
|
|
RELOAD_MAIN_WIN = 'RELOAD_MAIN_WIN',
|
|
OPEN_DEV_TOOLS = 'OPEN_DEV_TOOLS',
|
|
SHOW_EMOJI_PANEL = 'SHOW_EMOJI_PANEL',
|
|
|
|
OPEN_PATH = 'OPEN_PATH',
|
|
OPEN_EXTERNAL = 'OPEN_EXTERNAL',
|
|
|
|
SAVE_FILE_DIALOG = 'SAVE_FILE_DIALOG',
|
|
|
|
SHARE_NATIVE = 'SHARE_NATIVE',
|
|
|
|
// Clipboard Images
|
|
CLIPBOARD_IMAGE_SAVE = 'CLIPBOARD_IMAGE_SAVE',
|
|
CLIPBOARD_IMAGE_LOAD = 'CLIPBOARD_IMAGE_LOAD',
|
|
CLIPBOARD_IMAGE_DELETE = 'CLIPBOARD_IMAGE_DELETE',
|
|
CLIPBOARD_IMAGE_LIST = 'CLIPBOARD_IMAGE_LIST',
|
|
CLIPBOARD_IMAGE_GET_PATH = 'CLIPBOARD_IMAGE_GET_PATH',
|
|
CLIPBOARD_GET_FILE_PATHS = 'CLIPBOARD_GET_FILE_PATHS',
|
|
CLIPBOARD_COPY_IMAGE_FILE = 'CLIPBOARD_COPY_IMAGE_FILE',
|
|
CLIPBOARD_READ_IMAGE = 'CLIPBOARD_READ_IMAGE',
|
|
|
|
// Plugin Node Execution
|
|
PLUGIN_EXEC_NODE_SCRIPT = 'PLUGIN_EXEC_NODE_SCRIPT',
|
|
|
|
// Plugin OAuth
|
|
PLUGIN_OAUTH_PREPARE = 'PLUGIN_OAUTH_PREPARE',
|
|
PLUGIN_OAUTH_START = 'PLUGIN_OAUTH_START',
|
|
PLUGIN_OAUTH_CB = 'PLUGIN_OAUTH_CB',
|
|
|
|
// Generic OAuth callback for app protocol redirects (e.g. OneDrive)
|
|
OAUTH_CALLBACK = 'OAUTH_CALLBACK',
|
|
|
|
LOCAL_REST_API_REQUEST = 'LOCAL_REST_API_REQUEST',
|
|
LOCAL_REST_API_RESPONSE = 'LOCAL_REST_API_RESPONSE',
|
|
|
|
UPDATE_SETTINGS = 'UPDATE_SETTINGS',
|
|
UPDATE_TASK_WIDGET_SETTINGS = 'UPDATE_TASK_WIDGET_SETTINGS',
|
|
UPDATE_TITLE_BAR_DARK_MODE = 'UPDATE_TITLE_BAR_DARK_MODE',
|
|
|
|
TODAY_TASKS_UPDATED = 'TODAY_TASKS_UPDATED',
|
|
SWITCH_TASK = 'SWITCH_TASK',
|
|
|
|
// maybe_UPDATE_CURRENT_TASK = 'UPDATE_CURRENT_TASK',
|
|
// maybe_IS_IDLE = 'IS_IDLE',
|
|
// maybe_IS_BUSY = 'IS_BUSY',
|
|
// POMODORO_UPDATE = 'POMODORO_UPDATE',
|
|
// maybe_PROJECT_CHANGED = 'PROJECT_CHANGED',
|
|
// maybe_COMPLETE_DATA_RELOAD = 'COMPLETE_DATA_RELOAD',
|
|
}
|
|
|
|
export type IPCEventValue = `${IPC}`;
|