Commit Graph

813 Commits

Author SHA1 Message Date
Ulas Kalayci 9c4f876c5b Merge branch 'feature/cardav-contacts'
# Conflicts:
#	package-lock.json
2026-05-04 19:10:13 +02:00
Ulas Kalayci fe0254e225 docs: update PROGRESS.md - all 15 tasks completed
Tasks 14-15 documentation:
- Task 14: OpenAPI routes documented (d0eb638)
- Task 15: CardDAV router mounted (8891097)

All 109 tests passing across 20 suites.
Implementation ready for final review and release.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 18:39:19 +02:00
Ulas Kalayci 8891097c7b feat(server): mount CardDAV router at /api/v1/contacts/cardav
Import and mount cardavRouter with requireAuth middleware. All
CardDAV management routes now accessible under /api/v1/contacts/cardav.

Router mounted BEFORE contacts router to ensure /api/v1/contacts/cardav
paths match before /api/v1/contacts.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 18:37:56 +02:00
Ulas Kalayci d0eb63840f docs(openapi): add CardDAV routes and update contacts routes
Document 8 CardDAV management routes (accounts, addressbooks, sync)
and update 3 existing contacts routes to reflect multi-value field
support.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 18:37:05 +02:00
ulsklyc 8f87b295e9 Update FUNDING.yml 2026-05-04 18:33:28 +02:00
Ulas Kalayci 0dc303b81a feat(contacts): add multi-value fields support to PUT /contacts/:id
Extend PUT /contacts/:id route to support updating phones, emails, and
addresses arrays with replacement semantics. Uses atomic transactions
to DELETE all existing multi-values then INSERT new ones. Validates
all multi-value fields before updating. Response includes full contact
with multi-value fields via loadMultiValueFields() helper.

Backward compatible: multi-value fields only replaced when present in
request body. Scalar fields (name, category, etc.) continue to work
independently.

Tests added:
- Update with multi-value fields (replacement semantics verified)
- Validation error on invalid phone data (400)
- Backward compatibility: update without multi-values preserves them

All 109 tests pass.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 18:31:01 +02:00
ulsklyc 3a8a7dd3b0 Update Buy Me a Coffee username in FUNDING.yml 2026-05-04 18:29:58 +02:00
Ulas Kalayci 9e346dca5f docs: update PROGRESS.md for Task 12 completion 2026-05-04 18:25:30 +02:00
Ulas Kalayci 966a6d46e3 feat(contacts): add POST /contacts with multi-value fields
Implements Task 12: Extend POST /contacts to accept and persist phones,
emails, and addresses arrays. Uses atomic transactions to ensure all
related records are created together or rolled back on error.

- Validation: validatePhones/Emails/Addresses before insert
- Transaction: db.transaction() for atomic Contact + Multi-Values
- Backward compatible: Multi-value fields are optional
- Refactoring: Extracted loadMultiValueFields() helper (DRY)
- Response includes all multi-value fields with generated IDs

Tests: 3 new tests (create with multi-values, validation, backward compat)
TDD workflow: RED → GREEN → REFACTOR → Commit

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 18:25:18 +02:00
Ulas Kalayci 859a205299 docs: update PROGRESS.md for Task 11 completion 2026-05-04 18:18:38 +02:00
Ulas Kalayci fe8af33568 feat(contacts): add GET /contacts/:id with multi-value fields
Implements Task 11: Extend GET /contacts/:id to include phones, emails,
and addresses arrays. Each multi-value field is queried from its respective
table (contact_phones, contact_emails, contact_addresses) and mapped to
camelCase response format with isPrimary boolean conversion.

