Commit graph

865 commits

Author SHA1 Message Date
Chris Veleris
003844c7cd fix: add COOKIE_SECURE environment variable to control cookie security
Allows cookies to work over HTTP even in production mode by setting
COOKIE_SECURE=false. This is needed for local Docker deployments that
use HTTP instead of HTTPS.

When COOKIE_SECURE=false, the secure flag is disabled on cookies.
When COOKIE_SECURE is not set or set to any other value, it defaults
to the production mode behavior (secure cookies in production).
2026-04-13 13:22:09 +03:00
Chris Veleris
a3369d2c74 release: v1.1.0-dev.3 2026-04-13 13:08:48 +03:00
Chris Veleris
a89f2b72d9 fix: exempt public unauthenticated endpoints from CSRF protection
The lusca CSRF implementation was breaking login and registration because
the frontend doesn't fetch or send CSRF tokens. This is a structural issue
that requires frontend implementation.

As a pragmatic fix, this commit exempts public unauthenticated endpoints
from CSRF protection:
- /api/login, /api/register, /api/verify-email
- /api/version, /api/registration-status, /api/health
- /api/oidc/* (all OIDC authentication endpoints)
- /api/feature-flags

Authenticated endpoints still require CSRF tokens via lusca.

Also updates csrf.js to use lusca's token generation mechanism, making
it compatible with the global lusca CSRF middleware.

TODO: Implement proper CSRF token handling in the frontend for enhanced
security on public endpoints.
2026-04-13 13:05:33 +03:00
Chris Veleris
caffea977c release: v1.1.0-dev.2 2026-04-13 12:31:21 +03:00
Chris Veleris
94da7a4de9 fix: apply CSRF protection only to state-changing HTTP methods
The lusca CSRF middleware was being applied to all requests including
safe methods like GET, causing "CSRF token missing" errors for endpoints
like /api/registration-status.

CSRF protection should only apply to state-changing methods (POST, PUT,
PATCH, DELETE), not to safe methods (GET, HEAD, OPTIONS) which cannot
modify server state.

This fix adds a check to bypass CSRF validation for safe HTTP methods
while maintaining protection for all state-changing operations.
2026-04-13 12:30:16 +03:00
Chris Veleris
3f3e50f23e release: v1.1.0-dev.1 2026-04-13 12:18:32 +03:00
Chris
c2e9a1aa21
feat: Add OIDC/SSO authentication support (#1008)
* feat: add OIDC/SSO database schema and models (Phase 1)

Add database foundation for OpenID Connect authentication:

Database Migrations:
- Create oidc_identities table (links users to OIDC accounts)
- Create oidc_state_nonces table (OAuth state/nonce for CSRF protection)
- Create auth_audit_log table (security event logging)
- Make password_digest nullable in users table (allow OIDC-only users)

Models:
- OIDCIdentity: Links users to external OIDC providers
- OIDCStateNonce: Temporary OAuth state management
- AuthAuditLog: Authentication event audit trail

Changes:
- Updated User model to allow null password_digest
- Added model associations in models/index.js
- All migrations tested and verified

Related to #977

* feat: add OIDC core services (Phase 2)

- Install openid-client@^6.2.0 for OIDC protocol support
- Implement providerConfig.js for loading providers from .env
  - Support single provider or numbered providers (OIDC_PROVIDER_1_*, etc.)
  - Auto-provision and admin email domain configuration
  - Provider caching for performance
- Implement stateManager.js for OAuth state/nonce management
  - CSRF protection with 10-minute TTL
  - One-time use state consumption
  - Automatic cleanup of expired states
- Implement auditService.js for authentication event logging
  - Track login success/failure, logout, OIDC linking/unlinking
  - Store IP address, user agent, and metadata
  - Support for event queries and retention cleanup
- Add comprehensive unit tests (60 tests, all passing)
  - providerConfig: 36 tests for env parsing and validation
  - stateManager: 12 tests for state lifecycle and security
  - auditService: 12 tests for event logging and queries

Phase 2 completes the backend core services needed for OIDC authentication.

* feat: implement OIDC authentication flow (Phase 3)

Core OIDC Flow (service.js):
- Provider discovery with issuer caching
- Authorization URL generation with state/nonce
- OAuth callback handling and token exchange
- ID token validation using openid-client
- Token refresh functionality

JIT User Provisioning (provisioningService.js):
- Auto-create users from OIDC claims
- Link existing email accounts to OIDC identities
- Admin role assignment based on email domain rules
- Automatic username generation from email
- Transaction-safe identity creation

Identity Management (oidcIdentityService.js):
- List user's linked OIDC identities
- Link additional providers to existing accounts
- Unlink identities with safety checks
- Prevent unlinking last auth method
- Update identity claims on login

HTTP Layer (controller.js + routes.js):
- GET /api/oidc/providers - List configured providers
- GET /api/oidc/auth/:slug - Initiate OIDC flow
- GET /api/oidc/callback/:slug - Handle OAuth callback
- POST /api/oidc/link/:slug - Link provider to current user
- DELETE /api/oidc/unlink/:id - Unlink identity
- GET /api/oidc/identities - Get user's identities

Integration:
- Register OIDC routes in Express app (public + authenticated)
- Update auth service to reject password login for OIDC-only users
- Audit logging for all OIDC operations
- Session creation on successful authentication

Security:
- State/nonce CSRF protection
- One-time use state consumption
- Transaction-safe user provisioning
- Foreign key constraints enforced

* feat: implement OIDC frontend login flow (Phase 4)

- Created OIDCProviderButtons component for SSO login options
- Created OIDCCallback component for OAuth callback handling
- Updated Login page to fetch and display OIDC providers
- Added /auth/callback/:provider route to App.tsx
- Added i18n translations for OIDC UI elements
- Downgraded openid-client to v5.7.0 (CommonJS compatibility)
- Fixed linting issues in backend OIDC modules

Phase 4 completes the frontend login flow for OIDC/SSO authentication.
Users can now see configured SSO providers on the login page.

* feat: implement OIDC account linking UI (Phase 5)

Add Connected Accounts section to Profile Security tab allowing users to:
- View linked OIDC provider accounts
- Link new SSO providers to their account
- Unlink OIDC identities with validation
- Prevent unlinking last authentication method

Backend changes:
- Add has_password virtual field to User model
- Include has_password in profile API response
- Track whether user has password set for validation

Frontend changes:
- Create oidcService for OIDC API operations
- Create ConnectedAccounts component with link/unlink flows
- Add confirmation dialog before unlinking accounts
- Validate that users cannot unlink their last auth method
- Show warning if user has no password set
- Integrate Connected Accounts into SecurityTab

User experience:
- View all linked SSO provider accounts with email and link date
- Link additional providers via "Link Provider" buttons
- Unlink with two-step confirmation to prevent accidents
- Clear error messages when unlinking would leave no auth method
- Warning message suggesting password setup for OIDC-only users

Fixes #977

* feat: complete OIDC documentation and UI improvements (Phase 6)

This commit completes Phase 6 of the OIDC/SSO implementation with comprehensive
documentation, bug fixes, and UI reorganization.

Documentation:
- Add comprehensive user guide at docs/10-oidc-sso.md with:
  - Setup guides for 6 major providers (Google, Okta, Keycloak, Authentik, PocketID, Azure AD)
  - Configuration examples for single and multiple providers
  - User features documentation (login, account linking, management)
  - Advanced topics (auto-provisioning, admin role assignment, hybrid auth)
  - Comprehensive troubleshooting section
  - Security considerations and best practices
- Update README.md with OIDC/SSO section and quick setup examples

Internationalization:
- Add i18n support to OIDCProviderButtons component
- Add translation keys for all OIDC UI text
- Update English translations with "sign_in_with" key

Bug Fixes:
- Fix oidcService.ts to correctly unwrap API responses
  - Backend returns {providers: [...]} and {identities: [...]}
  - Frontend was expecting plain arrays, causing "map is not a function" error
- Fix initiateOIDCLink to properly handle POST response

UI Improvements:
- Move OIDC/SSO to dedicated tab in profile settings
  - Create new OIDCTab component with green LinkIcon
  - Remove ConnectedAccounts from SecurityTab
  - Add OIDC tab between Security and API Keys tabs
  - Update ProfileSettings with new tab configuration
- Security tab now focuses solely on password management

Testing:
- All linting passes
- All tests pass (82 suites, 1223 tests)

Related to #977

* feat: add OIDC/SSO translations for all 24 languages

Add i18n support for OIDC/SSO features across all supported languages:
- "Sign in with {{provider}}" button text
- "OIDC/SSO" tab label in profile settings
- OIDC authentication flow messages

Translations added for: Arabic, Bulgarian, Danish, German, Greek, Spanish,
Finnish, French, Indonesian, Italian, Japanese, Korean, Dutch, Norwegian,
Polish, Portuguese, Romanian, Russian, Slovenian, Swedish, Turkish,
Ukrainian, Vietnamese, and Chinese.

* fix: resolve 13 CodeQL security alerts

This commit addresses critical security vulnerabilities identified by CodeQL scanning:

**Security Configuration (2 fixes)**
- Fix insecure Helmet configuration - enable CSP and HSTS in production
- Fix clear text cookie transmission - enable secure cookies in production

**Path Injection (3 fixes)**
- Add path validation in users/controller.js to prevent arbitrary file deletion
- Add path validation in users/service.js for avatar operations
- Add path sanitization in attachment-utils.js deleteFileFromDisk function

**Cross-Site Scripting (1 fix)**
- Fix XSS vulnerability in GeneralTab.tsx avatar URL handling
- Add URL sanitization to prevent javascript: protocol attacks

**URL Security (2 fixes)**
- Fix double escaping in url/service.js HTML entity decoding
- Fix incomplete URL sanitization for YouTube domain validation

**Denial of Service (1 fix)**
- Add loop bound protection in inboxProcessingService.js (10k char limit)

**Rate Limiting (3 fixes)**
- Add rate limiting to auth routes (register, verify-email)
- Add rate limiting to task attachment upload/delete endpoints
- Add rate limiting to user avatar upload/delete endpoints

**GitHub Actions Security (1 fix)**
- Add explicit read-only permissions to CI workflow

Note: CSRF middleware (#10) requires frontend changes and is tracked separately.

Relates to PR #1008

* fix: allow test files in path validation for tests

* fix: format long condition in attachment-utils for Prettier compliance

Break the path validation condition across multiple lines to meet Prettier formatting requirements and fix CI linting failure.

* fix: resolve CodeQL security alerts

- Add rate limiting to OIDC authentication routes using authLimiter and authenticatedApiLimiter
- Implement CSRF protection middleware using csrf-sync (skips for API tokens and test environment)
- Add CSRF token endpoint at /api/csrf-token
- Fix incomplete URL scheme validation in GeneralTab to block all dangerous schemes (javascript:, data:, vbscript:, file:)

This addresses 5 high-severity CodeQL security vulnerabilities:
- Missing rate limiting on OIDC auth routes
- Missing CSRF middleware protection
- Incomplete URL sanitization in avatar handling

All 1223 tests passing.

* fix: implement CSRF protection with lusca for CodeQL compliance

Add CSRF protection using lusca.csrf (CodeQL's recommended library) to
protect session-based authentication while supporting hybrid auth patterns.

Implementation:
- Pre-check middleware marks exempt requests (test env, Bearer tokens)
- Lusca CSRF middleware applied with exemption flag check
- Session-based requests require valid x-csrf-token header
- Bearer token requests exempt (don't use cookies)
- Test environment exempt for test execution

This addresses CodeQL security alert js/missing-token-validation while
maintaining support for both cookie-based and token-based authentication.

Related: #977 (OIDC/SSO authentication feature)
2026-04-13 12:17:35 +03:00
dependabot[bot]
86f1bdcf1f
Bump nodemailer in the npm_and_yarn group across 1 directory (#1009)
Bumps the npm_and_yarn group with 1 update in the / directory: [nodemailer](https://github.com/nodemailer/nodemailer).


Updates `nodemailer` from 8.0.4 to 8.0.5
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v8.0.4...v8.0.5)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-version: 8.0.5
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-12 17:55:07 +03:00
dependabot[bot]
3956f2c1ca
Bump lodash in the npm_and_yarn group across 1 directory (#1010)
Bumps the npm_and_yarn group with 1 update in the / directory: [lodash](https://github.com/lodash/lodash).


Updates `lodash` from 4.17.23 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-12 17:54:53 +03:00
dependabot[bot]
0bd0d0e137
Bump @hono/node-server in the npm_and_yarn group across 1 directory (#1011)
Bumps the npm_and_yarn group with 1 update in the / directory: [@hono/node-server](https://github.com/honojs/node-server).


Updates `@hono/node-server` from 1.19.11 to 1.19.13
- [Release notes](https://github.com/honojs/node-server/releases)
- [Commits](https://github.com/honojs/node-server/compare/v1.19.11...v1.19.13)

---
updated-dependencies:
- dependency-name: "@hono/node-server"
  dependency-version: 1.19.13
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-12 17:54:41 +03:00
dependabot[bot]
1cb609e636
Bump hono in the npm_and_yarn group across 1 directory (#1012)
Bumps the npm_and_yarn group with 1 update in the / directory: [hono](https://github.com/honojs/hono).


Updates `hono` from 4.12.8 to 4.12.12
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.12.8...v4.12.12)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.12.12
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-12 17:54:25 +03:00
Oritro Ahmed
bce111ac91
fix: use correct InboxItem model name in MCP inbox tools (#986)
The Inbox model was imported as `Inbox` but the model is exported
from models/index as `InboxItem`, causing `add_to_inbox` and
`list_inbox` to throw "Cannot read properties of undefined (reading
'create'/'findAll')" at runtime.
2026-04-12 09:10:42 +03:00
Chris
e0769e4b25
fix: exclude cancelled tasks from Overdue and Due Today sections (#1007)
Fixes #997

- Add Task.STATUS.CANCELLED to exclusion list in fetchOverdueTasks()
- Add Task.STATUS.CANCELLED to exclusion list in fetchTasksDueToday()
- Cancelled tasks now properly filtered out from Today page sections
- All existing tests pass
2026-04-12 09:10:22 +03:00
Chris
50eb12d7ec
fix: add missing i18next dependency to package.json (#1006)
* fix: add missing i18next dependency to package.json

The i18next package was being imported throughout the frontend code but was not explicitly declared in devDependencies. It was only installed indirectly as a peer dependency of react-i18next, which can cause issues with strict package managers like pnpm.

This fix explicitly adds i18next ^24.2.3 to devDependencies to ensure it's always installed.

Fixes #987

* chore: update package-lock.json after adding i18next dependency
2026-04-12 09:01:52 +03:00
Chris
01a84c3598
Fix: Bi-weekly recurring task scheduling for multi-day patterns (#1005)
* fix: correct bi-weekly recurring task scheduling for multi-day patterns

Fixes #1004

Previously, when a recurring task was set to repeat every N weeks (where N > 1)
on multiple weekdays that span a week boundary (e.g., Saturday + Sunday), the
algorithm incorrectly calculated the next occurrence dates.

The issue was in the calculateWeeklyRecurrence function, which didn't properly
determine when to add the interval skip for multi-weekday patterns. It would:
- Correctly handle Sat -> Sun (adjacent days, same cycle)
- Incorrectly handle Sun -> Sat (should skip weeks, but didn't)

This fix improves the logic to:
1. Detect when the current day is the last weekday in the pattern cycle
2. Account for Sunday (day 0) wrapping around in the day-of-week numbering
3. Only add interval skips when truly moving to a new cycle, not when moving
   between weekdays within the same cycle

Test coverage added for:
- Bi-weekly Saturday + Sunday pattern (the reported bug)
- Starting from different days in the pattern
- Bi-weekly Tuesday + Thursday pattern
- Tri-weekly Friday + Saturday + Sunday pattern

* docs: update MEMORY.md with GitHub template requirements

- Add detailed PR template requirements and structure
- Expand bug report template requirements with all fields
- Update last modified date
2026-04-12 08:52:13 +03:00
Chris
e8c7eed226
fix: prevent subtasks from disappearing when updating parent task (#984)
This commit addresses a critical bug where subtasks would disappear when
updating the parent task (e.g., assigning tags). The issue had multiple
potential causes:

1. Backend vulnerability: The updateSubtasks() function would delete all
   subtasks if an empty array was sent, treating it as "delete everything
   not in this list"

2. Frontend state management: After reloading a task, subtasks weren't
   being preserved if the backend response didn't include them

3. Unclear error messages: "Invalid parent task" errors didn't provide
   enough context for debugging

Changes:
- Added defensive logging in updateSubtasks() to warn when all subtasks
  are being deleted with an empty array
- Enhanced validateParentTaskAccess() error messages to provide detailed
  diagnostics (task not found vs. permission issues vs. wrong user)
- Updated handleTagsUpdate() in TaskDetails to explicitly preserve
  subtasks when reloading task after tag updates

This fix is defensive in nature and adds better observability for
diagnosing similar issues in the future.

Fixes issue reported by user where subtasks disappeared after assigning
tags to parent task, and "Invalid parent task" errors occurred when
trying to update the orphaned subtasks.
2026-04-01 17:37:20 +03:00
Chris
11c3fe5e43
Fix: Resolve 20 security vulnerabilities in dependencies (#983)
* Fix critical bug causing subtasks to disappear when updating parent task

This fixes a serious bug where updating tags, priority, status, or due_date
on a parent task would inadvertently delete all its subtasks.

Root Cause:
1. Backend serializer returns `subtasks: []` when Subtasks association is not loaded
2. Frontend was spreading entire task object when updating, sending `subtasks: []`
3. Backend updateSubtasks() interpreted empty array as "delete all subtasks"

Solution:
Remove object spreading from updateTask calls. Only send the specific fields
being updated, not the entire task object.

Fixes the issue reported where assigning tags to a task caused subtasks to vanish.

Related: #TBD (GitHub issue to be created)

* fix: upgrade dependencies to resolve security vulnerabilities

- jest-environment-jsdom: 29.0.0 → 30.3.0
- nodemailer: 7.0.10 → 8.0.4
- sqlite3: 5.1.7 → 6.0.1

All npm audit vulnerabilities resolved (20 high/critical → 0).
Tests passing (1157 pass, 1 pre-existing failure unrelated to upgrades).
2026-03-28 23:37:23 +02:00
Chris Veleris
aeacf0a33a Update readme screenshots 2026-03-27 17:10:49 +02:00
Chris Veleris
a5cfa0c6cb release: v1.0.0 2026-03-27 16:43:33 +02:00
vincent
eeae4958b8
docs: clarify tag validation rules and Inbox hashtag syntax (#964)
- Add explicit note about # and @ being syntax markers, not part of tag names
- Clarify that #hashtag example shows invalid character in tag name
- Add cross-reference to Inbox page documentation
- Update workflow examples to clarify Inbox syntax vs actual tag names
- Bump document version to 1.0.1
2026-03-26 17:46:43 +02:00
Chris Veleris
0db21b9b7c release: v1.0.0-rc.3 2026-03-26 17:20:49 +02:00
Chris
65b9bbce39
Fix initial due date calculation for weekly tasks with multiple weekdays (#974) (#975)
Fix calculateInitialDueDate() to properly handle recurrence_weekdays array
when creating or updating weekly recurring tasks with multiple weekdays.

Previously, the function only checked for recurrence_weekday (singular) and
ignored recurrence_weekdays (plural array), causing tasks with multiple
weekdays to incorrectly get today's date instead of the next occurrence.

Changes:
- Add support for recurrence_weekdays array in calculateInitialDueDate()
- Fix buildUpdateAttributes() to pass recurrence_weekdays parameter
- Add 8 unit tests covering multiple weekdays scenarios
- Add 3 integration tests for API CREATE and UPDATE operations
- Maintain backward compatibility with single recurrence_weekday

The fix mirrors the proven logic from calculateWeeklyRecurrence() in
recurringTaskService.js and properly handles edge cases like unsorted
arrays, wrapping to next week, and JSON string parsing.

Fixes #974
2026-03-26 17:19:59 +02:00
Chris Veleris
8128180075 release: v1.0.0-rc.2 2026-03-24 18:16:06 +02:00
Chris Veleris
ebbc1d703e Add feature contribution requirements to CONTRIBUTING.md
Add a prominent section explaining that feature PRs require prior
discussion (GitHub issue, Discussions, or direct communication with
maintainer). This helps ensure alignment with project vision, avoids
wasted effort, enables collaborative design, and prevents duplicate work.

The section includes:
- Clear requirement statement with warning
- Explanation of why this is important
- Step-by-step guide on how to contribute features
- Clarification that this applies to features, not bug fixes

Also updated Before You Start, PR Requirements, and Questions sections
to reference and reinforce this requirement.
2026-03-24 17:57:22 +02:00
Chris
e80847b01f
Fix project name overflow and add 6-word validation limit (#972)
* Fix project name overflow and add validation

This commit addresses issue #971 by implementing both UI fixes and
validation to prevent excessively long project names.

Changes:
1. Add word-break and line-clamp to ProjectBanner.tsx to handle
   overflow gracefully with line-clamp-3 for names
2. Add frontend validation in ProjectModal.tsx limiting names to
   6 words maximum
3. Add backend validation in project.js model with custom wordCount
   validator
4. Show user-friendly error messages when validation fails

This ensures project names remain concise and UI-friendly while
preventing the extreme overflow cases that were possible before.

Fixes #971

* Add overflow-hidden to make line-clamp work properly

The line-clamp utility requires explicit overflow-hidden to function
correctly. Without it, the text continues to display in full rather
than being truncated with ellipsis.

* Fix line-clamp using inline CSS styles

Tailwind's line-clamp utilities weren't working, so switched to using
inline styles with the standard CSS approach:
- display: -webkit-box
- -webkit-line-clamp: 3
- -webkit-box-orient: vertical

This ensures the text truncation works reliably across browsers.

* Use Tailwind line-clamp utilities (already defined in CSS)

The project already has line-clamp-1/2/3 utilities properly defined
in tailwind.css with all the necessary webkit properties. Simplified
the component to use these existing utilities instead of inline styles.

* Add dedicated CSS classes with !important for line-clamp

Created custom project-name-clamp and project-desc-clamp classes
with !important flags to ensure they override any conflicting styles.
This should finally fix the text truncation issue.

* Use component-scoped styles for line-clamp

Adding inline style tag in the component to ensure the line-clamp
CSS is definitely loaded and applied. This bypasses any potential
issues with external CSS compilation or loading order.

* Change project name line-clamp from 3 to 2 lines

Limiting project name display to 2 lines with ellipsis for better
visual density and cleaner appearance.

* Increase line-height for project name in banner

Added line-height: 1.3 to project name for better readability
and visual spacing between lines.
2026-03-24 17:36:24 +02:00
Chris
402d5b05e1
Fix Today page task completion issues (#970)
* Fix Today page task completion issues

- Fix completed task border color staying as priority color
  - Add isInCompletedSection prop to TaskItem for explicit completed state
  - Tasks in completed section now always show green border regardless of priority
  - Fixes race condition where status wasn't updated during optimistic UI update

- Fix completed task reappearing after unmarking and page refresh
  - Add defensive check in backend to force clear completed_at when status is not DONE
  - Add development logging in tasksService for debugging completion toggle
  - Ensures database state is consistent even if handleCompletionStatus doesn't clear it

- Update TaskList and TasksToday components to pass isInCompletedSection prop
  - Explicitly marks tasks rendered in the completed section
  - Prevents border color flickering during state transitions

* Add comprehensive logging to debug completion issues

* Fix duplicate API requests causing completion state issues

- Separate state update logic from API call in handleTaskUpdate
- Create new updateTaskInState function for state-only updates
- Pass onTaskCompletionToggle to completed section to avoid duplicate calls
- This fixes the persistence issue where unmarked tasks came back after refresh
- Completion toggles now only make ONE API call instead of two

* Add debug logging to updateTaskInState
2026-03-24 16:45:31 +02:00
Chris
bdeb5bbc32
Fix Telegram notification spam by marking JSON field as changed (#969)
Fixes issue where Sequelize wasn't detecting changes to the
channel_sent_at JSON field, causing markChannelAsSent() to not
persist updates to the database.

This caused the same notification to be sent via Telegram every
15 minutes (on each scheduler run) because the rate limiting
timestamp was never saved.

The fix adds this.changed('channel_sent_at', true) before save()
to explicitly mark the field as modified, which is required for
Sequelize to detect changes to JSON fields.

Impact: Reduces duplicate Telegram notifications from every 15min
to at most once per 24 hours per task.
2026-03-24 14:30:31 +02:00
Chris
077addadde
Fix recurring task initial due date calculation to match recurrence pattern (#965)
* Fix recurring task initial due date calculation to match recurrence pattern

Resolves #963

When creating a recurring task without an explicit due date, the system
was incorrectly setting it to "today" regardless of the recurrence pattern.
This caused issues where:
- Monthly tasks set to recur on a specific day (e.g., 28th) would show the
  wrong due date (today/yesterday instead of the 28th)
- Tasks wouldn't appear correctly in the Upcoming view
- The base due_date didn't match the recurrence_month_day setting

Changes:
- Add calculateInitialDueDate() helper to compute correct first occurrence
- For monthly recurrence with specific day: calculate next occurrence of that day
- For weekly recurrence with specific weekday: calculate next occurrence of that weekday
- For other types (daily, etc.): maintain current behavior (use today)
- Apply same logic to both task creation and updates

Tests:
- Add comprehensive test suite (9 new tests) covering:
  - Monthly recurrence with future day in current month
  - Monthly recurrence with past day (should use next month)
  - Weekly recurrence with specific weekday
  - Daily recurrence (should still default to today)
  - Edge cases (31st of month, explicitly provided dates)
  - Task updates adding recurrence

All 54 recurring task tests pass.

* Fix UTC timezone bug in recurring task expansion and add comprehensive tests

- Fix expandRecurringTasks() to use setUTCHours instead of setHours
- Add 42 unit tests for recurringTaskService UTC consistency
- Add 24 DST transition tests (spring forward/fall back)
- Verify no occurrence skips or duplicates during DST
- Test month-end handling, leap years, and timezone boundaries
2026-03-23 18:24:54 +02:00
Chris
2b71938c30
Fix project update API to support clearing nullable fields (#961)
Resolves #960

The update_project endpoint now properly handles clearing nullable fields
(due_date_at, area_id, priority, image_url) when set to null or empty string.

Previously, sending an empty string would not clear the field. Now empty
strings are explicitly converted to null for database consistency.

Changes:
- Convert empty strings to null for nullable fields in project update
- Add tests for clearing due_date_at with null and empty string
- Apply same fix to area_id, priority, and image_url for consistency
2026-03-22 22:17:23 +02:00
Chris
22349fb46f
Fix visual overlap between subtasks icon and status dropdown (#958)
* Fix visual overlap between subtasks icon and status dropdown

Increased right padding from pr-44 to pr-48 in TaskHeader desktop view
to prevent the subtasks toggle button from overlapping with the expanded
status dropdown icon.

Fixes #957

* Improve fix for subtasks icon and status dropdown overlap

- Removed flex-1 from task name container to prevent unnecessary expansion
- Wrapped SubtasksToggleButton in flex-shrink-0 div to maintain its width
- Increased right padding from pr-48 to pr-56 for better spacing
- This prevents the subtasks icon from overlapping with the status dropdown

Fixes #957

* Fix subtasks icon placement to be adjacent to task name

Moved SubtasksToggleButton to be directly after the task name within
the same flex container, instead of in a separate container. This positions
the icon immediately next to the task name on the left, rather than being
pushed to the right where it overlaps with the status dropdown.

Fixes #957
2026-03-22 12:12:28 +02:00
Chris Veleris
35aa94ae9d release: v1.0.0-rc.1 2026-03-21 19:27:37 +02:00
Chris
2444e36f47
Fix date format inconsistency in Task detail screen (#956)
* Fix date format inconsistency in Task detail screen (#938)

Replace browser-dependent toLocaleDateString() with explicit country-based
date formatting to ensure consistent date formats based on user's timezone.

Problem:
- User with English language + Greek timezone saw MM/DD/YYYY format
- Expected DD/MM/YYYY format based on timezone/country
- Browser's Intl.DateTimeFormat had incomplete locale support for
  combined locales like "en-GR"

Solution:
- Add country-to-format mapping in dateUtils.ts (60+ countries)
- New formatDateByCountry() for dates (DD/MM/YYYY, MM/DD/YYYY, YYYY/MM/DD)
- New formatDateTimeByCountry() for datetimes with 24h time
- Update TaskDueDateCard and TaskDeferUntilCard to use new functions
- Uses date-fns for consistent cross-browser formatting

Testing:
- Added 40 comprehensive test cases covering all format types
- Verified with Greece (DD/MM), US (MM/DD), Japan (YYYY/MM/DD)
- All tests passing

Fixes #938

* chore: remove unused import in dateUtils.ts
2026-03-21 18:47:33 +02:00
Chris Veleris
84d30b5230 release: v1.0.0-dev.3 2026-03-20 16:56:15 +02:00
Chris
b63f684190
feat: Add MCP Integration with client-agnostic instructions (#953) 2026-03-20 16:55:49 +02:00
Chris Veleris
2d2bc15750 release: v1.0.0-dev.2 2026-03-19 20:26:41 +02:00
Chris
11cd77bedd
Fix Telegram notification spam with channel-level rate limiting (#951)
* Fix Telegram notification spam with channel-level rate limiting

Addresses issue #950 where Telegram notifications were sent excessively
(96-288 messages per day per task) due to the delete-and-recreate pattern
added in commit 105a913a to fix navbar notification pile-up.

Changes:
- Add channel_sent_at JSON field to notifications table to track when
  each channel (telegram, email, push) was last sent
- Add helper methods to notification model:
  - markChannelAsSent(channel): Records send timestamp
  - wasChannelRecentlySent(channel, threshold): Checks if sent within 24h
- Modify sendTelegramNotification() to check rate limit before sending
- Update service layer (dueTaskService, deferredTaskService,
  dueProjectService) to preserve channel_sent_at when recreating
  notifications
- Add comprehensive unit and integration tests (20 tests, all passing)

Impact:
- Reduces Telegram notifications from 96-288/day to 1/day per item
- Preserves in-app notification refresh behavior (every 5-15 min)
- Maintains navbar pile-up fix from original commit
- Rate limit configurable (default: 24 hours)

Fixes #950

* Fix linting and formatting issues

* Fix integration test that was trying to access private function

* Fix prettier formatting in integration test
2026-03-19 20:26:11 +02:00
Chris Veleris
471d29e495 release: v1.0.0-dev.1 2026-03-18 18:01:32 +02:00
debode
024ef0a522
fix: use getAssetPath() for logo images in Navbar and Login (#946)
Both Navbar.tsx and Login.tsx hardcode absolute paths for the logo
images ('/wide-logo-light.png', '/wide-logo-dark.png'). This breaks
when tududi is served behind a reverse proxy with a subpath, such as
Home Assistant ingress.

The getAssetPath() helper from config/paths already handles base path
detection (including HA ingress auto-detection), and Login.tsx already
uses it for the login graphic. This commit applies the same pattern to
the logo images.

Changes:
- Navbar.tsx: add getAssetPath to import, use it for logo src
- Login.tsx: use getAssetPath for logo src (was already imported)
2026-03-18 15:26:25 +02:00
Chris Veleris
e3a272afef Add db layout 2026-03-15 09:07:21 +02:00
Chris Veleris
a0b769164c Add LLM docs 2026-03-15 09:01:38 +02:00
Chris
105a913a8d
Fix notification deduplication to prevent pile-up in navbar (#945)
* Fix notification deduplication to prevent pile-up in navbar (#944)

When tasks/projects remained pending for multiple days, duplicate
notifications accumulated in the navbar instead of showing only the
most recent one.

Updated notification services to properly handle existing notifications:
- Delete unread notifications before creating new ones
- Respect dismissed notifications (don't recreate)
- Respect read notifications (don't duplicate)

Changes:
- Updated dueTaskService, deferredTaskService, and dueProjectService
- Added comprehensive unit tests with 8 test cases

Fixes #944

* Fix lint errors and add GitHub bug template reminder to docs
2026-03-14 19:45:24 +02:00
Chris
1d5de49b48
Add URL detection to inbox processing service (#942)
* fixup! Fix date format inconsistency in Defer Until field

* fixup! fixup! Fix date format inconsistency in Defer Until field

* fixup! fixup! fixup! Fix date format inconsistency in Defer Until field
2026-03-14 08:43:41 +02:00
Chris
f1ea7843a7
Fix date format inconsistency in Defer Until field (#941)
Fixes #938

The Defer Until field was using i18n.language directly instead of
resolveUserLocale(), causing it to display dates in MM/DD/YYYY format
(US default) regardless of timezone setting.

Now uses the same locale resolution pattern as the Due Date field,
which combines language with timezone-derived country code (e.g.,
"en" + Greek timezone → "en-GR" → DD/MM/YYYY format).

Changes:
- Import useMemo and resolveUserLocale
- Add displayLocale memo to resolve timezone-aware locale
- Use displayLocale instead of i18n.language in toLocaleString()
2026-03-14 08:04:00 +02:00
Chris
3486541272
Add comprehensive LLM development documentation (#939)
* Increase coverage

* Add comprehensive LLM development documentation

- Add CLAUDE.md as main documentation index
- Create 8 detailed documentation files in docs/:
  - architecture.md: Tech stack, data models, auth system
  - directory-structure.md: Complete file tree with paths
  - backend-patterns.md: Module architecture and patterns
  - database.md: Models, migrations, and workflows
  - development-workflow.md: Setup and daily development
  - code-conventions.md: Style guide and best practices
  - testing.md: Test organization and patterns
  - common-tasks.md: How-to guides for frequent tasks
- Update .gitignore to allow project-level CLAUDE.md
- 4,285 lines of comprehensive documentation
- Organized for easy navigation with cross-links
- LLM-optimized with absolute paths and code examples

* fixup! Add comprehensive LLM development documentation
2026-03-14 02:54:59 +02:00
Chris Veleris
25b11086e2 release: v0.89.0 2026-03-12 11:32:05 +02:00
Chris Veleris
6c6f2851f1 release: v0.89.0-rc.3 2026-03-10 18:44:43 +02:00
Chris
23131d686a
Fix sidebar toggle causing unnecessary task list reload (#888) (#937)
Remove isSidebarOpen from the useEffect dependency array and from the
API request parameters in Tasks.tsx. The sidebar visibility state has no
bearing on which tasks should be fetched, so toggling it should not
trigger a data re-fetch.
2026-03-10 18:37:07 +02:00
Chris
1a1b70b5e7
Fix subtask completion not persisting to backend (#920) (#936)
The toggle handler required onSubtaskUpdate callback to make the API
call, but TaskSubtasksCard never provided it. Extract a shared handler
that calls toggleTaskCompletion for persisted subtasks regardless of
whether onSubtaskUpdate is provided, falling back to updating local
state via onSubtasksChange.
2026-03-10 17:30:45 +02:00
Chris
c33682bac0
Fix subtask completion not persisting when onSubtaskUpdate is not provided (#920) (#932)
When toggling subtask completion in TaskSubtasksSection, the toggle handler
required onSubtaskUpdate callback to make the API call. Components like
TaskSubtasksCard don't pass this callback, so toggling fell through to the
local-only state update path, never sending a network request.

Add an intermediate code path: when the subtask is persisted but
onSubtaskUpdate is not provided, call toggleTaskCompletion() directly
to persist the change, then update local state.

Fixes #920
2026-03-10 17:23:29 +02:00
Chris
a01dd9f41c
Fix subtask icons invisible on Today page (#839) (#935)
The SubtasksToggleButton was placed inside the same flex container as
the task name with truncate, causing it to be squeezed to zero width.
Wrap the task name in an inner flex container (matching the upcoming
view layout) so the button stays visible.
2026-03-10 17:15:06 +02:00