Commit Graph

2033 Commits

Author SHA1 Message Date
kolaente 4c80932b64 fix: block login for StatusAccountLocked users 2026-03-20 11:23:21 +00:00
kolaente 7792bf6cea refactor: use StatusAccountLocked for TOTP lockouts 2026-03-20 11:23:21 +00:00
kolaente f42a045bdc feat: add StatusAccountLocked user status for TOTP lockouts 2026-03-20 11:23:21 +00:00
kolaente ddd9ef5f22 style: fix alignment in config key declarations 2026-03-20 11:08:00 +00:00
kolaente a498dd6991 fix: configure Echo IPExtractor to prevent rate limit bypass via spoofed headers 2026-03-20 11:08:00 +00:00
kolaente 26324a740a feat: add service.ipextractionmethod and service.trustedproxies config options 2026-03-20 11:08:00 +00:00
kolaente 17eccd848f test: add FileStat assertion to validate storage path in attachment test 2026-03-20 10:59:44 +01:00
kolaente 4cd63f93a4 fix: use file mime type instead of hardcoded application/zip in S3 export 2026-03-20 10:59:44 +01:00
kolaente 0e1f44e57e refactor: replace afero with FileStorage interface
Replace the github.com/spf13/afero dependency with a purpose-built
FileStorage interface (Open, Write, Stat, Remove, MkdirAll) with three
implementations: localStorage (with basePath), s3Storage (with key
prefix), and memStorage (for tests).

Each implementation owns its base path — callers pass only file IDs.
Delete s3fs.go, change File.File from afero.File to io.ReadCloser,
and fix duplication flows to buffer content for seeking.
2026-03-20 10:59:44 +01:00
maggch b0ede53c05 fix: handle S3 backend in user export download
http.ServeContent requires io.ReadSeeker which S3 files don't support.
Add S3 branch using io.Copy with explicit headers, matching the pattern
already used in task attachment downloads.

Refs: #2347

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-20 10:59:44 +01:00
maggch b065c62007 feat: replace afero-s3 with minimal S3 afero.Fs implementation
Replace the third-party afero-s3 library (and its temporary fork) with
a minimal in-tree afero.Fs implementation (~200 lines) that only supports
the three S3 operations Vikunja actually uses: Open (GetObject), Remove
(DeleteObject), and Stat (HeadObject).

The s3File implementation lazily opens a GetObject stream on first Read
and supports Seek by closing and re-opening the stream from the new offset,
matching the behavior of the fixed afero-s3 fork.

All other afero.Fs/File methods return ErrS3NotSupported since they are
never called in the S3 code path (writes already use direct PutObject).

This removes the fclairamb/afero-s3 dependency and the temporary replace
directive in go.mod entirely.