Tests: 2 new tests (contact with multi-values, empty arrays)
TDD workflow: RED → GREEN → Commit

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 18:18:07 +02:00
Ulas Kalayci 6e02c9e5b6 docs: session 2 handoff - Tasks 9-10 complete, ready for Task 11
Session 2 Summary:
- Task 9: PUT /addressbooks/:id (toggle enabled/disabled)
- Task 10: POST /accounts/:id/sync (with _mockSyncAccount)
- 101 tests passing (16 suites)
- Ready for Task 11: GET /contacts/:id with multi-value fields

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:57:41 +02:00
Ulas Kalayci 112cf55e66 docs: update PROGRESS.md for completed Task 10
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:54:45 +02:00
Ulas Kalayci 674fe796b3 feat(cardav): implement POST /accounts/:id/sync endpoint
Adds route to sync all enabled addressbooks for an account with mock support for tests.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:54:03 +02:00
Ulas Kalayci f895776911 docs: update PROGRESS.md for completed Task 9
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:47:55 +02:00
Ulas Kalayci 9ec7fda6b0 feat(cardav): implement PUT /addressbooks/:id endpoint
Adds route to toggle addressbook enabled/disabled state with bool validation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:47:26 +02:00
Ulas Kalayci 749e6ac79b docs: update PROGRESS.md for Tasks 6-8 completion (session pause) 2026-05-04 17:36:57 +02:00
Ulas Kalayci 362f711290 feat(validate): add bool validator
Add boolean field validator for use in CardDAV addressbook toggle
route and other boolean validation needs.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:13:54 +02:00
Ulas Kalayci c078a48884 feat(cardav): implement POST /accounts/:id/addressbooks/refresh endpoint
Add addressbook refresh route. Loads account from DB, delegates to
CardDAVSync.discoverAddressbooks() to run PROPFIND, then returns
updated addressbook list from DB.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:13:14 +02:00
Ulas Kalayci 12e8edf4c3 feat(cardav): implement GET /accounts/:id/addressbooks endpoint
Add addressbook listing route. Queries carddav_addressbook_selection
table directly, ordered by name. Returns empty array if account has
no addressbooks.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:11:53 +02:00
Ulas Kalayci 29646960fe docs: update PROGRESS.md for completed Task 5 2026-05-04 17:10:57 +02:00
Ulas Kalayci dd5ac8812c feat(cardav): implement POST /accounts/:id/test endpoint
Add connection test route. Loads account from DB and delegates to
CardDAVSync.testConnection() to verify credentials and discover
addressbooks without saving.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:10:25 +02:00
Ulas Kalayci 38fa84c3d4 docs: update PROGRESS.md for completed Task 4 2026-05-04 17:05:14 +02:00
Ulas Kalayci ca92cb2a86 feat(cardav): implement DELETE /accounts/:id endpoint
Add account deletion route with ID validation. Delegates to
CardDAVSync.deleteAccount() which cascades to addressbooks and
contacts via foreign key constraints.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:04:39 +02:00
Ulas Kalayci 39f3db99f7 docs: update progress after completing tasks 1-3
Tasks completed:
- Task 1: Multi-value validators (validatePhones, validateEmails, validateAddresses)
- Task 2: CardDAV router setup (GET /accounts)
- Task 3: POST /accounts endpoint

Test infrastructure established:
- _setTestDatabase/_resetTestDatabase for DB mocking
- _mockTestConnection for CardDAV API mocking
- 91 tests passing

Next: Task 4 (DELETE /accounts/:id)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:01:35 +02:00
Ulas Kalayci f7eb73b835 feat(cardav): implement POST /accounts endpoint
Add account creation route with validation. Delegates to
CardDAVSync.addAccount() which creates account and discovers
addressbooks. Returns 201 with account + addressbooks array.

Add _mockTestConnection() helper for testing CardDAV routes without
real server connections.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 16:54:52 +02:00
Ulas Kalayci 930800eed9 fix(cardav): improve router security and test coverage
- Remove error message leakage (return generic 'Interner Fehler')
- Remove unused imports (str, collectErrors, MAX_TITLE, MAX_URL)
- Add _resetTestDatabase() for proper test cleanup
- Add test for populated accounts case with shape validation
- Import 'after' from node:test for proper teardown

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 16:51:21 +02:00
Ulas Kalayci cf68bff25f feat(cardav): create cardav router with GET /accounts
Add CardDAV API router with GET /accounts endpoint. Returns all
CardDAV accounts from database via cardav-sync service. Added test
infrastructure with _setTestDatabase helper for isolated API testing.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 16:47:24 +02:00
Ulas Kalayci db5f95059f chore: release v0.44.1
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 16:39:16 +02:00
Copilot 21c85fd418 fix: remap apple external_source during CalDAV migration
Fixes #119

