Commit Graph
2646 Commits
Author SHA1 Message Date
kolaente 36cdc2ce2b fix(kanban): authorize body task_id when moving a task into a bucket (GHSA-5pg6-m483-7vrg) 2026-07-19 18:59:34 +02:00
kolaenteandkolaente a980e99c9e test(tasks): pin subtask root placement for fuzzy search and multi-parent scopes
The fuzzy mirror test is ParadeDB-only and fails without the previous
commit (child returned as a second root). The multi-parent test pins the
NOT EXISTS semantics from #3197: one in-scope and one out-of-scope
parent must still nest the child.

Part of #2954
2026-07-19 14:16:07 +00:00
kolaenteandkolaente 723ef2dce9 fix(search): mirror ParadeDB BM25 matching into the subtask parent search condition
The parent root-membership mirror always used the ILIKE fallback, so on
ParadeDB a parent matching the search only via fuzzy/token matching kept
its child as a duplicate root, and a substring-only ILIKE match could
hide a child under a parent absent from the results. The assumed
limitation no longer holds: pg_search plans ||| against the aliased
parent_tasks inside the correlated NOT EXISTS as a regular BM25 scan
(verified on 0.24.1), and score-free predicates never triggered the
"unsupported query shape" error on older versions either.

Part of #2954
2026-07-19 14:16:07 +00:00
maximilizeandGitHub 4f86f21316 fix(filters): resolve relative and absolute date filters in UTC (#3181) 🤖🤖🤖 (#3229) 2026-07-19 13:31:21 +02:00
Evan BakerandGitHub d0dad0437f fix(user): stop double-encoding FrontendSettings on UpdateUser (#2793) 2026-07-19 12:13:35 +02:00
TinkandGitHub 0dd27ed2d0 fix(tasks): make subtask root condition NULL-safe and search-aware (#3197) 2026-07-19 10:59:28 +02:00
kolaenteandkolaente f76e405b6c fix: disable echo group 404 auto-registration to keep pre-5.3.0 routing behavior
echo v5.3.0 reverted to v4 behavior where every group with middleware
implicitly registers a 404 route at its prefix. Our route setup creates
multiple groups with the same prefix (rate-limit subgroups of /api/v1),
which now panics with a duplicate-route error at startup.
2026-07-18 17:24:51 +00:00
Pierre Christenandkolaente 44ed50d508 docs: fix @Success 200 -> 201 on 5 PUT/Create endpoints
Five PUT-routed "create" endpoints document @Success 200, but they all
dispatch through the shared CreateWeb handler (pkg/web/handler/create.go),
which always responds 201 Created. Every other CreateWeb-routed endpoint
(labels, teams, filters, task relations/comments/assignees/duplicate,
task-labels) already correctly documents 201 - these five are stragglers
with the same stale annotation.

Affected endpoints:
- PUT /tokens
- PUT /projects/{id}/views/{view}/buckets
- PUT /projects/{id}/webhooks
- PUT /{kind}/{id}/reactions
- PUT /projects/{project}/views

Doc-only change, no behavior change. Regenerated pkg/swagger/* via
`swag init -g ./pkg/routes/routes.go --parseDependency -d . -o ./pkg/swagger`
(the command mage generate:swagger-docs runs) so the generated docs match
the annotations.
2026-07-18 17:05:32 +00:00
TowyTowyandGitHub ffcd470adf fix(caldav): parse ISO 8601 week durations in reminder triggers (#3183) 2026-07-18 18:55:17 +02:00
Frederick [Bot] 2b7d31bfbb [skip ci] Updated swagger docs 2026-07-18 15:00:26 +00:00
kolaenteandkolaente c34972a6ba test: assert I18nParams keys against en.json translation placeholders 2026-07-18 13:41:15 +00:00
kolaenteandkolaente f388015dda fix: interpolate dynamic values into translated error toasts
Parametrised error translations (invalid api token permission, invalid
timezone, invalid claim data) rendered their placeholders as empty text
because the backend only baked the values into the English message and
the frontend translated the error code without any interpolation params.

Carry the values as i18n_params on error responses, keyed by the
placeholder names of the frontend's error translations, on both v1 and
the v2 problem+json body, and pass them through when translating the
toast. A 14002 toast now reads e.g. "The permission time_entries of
group tasks is invalid." instead of "The permission of group is
invalid."
2026-07-18 13:41:15 +00:00
TinkandGitHub 6b9a879574 fix(notifications): render Markdown in plain-text emails (#3219) 2026-07-18 14:39:32 +02:00
TinkandGitHub e2c09d593c fix: hide license-gated routes from api token scope list (#3216) 2026-07-18 10:00:37 +02:00
kolaenteandGitHub 9237c6d3f9 chore(deps): replace vendored humaecho5 with upstream humaecho adapter (#3205) 2026-07-17 22:35:53 +02:00
kolaente dd82549f17 chore(config): disable icon changes by default
We have recieved feedback from enterprise users who found this to be very unprofessional not not worthy of enterprise software. Therefore, we made the difficult decision to disable icon changes by default. We're looking into adding more clues that transport the same message but are less obvious.
2026-07-15 12:19:41 +02:00
3af3c04f03 fix(filter): accept non-zero-padded dates in task filters
The manual date fallback in parseTimeFromUserInput parses dates like
2022-11-1, but `year, err :=` shadowed the named err return. On a
successful manual parse the function returned a valid time alongside the
stale error from the earlier failed layout attempts, so filters such as
`due_date = 2022-11-1` were rejected as invalid. Assign to the named
return so success clears the error.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-13 07:50:32 +00:00
TinkandGitHub 4425e0d146 fix(attachments): keep blob mime type so pdf previews open inline (#3157) 2026-07-11 18:53:09 +02:00
TinkandGitHub b396bad3bb fix(postgres): resolve all statements against the configured schema (#3156) 2026-07-11 18:02:57 +02:00
TowyTowyandGitHub efdc675fa7 fix(caldav): compute VTODO DURATION components correctly (#3155) 2026-07-11 17:28:59 +02:00
kolaenteandGitHub 85231284ab feat(auth): add OpenID provider availability monitoring and retry logic (#3145) 2026-07-10 17:52:01 +02:00
TinkandGitHub 190ff249ab fix(notifications): queue mails only after the notification row is committed (#3150) 2026-07-10 17:50:55 +02:00
kolaenteandkolaente 0b70fb2658 fix(cli): drain the mail queue before user commands exit
Mail sending is asynchronous: SendMail only enqueues the message and a
background daemon goroutine performs the actual SMTP delivery. CLI
commands exited as soon as their run function returned, killing the
daemon before the handshake completed, so `user reset-password`,
`user delete` and `user create` reported success without ever
sending their mail.

Add mail.StopMailDaemon which closes the queue and blocks until the
daemon has delivered all remaining messages (with a timeout so a broken
SMTP server can't hang the CLI), and call it from a PersistentPostRun
hook on the user command. The web server's non-blocking behavior is
unchanged.
2026-07-09 17:05:21 +00:00
kallegrensandkolaente 82ee780161 feat(caldav): implement RFC 6578 sync-collection REPORT to sync task deletions
iOS Reminders (and other RFC 6578 clients) never learned about tasks
deleted on the Vikunja side: caldav-go answers unknown REPORT types
with 412, which makes iOS silently stop syncing, and deleted tasks left
no protocol-level trace a client could observe.

Intercept sync-collection REPORTs before caldav-go sees them and answer
per RFC 6578: tasks changed since the sync token as 200 entries and
deleted tasks as 404 entries. Deletion records come from task
soft-deletes, which are kept for 30 days before being purged.

Token edge cases: delta comparisons are inclusive because tokens have
second granularity (re-reported items keep their etag, so clients skip
the download), and tokens older than the soft-delete retention answer
403 + valid-sync-token to force a full resync instead of a delta that
would silently miss purged deletions.
2026-07-07 11:57:04 +00:00
kolaenteandkolaente c85a56511b fix(tasks): bump project and task updated times when task sub-entities change
Relations, comments, attachments, assignees (via the shared listener)
and label add/remove now advance the project's updated timestamp, and
label changes also advance the task's, so CalDAV ctags and
sync-collection deltas reflect every change that is visible in a VTODO.
2026-07-07 11:57:04 +00:00
kallegrensandkolaente c8f136a11b feat(caldav): emit STATUS:NEEDS-ACTION for incomplete todos
Some clients, iOS Reminders among them, classify VTODOs more reliably
when the status is explicit instead of implied by a missing
STATUS:COMPLETED.
2026-07-07 11:57:04 +00:00
Frederick [Bot] 3ac5b29aef [skip ci] Updated swagger docs 2026-07-07 09:51:58 +00:00
TinkandGitHub d83e90541c feat(tasks): soft-delete tasks with permanent deletion after 30 days (#3119) 2026-07-07 11:38:33 +02:00
Frederick [Bot] 9d3c505e7f [skip ci] Updated swagger docs 2026-07-06 16:26:52 +00:00
kolaenteandkolaente 8a79c30477 chore(search): consolidate scoring comments and deduplicate test helpers
The ranking-gate comment still claimed title and description weigh
equally, which the 1.5x boost made wrong; scoring semantics now live only
at the boost call site. Test subtests share searchTaskPositions instead
of hand-building position maps four times.
2026-07-06 15:45:55 +00:00
kolaenteandkolaente 0986782321 feat(search): boost title matches 1.5x over description matches
With constant-score relevance, 1.5 ranks a title match above a description
match while two matched words (2.0) still beat a single boosted title word,
preserving "more matched words wins". ParadeDB-only; the substring fallback
has no scoring.
2026-07-06 15:45:55 +00:00
kolaenteandkolaente f980f0424f docs(search): describe the actual constant-score relevance semantics
The fuzzy query shape makes pdb.score a constant-score sum (1.0 per
exact/prefix word-field match, 0.5 per one-edit match), not BM25, and no
field boosting is configured. Correct the comments claiming otherwise.
2026-07-06 15:45:55 +00:00
kolaenteandkolaente 350704573e test(search): pin ParadeDB matching contract with a behavioral test suite
Covers per-token matching across title and description, word-order
independence, description-only matches, and fuzzy(1)-against-token-prefix
matching, including its surprising looseness ("two" matching "world" via
the one-edit prefix "wo"). Skipped on databases without ParadeDB, whose
whole-string substring fallback intentionally matches less.
2026-07-06 15:45:55 +00:00
kolaenteandkolaente 167556ce2b feat(search): add explicit relevance sort field
sort_by=relevance renders as pdb.score(tasks.id) DESC at its requested
position, so clients can express orderings like done,relevance (undone
first, most relevant within each group). The implicit no-sort ranking
reuses the same mechanism via an injected sort param.

When the database or query shape cannot score, relevance params are
dropped and the remaining sorts plus the id tiebreaker apply, so the
field is safe to send unconditionally; getOrderByDBStatement also only
emits pdb.score when ParadeDB is actually available.

The quick-actions search sends sort_by=done,relevance (typed via
TaskFilterParams) and keeps surfacing undone tasks first.
2026-07-06 15:45:55 +00:00
kolaenteandkolaente e24ec07293 feat(search): rank ParadeDB search results by relevance
Searching with ParadeDB returns fuzzy/OR matches in id order, so a task
matching all query words can sink below tasks matching only one. When a
search term is present and the client sends no explicit sort, order the
results by pdb.score(tasks.id), keeping the stable id tiebreak.

Numeric #id searches and the Favorites view keep the default order since
pdb.score rejects those query shapes. On the all-projects scope the
favorites arm is dropped when every favorited task already lies inside
the project scope, so global search stays ranked for users with
favorites; out-of-scope favorites fall back to the unranked order.

The frontend omits sort_by while searching in project views unless the
user explicitly picked a sort, so the backend ranking engages.
2026-07-06 15:45:55 +00:00
nithin varmaandGitHub afdc471c24 fix(task): recurring tasks return to original bucket when no default is set (#2806) 2026-07-03 23:18:00 +02:00
kolaenteandGitHub cc627ee634 fix(tasks): prevent position race conditions with atomic upserts and locking (#3098) 2026-07-03 19:58:39 +02:00
TinkandGitHub 0e3171ebbb fix(tasks): show filter-matched subtasks whose parent is filtered out (#2952) 2026-07-03 19:38:01 +02:00
kolaenteandkolaente 7217604010 fix: only audit denied admin access for confirmed non-admins
A failed user read (transient DB error, or a deleted user with a
still-valid JWT) previously also emitted admin.access.denied, writing
a misleading probing entry for what may be a legitimate admin.
2026-07-03 16:56:03 +00:00
kolaenteandkolaente 3a666da860 feat: audit admin reads and denied admin access
GET /admin/users returns every user's email address; compliance
regimes commonly require admin PII access to be logged, so the list
read now emits admin.users.listed. The admin gate additionally emits
admin.access.denied (outcome=failure, with method and path) whenever
an authenticated user without the instance-admin flag probes an
/admin/* route — dispatched directly with the request context since
there is no transaction.

Both entries have no single affected resource, so the entry target is
now omitted when empty instead of serialising a zero value.
2026-07-03 16:56:03 +00:00
kolaenteandkolaente 6f21c57c78 feat: audit admin user and project mutations
Every mutating admin action now dispatches a typed event on commit
(admin.user.created, admin.user.admin.granted/revoked,
admin.user.status.changed, admin.user.password.set,
admin.user.password_reset.sent, admin.user.deleted,
admin.project.owner.changed) which the audit catalog maps onto an
entry with the acting admin as actor and the affected user or project
as target. The model actions grow a doer parameter since the acting
admin was not threaded into the models layer before.

Admin creation deliberately emits both user.created (actor = the new
user, unchanged semantics) and admin.user.created (actor = the admin).
The events are instance-level, not project-scoped, so they are not
exposed as webhook events. No password or reset-token material is ever
part of a payload.
2026-07-03 16:56:03 +00:00
kolaenteandkolaente 39bbf8d006 fix: dispatch pending events in admin handlers
Neither the v1 nor the v2 admin handlers called events.DispatchPending
after commit, so events queued via DispatchOnCommit were silently
dropped: admin-created users never fired user.created (no webhook,
notification or audit entry) and mode=now user deletion dropped the
cascaded project.deleted events. Each dropped queue also leaked an
entry in the pendingEvents map keyed on the dead session.

Dispatch after commit, clean up on rollback, and thread the real
request context through the v2 handlers so request metadata flows onto
the events.
2026-07-03 16:56:03 +00:00
61d74ee2ef fix: assign bot-created projects to the bot's owner
When a bot user creates a project, the bot's owner (the human who
created the bot) now owns the project instead of the bot itself. The bot
retains admin access via an explicit project share. This ensures the
human can always see and manage projects their bots create.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-03 06:53:29 +00:00
TinkandGitHub f11c90ca6b fix: create saved filter positions before the fetch query (#3084) 2026-07-02 19:43:15 +02:00
TinkandGitHub d0dccf2736 feat(pro): admin password reset for existing users (#3085) 2026-07-02 19:39:00 +02:00
Robert SzulistandGitHub 7ae80c073b fix: GCS signing issue (#2920) 2026-07-02 17:38:37 +02:00
bdb07799d3 fix(api): return 200 instead of 500 when listing attachments on a task with none
ReadAll used a bare return when len(attachments) == 0, which returned
nil for the interface{} result. The v2 handler's type assertion then
failed on nil, producing an untyped error that Huma surfaced as 500.

Return the empty slice explicitly so the assertion succeeds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-01 12:42:32 +00:00
kolaenteandTink 93f88084de fix: resolve golangci-lint v2.12 findings 2026-06-30 18:53:56 +00:00
kolaenteandTink c7413a1d86 fix: adapt to sentry-go v0.47 API changes
Scope.SetExtra was removed in sentry-go v0.47.0 (deprecated earlier,
dropped in getsentry/sentry-go#1274). For error events the recommended
replacement is SetContext, so attach the request URL via SetContext
instead.
2026-06-30 18:28:11 +00:00
kolaente e1afa039cb fix: use correct type for permission error 2026-06-29 17:18:05 +02:00