Commit Graph
2673 Commits
Author SHA1 Message Date
kolaente 7918351e12 fix: sort subtasks by their position in the view being read
Nested subtasks were returned in the order their relations were created, so
they ignored the view's own order - reordering a subtask by dragging it, or
sorting the view, left the nesting untouched.

Positions only exist per view, and not every caller reads tasks through one:
the task detail page, CalDAV and exports pass no view, and a subtask in another
project has no row for this view either. Those keep relation id order, which
stays stable, and sort after the ones that do have a position.
2026-07-26 18:25:35 +02:00
kolaente 426d8b92e2 test: note sqlite insertion-order caveat in subtask order test
Address pr-swarm finding.
2026-07-26 17:46:23 +02:00
kolaente 55149aed00 fix: return subtasks in the order their relations were created
Clients render nested subtasks in the order the api hands them back, but the
task relation query had no ORDER BY, so the order was up to the database.
2026-07-26 16:43:33 +02:00
kolaenteandkolaente aa62c00b78 test: guard parent_project_id in project responses
Route-level tests on both api versions, since the pseudo-projects only get
their parent through the read path. The v2 read body embeds models.Project and
Huma's $schema wrapper copies its fields into a generated struct, so a custom
MarshalJSON on the model would not have applied there — assert the raw JSON
instead of the struct.
2026-07-26 09:39:41 +00:00
kolaenteandkolaente 2e0cd81ce0 fix: always serialize parent_project_id for pseudo-projects
781ffac19 (GHSA-44v6-7fxq-vgf4) turned ParentProjectID into a *int64 with
`omitempty` so a write can tell an omitted field from an explicit 0 (detach,
admin only). That also dropped the key from every response where the pointer is
nil: the Favorites and saved-filter pseudo-projects, which are built in Go and
never set it, plus rows whose nullable parent_project_id column was never
backfilled. Clients parse the field as a plain int, so the whole project list
fails to decode (go-vikunja/app#295).

Keep the pointer and its three-way write semantics, but never hand out a nil:
normalize DB rows in AfterLoad and set the field where the pseudo-projects are
built.

Closes go-vikunja/app#295
2026-07-26 09:39:41 +00:00
kolaenteandkolaente 4daccae956 fix(filters): don't let one filter fail the whole filter view maintenance
Both the listener and the cron bailed out completely as soon as a single saved
filter errored, so no filter view in the instance got updated. Skip the failing
filter instead. Genuine db errors still bubble up so the retry middleware can
do its job.
2026-07-26 01:46:14 +00:00
kolaenteandkolaente 771de18c8c fix(filters): skip saved filters owned by disabled users
Evaluating a saved filter needs the list of projects its owner has access to,
which getRawProjectsForUser refuses to return for disabled or locked accounts.
Since the listener walks every manual kanban filter view in the instance, a
single filter of a disabled user made the whole handler fail with "Account is
disabled" - for every task update, for everyone. The recalculation which would
un-crowd the positions is the very thing that failed, so the state never healed.
2026-07-26 01:46:14 +00:00
kolaenteandkolaente 8df183123d perf(tasks): index task_positions on (project_view_id, position)
Every task creation looks for rows with the same position in the view to
resolve conflicts. Without an index on position that scans all positions of
the view, so creating n tasks in one project costs O(n²) row reads - the
second half of the slow import in #3297.
2026-07-26 01:13:08 +00:00
kolaenteandkolaente 0101dc9386 fix(migration): seed task positions for exports without order information
Tasks imported without a position are inserted in front of the lowest one by
halving it. That reaches the minimum spacing every few dozen inserts, which
recalculates every position in the view and makes a large import O(n²) - a
10k task TickTick export with an empty Order column never finished.

Numbering the tasks up front skips the halving entirely and keeps the order
the export was written in.

Closes #3297
2026-07-26 01:13:08 +00:00
Frederick [Bot] c0f026ff47 chore(i18n): update translations via Crowdin 2026-07-25 00:24:11 +00:00
kolaenteandkolaente f02df25510 test: guard schema-link creation for all v2 response types
Registers the full v2 API and fails on Huma's 'unable to create schema link' stderr warning, catching any future response body type Huma cannot wrap. Fixes #3272.
2026-07-24 12:28:45 +00:00
kolaenteandkolaente b8492a0ab9 fix: embed LinkSharing by value in LinkShareToken
Same pointer-embed problem as AdminUser; the share auth response was missing $schema.
2026-07-24 12:28:45 +00:00
kolaenteandkolaente 4cc647b9b8 fix: embed user.User by value in AdminUser
A pointer embed's method set is never empty, so Huma could not create the $schema wrapper for admin user responses.
2026-07-24 12:28:45 +00:00
kolaenteandkolaente 6f65621c14 fix: shadow embedded CRUDable ReadAll method on SavedFilter
Same mechanism as LinkSharing.Update: the promoted method broke Huma's $schema wrapper for savedFilterReadBody.
2026-07-24 12:28:45 +00:00
kolaenteandkolaente c580d516f4 fix: shadow embedded CRUDable Update method on LinkSharing
The unshadowed promoted method made LinkSharing's value method set non-empty, which breaks Huma's $schema wrapper for response bodies embedding it (reflect.StructOf, go#15924).
2026-07-24 12:28:45 +00:00
Frederick [Bot] 675ba6dd97 chore(i18n): update translations via Crowdin 2026-07-24 00:21:02 +00:00
kolaente 6895a7765e fix(security): reject cross-project views in ProjectView Can{Delete,Update} (GHSA-gg93-x632-9ccv) 2026-07-19 18:59:34 +02:00
kolaente 781ffac198 fix(security): require Admin to detach a project from its parent (GHSA-44v6-7fxq-vgf4) 2026-07-19 18:59:34 +02:00
kolaente 1c13624a8e fix(api): reject link shares on reaction and task-read endpoints (consistency; GHSA-vvcv-vpph-h844) 2026-07-19 18:59:34 +02:00
kolaente 3a0ea15d8c fix(security): hash password-reset, email-confirm and deletion tokens at rest (GHSA-r6w9-259g-gwrv) 2026-07-19 18:59:34 +02:00
kolaente 00fd2c6155 fix(security): clear plaintext user tokens for reset/confirm/deletion (GHSA-r6w9-259g-gwrv) 2026-07-19 18:59:34 +02:00
kolaente 4ae2e09301 fix(auth): reject API tokens at the OAuth authorize endpoint (GHSA-v3p6-34mc-hj7v) 2026-07-19 18:59:34 +02:00
kolaente 7854f2729a fix(auth): require verified email before OIDC email-fallback account linking (GHSA-xv7q-fvmc-jx96) 2026-07-19 18:59:34 +02:00
kolaente be36c11e67 fix(api): derive API-token ownership from a verified user principal (GHSA-vvcv-vpph-h844) 2026-07-19 18:59:34 +02:00
kolaente d911caaa11 fix(projects): enforce write permission on target parent when duplicating a project (GHSA-f27p-pw2p-9pr4) 2026-07-19 18:59:34 +02:00
kolaente cfb9c24519 fix(kanban): pin link-share task collection view to the share's project (GHSA-rj9j-8772-4h6c) 2026-07-19 18:59:34 +02:00
kolaente b31d606b88 fix(kanban): prevent cross-tenant bucket relocation via project_view_id mass-assignment (GHSA-569v-q83c-3j3g) 2026-07-19 18:59:34 +02:00
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