Moves apple→caldav conversion into table rebuild to avoid CHECK constraint violation during migration to v0.44.0.
2026-05-04 16:38:07 +02:00
Ulas Kalayci a71547562e feat(contacts): add multi-value array validators
Add validatePhones, validateEmails, validateAddresses for CardDAV
multi-value contact fields. Validates array structure, required
fields, type checks, and max lengths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 13:01:22 +02:00
Ulas Kalayci 8f78ed6fa2 fix: Isolate Contact Merge Logic tests via suite-level before hook
All 4 previously interdependent tests now use shared aliceContact from
before() hook. 54/54 tests passing with full isolation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:32:15 +02:00
Ulas Kalayci 8b8ac083a5 docs: Clarify REPLACEMENT semantics for PUT /contacts/:id multi-values
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:29:29 +02:00
Ulas Kalayci bb961a417c docs: Add CardDAV API Routes implementation design
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:28:17 +02:00
Ulas Kalayci 7bdf88f94c docs: add progress tracker for CardDAV implementation
Task #1 completed, Task #2 needs 4 test interdependency fixes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:08:38 +02:00
Ulas Kalayci a38c2c84fd Fix test interdependencies and remove duplicate test suite in test-carddav.js
- Make all tests independent by creating their own test data
- Remove duplicate Account Management Operations suite (lines 744-801)
- Each test now creates its own accounts instead of relying on previous tests
- Fix unique constraint violations by using distinct account URLs
- All 54 tests now pass independently

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:02:42 +02:00
Ulas Kalayci 96b4f43aff test: add comprehensive account and addressbook management tests
Add test coverage for CardDAV account management, addressbook discovery UPSERT logic, and contact merge scenarios. Tests verify plain-text password storage, duplicate prevention, CASCADE SET NULL on account deletion, addressbook toggle functionality, and NULL-only field updates during sync.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 11:53:01 +02:00
Ulas Kalayci c4b8b76221 Fix critical database issues in CardDAV sync service
Issue #1: Wrapped DELETE + INSERT operations in updateContactMultiValues in transaction to prevent inconsistent state if INSERT fails after DELETE.

Issue #2: Replaced N+1 query pattern with batch INSERT statements using VALUES list for phones, emails, and addresses. Also optimized primary entry check queries to use SELECT 1 instead of COUNT(*).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 11:45:07 +02:00
Ulas Kalayci 689b479b2d Implement CardDAV sync service with account and contact management
- Add server/services/cardav-sync.js with full CardDAV functionality
- Implement account management (add, delete, list, test connection)
- Implement addressbook discovery and selection toggle
- Add vCard parser with support for all standard fields (FN, N, TEL, EMAIL, ADR, ORG, TITLE, URL, BDAY, PHOTO, NICKNAME, NOTE, CATEGORIES)
- Implement smart merge logic for contact deduplication (UID match, email/phone match)
- Handle multi-value fields (phones, emails, addresses) with primary flag preservation
- Add comprehensive tests for vCard parsing and database operations
- All 46 tests passing

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 11:34:25 +02:00
Ulas Kalayci 3f77fdb11d Fix Migration 30 code quality issues
C1 (CRITICAL): Refactor test-carddav.js to import migrations
- Import MIGRATIONS from server/db.js instead of duplicating SQL
- Export MIGRATIONS array from server/db.js
- Use better-sqlite3 to apply Migration 30 dynamically
- Ensures future changes to Migration 30 are reflected in tests

I1 (IMPORTANT): Add UNIQUE index on carddav_uid
- Create idx_contacts_carddav_uid_unique partial index
- Prevents duplicate CardDAV contacts per account+addressbook
- WHERE clause excludes NULL values (manual contacts allowed)
- Add test to verify unique constraint enforcement

I3 (IMPORTANT): Rename cardav → carddav (RFC 6352 compliance)
- Table: cardav_accounts → carddav_accounts
- Table: cardav_addressbook_selection → carddav_addressbook_selection
- Column: contacts.cardav_* → contacts.carddav_*
- Index: idx_cardav_* → idx_carddav_*
- Test file: test-cardav.js → test-carddav.js
- Package.json: test:cardav → test:carddav
- All test assertions updated

All 25 tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 10:55:30 +02:00
Ulas Kalayci 18310dbfe5 feat(cardav): add Migration 30 for CardDAV contacts schema
Add comprehensive database schema for CardDAV multi-account contacts sync:

New tables:
- cardav_accounts: Store CardDAV server credentials
- cardav_addressbook_selection: Per-account addressbook enable/disable
- contact_phones: Multiple phone numbers per contact with labels
- contact_emails: Multiple email addresses per contact with labels
- contact_addresses: Multiple postal addresses per contact with labels

Extended contacts table with 9 new columns:
- organization, job_title, birthday, website, photo, nickname
- cardav_account_id (FK to cardav_accounts, ON DELETE SET NULL)
- cardav_uid (vCard UID from server)
- cardav_addressbook_url (source addressbook URL)

