Commit Graph
14890 Commits
Author SHA1 Message Date
kolaente cbd33ef8cb feat(packages)!: store data in /var/lib/vikunja
The os packages previously stored the sqlite database and all other
runtime data in /opt/vikunja, next to the binary. Fresh installs now
default to /var/lib/vikunja, which is the correct FHS location for
variable state data. The package creates the directory and the service
units use it as their working directory.

BREAKING CHANGE: New installations store their data in /var/lib/vikunja
instead of /opt/vikunja. Existing installations keep their current
config and are unaffected, but setups relying on the old default path
for new machines need to move their data or adjust the config.
2026-08-05 19:22:42 +02:00
kolaente 10cfb6ae8d fix: don't tag prerelease docker images as latest
The unconditional type=raw,value=latest applied the latest tag to every
pushed git tag, including prereleases like v1.0.0-rc1. Removing it lets
metadata-action's default latest=auto flavor handle it, which only tags
latest for non-prerelease semver versions.

Fixes #2161
2026-08-05 19:17:38 +02:00
renovate[bot]andkolaente b53ab91711 chore(deps): update node.js to v24.19.0 2026-08-05 14:22:52 +02:00
renovate[bot]andkolaente b7e0ed40ff chore(deps): update dependency basic-ftp to v6.2.0 2026-08-05 14:17:43 +02:00
renovate[bot]andkolaente 396d2c7c7a chore(deps): update dependency @types/codemirror to v5.60.18 2026-08-05 14:17:18 +02:00
renovate[bot]andkolaente 5fcc3d4f85 chore(deps): update dependency basic-ftp to v6.1.0 2026-08-04 21:48:11 +02:00
TinkandGitHub 813e953db1 fix(static): don't unescape already-decoded request paths (#3437) 2026-08-04 19:31:59 +00:00
kolaente 937ee313b1 fix(migration): don't fail todoist migration on undownloadable attachments
Todoist returns opaque identifiers instead of urls in file_url for
attachments it does not host itself (mail attachments for example).
Passing those to the http client failed with "unsupported protocol
scheme" and aborted the entire migration.

