Commit Graph

2335 Commits

Author SHA1 Message Date
kolaente b52a451db4 feat(api/v2): enable AutoPatch for automatic JSON Merge Patch 2026-05-31 12:56:57 +00:00
kolaente c6c57d9d15 refactor(models): remove *Arr helper fields now handled by normalizer 2026-05-31 12:56:57 +00:00
kolaente fb9119c98d feat(middleware): normalize PHP-style array query params 2026-05-31 12:56:57 +00:00
kolaente 132f973486 fix(routes): set Cache-Control: no-store on /api/v2 too
The /api/v1 group sets Cache-Control: no-store to prevent browsers
from heuristically caching JSON responses. /api/v2 was missing the
same header, which could lead to stale reads. Extracted the inline
middleware into a shared noStoreCacheControl helper and applied it
to both groups.
2026-05-31 12:56:57 +00:00
kolaente 4125fd47c3 feat(api/v2): declare JWTKeyAuth security scheme 2026-05-31 12:56:57 +00:00
kolaente b56a74d6a7 feat(models): accept v2 PATCH as alias for PUT in API token matcher
Huma's AutoPatch synthesises a PATCH counterpart for every PUT, and both
verbs collapse to the same "update" permission. PATCH is still skipped
during collection (it would clobber PUT under the shared key), but the
matcher now accepts it as an alias for the stored PUT route on the same
path, so token holders aren't forced to use PUT exclusively.
2026-05-31 12:56:57 +00:00
kolaente 8a4f5cbe11 fix(models): make API tokens work on /api/v2 routes
Sub-phase G validation caught that a token scoped to e.g.
`labels.read_one` was rejected on /api/v2/labels because the route
collector only stripped /api/v1/ from paths and did not know about
v2's REST-style verbs (POST create, PUT/PATCH update, inverted
from v1 where PUT creates and POST updates).

Introduce a shadow apiTokenRoutesV2 map keyed under the same
(group, permission) names as the v1 entries. Route collection now
routes v2 paths into this shadow map and CanDoAPIRoute consults
both tables, so the same permission bit authorizes the v1 and v2
endpoints for the same resource without changing the data shape
served at /api/v1/routes (which the frontend token UI depends on).

Also teach getRouteDetail about PATCH so Huma's AutoPatch-synthesized
PATCH routes collapse to the `update` permission instead of being
dropped.
2026-05-31 12:56:57 +00:00
kolaente 15d8ac5f49 feat(auth): add GetAuthFromContext for Huma handlers 2026-05-31 12:56:57 +00:00
kolaente 5fefa88577 feat(routes): scaffold /api/v2 Echo group 2026-05-31 12:56:57 +00:00
kolaente 5fa6d66c41 feat: vendor humaecho adapter for echo/v5 2026-05-31 12:56:57 +00:00
kolaente e31d73b3df fix(keyvalue): treat undecodable cached values as a cache miss
A GetWithValue deserialization error in RememberFor was returned as fatal.
On a Redis upgrade the metrics counters live under the same keys as before
but were stored as plain int64, so the first decode into the new envelope
would fail and the metric would break permanently. Treat such errors as a
miss and recompute/overwrite so the cache self-heals.
2026-05-30 13:48:01 +00:00
kolaente 9a810f7632 refactor(user): remove the now-empty listeners file
The user package no longer registers any event listeners, so drop the
empty RegisterListeners hook and its caller.
2026-05-30 13:48:01 +00:00
kolaente 71dcb096be test(metrics): verify counts are read from the right table 2026-05-30 13:48:01 +00:00
kolaente 054050b1e2 test(keyvalue): cover RememberFor TTL caching 2026-05-30 13:48:01 +00:00
kolaente 0248bdf5e7 feat(metrics): invalidate the user count cache on registration
Registration is the one hot path where instant freshness is worth an
extra COUNT(*), so bust the cache there rather than waiting for the TTL.
2026-05-30 13:48:01 +00:00
kolaente 9e3e884dac refactor(metrics): drop inline file count tracking
The file count is now read from the database on demand.
2026-05-30 13:48:01 +00:00
kolaente 72a231620d refactor(metrics): drop the project/task/team/attachment count listeners
These counts are now read from the database on demand. The events
themselves stay - they are still used by webhooks and notifications.
2026-05-30 13:48:01 +00:00
kolaente 06000b7a03 refactor(metrics): drop the user count listener
The user count is now counted on demand, so the increment-on-create
listener is no longer needed.
2026-05-30 13:48:01 +00:00
kolaente 051f734f3d refactor(metrics): count entities on demand with a TTL cache
Instead of priming a counter at startup and keeping it in sync via events,
each entity count is now read directly from the database and cached for
30s (countCacheTTL). The cache is the correctness guarantee: counts are at
most one TTL stale and self-healing, so they can never permanently drift.

