Commit Graph

1802 Commits

Author SHA1 Message Date
John Starich
b2715bb56d refactor: use Go idioms for running tests 2026-02-17 18:01:05 +01:00
John Starich
591a646f84 refactor: remove environment variable requirements for go test 2026-02-17 18:01:05 +01:00
Micah
31da3c4533 fix(migration): make migration from Microsoft Todo work for those with previously migrated wunderlist accounts (#2126) 2026-02-17 16:54:03 +01:00
Martin Lindvik
e3695c17c6 feat: add Swedish for language selection (#2248)
The Swedish translations were finished on crowdin recently but I noticed
that the language selection was still missing so I went ahead and added
it.
2026-02-17 14:32:01 +00:00
kolaente
79d0942780 fix: use DelPrefix in upload avatar FlushCache to clear all cached sizes
FlushCache was using keyvalue.Del with the base key
(avatar_upload_{userID}) but the actual cache entries are stored with
size suffixes (avatar_upload_{userID}_{size}). The Del call targeted a
key that never existed, so cached avatars were never invalidated.

Switch to keyvalue.DelPrefix to delete all size variants at once,
matching the pattern the gravatar provider already uses correctly.
2026-02-13 09:31:28 +01:00
kolaente
c93fa1b4ae test: add failing test for upload avatar FlushCache
The test populates the cache with multiple size-suffixed keys
and verifies that FlushCache removes all of them. Currently fails
because FlushCache uses Del with the base key which doesn't match
the actual size-suffixed cache keys.
2026-02-13 09:31:28 +01:00
Frederick [Bot]
be4fb77981 chore(i18n): update translations via Crowdin 2026-02-10 01:25:29 +00:00
kolaente
e90cb2631d fix(auth): remove unnecessary fields from JWT token payloads
Remove email, name, emailRemindersEnabled, and isLocalUser from user JWT
claims, and isLocalUser from link share JWT claims. These fields are never
used from the token - the backend always fetches the full user from the
database by ID, and the frontend fetches user data from the /user API
endpoint immediately after login.

Also simplify GetUserFromClaims to only extract id and username, and
remove the now-unnecessary email override in the frontend's
refreshUserInfo.
2026-02-08 21:30:07 +01:00
kolaente
0e05d1cc9d fix(log): write each log category to its own file (#2206)
Previously, `makeLogHandler()` hardcoded "standard" as the logfile name
passed to `getLogWriter()`, causing all log categories (`database`,
`http`, `events`, `mail`) to write to `standard.log` instead of their
own files.

Add a logfile parameter to `makeLogHandler()` so each caller specifies
its category name, producing `database.log`, `http.log`, `echo.log`,
`events.log`, and `mail.log` as expected.

Fixes https://github.com/go-vikunja/vikunja/issues/2177
2026-02-08 15:22:58 +00:00
kolaente
b6974ffcfd feat: add UNSIGNED-PAYLOAD config option for S3-compatible stores (#2205)
Adds `files.s3.disablesigning` config option that sends
`UNSIGNED-PAYLOAD` instead of computing SHA256 hashes for S3 request
signing which fixes `XAmzContentSHA256Mismatch` errors with
S3-compatible providers like Ceph RadosGW and Clever Cloud Cellar

Resolves https://github.com/go-vikunja/vikunja/issues/2181
2026-02-08 15:03:19 +00:00
kolaente
bcfde14b14 fix(backgrounds): stream unsplash download to temp file instead of memory
Use a temp file instead of io.ReadAll to avoid buffering the entire
Unsplash image in RAM, which could cause OOM with large images or
high maxsize configuration.
2026-02-08 15:31:25 +01:00
kolaente
0d395a9e5d refactor(files): remove redundant seek operations in writeToStorage
Move the seek-to-start into the local file branch only and simplify
contentLengthFromReadSeeker to seek to end then back to 0 instead of
saving/restoring the original position. This reduces the S3 upload
path from 5 seek operations to 2.
2026-02-08 15:31:25 +01:00
kolaente
56a0ea44cf fix(backgrounds): avoid integer overflow in max size calculation
Keep maxSize as uint64 and cast safely when comparing with
resp.ContentLength to avoid potential integer overflow.
2026-02-08 15:31:25 +01:00
kolaente
ea78e87147 fix(dump): limit copy size to prevent decompression bombs
Use io.CopyN with a max size limit when extracting files from zip
archives during restore to prevent potential DoS via decompression bombs.
2026-02-08 15:31:25 +01:00
kolaente
19f6e4b7c9 fix(backgrounds): enforce max file size for unsplash downloads
Check Content-Length and use io.LimitReader to prevent OOM from
unexpectedly large unsplash responses before buffering into memory.
2026-02-08 15:31:25 +01:00
kolaente
41b511b322 fix(files): seek to start before writing for consistent behavior
Both local and S3 backends now seek to position 0 before writing,
ensuring consistent behavior regardless of the reader's current offset.
2026-02-08 15:31:25 +01:00
kolaente
ab705d7d21 fix(dump): stream files during restore to avoid memory pressure
Use a temporary file instead of io.ReadAll when restoring attachments
from a dump. This prevents loading entire files into memory, which could
cause OOM errors for large attachments during restore.
2026-02-08 15:31:25 +01:00
kolaente
82933a0836 test(files): update tests for io.ReadSeeker API
- Replace custom testfile structs with bytes.NewReader
- Remove readerOnly wrapper and non-seekable reader tests (no longer
  possible at the type level)
- Update S3 unit tests to remove temp file assertions
2026-02-08 15:31:25 +01:00
kolaente
dbd74491c4 fix(files): update all callers to provide seekable readers for S3 uploads
Update all code paths that pass file content to the storage layer to
provide io.ReadSeeker instead of io.Reader:

- Avatar upload: use bytes.NewReader instead of bytes.Buffer
- Background upload handler: use bytes.NewReader instead of bytes.Buffer
- Unsplash background: buffer response body into bytes.NewReader
- Dump restore: buffer zip entry into bytes.NewReader
- Migration structure: pass bytes.NewReader directly instead of wrapping
  in io.NopCloser
- Task attachment: change NewAttachment parameter from io.ReadCloser to
  io.ReadSeeker
2026-02-08 15:31:25 +01:00
kolaente
728a3e4f7b fix(files): require io.ReadSeeker for S3 uploads, remove temp file fallback
The S3 upload path used temp files (vikunja-s3-upload-*) to buffer
non-seekable readers. In Docker containers with restrictive permissions,
these temp files could not be created, causing "permission denied"
errors for avatar and background image uploads.

By changing the file storage API (Create, CreateWithMime,
CreateWithMimeAndSession, Save) to require io.ReadSeeker instead of
io.Reader, the temp file fallback is no longer needed and is removed.
This enforces at the type level that all callers provide seekable
readers, preventing this class of bug from recurring.

Closes go-vikunja/vikunja#2185
2026-02-08 15:31:25 +01:00
kolaente
7fce4694fa refactor(db): extract testable ResolveDatabasePath function (#2193)
Add DatabasePathConfig struct and ResolveDatabasePath function that
takes all dependencies as parameters, making it easier to test path
resolution logic in isolation. Should also fix the reported cases.

Resolves #2189
2026-02-08 10:47:57 +00:00
kolaente
acbf751ba0 feat(doctor): add user namespace detection and improved storage diagnostics (#2180)
This PR adds support for detecting and handling Linux user namespaces (commonly used in rootless Docker containers) and improves error diagnostics when file storage validation fails.

Docs PR: https://github.com/go-vikunja/website/pull/289

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 11:57:35 +01:00
Frederick [Bot]
e6e7b26a6e [skip ci] Updated swagger docs 2026-01-30 14:14:52 +00:00
rhclayto
cf029cef0c feat: add option to send Basic Auth header with webhook requests (#2137)
Resolves https://github.com/go-vikunja/vikunja/issues/2136
Docs PR: https://github.com/go-vikunja/website/pull/284
2026-01-30 15:07:31 +01:00
kolaente
a89b1bed85 feat(doctor): add detailed file diagnostics for local storage (#2179)
When using local file storage, the doctor command now reports:
- Whether the files directory exists
- Directory permissions (octal mode)
- Directory owner and group with uid/gid (Unix)
- Ownership mismatch warning if Vikunja runs as a different user
- Total number of stored files and their combined size

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-30 10:23:39 +00:00
XiangCany
d238385199 fix(files): make sure base directory exists when using local file system (#2166)
Resolves  #2162
2026-01-27 13:11:44 +01:00
kolaente
3aa1e90d7f feat: add vikunja doctor command for diagnostic checks (#2165)
Add a new `vikunja doctor` CLI command that performs diagnostic checks.

Checks performed:

- **System**: Version, Go version, OS/arch, running user, working
directory
- **Configuration**: Config file path, public URL, JWT secret, CORS
origins
- **Database**: Connection test, server version
(SQLite/MySQL/PostgreSQL)
- **Files**: Storage path, writability, disk space (Unix only)
- **Optional services** (when enabled):
  - Redis: Connection ping
  - Typesense: Health endpoint
  - Mailer: SMTP connection
  - LDAP: Bind authentication test
  - OpenID Connect: Discovery endpoint for each configured provider
2026-01-27 09:12:31 +00:00
kolaente
28593e6460 fix: use dark shadows for email template in dark mode (#2155) 2026-01-26 15:46:44 +01:00
kolaente
b21c9acb0d fix(routes): restore SPA routing after Echo v5 upgrade
In Echo v5, the 404 error for unmatched routes implements the
HTTPStatusCoder interface but is not a *HTTPError. This caused
the static middleware to fail to catch 404s and serve index.html
for SPA routes, leading to reloading SPA routes returning 404.

Caused by regression introduced in 9a61453e8.

Fixes #2149
Fixes #2152
2026-01-25 11:07:48 +01:00
renovate[bot]
9a61453e86 fix(deps): update module github.com/labstack/echo/v4 to v5 (#2131)
Closes https://github.com/go-vikunja/vikunja/pull/2133

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kolaente <k@knt.li>
2026-01-24 20:38:32 +01:00
kolaente
4df8da549e fix(auth): scope query binding
Resolves https://github.com/go-vikunja/vikunja/issues/2146
2026-01-24 17:51:35 +01:00
kolaente
731b7c3001 fix: avoid mutating global http.DefaultClient in webhook proxy (#2145)
Fixes a bug where the webhook HTTP client was mutating `http.DefaultClient` (the global singleton), causing ALL HTTP requests in the application to use the webhook proxy. This broke OIDC authentication and other external HTTP calls when webhook proxy was configured.

Fixes #2144
2026-01-24 13:58:47 +01:00
kolaente
0cd25f47e5 fix: populate complete entity data in deletion event webhooks (#2135)
Fixes webhook payloads for deletion events that were previously
containing incomplete or empty entity data. This occurred because
entities were being deleted from the database before the webhook event
was dispatched.

## Changes

This PR implements four targeted fixes to ensure complete entity data in
deletion event webhooks:

### 1. TaskAssignee Deletion (`pkg/models/listeners.go`)
- Extended `reloadEventData()` to fetch full assignee user data by ID
- Webhook payload now includes complete user object (username, email,
timestamps, etc.)

### 2. TaskComment Deletion (`pkg/models/task_comments.go`)
- Modified `Delete()` to call `ReadOne()` before deletion
- Ensures comment text, author, and timestamps are included in webhook
payload
- Follows the same pattern used by `Task.Delete()` and
`TaskAttachment.Delete()`

### 3. TaskAttachment Deletion (`pkg/models/task_attachment.go`)
- Extended `ReadOne()` to fetch the `CreatedBy` user
- Webhook payload now includes file creator information

### 4. TaskRelation Deletion (`pkg/models/task_relation.go`)
- Modified `Delete()` to fetch complete relation including `CreatedBy`
user before deletion
- Webhook payload now includes relation timestamps and creator
information

Fixes #2125
2026-01-24 12:50:18 +01:00
renovate[bot]
843ef7218a fix(deps): update module github.com/fclairamb/afero-s3 to v0.4.0 (#2087) 2026-01-15 16:21:16 +01:00
rhclayto
c5969d9898 feat: add configurable gravatar-compatible base URL (#2083)
This adds the ability to set a base URL for a Gravatar-compatible avatar
service (Gravatar itself, or Libravatar, for instance). The default will
be www.gravatar.com, so nothing will change from current behaviour unless
the user explicitly configures another URL.

Resolves #2082
2026-01-13 14:58:08 +01:00
kolaente
534483f237 fix(filter): ensure year is always within mysql allowed range
Fixes https://github.com/go-vikunja/vikunja/issues/2077
2026-01-09 23:11:22 +01:00
kolaente
e085fcaef2 feat(migration/todoist): migrate from Sync API v9 to API v1 (#2072)
Migrates the Todoist migration module from the deprecated Sync API v9 to the new unified Todoist API v1.
2026-01-09 22:50:27 +01:00
kolaente
8987f0890a fix(mail): disable queue when mailer disabled (#2069)
- Don't create the mail queue when the mailer is disabled to prevent
`SendMail()` from blocking indefinitely
- Add guard clause in `SendMail()` to return early when mailer is
disabled or queue is nil
- Add test to verify notifications don't block when mailer is disabled

This implements the changes from #1080 with the review feedback
addressed (using `package notifications` instead of `package
notifications_test`).


Closes #1080
2026-01-08 15:51:31 +01:00
kolaente
6c7a800bf4 fix: set log path before creating log handler (#2064)
Resolves #2020
2026-01-08 13:53:08 +00:00
kolaente
39b4568bc5 refactor: centralize HTTP error handling (#2062)
This changes the error handling to a centralized HTTP error handler in `pkg/routes/error_handler.go` that converts all error types to proper HTTP responses. This simplifies the overall error handling because http handler now only need to return the error instead of calling HandleHTTPError as previously.
It also removes the duplication between handling errors with and without Sentry.

🐰 Hop along, dear errors, no more wrapping today!
We've centralized handlers in a shiny new way,
From scattered to unified, the code flows so clean,
ValidationHTTPError marshals JSON supreme!
Direct propagation hops forward with glee,
A refactor so grand—what a sight to see! 🎉
2026-01-08 10:02:59 +00:00
kolaente
4f31300915 fix(export): use os-level temp file to create user data export 2026-01-08 10:37:51 +01:00
kolaente
49af08d3f6 feat(filters): add UI for marking saved filters as favorites (#2055)
This PR adds UI support for marking saved filters as favorites. The backend already supports the `is_favorite` field for saved filters, but the frontend didn't expose this functionality. Users can now favorite/unfavorite saved filters just like regular projects.
2026-01-07 16:21:41 +00:00
kolaente
7365de257d feat(files): validate file storage is writable on startup (#2053)
Adds startup validation that checks if the configured file storage is writable. To do this, Vikunja now tries to create a temporary file and clean it up afterwards.
2026-01-06 16:29:56 +01:00
Samuel Rodda
3a47c062da fix(filters): preserve IsFavorite for saved filters in ReadOne (#2031)
- Saved filters' `IsFavorite` field was not being properly returned when
fetched as pseudo-projects via `/projects/{id}`
- This caused favorited filters to appear in both the Favorites and
Filters sections initially, but then disappear from Favorites after
clicking on them (navigating to the filter)

Fixes #1989

Co-authored-by: iamsamuelrodda <iamsamuelrodda@users.noreply.github.com>
2026-01-06 15:17:17 +00:00
kolaente
6f0b685e38 fix: handle mixed-format bucket configurations in migration (#2033)
This change modifies the migration `20251001113831` to flexibly parse bucket configuration filters. 

This fixes this migration issue:

```
json: cannot unmarshal object into Go struct field bucketConfigurationCatchup.filter of type string
```

This occurred when a single `bucket_configuration` JSON array contained
mixed formats - some buckets with old string filters and some with
already-converted object filters.
2026-01-05 22:30:10 +01:00
kolaente
c6fe4c1a6e fix(auth): retry up to three times when an auth provider cannot be reached
Resolves https://github.com/go-vikunja/vikunja/issues/2050
2026-01-05 21:50:40 +01:00
kolaente
a9adc3490d fix(positions): detect and repair duplicate task positions automatically (#1998)
Relates to:
https://community.vikunja.io/t/reordering-not-possible-position-value-the-same-for-different-tasks/4078

Duplicate positions can occur due to race conditions or historical bugs, causing tasks to appear in the wrong order or jump around when the page is refreshed.

This change adds a `repair-task-positions` CLI command to detect and resolve task position conflicts, with dry-run preview option.
Also implemented automatic conflict detection and resolution to ensure
unique task positions.

🐰 Positions once conflicted, clustered tight,
But now we nudge them back into the light!
MinSpacing guards precision from decay,
While conflicts heal and duplicates give way. 
2025-12-20 19:38:28 +01:00
kolaente
82558dba87 fix(filters): ensure saved filter views never have position=0 (#1996)
Fixes #724 - Tasks in saved filter views get `position: 0` when they first appear in the filter, causing drag-and-drop sorting to not persist correctly.

**Changes:**
- Remove harmful `Position: 0` inserts from cron job and
`SavedFilter.Update` - `RecalculateTaskPositions` already creates
positions with proper values, so the intermediate inserts created a race
window
- Add on-demand position creation when fetching tasks for saved filter
views - safety net for newly matching tasks before the cron runs
- Add 5 new tests covering the fix and regression scenarios

🐰 Positions once zero, now bloom with care,
Sorted with grace, no more despair,
When filters call and tasks appear,
Numbers spring up, crystal clear!
Issue 724 hops away—
Sorting's fixed to stay, hooray! 🎉
2025-12-16 22:13:40 +00:00
kolaente
4afd223cd3 fix(files): upload should work with vhost style (#1994)
resolves https://github.com/go-vikunja/vikunja/issues/1905
2025-12-16 20:32:15 +00:00
Frederick [Bot]
3f48837b8c [skip ci] Updated swagger docs 2025-12-15 15:44:47 +00:00