Skip attachments without an http(s) url and log-and-continue when a
single download fails instead of failing the whole migration.
2026-08-04 21:21:05 +02:00
8d92393ea3 fix(ci): bump LLM-action fork pin to root-container fix (#3430)
The auto-label workflow's LLM call succeeds, but the docker action ran
as a non-root user (`USER appuser` in the Dockerfile) and could not
write the runner's `GITHUB_OUTPUT` file:

```
Error: failed to set output: ... open /github/file_commands/set_output_...: permission denied
```

GitHub docker actions must run as root — the runner's file-command files
are owned by the runner user. Fixed in the fork
(tink-bot/LLM-action@8526bab removes the non-root user) and bumped the
pin here.

Failing run:
https://github.com/go-vikunja/vikunja/actions/runs/30904804523/job/91977288580

Co-authored-by: kolaente <k@knt.li>
2026-08-04 13:56:36 +00:00
kolaente 5b282faa09 fix(packaging): stop rpm upgrades from replacing the config
The config was shipped as plain `%config`, so rpm installs the packaged
file on every upgrade and moves the user's version aside as `.rpmsave`.
The postinstall script then re-ran its seds against the fresh sample and
generated a new JWT secret, logging everyone out — with the real config
no longer in place.

Mark it `config|noreplace` so rpm keeps the existing file and writes
`.rpmnew` instead. deb and apk treat the type exactly like `config`, so
nothing changes there.
2026-08-04 15:19:27 +02:00
kolaente caaff7ae9a fix(packaging): run the postinstall script on apk and arch upgrades
nfpm only maps `scripts.postinstall` to apk's `.post-install` and
archlinux's `post_install`, both of which run on a fresh install. Their
upgrade hooks come from the packager-specific `apk.scripts.postupgrade`
and `archlinux.scripts.postupgrade` keys, which we never set — so
upgrading on Alpine or Arch executed no maintainer script whatsoever, and
the restart added in the previous commit would never have fired there.

Point both at the matching install script.
2026-08-04 15:19:27 +02:00
kolaente 16185a5d6d fix(packaging): restart the service on package upgrades
The postinstall script only ran `systemctl enable`, which creates the
wants-symlink and nothing else. On an upgrade the package manager
replaced the binary while the old process kept running the old inode, so
users had to restart vikunja by hand to get the new version. Changes to
vikunja.service were never picked up either, for lack of a
`daemon-reload`.

Add a daemon-reload plus `try-restart` (`rc-service restart` on OpenRC).
try-restart is a no-op while the unit is stopped, so fresh installs keep
their current behaviour of being enabled but not started.

The config fixups move above the service handling so a restart never
races a half-patched config.
2026-08-04 15:19:27 +02:00
kolaente 339b9a2deb feat: regenerate and commit swagger docs and yaegi symbols during release tagging 2026-08-04 14:33:57 +02:00
Frederick [Bot] 734d50d787 [skip ci] Updated yaegi symbols 2026-08-04 12:16:25 +00:00
kolaente ef2200e942 chore: v2.5.0 release preparations v2.5.0 2026-08-04 13:42:43 +02:00
kolaente d545e3bbb1 refactor: drop redundant link share branch in comment permissions
GetID() delegates to getUserID(), so both branches compute the same value.
2026-08-04 13:40:11 +02:00
kolaente ed8dc89f75 fix: report the real link share id in saved filter errors
auth.GetID() is now negated, so ErrSavedFilterNotAvailableForLinkShare
carried a value that is not a link share id.
2026-08-04 13:40:11 +02:00
kolaente ccba2e4546 fix: avoid panic on null jwt claim values
reflect.TypeOf returns a nil Type for a null claim value, so building the
error message panicked.
2026-08-04 13:40:11 +02:00
kolaente dbe664dec5 fix(security): reject non-user tokens in GetUserFromClaims
GetUserFromClaims read the id claim straight into User.ID without checking the
token type. A link share JWT carries the raw, positive share id in that claim
and reaches every authenticated route, so the only thing preventing
impersonation was the incidental absence of a username claim in link share
tokens. Adding one would have reintroduced the confusion with a positive id,
bypassing the GetID negation entirely.

AuthTypeUser moves to pkg/user, which parses the claims and cannot import
pkg/modules/auth, so the value exists once.
2026-08-04 13:40:11 +02:00
kolaente 4b35f5d7e2 test: assert link share attachments are not attributed to a colliding user
files.Create stores GetID() straight into files.created_by_id, so a share
uploading an attachment used to persist a row indistinguishable from one
created by the user with the same id.
2026-08-04 13:40:11 +02:00
kolaente 2b1558e8df test: cover link share principal confusion at the HTTP layer
A link share JWT reaches every authenticated route; nothing rejects it at the
route group.
2026-08-04 13:40:11 +02:00
kolaente ce0d1355cf fix(security): reject link shares in Webhook.ReadAll
The guard on Webhook.CanRead is unreachable: no route exposes a read-one
webhook, and DoReadAll never calls CanRead. Two paths were left open:

- the v2 user-webhook list passes a.GetID() into Webhook.UserID, which is
  negative for a link share, so the w.UserID > 0 branch and its link share
  check were skipped and the request fell through to the project branch with
  project id 0, returning 404 instead of 403.
- the project branch never rejected link shares at all, so any holder of a
  public share link could list the project's webhooks. target_url is a bearer
  secret for Slack, Discord, Teams and Zapier.

Guard both by rejecting link shares at the top of ReadAll.
2026-08-04 13:40:11 +02:00
kolaente a717d64d56 fix(security): reject link shares at team, bot and webhook permission checks
These checks all compare the auth id against a users.id column, so a link
share whose id collided with a user id passed them as that user.
2026-08-04 13:40:11 +02:00
kolaente 6ffe265efa fix(security): return the negated user id from LinkSharing.GetID
web.Auth is satisfied by both *user.User and *LinkSharing, so returning the
raw positive share.ID made a share with id N indistinguishable from the user
with id N at every permission check comparing against a users.id column.

The rest of the codebase already keys shares negatively
(getUsersOrLinkSharesFromIDs, toUser), so this makes GetID consistent with
that contract instead of an exception to it.
2026-08-04 13:40:11 +02:00
renovate[bot]andkolaente febb83bcd6 chore(deps): update dependency rollup to v4.62.4 2026-08-04 09:31:52 +02:00
renovate[bot]andkolaente 4d92ed94ff fix(deps): update module github.com/redis/go-redis/v9 to v9.22.0 2026-08-04 09:00:41 +02:00
renovate[bot]andkolaente 3b6e969f9b fix(deps): update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.106.4 2026-08-04 09:00:28 +02:00
renovate[bot]andkolaente bc2af328ef chore(deps): update dependency js-yaml to v5.2.3 2026-08-04 09:00:05 +02:00
renovate[bot]andkolaente b4ca03b2db chore(deps): update dependency js-yaml@4 to v5.2.3 2026-08-04 08:59:53 +02:00
Frederick [Bot] 0ccecb36c9 chore(i18n): update translations via Crowdin 2026-08-04 00:23:49 +00:00
kolaente 5737d0df6f fix(views): drop deleted bucket from view state after deleting it
The store kept a deleted bucket as the view's default or done bucket,
so the next view save echoed the stale id and failed.
2026-08-04 01:09:10 +02:00
kolaente e329197211 fix(views): preselect manual bucket mode in the view edit form
Neither bucket mode radio matches the none mode a view has before it
becomes a kanban view, so both stayed unselected. A single watcher
normalizes the mode for kanban views, on load as well as when the kind
changes.
2026-08-04 01:09:10 +02:00
kolaente 7eeb88da23 fix(migration): repair kanban views without a bucket configuration mode
Views broken by the bug above keep their state until something writes
them again, so repair them on startup: set the manual mode, seed the
default buckets when the view has none and place the project's tasks in
the default bucket.

Views whose project or saved filter is gone are skipped rather than
repaired with a dangling creator, soft-deleted tasks stay out of the
backfill, and the mode flip is the last write per view so an
interrupted run picks the view up again.
2026-08-04 01:09:10 +02:00
kolaente 2d8198e942 fix(views): seed and maintain kanban buckets when a view's kind changes
Switching an existing view to kanban left bucket_configuration_mode at
none, so the tasks endpoint returned a flat task list which the
frontend rendered as empty bucket columns.

The mode is now normalized on create and update: a kanban view without
a mode becomes manual, a non-kanban view loses its mode, and an update
which omits the mode keeps the stored one together with its bucket
configuration. Becoming a manual kanban view seeds the default buckets
and backfills task_buckets rows for tasks which have none in that view,
so tasks created while the view was of a different kind stay visible.
Bucket ids from the request are validated against the view: an id of a
bucket which is gone resets to zero instead of locking the view, an id
belonging to another view is rejected, and non-kanban views no longer
write those columns at all so a round trip can restore them.

The backfill selects only task ids, scopes saved filter views to the
projects their owner can see, batches its inserts and ignores conflicts
with concurrently placed tasks.

Fixes https://github.com/go-vikunja/vikunja/issues/3386
2026-08-04 01:09:10 +02:00
renovate[bot]andkolaente f045a59ea7 chore(deps): update node.js to v24.19.0 2026-08-04 00:30:22 +02:00
kolaente d1cec84b11 feat(dev): add paseo config 2026-08-04 00:24:53 +02:00
renovate[bot]andkolaente 9c565bc722 fix(deps): update module github.com/danielgtaylor/huma/v2 to v2.39.1 2026-08-03 23:49:30 +02:00
renovate[bot]andkolaente 15ed7e2dfd chore(deps): update playwright to v1.62.1 2026-08-03 23:49:13 +02:00
renovate[bot]andkolaente dd661882d3 chore(deps): update dependency ip-address to v10.4.0 2026-08-03 23:48:53 +02:00
renovate[bot]andkolaente 91ce89d0f3 chore(deps): update danielroe/provenance-action digest to 193227b 2026-08-03 23:48:32 +02:00
renovate[bot]andkolaente 5c0297edd4 chore(deps): update pnpm to v11.19.0 2026-08-03 23:48:06 +02:00
renovate[bot]andkolaente 2a9d543442 chore(deps): update pnpm/action-setup action to v6.0.10 2026-08-03 23:47:26 +02:00
renovate[bot]andkolaente c72e0777d4 chore(deps): update dependency markdown-it to v15 2026-08-03 23:47:17 +02:00
renovate[bot]andkolaente cf79ad5a87 chore(deps): update dev-dependencies 2026-08-03 23:47:15 +02:00
kolaente 4c6fb252bc fix(ci): pin LLM-action to fork with reasoning model support
Upstream rejects max_tokens/temperature for gpt-5 series models,
breaking auto-label with gpt-5.6-luna. Pin to the fork carrying
appleboy/LLM-action#24 until it is merged and released.
2026-08-03 23:46:29 +02:00
TinkandGitHub 855f6a24a0 fix(ci): replace retired GitHub Models inference in auto-label workflow (#3415) 2026-08-03 12:01:17 +02:00
kolaente 6f473835f9 test(e2e): cover multiline creation order and subtasks
Order asserted both right after creation and after a reload; the
subtask test pins which task became the parent.
2026-08-02 21:11:28 +02:00
kolaente 0e47556956 feat(tasks): create quick-add tasks through the bulk endpoint
Multiline quick add previously created each line with its own request
plus client-side index guessing — parallel creates raced and persisted
scrambled task order. All input now goes through the atomic bulk
endpoint (single lines too, per review), so the server assigns indexes
and positions in input order. This drops the defaultPosition prop and
ProjectList's firstNewPosition.

Created tasks are emitted once as a batch: per-task emits made
non-position-sorted lists reload per task and forced consumers'
insert order into AddTask. Relations run serialized through runWrites,
and a relation failure toasts instead of restoring input for tasks that
already exist. Creation failure restores the original input string.
E2E waits pinned to the old v1 create request follow the flow to the
bulk endpoint.

Fixes #3288
2026-08-02 21:11:28 +02:00
kolaente e106150ed4 feat(tasks): add bulk quick-add creation to the task store
createNewTasksBulk builds full task payloads from quick add magic
titles (extracted from createNewTask so both paths share it) and hands
them to the service in one call. Label application failures toast and
continue — the tasks already exist server-side, failing the whole
action would invite duplicate resubmits.

runWrites moves to helpers/ so components can use the write-serializing
util without importing the store module.
2026-08-02 21:11:28 +02:00
kolaente aa0e9d0c01 feat(tasks): add bulk creation to the task service
bulkCreate posts to /api/v2/projects/{id}/tasks/bulk: tasks grouped per
project, chunked at the endpoint's 100-task limit, requests strictly
sequential (server assigns indexes at insert time and concurrent bulk
writes fail under write contention), batches of one project posted last
chunk first because the server places each batch on top of every view.
Returns slots aligned 1:1 with the input plus the first error, so
partial progress survives a failed batch.

The payload is an explicit allowlist — the v2 schema rejects unknown
properties, so sending the full processModel output 422s. Response
shape is guarded and the error is translated via the message module
(direct i18n.global.t in the service trips vue-i18n's type
instantiation limit).
2026-08-02 21:11:28 +02:00