The api renumbers all sibling positions when they drift too close together but
only returns the updated project. The store kept stale positions for the
siblings, so navigating to one of them pulled in its recalculated position and
made it jump to a different spot in the sidebar.
recalculateProjectPositions ran before the new position was written, so the
moved project kept its tiny position while all its siblings were renumbered.
Every further move to the top then triggered another recalculation, and root
projects with a NULL parent_project_id were never healed at all.
The refresh-token and OAuth token endpoints shared the 10 requests/minute
floor that protects login, register and password reset. Access tokens live
for 10 minutes, so every client renews its session several times an hour --
and behind a reverse proxy `service.ipextractionmethod` defaults to `direct`,
which keys all of them to the proxy's address. Routine renewals therefore
used up the login budget, and users got "Too many requests" when signing in.
This hit the desktop app hardest: it exchanges its authorization code on the
very same endpoint it refreshes tokens on.
Renewal now has a separate limiter with its own `ratelimit.tokenrefreshlimit`
setting, defaulting to 60 requests/minute. Both limits stay enforced.
Every limiter got its own store, but the redis store derives its keys from
a prefix that was identical everywhere. With `ratelimit.store: redis` the
unauthenticated floor and the global limiter therefore decremented the same
counter, so one budget silently applied to all of them.
The plugin docs show yaegi plugins reading Vikunja configuration via
pkg/config and github.com/spf13/viper, but neither package was registered
in the yaegi symbol table. Plugins importing them failed to load at the
import itself.
Add both packages to yaegiSymbolPackages and commit the generated symbol
files so interpreted plugins can read configuration as documented.
Fixes#3500.
The sidebar drag handle and the project color bubble share the same 1rem box and
swap on hover. Two rules forced both of them visible at once whenever the
`is-touch` body class was set, which `is-touch-device` sets for anything exposing
touch events — including a desktop with a touchscreen. Those machines report
`pointer: fine`, so the coarse-pointer rule that hides the handle didn't apply and
the grip icon was painted on top of the color dot permanently.
Key the overrides off `(pointer: coarse)` instead, matching how the handle is
already hidden there and how ProjectList/ProjectKanban detect touch.
Fixes#3449
When a client PUT a task to a collection the task no longer lives in, the
project consistency guard made GetResource report it as missing, so the PUT
handler fell through to CreateResource and created a second copy of the task
in that project. iOS Reminders hits this whenever it completes a task it still
believes lives in the old project, which is why the copies all show up as
STATUS:COMPLETED - and stay invisible in the web UI, where done tasks are
hidden by default.
Refuse to create a task whose UID already exists, so the client resyncs and
finds the task under its real collection instead.
Requests arriving through a unix socket have no peer IP, Go sets
RemoteAddr to "@" for them. Echo's IP extractors cannot parse that and
bail out before looking at X-Forwarded-For or X-Real-IP, so remote_ip was
always empty and everything keyed on the client IP (http log, rate
limits, audit log, session records) lost it.
Substitute loopback as the peer address in that case - a unix socket peer
is on the same host by definition - so the configured extraction method
works the same as it does over TCP.
Fixes#3529
Echo's X-Real-IP extractor checks the header value against the trusted
ranges, not the peer that sent it, so it cannot report public client IPs.
Say so and point users behind a reverse proxy at xff.
The single-provider auto-redirect added in 18ee92f2 fires on every visit to
/login, including the two cases where the login page has to stay put:
- A native client's /oauth/authorize URL is parked in the login hash so it can
be copied into the browser the user is actually signed in to (#2654). The
auto-redirect replaced that URL with the provider's before it could be
copied. Copying the provider URL instead does not work either, because the
OIDC state lives in the originating browser's localStorage, so finishing the
flow elsewhere fails the state check.
- Inside the Electron window, login is handed off to the system browser by
DesktopLogin. Redirecting to the provider in-window stranded the user there.
Move the decision into getAutoRedirectProvider() and cover it with unit tests.
Fixes#3473
Backspace at the start of a list item used to lift it out of the list, which
split the list in two and left a bare paragraph between the halves. Now the
item merges into the item above, unless it is the first item of its list -
that keeps lifting so there is still a way out of a list.
Fixes#3480
After a successful registration the frontend auto-logs-in. With email
confirmation enabled that login fails with 412 (code 1012), which
surfaced as a raw "Request failed with status code 412" error in the
register form even though the account was created fine. Treat that case
as a success and tell the user to check their inbox instead.
The default-project creation triggered by the user-created event wrote the
stale in-memory user back with a full UpdateUser, racing registration and
sometimes resetting the status from "email confirmation required" back to
active. Only update the default_project_id column and keep the returned
user's status in sync with what was persisted.
Webhook target URLs like Discord's contain no break opportunities, so the
cell's min-content width forced the whole table past its container and
pushed the other columns out of view.
Fixes#3456
shouldShowTaskInListView() skipped the parent-in-view check for saved
filters, so a subtask expanded for context was rendered nested *and* as
its own top-level row, even when it did not match the filter.
The bypass was a stopgap for #2494 (a matching subtask was invisible when
its parent did not match). That is now handled in the backend by
buildSubtaskRootCondition, which returns such a subtask as a root, so the
frontend check is safe to apply to saved filters too.
Fixes#3462
Selecting an option unmounts the result list, so focus fell back to the
document body. On the task detail view, subsequent keystrokes were then
picked up by the global task shortcuts, which looked like focus jumping
to the description or assignee field.
Resolves#3464
Since e25295422 the subscriber loops in the comment, assigned, deleted and
project-created listeners logged a failed Notify and moved on. On SQLite the
insert regularly fails with "database is locked" (SQLITE_BUSY_SNAPSHOT: the
listener's deferred transaction cannot upgrade to a write once a sibling
listener has committed), so the notification was silently lost.
Return the error again so the events router retries the handler with a fresh
session, as it did before 2.5.0.
Reported at https://community.vikunja.io/t/notification-problems-since-2-5-0/4750