Commit graph

10 commits

Author SHA1 Message Date
Johannes Millan
a4dee9d5f7
fix(sync): isolate provider encryption settings + enforce critical e2e coverage (#9044)
* docs(plugins): add microsoft 365 calendar provider plan

* docs(plugins): add microsoft 365 calendar provider plan

* docs(ios): plan internal testflight builds

* fix(sync): isolate provider encryption settings

* test: enforce critical end-to-end coverage
2026-07-15 14:24:49 +02:00
johannesjo
362847036d fix(sync): handle wrapped backup encryption import 2026-05-14 12:41:09 +02:00
Johannes Millan
00098f52fb refactor(sync-providers): add tiered package exports 2026-05-13 14:11:15 +02:00
Johannes Millan
49bf056ee0
refactor(sync): simplify SuperSync client code (#6728)
- Unify duplicated HTTP methods in SuperSyncProvider into shared helpers
- Remove 5 encryption DI injection tokens, use private properties for test spying
- Remove DerivedKeyCacheService wrapper, use direct imports
- Cache _getServerSeqKey() result in SuperSyncProvider
- Split SyncProviderServiceInterface into SyncProviderBase + FileSyncProvider
- Remove dead file-op stubs from SuperSync provider
- Extract shared delete-and-reupload pattern into SnapshotUploadService
- Extract helper methods from operation-log-sync.service.ts
- Auto-invalidate WrappedProviderService cache on config changes
- Fix: preserve auth credentials in encryption toggle revert paths
- Fix: add config revert on disableEncryption failure

~720 lines net reduction across 26 files.
2026-03-04 19:01:20 +01:00
Johannes Millan
3ef23354e4 feat(sync): add mandatory encryption for SuperSync
Implement end-to-end encryption for SuperSync with the following:

- Make encryption mandatory for SuperSync provider (optional for others)
- Add encryption setup during initial SuperSync configuration
- Add password strength indicator and toggle visibility in dialogs
- Probe server for encrypted data before showing encryption dialog
- Handle encryption state preservation during file import
- Prevent duplicate password dialogs and cascade between clients
- Simplify encryption codebase by removing dead code and deduplicating
- Add password-strength UI component
- Default sync provider to SuperSync in initial setup dialog
- Remove legacy dialog components (incomplete-sync, incoherent-timestamps)
2026-03-01 21:27:08 +01:00
Johannes Millan
fd54a48d36 fix(sync): update encryption state observable after file import
Use providerManager.setProviderConfig() instead of direct
setPrivateCfg() in ImportEncryptionHandlerService so the
currentProviderPrivateCfg$ observable is updated. Without this,
the settings form still showed stale encryption buttons after
importing a backup with different encryption settings.
2026-02-26 16:28:56 +01:00
Johannes Millan
5f44e18aa2 fix(sync): show clear error when encryption unavailable on Android
WebCrypto (crypto.subtle) is unavailable in insecure contexts like
Android Capacitor (serves from http://localhost). Previously, encryption
operations would fail silently, making sync appear successful when data
wasn't actually syncing.

Changes:
- Add isCryptoSubtleAvailable() check function in encryption.ts
- Add assertCryptoSubtleAvailable() guard to encrypt/decrypt functions
- Add WebCryptoNotAvailableError handler in sync-wrapper.service.ts
- Check crypto availability BEFORE deleting server data in
  encryption-enable.service.ts and import-encryption-handler.service.ts
- Add WEB_CRYPTO_NOT_AVAILABLE translation key with clear error message

This ensures users see a clear error message instead of silent failure
when trying to use encryption on unsupported platforms.
2026-01-27 14:49:00 +01:00
Johannes Millan
0d2538fc53
Claude/analyze architecture improvements mh bpq (#6164)
* refactor(sync): extract shared snapshot upload logic into SnapshotUploadService

Extract duplicated snapshot upload mechanics from three services into
a shared SnapshotUploadService:
- EncryptionDisableService (126 → 84 lines)
- EncryptionEnableService (140 → 98 lines)
- ImportEncryptionHandlerService (263 → 236 lines)

The new service provides:
- getValidatedSuperSyncProvider(): Validates SuperSync provider is active
- gatherSnapshotData(): Gathers state, vector clock, client ID
- uploadSnapshot(): Low-level upload mechanics
- updateLastServerSeq(): Updates server sequence after upload

Config orchestration (timing, error recovery) remains in domain services.
This follows the reviewer's recommendation of "upload mechanics only"
extraction rather than over-abstracting the different behaviors.

* refactor(sync): extract shared dialog SCSS into _dialog-encryption-common.scss

Extract ~300 lines of duplicated SCSS across 4 encryption dialog
components into a shared mixin file.

Changes:
- Create _dialog-encryption-common.scss with reusable mixins:
  - dialog-warning-box: Orange warning alerts
  - dialog-info-box: Blue informational alerts
  - dialog-info-section: Section with heading and list
  - dialog-note: Inline note with icon
  - dialog-note-multiline: Multiline note with flex-start alignment
  - dialog-note-warn: Warning-colored note background
  - dialog-spinner-inline: Inline spinner display
  - dialog-encryption-common-classes: Convenience mixin for all common classes

- Refactor dialog components to use shared styles:
  - dialog-disable-encryption: 88 → 3 lines
  - dialog-enable-encryption: 88 → 3 lines
  - dialog-change-encryption-password: 92 → 48 lines (keeps unique styles)
  - dialog-import-encryption-warning: 136 → 73 lines (keeps unique styles)

Uses existing CSS variables (--color-warning, --c-dark-60, etc.) and
the darkTheme mixin for consistent theming.

* fix(sync): encrypt payload before upload in EncryptionEnableService

Fix pre-existing bug where EncryptionEnableService passed unencrypted
state to uploadSnapshot() while setting isPayloadEncrypted=true.

The bug was present since the service was created (commit e2db30c).
Other services like ImportEncryptionHandlerService and
OperationLogUploadService correctly encrypt before upload.

Changes:
- Inject OperationEncryptionService
- Call encryptPayload() before uploadSnapshot()
- Pass encrypted payload instead of raw state

* style: fix prettier formatting issues

* style: fix remaining prettier formatting issues

* style: fix prettier line break in spec file

* style: fix prettier formatting in spec file

* fix(sync): correct import paths for OperationSyncCapable, VectorClock, AppStateSnapshot

* test(sync): fix SnapshotUploadService unit test mocks

- Mock isReady as spy returning Promise<boolean> (not boolean property)
- Use supportsOperationSync property for isOperationSyncCapable check
- Remove non-existent save method from privateCfg mock
- Use regex matcher for error message assertion

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-25 12:08:35 +01:00
Johannes Millan
c062f0b224 fix(sync): prevent archive data loss in encryption enable/disable/import
Critical fix: Fixed 3 additional services that still used sync getStateSnapshot()
instead of async version, which would cause archive data loss during encryption
operations.

Files fixed:
1. encryption-disable.service.ts (line 63)
   - Disabling encryption would delete all archived tasks
2. encryption-enable.service.ts (line 68)
   - Enabling encryption would delete all archived tasks
3. import-encryption-handler.service.ts (line 159)
   - Changing encryption during import would delete all archived tasks

All three now use getStateSnapshotAsync() to properly load archives from
IndexedDB before creating SYNC_IMPORT operations.

Also fixed:
- Remove encryption button now properly disables during both operations
  (password change AND encryption removal) for consistent UX
2026-01-24 21:14:58 +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