Commit graph

18376 commits

Author SHA1 Message Date
Johannes Millan
d982b708cc fix(dev): update default SuperSync port to 1901 for local development
- Change default dev URL from port 1900 to 1901 in frontend config
- Add WebAuthn environment variables to supersync override for port 1901
2026-01-24 21:14:57 +01:00
Johannes Millan
842baf81bf fix(sync): warn about unsynced changes before password change
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
4ea30b183b fix(sync): add mutex lock between sync and password change
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
2026-01-24 21:14:57 +01:00
Johannes Millan
1c41228a7f test(supersync): add comprehensive encryption enable/disable tests
Add E2E tests for scenarios C and D:
C) Password removal (encryption disabled) - verifies other clients adapt
D) Password addition (encryption enabled) - verifies other clients require password

Tests cover:
- Disabling encryption triggers clean slate and clients adapt automatically
- Enabling encryption triggers clean slate and clients detect encrypted data
- Multiple encryption state changes work correctly
- Concurrent changes are overwritten by encryption state change (expected behavior)

Also adds helper methods to SuperSyncPage:
- openEncryptionDialog() - Opens sync settings dialog
- enableEncryption(password) - Enables encryption via UI
- disableEncryption() - Disables encryption via UI
2026-01-24 21:14:57 +01:00
Johannes Millan
e924b068ac fix(passkey): skip email verification in TEST_MODE for passkey registration
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
c576372d00 feat(sync): implement clean slate mechanism for encryption password changes
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
2026-01-24 21:14:57 +01:00
Johannes Millan
933b3a2bc3 chore(deps): remove unused Vitest dependencies from package-lock.json 2026-01-24 21:14:57 +01:00
Johannes Millan
1628760d48 fix(upload): handle missing encryption key for piggybacked encrypted operations 2026-01-24 21:14:57 +01:00
Johannes Millan
737d11d8f3 feat(cors): normalize domain to lowercase and validate non-empty origins 2026-01-24 21:14:57 +01:00
Johannes Millan
ec847ce897 fix(sync): use manual code entry for Dropbox auth on all platforms
- 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
2026-01-24 21:14:57 +01:00
Johannes Millan
d3b85dbd7e fix(sync): await auth configuration to prevent dialog race condition
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
08343042c5 test(supersync): fix flaky E2E tests with timing adjustments
Increase wait times and add sync cycles to address race conditions in:
- Storage quota test: poll for alert instead of fixed wait
- LWW delete vs update test: increase timestamp separation delays
- Server migration test: add multiple sync cycles and extended timeouts
2026-01-24 21:14:57 +01:00
Johannes Millan
e2db30cfc2 feat(supersync): improve encryption UX with confirmation dialogs
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
1ba4c0ca7f debug: add verbose logging to deleteAllData and uploadSnapshot
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
b10b2e499b debug: add comprehensive logging to diagnose Android sync config issue
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
4c1aac9eb5 fix(supersync): sync form values explicitly before save on Android
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
a7db5e14e8 fix(supersync): prevent credential loss on Android dialog close
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
ec2db19115 fix(supersync): show informative message when disable encryption preconditions not met
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
3a6b69a1ba test(supersync): add integration tests for CORS wildcard patterns 2026-01-24 21:14:57 +01:00
Johannes Millan
be192ce107 docs(supersync): document CORS wildcard syntax in .env.example 2026-01-24 21:14:57 +01:00
Johannes Millan
ded17e96fc fix(supersync): parse wildcard patterns when universal wildcard present
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.
2026-01-24 21:14:57 +01:00
Johannes Millan
1da3d90c6b feat(supersync): parse wildcard patterns in CORS_ORIGINS env var 2026-01-24 21:14:57 +01:00
Johannes Millan
6a743181e5 fix(supersync): prevent domain confusion in parseCorsOrigin wildcard conversion 2026-01-24 21:14:57 +01:00
Corey Newton
db5f927062
docs(wiki) v0.2: refactor and populate meta notes (#6134)
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.
2026-01-24 21:14:29 +01:00
Corey Newton
d252984d0f
docs(ci): expand permissions for CI (#6150)
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.
2026-01-24 21:13:58 +01:00
Gitoffthelawn
8aa46a9946
Improve English UI (#6145)
* Many English UI improvements

* Additional improvements
2026-01-24 17:09:18 +01:00
Johannes Millan
8fe0998fd0
Claude/fix v17 data loss h oj6d (#6140)
* 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>
2026-01-24 14:31:39 +01:00
Johannes Millan
d4152c2195 perf(sync): expand cached key usage to all encryption call sites
Extends session-level key caching (from PR #6130) to remaining encryption
call sites, eliminating redundant Argon2id derivations (500-2000ms each).

Changes:
- Export base642ab/ab2base64 utilities to eliminate code duplication
- Optimize EncryptAndCompressHandlerService to use deriveKeyFromPassword()
  + encryptWithDerivedKey/decryptWithDerivedKey for snapshot encryption
- Fix cache clearing to detect ANY password change (not just disable)
- Add comprehensive input validation (empty passwords, batch size limits)
- Add 24-hour key expiry for security (configurable via KEY_LIFETIME_MS)

Bug Fixes:
- Fixed cache not clearing on encryption password changes
- Added validation to reject empty encryption passwords
- Added null checks for race conditions in cache access

Test Coverage:
- Added input validation tests (empty passwords, oversized batches)
- Added 24-hour key expiry tests (cache expiration and re-derivation)
- Added cache clearing tests (password changes, enable/disable encryption)

All encryption call sites now benefit from session-level caching:
- Batch operations (already optimized in PR #6130)
- File-based sync snapshots (NEW)
- Single-item operations (NEW)
- Password change operations (NEW)

Impact: Reduces mobile sync time from minutes to seconds for encrypted data.
2026-01-24 13:25:24 +01:00
Johannes Millan
d990173b34 fix(supersync): prevent domain confusion in preview CORS pattern 2026-01-24 13:23:21 +01:00
Johannes Millan
21743df401 fix(gitlab): allow null project field for backward compatibility
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
2026-01-24 13:21:03 +01:00
Johannes Millan
300c4ecfac fix(android): await flush after notification actions to prevent data loss (#5842)
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
2026-01-24 13:19:52 +01:00
Johannes Millan
aa55c0475b feat(supersync): add preview deployments to default CORS origins 2026-01-24 13:14:35 +01:00
Johannes Millan
be26d6de05 feat(supersync): add parseCorsOrigin helper with wildcard support 2026-01-24 12:57:30 +01:00
Johannes Millan
8931759745 docs(supersync): add CORS wildcard support design 2026-01-24 12:56:11 +01:00
Johannes Millan
5635c6a047 fix(ci): exclude @webdav and @supersync tests from CI workflow
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.
2026-01-24 12:56:10 +01:00
Johannes Millan
76defad732
feat(sync): add confirmation dialog when disabling SuperSync encryption (#6129)
* 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>
2026-01-24 12:54:58 +01:00
Johannes Millan
929fb56477
Merge pull request #6128 from super-productivity/claude/review-yesterday-changes-L0OTN
fix: address code review issues from yesterday's changes
2026-01-24 12:20:29 +01:00
Johannes Millan
d1efbb1677
Merge pull request #6137 from super-productivity/claude/fix-failing-eye-tests-AqO9x
test(sync): increase wait buffer for TODAY_TAG repair effect
2026-01-24 12:19:46 +01:00
Johannes Millan
c9d880daa6
Merge pull request #6130 from super-productivity/claude/fix-mobile-sync-performance-N7UPg
perf(sync): optimize encryption for mobile by caching derived keys
2026-01-24 12:14:28 +01:00
Claude
588e912dec
revert: remove error message field from internal_error_report.yml 2026-01-24 10:47:53 +00:00
Claude
4cfaa2c3e1
test(encryption): fix test expectations for session caching
- 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
2026-01-24 10:24:49 +00:00
Claude
9b7269f2fc
test(schedule): fix weeksToShow test by triggering signal recomputation
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.
2026-01-24 10:21:19 +00:00
Claude
88e538db0d
test(sync): increase wait buffer for TODAY_TAG repair effect
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.
2026-01-24 09:50:11 +00:00
Claude
0863c81515
test(encryption): fix test expectations for session caching
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
2026-01-24 09:44:32 +00:00
Claude
d0abcf1d68
fix(schedule): use SCSS interpolation for CSS custom property value
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).
2026-01-24 09:39:58 +00:00
Claude
caf22e3864
perf(encryption): parallelize batch operations and fix cache invalidation
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
2026-01-24 09:32:51 +00:00
Claude
bf79266640
perf(encryption): remove cache timeout and add password change invalidation
- Remove 30-minute cache timeout - keys now persist for entire session
- Add cache invalidation in SyncConfigService.updateEncryptionPassword()
- Add comprehensive tests for session caching:
  - Cache reuse across multiple batch calls
  - Cache invalidation when password changes
  - clearSessionKeyCache() functionality
  - getSessionKeyCacheStats() accuracy

The session cache is cleared automatically when password changes,
ensuring the new password derives a fresh key.
2026-01-24 00:56:11 +00:00
Claude
420fb401a3
perf(encryption): add session-level key caching for faster syncs
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
2026-01-24 00:41:32 +00:00
Claude
39f95015ee
fix(encryption): fix cache key bug and add missing tests
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()
2026-01-24 00:20:25 +00:00
Claude
2fbc1815f4
test(schedule): add tests for responsive week calculation logic
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.
2026-01-24 00:19:40 +00:00