This fixes vikunja_user_count never updating after registration (#2650):
the count no longer depends on every mutation path dispatching an event.
2026-05-30 13:48:01 +00:00
kolaente ec2f154e10 feat(keyvalue): add RememberFor for TTL-cached values 2026-05-30 13:48:01 +00:00
Rémi Lapeyre 069685f2a7
fix(caldav): return 404 when trying to access a project that cannot exist with CalDAV (#2796) 2026-05-28 08:14:52 +02:00
Frederick [Bot] 6abf6c6012 chore(i18n): update translations via Crowdin 2026-05-27 02:31:52 +00:00
Tink bot b8cabcd825 fix(assignees): use db.ILIKE helper for assignee search count query 2026-05-26 19:43:16 +00:00
nithinvarma411 b6a02cb6a5 fix(assignees): resolve 500 error when reading task assignees 2026-05-26 18:59:33 +00:00
Tink bot 20e04f4fcb feat(logging): include user agent in HTTP access log 2026-05-21 13:42:03 +00:00
Frederick [Bot] 9dfa6fbf89 chore(i18n): update translations via Crowdin 2026-05-21 02:14:41 +00:00
kolaente f05ef2df94
feat(sharing): sort team members by display name in UI and by ID in API (#2784) 2026-05-20 23:32:47 +02:00
kolaente 6fc36cb700 feat(comments): treat quoted comment authors as implicit mentions
A comment whose body contains <blockquote data-comment-id="…"> nodes
now triggers the same task-comment mention notification for the
quoted comments' authors, respecting CanRead, subscription, and
existing dedup. Self-quotes, wrong-task quotes, and malformed ids
are silently skipped.
2026-05-20 21:02:14 +00:00
Tink bot a1f81524ab feat(i18n): make Greek available in the language selector
el-GR translations are around 36% complete but were not yet listed in the
UI. Add it to the supported locales list (frontend and backend) and wire
up the dayjs locale mapping.
2026-05-20 20:25:17 +00:00
Frederick [Bot] 2fca6a46e5 [skip ci] Updated swagger docs 2026-05-19 09:43:17 +00:00
Tink bot fa6e1f8e49 fix(migration): reuse existing labels on re-import
Seed the dedup map at the start of insertFromStructure with the importing
user's existing labels, keyed by title + normalized hex color. Previously
the map was empty on each run, so importing the same CSV (or any other
migration format) twice would create a second copy of every label.

Scoped to the user's own labels so imports don't silently link to other
users' labels visible via shared projects.

Fixes #2742
2026-05-19 09:09:59 +00:00
Tink bot 15badb382a test(api): cover positive project-identifier resolution
Adds back the by-identifier and case-insensitive-input cases now that
project identifiers are stored uppercase across the codebase.
2026-05-19 08:53:25 +00:00
Tink bot c6fa7991d6 fix(api): uppercase project identifier before by-index lookup
Switches the input normalisation from lower- to uppercase so identifiers
canonicalise the same way GitHub-style refs do (e.g. "PROJ-42"). The
positive identifier tests are dropped for now because the existing
fixtures store identifiers as lowercase ("test1") and the SQL comparison
remains case-sensitive — once the column-side case-insensitive match
lands, full coverage can be reinstated.
2026-05-19 08:53:25 +00:00
Tink bot 04148e14db feat(api): lowercase project identifier before by-index lookup
Normalises the input side so GitHub-style references like "TEST1-42" and
"test1-42" resolve to the same project. The SQL comparison itself remains
case-sensitive for now; case-insensitive matching on the column will be
addressed separately.
2026-05-19 08:53:25 +00:00
Tink bot 466d39e6de feat(api): accept project identifier in by-index task route
Allows GET /projects/{project}/tasks/by-index/{index} to resolve {project}
as either a numeric id or a project identifier (e.g. "PROJ"), so callers
can build GitHub-style task references like "PROJ-42" without first
looking up the project's numeric id. Pure-digit values remain interpreted
as ids, which makes identifiers consisting solely of digits unreachable
via this route.
2026-05-19 08:53:25 +00:00
kolaente 21ce33f8fd
feat(projects): always store identifiers as uppercase (#2775) 2026-05-19 10:35:43 +02:00
Frederick [Bot] c761ab9761 chore(i18n): update translations via Crowdin 2026-05-19 02:26:35 +00:00
Tink bot fee2d2ea58 fix(notifications): skip logo attachment for conversational mails
The conversational mail template does not reference cid:logo.png, but
RenderMail still attached the embedded logo to every outgoing mail.
That left an orphan inline part that some clients render as a stray
attachment. Only embed logo.png when the formal template is in use.
2026-05-18 19:06:49 +00:00
Tink bot 6b14307896 test(trello): drop redundant BackgroundImage assignment in getTestBoard 2026-05-15 15:16:11 +00:00
Tink bot fc373ae963 test(trello): serve testimage from local server instead of vikunja.io
Mirrors the Todoist migration test setup so TestConvertTrelloToVikunja
no longer depends on https://vikunja.io/testimage.jpg being reachable.
2026-05-15 15:16:11 +00:00
kolaente 70393f38d2
feat: add Atom feed for user notifications with API token auth (#2758) 2026-05-15 17:25:09 +02:00
Tink bot aa1956e1aa fix(oauth2server): accept all loopback redirect forms
Hardcoding the three exact strings localhost / 127.0.0.1 / ::1 rejected
legitimate loopback redirects like 127.0.0.2:1234 (anywhere in 127.0.0.0/8)
or [0:0:0:0:0:0:0:1]:1234 (expanded IPv6 loopback). Use net.IP.IsLoopback()
to cover the full loopback ranges, and match "localhost" case-insensitively.
0.0.0.0 stays rejected as it is not a loopback address.

https://claude.ai/code/session_01LsTDrCJ7trE6WQ4FYf78UB
2026-05-07 22:03:49 +00:00
Tink bot c6bda7a2dd feat(oauth2server): accept loopback redirect URIs
Previously the OAuth server rejected every redirect_uri that did not start
with a vikunja- custom scheme. Native apps that cannot register a custom
scheme (e.g. CLIs, desktop tools) need loopback redirects per RFC 8252, so
also allow http://localhost, http://127.0.0.1 and http://[::1] (any port).
Non-loopback http:// and https:// targets remain rejected.

https://claude.ai/code/session_01LsTDrCJ7trE6WQ4FYf78UB
2026-05-07 22:03:49 +00:00
MidoriKurage beaf4e9e65 fix(static): Correct the API_URL value to replace in index.html 2026-05-06 16:31:48 +00:00
kolaente 7800102f93
fix(models): allow user-delete cascade to complete for disabled creators
TaskAttachment.ReadOne now swallows ErrAccountDisabled/ErrAccountLocked
from the creator lookup, matching the existing ErrUserDoesNotExist
swallow. Without this, deleting a disabled user that owned a project
with task attachments would fail when the cascade re-loaded the
attachment to delete it.
2026-05-06 16:08:16 +02:00
Frederick [Bot] 6a604dd949 [skip ci] Updated swagger docs 2026-05-04 11:19:21 +00:00
Claude d9a5958bb8 feat: always enable bot users
Removes the `service.enablebotusers` config flag, the matching
`bot_users_enabled` field on /info, and the now-unused
`ErrBotUsersDisabled` error. Bot user routes and the frontend
settings tab are now always available.

https://claude.ai/code/session_01VhAR6xnoCdG1fpX52bzaCC
2026-05-04 10:38:53 +00:00
Frederick [Bot] 0adf85dc2d [skip ci] Updated swagger docs 2026-05-01 15:01:51 +00:00
kolaente 22d82e292b feat(user): always include own bots in user search
User search previously filtered bots only when they happened to match the
search string. That produced two bad behaviours:

1. Bots owned by other users could surface on an exact-username match,
   leaking them into assignee pickers and similar UI.
2. A user could not reliably find their own bots by typing a partial
   name, so bots became awkward to assign to tasks.

Change ListUsers to treat bot ownership explicitly: the existing match
branch excludes rows owned by someone else, and a second branch always
returns bots owned by the calling user. The own-bots branch also
respects any AdditionalCond passed in so project-scoped listings don't
start leaking bots from outside the project.
2026-05-01 14:44:10 +00:00
kolaente 999e28435e feat(avatar): use distinct marble palette for bot users
Bot users now render with a cool-toned (blue/cyan/violet/teal/indigo)
marble variant so they're visually distinguishable from human users.
Marble's rendering logic is parameterized with a palette; the route
forces the bot palette whenever the resolved user is a bot, overriding
whatever avatar provider they'd otherwise inherit.
2026-05-01 14:44:10 +00:00