Closes #2347

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-20 10:59:44 +01:00
Henry Cole e7f1e99878
fix(caldav): use /dav/projects/ as home to make iOS/MacOS reminders work (#2417)
Resolves issue #475 by modifying CalDAV discovery so Apple Reminders can
use /dav/projects/ as the home set without exposing that synthetic path
as a real task list, preserving the existing principal-based flow. This
is because Apple Reminders defaults back to the /dav/projects/ URL,
rather than accepting the /dav/principals/username/ URL specified in
Vikunja.

Resolves #475
2026-03-20 09:33:56 +00:00
kolaente 629b6d447c test(webhooks): allow non-routable IPs in E2E tests
E2E tests use httptest.NewServer bound to 127.0.0.1, which is now
blocked by default SSRF protection. Opt in to non-routable IPs.
2026-03-19 15:18:06 +01:00
kolaente 8d9bc3e65e feat(webhooks): add built-in SSRF protection using daenney/ssrf
Block webhook requests to non-globally-routable IP addresses by default.
Uses net.Dialer.Control hook to validate resolved IPs against IANA
Special Purpose Registries after DNS resolution, preventing DNS rebinding.

Configurable via webhooks.allownonroutableips (default: false).
2026-03-19 15:18:06 +01:00
kolaente d5dbf04bd0 test(webhooks): add SSRF protection tests 2026-03-19 15:18:06 +01:00
kolaente 37fdd088d6 feat(config): add webhooks.allownonroutableips setting 2026-03-19 15:18:06 +01:00
kolaente 3bc0093686 fix: invalidate all sessions when enabling TOTP
When a user enables two factor authentication, all existing sessions are
now invalidated, requiring re-authentication. This prevents pre-existing
sessions from bypassing 2FA. The frontend now shows a notice explaining
the logout before the user confirms, and properly logs out after enabling.

Ref: GHSA-pgc7-cmvg-mvp4
2026-03-19 12:27:44 +01:00
Frederick [Bot] 369b456d64 [skip ci] Updated swagger docs 2026-03-19 09:26:05 +00:00
Tink d11f097eee
fix(tasks): support both expand and expand[] query parameter formats (#2415)
The `expand` query parameter only supported the `expand[]=foo` array
format, but the swagger docs described it as a plain string parameter.
This adds support for both formats (`expand=foo` and `expand[]=foo`),
matching the existing pattern used by `sort_by` and `order_by`
parameters.

Closes #2408

---------

Co-authored-by: kolaente <k@knt.li>
2026-03-19 09:18:11 +00:00
Tink ada2ebab9e
fix: preserve CalDAV inverse relations when parent has no RELATED-TO (#2389)
- Fixes `removeStaleRelations` in CalDAV storage provider to only remove
relations of kinds explicitly declared in the incoming VTODO's
`RELATED-TO` properties
- When a VTODO has no `RELATED-TO` at all (e.g., a parent task from
Tasks.org), no relations are removed — they were auto-created as
inverses by child tasks
- When a VTODO declares specific relation kinds (e.g.,
`RELATED-TO;RELTYPE=PARENT`), only relations of that kind are checked
for staleness; other kinds (like auto-created `subtask` inverses) are
preserved

Fixes #2383

---------

Co-authored-by: kolaente <k@knt.li>
2026-03-11 09:40:09 +01:00
kolaente e19bea8e3a fix: register bulk label route correctly for API token permissions
The tasks_labels_bulk route was not recognized as a CRUD route by
isStandardCRUDRoute, causing it to be processed as a non-CRUD route
and registered in the wrong apiTokenRoutes group. API tokens with
tasks_labels permissions could not access the bulk endpoint, resulting
in a 401 error.

Fixes https://github.com/go-vikunja/vikunja/issues/2375
2026-03-10 23:58:44 +01:00
kolaente 554593cdb6 test: add failing test for bulk label API token route registration 2026-03-10 23:58:44 +01:00
kolaente 675dfb3ea4 test: add web tests for bulk label task endpoint 2026-03-10 23:58:44 +01:00
kolaente 79a612aa5d fix: send account deletion notification before deleting user row
When deleting a user via CLI (`vikunja user delete <id> -n`), the user
row was deleted first, then `notifications.Notify` was called. But
`Notify` calls `User.ShouldNotify()` which queries the database to check
the user's status — and since the row was already deleted within the same
transaction, it returned `ErrUserDoesNotExist`.

Move the notification call before the `DELETE` so the user row still
exists when `ShouldNotify` checks it.

Closes go-vikunja/vikunja#2335
2026-03-10 23:44:53 +01:00
Frederick [Bot] 30fccfb058 chore(i18n): update translations via Crowdin 2026-03-10 01:08:39 +00:00
kolaente a043940e14 refactor: use config.ResolvePath for all rootpath-relative paths
Replaces ad-hoc path joining in files, config value loading, and
default config values with the centralized ResolvePath function.
2026-03-09 16:02:05 +01:00
kolaente 2a7165aaba refactor: add centralized ResolvePath for rootpath-relative paths 2026-03-09 16:02:05 +01:00
kolaente d3cbc4fc4f fix: prefer working directory for service.rootpath default
When running as a systemd service, the binary is often in /usr/local/bin
but WorkingDirectory points to a data directory like /var/lib/vikunja.
Previously, rootpath defaulted to the binary's directory, causing
Vikunja to attempt creating files/db in /usr/local/bin.

Now os.Getwd() is preferred, which respects systemd's WorkingDirectory
and is the intuitive default for all deployment scenarios.
2026-03-09 16:02:05 +01:00
Frederick [Bot] d5f52868c5 chore(i18n): update translations via Crowdin 2026-03-09 01:14:37 +00:00
Frederick [Bot] 74771ed132 [skip ci] Updated swagger docs 2026-03-08 18:53:26 +00:00
kolaente 05cc65fe9e test: add e2e tests for user-level webhooks
Add comprehensive e2e tests for user-level webhook CRUD operations
and update existing project webhook tests to use LoadFixtures() for
cleanup instead of manual DELETE queries.
2026-03-08 19:45:53 +01:00
kolaente 47a0775c73 feat: add API routes for user-level webhooks
Add CRUD endpoints for user-level webhooks under /user/settings/webhooks.
Users can create webhooks that fire on user-directed events (task.reminder.fired,
task.overdue, tasks.overdue). Includes proper permission checks via
canDoWebhook which loads webhook ownership from DB.
2026-03-08 19:45:53 +01:00
kolaente dbbc80aea6 feat: extend WebhookListener for user-level webhooks
Add User field to reminder and overdue events so the webhook listener
can look up user-level webhooks. Add conditional user filtering to
reminder and overdue cron jobs - when only email is enabled, filter to
email-enabled users; when webhooks are enabled, fetch all users so
events can be dispatched. Dispatch TasksOverdueEvent and
TaskReminderFiredEvent for webhook consumption.
2026-03-08 19:45:53 +01:00
kolaente d4577c660f feat: add user_id to webhooks and user-directed event infrastructure
Add user_id column to webhooks table (nullable, for user-level webhooks
vs project-level). Extend webhook model, permissions, and listener to
support user-level webhooks that fire for user-directed events like
task reminders and overdue task notifications.

Add TasksOverdueEvent for dispatching overdue notifications via webhooks.
Update webhook permissions to handle both user-level and project-level
ownership. Add webhook test fixture and register webhooks table in test
fixture loader.
2026-03-08 19:45:53 +01:00
kolaente aacf650ec2 test: add tests for conversational email system
Add tests for conversational header generation, HTML rendering
with p-tag wrapping, plain-text conversion, footer handling,
and conditional action links. Update mention test fixtures
with Project field.
2026-03-08 16:03:47 +01:00
kolaente def73e2f8e feat: add translation keys for conversational emails
Add action strings with doer names for comments, mentions,
assignments, and reminders. Add notification settings footer
and task identifier format strings.
2026-03-08 16:03:47 +01:00
kolaente b3572c5932 feat: convert notifications to conversational email style
Convert task comment, mention, assignment, and reminder notifications
to use the conversational email format. Add Project field to notification
structs, include task identifiers in subjects and headers, add doer
avatars, notification settings links in footers, and From headers.
2026-03-08 16:03:47 +01:00
kolaente d4b03026f0 feat: add conversational email template and rendering
Add conversational email style with GitHub-inspired header design.
Includes mail struct extensions (headerLine, conversational flag),
CreateConversationalHeader helper, HTML template with avatar support,
p-tag wrapping for content lines, plain-text stripping, and
conditional action link rendering.
2026-03-08 16:03:47 +01:00
Casper Børgesen b5afe82713
fix(cli): make user deletion confirmation check Windows compatible (#2339)
The current implementation of the user confirmation when deleting a user
using the CLI seems to favour Linux. On Windows the <ENTER> command adds
"\r\n" to the user input while Linux only adds "\n".

I have added an extra check to the confirmation, but GO is a new
language for me, so there is probably a much better way to do this.

---------

Co-authored-by: kolaente <k@knt.li>
2026-03-05 15:19:08 +01:00
kolaente d36ac9ddda test: fix ParadeDB project search count to 27
The recursive CTE pulls in child projects of matched parents,
resulting in 27 total results, not 12.
2026-03-05 13:57:05 +01:00
kolaente df0e3a84a9 test: fix non-ParadeDB project search count assertion
ILIKE '%Test1%' matches Test1, Test10, Test11, Test19 + favorites = 5,
not 2. Also use 'Test2"' pattern to avoid matching Test20/Test21.
2026-03-05 13:57:05 +01:00
kolaente c7c63e8ead test: add result count assertions for ParadeDB search tests
Address review feedback: assert exact result counts when ParadeDB is
active. fuzzy(1, prefix=true) broadens matches via edit distance,
returning 6 projects for "TEST10", 14 tasks for "number #17", and
12 projects for "Test1".
2026-03-05 13:57:05 +01:00
kolaente b69705e64b test: fix lint and adjust project search test for ParadeDB fuzzy matching
- Use require.NotEmpty instead of require.Greater for testifylint
- Skip exclusion assertions in web project search test when ParadeDB is
  active, since fuzzy(1, prefix=true) on "Test1" also matches Test2, Test3
2026-03-05 13:57:05 +01:00
kolaente 6268c48f15 test: adjust ParadeDB search tests for fuzzy prefix match broadening
ParadeDB fuzzy(1, prefix=true) returns more results than ILIKE due to
edit-distance tolerance on tokenized terms. Adjust assertions to check
containment rather than exact result sets when ParadeDB is active.
2026-03-05 13:57:05 +01:00
kolaente 3568aaacee test: add task #48 to expected results in feature tests
The new fixture task #48 (Landingpages update, project 1) needs to
appear in all feature test expected result sets that list project 1
tasks. Also bumps the expected next index in TestTask_Create.
2026-03-05 13:57:05 +01:00
kolaente e6cbd67ab5 test: call real MultiFieldSearch function and branch on db engine
Instead of manually constructing builder.Expr conditions to simulate
the ParadeDB path, call MultiFieldSearchWithTableAlias() directly and
use isParadeDB() to branch assertions. When ParadeDB is available,
assert the ||| operator with ::pdb.fuzzy(1, t) syntax; otherwise
assert the LIKE/ILIKE fallback. Tests skip when no database engine
is initialized (x == nil).
2026-03-05 13:57:05 +01:00
kolaente 0a38ec0838 fix: use ParadeDB v2 fuzzy prefix matching for search (#2346)
Switch from legacy @@@ paradedb.match() to v2 ||| operator with
::pdb.fuzzy(1, t) cast. This enables prefix matching so 'landing'
matches 'landingpages', and adds single-character typo tolerance.
2026-03-05 13:57:05 +01:00
kolaente ee2723d9cf test: rewrite MultiFieldSearch tests with SQL output verification 2026-03-05 13:57:05 +01:00
kolaente 892b38b3b6 test: add web tests for prefix/substring search (#2346) 2026-03-05 13:57:05 +01:00
kolaente 275f714224 test: add fixture task with compound word for prefix search testing 2026-03-05 13:57:05 +01:00
kolaente 1f3509bf27 test: add e2e API test package with webhook pipeline verification
New pkg/e2etests/ package runs with the real Watermill event system to
verify the full async pipeline: web handler -> DB -> event dispatch ->
Watermill -> listener -> side effect.

First test (TestTaskUpdateWebhookE2E) updates a task and asserts that
a webhook HTTP POST arrives at a test server.
2026-03-05 12:49:27 +01:00
kolaente 1b1e8e5b19 feat: add InitEventsForTesting and Unfake for real event dispatch in tests
InitEventsForTesting sets up a real Watermill GoChannel router with a
cancellable context, returning a readiness channel. Skips Prometheus
metrics and poison queue to avoid duplicate registration panics.

Unfake re-enables real event dispatch after test init helpers call Fake().

Also guards Dispatch against nil pubsub with a clear error message.
2026-03-05 12:49:27 +01:00
kolaente f9cb0a2de1 fix: include remote IP address in HTTP request logs 2026-03-04 23:47:40 +01:00
kolaente 7288483879 fix: handle deleted user in saved filter view event listener
Use a separate error variable for the user lookup in
UpdateTaskInSavedFilterViews so that ErrUserDoesNotExist does not
pollute the named return `err`. The `:=` inside the for loop shadowed
the outer `err`, leaving it set to the stale user-not-found error,
which caused the handler to be poisoned.

Closes #2359
2026-03-04 22:05:01 +01:00
Frederick [Bot] ff70fa1369 [skip ci] Updated swagger docs 2026-03-04 19:40:40 +00:00
kolaente b5086febc7 docs: update user search endpoint description for external team bypass 2026-03-04 20:32:11 +01:00
kolaente 54c7c4aef2 refactor: move ListUsers tests from pkg/user to pkg/models
The ListUsers function now references team_members and teams tables
via a subquery for external team discoverability. The pkg/user test
environment only syncs user tables, so these tests need to run in
pkg/models which has the full schema and all fixtures.

Also adds new tests for the external team discoverability bypass
directly in the models package alongside the moved tests.
2026-03-04 20:32:11 +01:00
kolaente 06617891fa test: verify email masking for external team name search 2026-03-04 20:32:11 +01:00
kolaente 28b913f29f feat: bypass discoverability settings for external team members 2026-03-04 20:32:11 +01:00
kolaente 3a730165bc test: add tests for external team user discoverability bypass 2026-03-04 20:32:11 +01:00
kolaente 64e455a613 test: add user 11 to external team 14 for discoverability tests 2026-03-04 20:32:11 +01:00
Weijie Zhao 54d977532e
fix: allow browser caching for file downloads (#2349) 2026-03-04 17:43:03 +01:00
Frederick [Bot] 740ec183d9 [skip ci] Updated swagger docs 2026-03-04 16:29:06 +00:00
kolaente 9c23e19644 fix: preserve cover image when duplicating task
Track old-to-new attachment ID mapping during duplication and
re-set CoverImageAttachmentID on the new task if the original
had a cover image configured.
2026-03-04 17:20:26 +01:00
kolaente 7aad96b199 fix: close source file handle when duplicating attachments
Save the source file handle before calling NewAttachment (which
overwrites attachment.File) and use defer to ensure it gets closed.
This prevents file descriptor leaks on both success and error paths.
2026-03-04 17:20:26 +01:00
kolaente 692357a648 refactor: use TaskRelation.Create for copy relation
Use the TaskRelation Create method instead of raw inserts. This
handles the bidirectional relation automatically and dispatches
the appropriate event.
2026-03-04 17:20:26 +01:00
kolaente e07eeed211 refactor: batch label inserts during task duplication
Collect all LabelTask records in the loop and insert them in a single
database call instead of inserting one at a time.
2026-03-04 17:20:26 +01:00
kolaente 6da0f68562 fix: remove debug log statements from task duplicate
Remove all log.Debugf calls from the Create method as they were
leftover development aids and should not be in production code.
2026-03-04 17:20:26 +01:00
kolaente 4d494ba442 test: add web integration tests for task duplication 2026-03-04 17:20:26 +01:00
kolaente 77fdf1b84b feat: register task duplicate API route 2026-03-04 17:20:26 +01:00
kolaente d8f3a96b06 feat: add task duplicate backend model and tests 2026-03-04 17:20:26 +01:00
kolaente cd7d40583a fix: only dump Vikunja-owned tables
Same fix as WipeEverything - use the registered table list instead
of x.DBMetas() to avoid including PostgreSQL extension tables in
database dumps.
2026-03-04 15:37:54 +01:00
kolaente 14e2c95a83 fix: only drop Vikunja-owned tables in WipeEverything
Previously WipeEverything used x.DBMetas() which returns all tables
in the database, including those owned by PostgreSQL extensions like
PostGIS. This caused restore to fail with 'cannot drop table
spatial_ref_sys because extension postgis requires it'.

Now uses the registered table list instead.

Fixes go-vikunja/vikunja#2219
2026-03-04 15:37:54 +01:00
kolaente 0a8534ded9 feat: add RegisteredTableNames helper to db package 2026-03-04 15:37:54 +01:00
kolaente 3dd2ba4aa4 feat: register Vikunja tables with db package at init 2026-03-04 15:37:54 +01:00
kolaente d26936f869 feat: add table registration to db package
Part of the fix for dump/restore failing when PostgreSQL extensions
add extra tables (e.g. PostGIS spatial_ref_sys).
2026-03-04 15:37:54 +01:00
kolaente 18f16878a8 fix: prevent nil pointer panic in mention notification listeners
When a task bucket is updated without changing buckets (early return in
updateTaskBucket), b.Task remains nil. The TaskUpdatedEvent was then
dispatched with a nil Task, causing a nil pointer dereference in
HandleTaskUpdatedMentions when accessing event.Task.Description.

This adds:
- A nil guard in TaskBucket.Update to skip event dispatch when b.Task is nil
- Nil checks in HandleTaskUpdatedMentions, HandleTaskCreateMentions,
  HandleTaskCommentEditMentions, and UpdateTaskInSavedFilterViews
- Tests verifying the handlers gracefully handle nil task events

Closes #2351
2026-03-04 10:29:16 +01:00
Tink 40bcf2b36f
fix: validate default settings timezone on startup (#2345) 2026-03-03 12:16:37 +00:00
kolaente f516bbe560 test: update event assertions to work with deferred dispatch
Tests that call model methods directly now call events.DispatchPending
before asserting event dispatch.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente 6ed684d708 fix(events): dispatch pending events in migration and export handlers
Refs #2315
2026-03-03 12:46:34 +01:00
kolaente 924eef58d1 fix(events): dispatch pending events in CalDAV handlers after commit
CalDAV handlers manage their own database sessions. Now that model
methods use DispatchOnCommit, the CalDAV handlers must call
DispatchPending after commit and CleanupPending on rollback.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente 1f363dbd43 fix(events): defer event dispatch for user creation and task positions
Refs #2315
2026-03-03 12:46:34 +01:00
kolaente 8afbdf2deb fix(events): defer event dispatch for team operations
Convert events.Dispatch to events.DispatchOnCommit in team and team
member CRUD. Also removes premature s.Commit() from TeamMember.Delete
since the handler manages the transaction lifecycle.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente dd7f7de518 fix(events): defer event dispatch for project operations
Convert events.Dispatch to events.DispatchOnCommit in project CRUD,
project-user sharing, and project-team sharing.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente fe459c9297 fix(events): defer event dispatch for task sub-entities
Convert events.Dispatch to events.DispatchOnCommit in task assignees,
comments, attachments, relations, and kanban bucket operations.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente 3eb289262f fix(events): defer task event dispatch until after transaction commit
Convert events.Dispatch to events.DispatchOnCommit in Task.Create,
updateSingleTask, Task.Delete, and triggerTaskUpdatedEventForTaskID.
Events are now dispatched by the handler after s.Commit(), ensuring
webhook listeners see committed data.

Fixes #2315
2026-03-03 12:46:34 +01:00
kolaente 217a481162 feat(handlers): dispatch pending events after transaction commit
All generic CRUD handlers now call events.DispatchPending(s) after
s.Commit() and events.CleanupPending(s) on rollback paths. This is
preparation for switching model methods from events.Dispatch to
events.DispatchOnCommit.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente 564573bdd5 feat(events): add DispatchOnCommit/DispatchPending for deferred event dispatch
Events dispatched inside model methods run before the transaction commits,
causing listeners (especially webhooks) that open new sessions to read
stale data. These new functions allow accumulating events during a
transaction and dispatching them only after commit.

Refs #2315
2026-03-03 12:46:34 +01:00
Dominik Pschenitschni d94429d33c fix(caldav): parse timestamps in configured timezone 2026-03-03 12:18:48 +01:00
kolaente 51f789bf5c fix(e2e): drain event handlers and stop browser between tests
Async event handlers (via Watermill) from the previous test can hold
SQLite connections, starving the next test's fixture setup PATCH request.

Three changes fix this:

1. Track in-flight event handler goroutines with a WaitGroup.
2. Call WaitForPendingHandlers() in the test endpoint before
   truncating/inserting data.
3. Navigate the browser to about:blank in fixture teardown to stop
   notification polling and other frontend requests between tests.
2026-03-03 10:41:19 +01:00
kolaente 39acdac531 fix(caldav): eliminate nested db session in CalDAV auth
checkUserCaldavTokens called user.GetCaldavTokens which creates its own
db.NewSession(), while the caller (BasicAuth) already holds an open
session. With SQLite this caused a deadlock because the second session
blocks on the write lock held by the first session in the same goroutine.

Add GetCaldavTokensWithSession that accepts an existing session and use
it from checkUserCaldavTokens.
2026-03-03 10:41:19 +01:00
kolaente 530973c475 fix(auth): make SameSite=None conditional on HTTPS for refresh cookie
SameSite=None requires Secure=true per browser spec. When running over
plain HTTP (local dev, e2e tests), browsers reject or downgrade the
cookie, breaking session refresh. Fall back to SameSite=Lax for HTTP
while keeping SameSite=None for HTTPS (needed for the Electron desktop
app cross-origin scenario).
2026-03-03 10:41:19 +01:00
kolaente 98f2893ffe fix(db): use WAL mode for SQLite and temp file for ephemeral databases
Three SQLite connection issues are fixed:

1. The refactoring in 26c0f71 accidentally dropped _busy_timeout from
   the file-based SQLite connection string. Without it, concurrent
   transactions get instant SQLITE_BUSY errors instead of waiting.

2. _txlock=immediate forced ALL transactions (including reads) to
   acquire the write lock at BEGIN, serializing all database access.
   WAL mode makes this unnecessary: readers use snapshots and never
   block writers, so the SHARED-to-RESERVED deadlock cannot occur.

3. In-memory shared cache (file::memory:?cache=shared) uses table-level
   locking where _busy_timeout is ineffective (returns SQLITE_LOCKED,
   not SQLITE_BUSY) and concurrent connections deadlock. Replace with a
   temp file using WAL mode for proper concurrency.
2026-03-03 10:41:19 +01:00
kolaente 26c0f71b6c
fix(db): use immediate txlock for SQLite instead of MaxOpenConns(1)
MaxOpenConns(1) caused Go-level deadlocks: when two goroutines needed
database connections concurrently, the second blocked forever waiting
for the single connection pool slot. This broke CI (sqlite web tests
timed out after 45min, e2e tests hung).

The actual "database is locked" errors were caused by SQLite's default
deferred transaction locking: two connections both acquire SHARED locks,
then deadlock when both try to promote to RESERVED for writing. SQLite
detects this instantly and returns SQLITE_BUSY, bypassing busy_timeout.

_txlock=immediate fixes this by acquiring the write lock at BEGIN time.
The second concurrent transaction waits (up to busy_timeout) instead of
deadlocking. Combined with WAL mode (concurrent readers + single writer),
this handles concurrency correctly without restricting the Go connection
pool.
2026-03-02 14:03:33 +01:00
kolaente 28f98a7a96
fix(auth): use SameSite=None for refresh token cookie to fix desktop app
SameSite=Strict prevents the browser from sending the HttpOnly refresh
token cookie in cross-origin contexts like the Electron desktop app,
where the page runs on localhost but the API is on a remote host. This
caused sessions to expire quickly because refresh requests never
included the cookie.

SameSite=None allows cross-origin sending while HttpOnly still prevents
JavaScript from reading the cookie value (XSS protection).

Resolves #2309
2026-03-02 13:54:10 +01:00
kolaente 79dbb40985
fix(db): prevent SQLite "database is locked" errors under concurrent writes
Configure SQLite connections with WAL journal mode, a 5-second busy
timeout, shared cache, and a max of 1 open connection. SQLite only
supports a single writer at a time, so without these settings concurrent
API requests (e.g. bulk task creation) would immediately fail with
"database is locked" instead of waiting and retrying.
2026-03-02 12:37:55 +01:00
Weijie Zhao 3ca4913fcb
fix: use MinPositionSpacing threshold in calculateNewPositionForTask (#2320)
calculateNewPositionForTask only checked for lowestPosition == 0 before
triggering a full position recalculation. Extremely small position
values (e.g. 3.16e-285) passed this check, causing new tasks to get
meaningless positions via the index * 2^16 fallback, breaking sort
order.

Use the same < MinPositionSpacing threshold that
createPositionsForTasksInView and the position update handler already
use.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-02 09:14:38 +01:00
kolaente 23d84e7811
fix(views): assign default position when creating new project views
When creating a new view without specifying a position, it defaulted to
0, causing it to always sort before all other views. Apply
calculateDefaultPosition to assign a unique position based on the view
ID, consistent with how projects, tasks, and buckets handle this.

Fixes go-vikunja/vikunja#2319
2026-03-02 08:35:35 +01:00
kolaente a7e4a4f4af
fix(migration): support space-separated date format in TickTick importer
Fixes https://github.com/go-vikunja/vikunja/issues/2324
2026-03-02 08:35:35 +01:00
kolaente 412215ee2f fix(auth): correctly delete older password reset tokens in cron 2026-02-27 14:44:26 +01:00