Add check for unsynced operations before proceeding with encryption
password change. If there are pending operations that haven't been
synced yet, throw an error asking the user to wait for sync to complete.
This prevents potential data loss where local changes would be discarded
during the clean slate mechanism.
Prevents race conditions where sync could interfere with password change:
- Added runWithSyncBlocked() method to SyncWrapperService
- Password change now waits for ongoing sync to complete
- New syncs are blocked during password change operation
- Added test verifying sync blocking is used
When TEST_MODE is enabled with autoVerifyUsers, passkey registration now
auto-verifies users immediately instead of sending verification emails.
This enables local development without SMTP configuration.
Adds clean slate approach for encryption password changes and full imports:
- Creates fresh SYNC_IMPORT with new client ID
- Server deletes all data atomically when isCleanSlate flag is set
- Prevents encrypted/unencrypted operation mixing
- Simpler error handling than previous recovery-based approach
Client changes:
- Add CleanSlateService for generating fresh sync baselines
- Add PreMigrationBackupService (placeholder for future backup functionality)
- Update EncryptionPasswordChangeService to use clean slate approach
- Add isCleanSlate flag to upload options and provider interface
- Update SuperSync provider to pass flag to API
Server changes:
- Add isCleanSlate parameter to upload operations endpoint
- Delete all operations, devices, and sync state when flag is true
- Clear caches after successful clean slate transaction
Tests:
- 24 unit tests for clean slate services (all passing)
- 9 unit tests for encryption password change service (8/9 passing)
- E2E tests verify encryption password change flow works correctly
- Force dialog to show input field instead of OAuth redirect spinner
- Remove platform-specific OAuth redirect URI logic
- Ensures reliable copy/paste auth flow on Android, web, and Electron
The sync dialog was closing before the auth code flow completed due to a
missing await. This caused the auth code dialog to open after the parent
dialog closed, preventing credentials from being saved. Now the dialog
waits for auth configuration to complete before closing.
Add two encryption UX improvements for SuperSync:
1. Enable encryption confirmation dialog: When enabling encryption,
show a warning dialog explaining that all server data will be deleted
and re-uploaded with encryption. On confirm, performs the operation
automatically. On cancel, reverts the checkbox.
2. Password prompt dialog for missing encryption key: When receiving
encrypted data without a configured password, show a dialog prompting
the user to enter their encryption password instead of just showing
a snack bar error. On save, stores the password and re-syncs.
Track API calls during encryption disable:
- Log when DELETE /api/sync/data starts and completes
- Log when snapshot upload starts and response received
- Show server responses to diagnose silent failures
This will help identify why encryption disable completes without
visible network activity.
Add detailed console logs at every step of sync configuration:
- Dialog save method (form validation, values before/after merge)
- SyncConfigService.updateSettingsFromForm
- _updatePrivateConfig (provider config, filtering, final values)
This will help identify where the token is getting lost during save.
Formly's (modelChange) event doesn't fire reliably on Android WebView,
causing _tmpUpdatedCfg to remain stale when user enters credentials.
Now explicitly read form.value in save() method to ensure current form
state is captured regardless of change detection issues.
Remove resetOnHide from credential fields (accessToken, encryption passwords)
to fix race condition where Formly clears form values during dialog close
sequence on Android WebView, causing config save to fail. Also add missing
translation keys for disable encryption precondition errors.
The disable encryption dialog now validates preconditions on open and shows
a user-friendly message when sync is not enabled/configured or when a non-
SuperSync provider is active, instead of failing silently or showing errors.
Fixed logic bug where wildcard subdomain patterns (e.g., https://*.example.com)
were not being parsed to RegExp when the universal wildcard (*) was present in
CORS_ORIGINS. All origins were incorrectly stored as strings.
Before: CORS_ORIGINS="https://*.example.com,*,https://app.com"
Result: ['https://*.example.com', '*', 'https://app.com'] (all strings)
After:
Result: [RegExp, '*', 'https://app.com'] (wildcard pattern → RegExp)
Changes:
- Modified loadConfigFromEnv() to map over origins when * is present
- Keep * as-is, but parse other origins through parseCorsOrigin()
- Added test case verifying mixed origins with universal wildcard
All existing tests pass. New test validates RegExp matching works correctly.
Substantial re-work and addition of meta notes. Still many stub notes
and incomplete placeholders but the majority of the structure is
established and filling out the actual content is now a priority.
This should minimize false warnings and allow for merges without
concern. Slight renaming of jobs should also help with review. Exclusion
of new GH Action for docs/wiki/** added too.
* fix(migration): prevent data loss when legacy database check fails
CRITICAL FIX: Users upgrading from v16.9.4 to v17 were losing all data
because the hasUsableEntityData() method silently returned false on any
database access error. This caused the migration to be skipped, leaving
users with an empty app.
Root cause:
- hasUsableEntityData() caught ALL exceptions and returned false
- This made it look like there was no v16 data to migrate
- The app would start "fresh" instead of migrating existing data
Changes:
1. hasUsableEntityData() now throws errors instead of silently failing
- First checks if database exists (returns false if not)
- Throws Error if database exists but can't be read
- This distinguishes "no data" from "can't access data"
2. Added tag detection to hasUsableEntityData()
- Previously only checked task, project, globalConfig
- Users with only tags (like TODAY) could be missed
- Now also checks for tag.ids array
3. Migration service now catches and displays database errors
- Shows user-friendly error dialog
- Prevents silent data loss by not proceeding with "fresh start"
- Re-throws error to stop app startup
4. Added comprehensive logging
- Logs what data was found during detection
- Logs CRITICAL errors when database access fails
- Helps debug future migration issues
Fixes data loss issue when upgrading from v16.9.4 to v17.
* fix(startup): skip restore backup dialog when legacy v16 data exists
Users upgrading from v16.9.4 to v17 were seeing an incorrect "restore
from backup" dialog. The dialog was shown because:
1. StartupService._initBackups() checked only for SUP_OPS state cache
2. For v16 upgrades, there's no state cache yet (new database)
3. The code incorrectly treated this as a "fresh install"
4. So it showed the restore backup dialog
The fix:
- Before showing the backup dialog, also check for legacy pf data
- If legacy data exists, skip the backup dialog (migration will handle it)
- If legacy check throws an error, assume data exists (don't show dialog)
This ensures the restore backup dialog is only shown for truly fresh
installs, not for v16→v17 upgrades.
* fix(test): fix lint error and use Observable for dialog mock
- Fix prettier lint error in legacy-pf-db.service.spec.ts (line too long)
- Fix test mock to return Observable instead of object with toPromise()
(firstValueFrom expects an Observable, not a toPromise wrapper)
* fix(test): fix prettier lint error in migration service spec
---------
Co-authored-by: Claude <noreply@anthropic.com>
Fixes#6139, fixes#6131
Changes GitLabCfg.project type from 'string' to 'string | null' to support
legacy data from v16.9.4 where project could be null. This resolves state
validation errors that blocked users from upgrading to v17.0.0.
- Updated GitlabCfg interface to allow project: string | null
- Added defensive null checks in gitlab-common-interfaces.service.ts
- Added assertTruthy() check in gitlab-api.service.ts for runtime safety
- Form validation still requires project field when saving configs
When users tap "Pause" or "Done" in the time tracking notification and immediately close the app, tracked time was being lost. The root cause was _flushPendingOperations() not being awaited, creating a race condition where IndexedDB writes didn't complete before app closure.
Changes:
- Add await to _flushPendingOperations() calls in pause/done handlers (lines 207, 231)
- Make _flushPendingOperations() properly async with error handling
- Add user notifications for critical sync failures (task not found, flush errors)
- Handle negative duration edge case when native service crashes/resets
- Enhance logging with warnings instead of silent failures
- Add comprehensive test coverage (41 tests passing)
Fixes#5842
Aligns PR checks with release build configuration by excluding
tests that require external services (WebDAV server on port 2345
and SuperSync server on port 1901). These tests still run in the
nightly e2e-scheduled.yml workflow with Docker services.
This prevents connection refused errors in CI and reduces test
runtime by ~45 tests while maintaining full coverage via scheduled
runs.
* feat(sync): add confirmation dialog when disabling SuperSync encryption
When users toggle off encryption for SuperSync, show a confirmation dialog
that explains the consequences:
- All encrypted sync data on the server will be deleted
- Current data will be re-uploaded without encryption
- Other devices will sync the unencrypted data
This ensures users understand that turning off encryption requires a fresh
snapshot to prevent mixing encrypted and unencrypted operations.
Changes:
- Add EncryptionDisableService to handle the disable encryption flow
- Add DialogDisableEncryptionComponent for user confirmation
- Add hook to encryption toggle in sync form to intercept disable action
- Add openDisableEncryptionDialog to dialog opener service
- Add translation keys for the new dialog
* fix(sync): initialize dialog opener and fix memory leak in encryption toggle
- Add EncryptionPasswordDialogOpenerInitService to initialize the module-level
dialog opener instance during app bootstrap
- Register the init service in main.ts using APP_INITIALIZER
- Fix memory leak by properly cleaning up subscription in onDestroy hook
- Add guard to prevent multiple dialogs opening simultaneously
- Fix race condition with async dialog handling
* feat(sync): handle encryption state change during backup import
When importing a backup file that has different encryption settings than
the current SuperSync configuration, the system now properly handles the
transition:
1. Detects encryption state change (enabled -> disabled or vice versa)
2. Deletes all server data (encrypted ops can't mix with unencrypted)
3. Uploads a fresh snapshot with the correct encryption settings
4. Updates the sync provider config to match the imported settings
This ensures a "tabula rasa" for the sync state when encryption changes,
preventing corrupt state where encrypted and unencrypted operations coexist.
Changes:
- Add ImportEncryptionHandlerService to detect and handle encryption changes
- Integrate the handler into FileImexComponent import flow
- Add E2E test for import with encryption state change scenario
* feat(sync): add confirmation dialog for import with encryption state change
Show a warning dialog to users when importing a backup that has different
encryption settings than their current SuperSync configuration. The dialog
explains the consequences:
- All server data will be deleted
- Sync history will be lost
- All devices will need to resync
This ensures users are informed before any encryption state change action.
* refactor(sync): improve UX of encryption state change dialogs
- Add context-specific messaging (enabling vs disabling encryption)
- Use visual state comparison with arrow indicator for import dialog
- Improve button label: "Import and Change Encryption" (clearer action)
- Add disableClose: true to prevent accidental dialog dismissal
- Improve terminology: "Your current configuration" → "After import"
- Clarify "resync" instructions for other devices
- Add aria-labels for screen reader accessibility
- Remove redundant warning icon from dialog title
* fix(sync): correct translation key path in import encryption warning dialog
Fix path from T.F.SYNC.IMPORT_ENCRYPTION_WARNING to
T.F.SYNC.FORM.IMPORT_ENCRYPTION_WARNING to match t.const.ts structure.
* fix(sync): use DI for OperationEncryptionService and fix tests
- Inject OperationEncryptionService properly instead of using `new`
which fails because the service uses Angular's inject() internally
- Add mock for ImportEncryptionHandlerService in FileImexComponent tests
* fix(ci): handle existing swap file in Lighthouse job
The Lighthouse job was failing with "fallocate: Text file busy" because
GitHub Actions runners may already have /swapfile in use. This fix:
- Disables existing swap file if present (swapoff)
- Removes the file before creating a new one
- Gracefully handles errors using || true
This resolves workflow failures while ensuring Lighthouse/Chrome still
get the required memory allocation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Add mock batch encryption providers to integration tests
- Fix legacy format fallback in decryptBatch (handles long legacy data ≥44 bytes)
- Argon2 decryption now falls back to legacy on failure
The computed signal `daysToShow` wasn't recomputing after changing the
mock's return value. Setting `_selectedDate` triggers the dependency
chain and forces recomputation with the new mock data.
Increased post-sync cooldown wait from 2.5s to 3.5s in the
"LWW: Recreated task with dueDay=today appears in TODAY view" test.
The previous 2.5s wait only provided 500ms buffer after the 2s
POST_SYNC_COOLDOWN_MS period ended. Under CI resource contention,
this wasn't always enough time for the repair effect to dispatch
its action and for the UI to render.
The new 3.5s wait provides 1.5s buffer, which should be sufficient
even under heavy CI load.
Update batch encryption tests to reflect session caching behavior:
- Same password now reuses cached key (same salt) - this is expected
- Different passwords use different salts
- Added explicit cache clearing in tests for isolation
SCSS variables must use #{$var} interpolation syntax when assigned
to CSS custom properties. Without interpolation, the literal variable
name was output instead of its value (3em).
PERFORMANCE:
- Parallelize encryptBatch using Promise.all (10-100x faster)
- Parallelize decryptBatch in 3 phases:
1. Analyze all items (fast, no crypto)
2. Derive keys for unique salts in parallel
3. Decrypt all items in parallel using cached keys
SECURITY FIX:
- Add cache invalidation in EncryptionPasswordChangeService.changePassword()
- Now cache is cleared in both password change flows
TESTS:
- Add large batch test (50 items) for parallel processing
- Add order preservation test for parallel operations
Cache derived Argon2id keys across sync cycles:
- First sync: derive key (500ms-2000ms on mobile)
- Subsequent syncs: reuse cached key (instant)
Features:
- Encryption cache: reuses key by password hash
- Decryption cache: stores up to 100 keys by password+salt
- 30-minute max age for security
- clearSessionKeyCache() for invalidation when password changes
- DerivedKeyCacheService for Angular DI integration
Fix critical bug in decryptBatch where salt.buffer returned the entire
ArrayBuffer instead of just the 16-byte salt, defeating key caching.
- Use salt.slice().buffer to create proper cache key
- Add tests for encryptPayload() and decryptPayload() methods
- Fix test assertion to verify reference identity with toBe()
Add test coverage for previously untested computed signals:
- _daysToShowCount: verifies week view returns 7, month view returns
value bounded by MIN_WEEKS (3) and MAX_WEEKS (6)
- shouldEnableHorizontalScroll: verifies month view returns false,
week view uses HORIZONTAL_SCROLL_THRESHOLD constant
- weeksToShow: verifies correct calculation from days array length
These tests ensure the SCHEDULE_CONSTANTS.MONTH_VIEW values are
properly used in the responsive layout calculation.