Features:
- UNIQUE constraints on (cardav_url, username) and (account_id, addressbook_url)
- CASCADE delete for addressbook selection and contact sub-tables
- Performance indices for cardav_uid and email lookups
- Support for manual contacts (NULL cardav_account_id)
- is_primary flag for phone/email/address selection

Test coverage:
- 23 comprehensive tests verify all tables, constraints, indices
- FK CASCADE delete behavior validated
- UNIQUE constraints enforced
- Data integrity scenarios tested

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 10:47:16 +02:00
Ulas Kalayci 6cc72676c6 docs: add CardDAV contacts sync design spec
- Multi-account CardDAV support with addressbook selection
- Inbound-only sync (CardDAV → Oikos)
- Smart merge by email/phone, preserve manual changes
- Extended contacts schema: organization, job_title, birthday, website, photo, nickname
- Separate tables for multiple phones/emails/addresses
- UI integration in Settings → Calendar tab
- Issue #10

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 10:33:01 +02:00
Ulas Kalayci dc0352545a chore: release v0.44.0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 08:53:01 +02:00
Ulas Kalayci 4b4d9a1ffd feat: generic CalDAV multi-account sync (#90)
Replaces single Apple CalDAV with generic multi-account CalDAV integration.

Features:
- Multiple CalDAV accounts (iCloud, Nextcloud, Radicale, Baikal)
- Per-account calendar selection via checkboxes
- Bidirectional sync (CalDAV ↔ Oikos)
- Optional outbound target selection per event
- Migration of existing Apple CalDAV data

Technical:
- New tables: caldav_accounts, caldav_calendar_selection
- New service: server/services/caldav-sync.js
- New API routes: /calendar/caldav/*
- Enhanced UI in Settings and Calendar event modal
- 7 new tests, all passing

Closes #90

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 08:52:36 +02:00
Ulas Kalayci 2a4a6b945b docs: update documentation for CalDAV multi-account feature
- README.md: Updated Calendar feature description to mention multi-account CalDAV support
- docs/SPEC.md: Added caldav_accounts and caldav_calendar_selection table schemas, updated external_source enum, documented target columns
- public/locales/en.json: Added missing CalDAV i18n keys (calendarEnabled, calendarDisabled, calendarsRefreshed, deleteAccountConfirm, lastSync)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 08:51:38 +02:00
Ulas Kalayci 4bca84a609 test(caldav): add test suite for CalDAV multi-account sync
Add comprehensive test coverage for CalDAV multi-account functionality:
- Schema validation for caldav_accounts and caldav_calendar_selection tables
- UNIQUE constraint enforcement on account credentials
- Foreign key CASCADE delete behavior
- Calendar selection enable/disable filtering
- Target calendar columns in calendar_events table

All 7 tests pass using Node.js in-memory SQLite.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 08:44:06 +02:00
Ulas Kalayci 3c9b2840e7 feat(caldav): add Settings UI and Event Modal CalDAV target selection
- Add CalDAV accounts card to Settings page with:
  * List of configured accounts showing URL and last sync
  * Expandable calendar list with enable/disable checkboxes
  * Sync Now, Refresh Calendars, and Delete actions per account
  * Add Account modal with name, URL, username, password fields
- Add CalDAV target selector to event modal:
  * Dropdown showing local and all enabled CalDAV calendars
  * Grouped by account using optgroups
  * Pre-selects current target when editing events
  * Includes target_caldav_account_id and target_caldav_calendar_url in save
- Add CalDAV component styles to settings.css:
  * Account cards with header, meta, and action sections
  * Expandable calendar details with checkboxes and color dots
  * Empty state for no accounts
- Add missing i18n keys for calendar enable/disable, refresh, delete confirm
- Load CalDAV targets async when modal opens
- Admin-only access to account management

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 08:41:02 +02:00
Ulas Kalayci e149f5c01e feat(caldav): add German and English i18n keys 2026-05-04 08:34:31 +02:00
Ulas Kalayci c5a9799983 feat(caldav): add calendar selection, sync, and API routes
- Add getCalendars() and updateCalendarSelection() to caldav-sync.js
- Add sync() function for bidirectional CalDAV synchronization
- Add getStatus() to report on all accounts and enabled calendars
- Add 8 new API routes to calendar.js for CalDAV account and calendar management
- All routes require admin role for security

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 08:32:46 +02:00
Ulas Kalayci 01ccf715e5 feat(caldav): add account management functions (add, list, update, delete) 2026-05-04 08:20:17 +02:00